##// END OF EJS Templates
Adding keyboard shortcuts.
Brian E. Granger -
Show More
@@ -1,835 +1,898
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Notebook
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var Notebook = function (selector) {
17 17 this.element = $(selector);
18 18 this.element.scroll();
19 19 this.element.data("notebook", this);
20 20 this.next_prompt_number = 1;
21 21 this.kernel = null;
22 22 this.dirty = false;
23 23 this.msg_cell_map = {};
24 24 this.metadata = {};
25 this.control_key_active = false;
25 26 this.style();
26 27 this.create_elements();
27 28 this.bind_events();
28 29 };
29 30
30 31
31 32 Notebook.prototype.style = function () {
32 33 $('div#notebook').addClass('border-box-sizing');
33 34 };
34 35
35 36
36 37 Notebook.prototype.create_elements = function () {
37 38 // We add this end_space div to the end of the notebook div to:
38 39 // i) provide a margin between the last cell and the end of the notebook
39 40 // ii) to prevent the div from scrolling up when the last cell is being
40 41 // edited, but is too low on the page, which browsers will do automatically.
41 42 var that = this;
42 43 var end_space = $('<div class="end_space"></div>').height(150);
43 44 end_space.dblclick(function (e) {
44 45 var ncells = that.ncells();
45 46 that.insert_code_cell_after(ncells-1);
46 47 });
47 48 this.element.append(end_space);
48 49 $('div#notebook').addClass('border-box-sizing');
49 50 };
50 51
51 52
52 53 Notebook.prototype.bind_events = function () {
53 54 var that = this;
54 55 $(document).keydown(function (event) {
55 56 // console.log(event);
56 57 if (event.which === 38) {
57 58 var cell = that.selected_cell();
58 59 if (cell.at_top()) {
59 60 event.preventDefault();
60 61 that.select_prev();
61 62 };
62 63 } else if (event.which === 40) {
63 64 var cell = that.selected_cell();
64 65 if (cell.at_bottom()) {
65 66 event.preventDefault();
66 67 that.select_next();
67 68 };
68 69 } else if (event.which === 13 && event.shiftKey) {
69 70 that.execute_selected_cell();
70 71 return false;
71 72 } else if (event.which === 13 && event.ctrlKey) {
72 73 that.execute_selected_cell({terminal:true});
73 74 return false;
75 } else if (event.which === 77 && event.ctrlKey) {
76 console.log("Activating control key")
77 that.control_key_active = true;
78 return false;
79 } else if (event.which === 68 && that.control_key_active) {
80 // Delete selected cell = d
81 that.delete_cell();
82 that.control_key_active = false;
83 return false;
84 } else if (event.which === 65 && that.control_key_active) {
85 // Insert code cell after selected = a
86 that.insert_code_cell_after();
87 that.control_key_active = false;
88 return false;
89 } else if (event.which === 66 && that.control_key_active) {
90 // Insert code cell before selected = a
91 that.insert_code_cell_before();
92 that.control_key_active = false;
93 return false;
94 } else if (event.which === 67 && that.control_key_active) {
95 // To code = c
96 that.to_code();
97 that.control_key_active = false;
98 return false;
99 } else if (event.which === 77 && that.control_key_active) {
100 // To markdown = m
101 that.to_markdown();
102 that.control_key_active = false;
103 return false;
104 } else if (event.which === 84 && that.control_key_active) {
105 // Toggle output = t
106 that.toggle_output();
107 that.control_key_active = false;
108 return false;
109 } else if (event.which === 83 && that.control_key_active) {
110 // Save notebook = s
111 IPython.save_widget.save_notebook();
112 that.control_key_active = false;
113 return false;
114 } else if (event.which === 74 && that.control_key_active) {
115 // Move cell down = j
116 that.move_cell_down();
117 that.control_key_active = false;
118 return false;
119 } else if (event.which === 75 && that.control_key_active) {
120 // Move cell up = k
121 that.move_cell_up();
122 that.control_key_active = false;
123 return false;
124 } else if (event.which === 38 && that.control_key_active) {
125 // Select previous = up arrow
126 that.select_prev();
127 that.control_key_active = false;
128 return false;
129 } else if (event.which === 40 && that.control_key_active) {
130 // Select next = down arrow
131 that.select_next();
132 that.control_key_active = false;
133 return false;
134 } else if (that.control_key_active) {
135 that.control_key_active = false;
136 return true;
74 137 };
75 138 });
76 139
77 140 this.element.bind('collapse_pager', function () {
78 141 var app_height = $('div#main_app').height(); // content height
79 142 var splitter_height = $('div#pager_splitter').outerHeight(true);
80 143 var new_height = app_height - splitter_height;
81 144 that.element.animate({height : new_height + 'px'}, 'fast');
82 145 });
83 146
84 147 this.element.bind('expand_pager', function () {
85 148 var app_height = $('div#main_app').height(); // content height
86 149 var splitter_height = $('div#pager_splitter').outerHeight(true);
87 150 var pager_height = $('div#pager').outerHeight(true);
88 151 var new_height = app_height - pager_height - splitter_height;
89 152 that.element.animate({height : new_height + 'px'}, 'fast');
90 153 });
91 154
92 155 this.element.bind('collapse_left_panel', function () {
93 156 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
94 157 var new_margin = splitter_width;
95 158 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
96 159 });
97 160
98 161 this.element.bind('expand_left_panel', function () {
99 162 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
100 163 var left_panel_width = IPython.left_panel.width;
101 164 var new_margin = splitter_width + left_panel_width;
102 165 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
103 166 });
104 167
105 168 $(window).bind('beforeunload', function () {
106 169 var kill_kernel = $('#kill_kernel').prop('checked');
107 170 if (kill_kernel) {
108 171 that.kernel.kill();
109 172 }
110 173 if (that.dirty) {
111 174 return "You have unsaved changes that will be lost if you leave this page.";
112 175 };
113 176 });
114 177 };
115 178
116 179
117 180 Notebook.prototype.scroll_to_bottom = function () {
118 181 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
119 182 };
120 183
121 184
122 185 Notebook.prototype.scroll_to_top = function () {
123 186 this.element.animate({scrollTop:0}, 0);
124 187 };
125 188
126 189
127 190 // Cell indexing, retrieval, etc.
128 191
129 192
130 193 Notebook.prototype.cell_elements = function () {
131 194 return this.element.children("div.cell");
132 195 }
133 196
134 197
135 198 Notebook.prototype.ncells = function (cell) {
136 199 return this.cell_elements().length;
137 200 }
138 201
139 202
140 203 // TODO: we are often calling cells as cells()[i], which we should optimize
141 204 // to cells(i) or a new method.
142 205 Notebook.prototype.cells = function () {
143 206 return this.cell_elements().toArray().map(function (e) {
144 207 return $(e).data("cell");
145 208 });
146 209 }
147 210
148 211
149 212 Notebook.prototype.find_cell_index = function (cell) {
150 213 var result = null;
151 214 this.cell_elements().filter(function (index) {
152 215 if ($(this).data("cell") === cell) {
153 216 result = index;
154 217 };
155 218 });
156 219 return result;
157 220 };
158 221
159 222
160 223 Notebook.prototype.index_or_selected = function (index) {
161 224 return index || this.selected_index() || 0;
162 225 }
163 226
164 227
165 228 Notebook.prototype.select = function (index) {
166 229 if (index !== undefined && index >= 0 && index < this.ncells()) {
167 230 if (this.selected_index() !== null) {
168 231 this.selected_cell().unselect();
169 232 };
170 233 this.cells()[index].select();
171 234 };
172 235 return this;
173 236 };
174 237
175 238
176 239 Notebook.prototype.select_next = function () {
177 240 var index = this.selected_index();
178 241 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
179 242 this.select(index+1);
180 243 };
181 244 return this;
182 245 };
183 246
184 247
185 248 Notebook.prototype.select_prev = function () {
186 249 var index = this.selected_index();
187 250 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
188 251 this.select(index-1);
189 252 };
190 253 return this;
191 254 };
192 255
193 256
194 257 Notebook.prototype.selected_index = function () {
195 258 var result = null;
196 259 this.cell_elements().filter(function (index) {
197 260 if ($(this).data("cell").selected === true) {
198 261 result = index;
199 262 };
200 263 });
201 264 return result;
202 265 };
203 266
204 267
205 268 Notebook.prototype.cell_for_msg = function (msg_id) {
206 269 var cell_id = this.msg_cell_map[msg_id];
207 270 var result = null;
208 271 this.cell_elements().filter(function (index) {
209 272 cell = $(this).data("cell");
210 273 if (cell.cell_id === cell_id) {
211 274 result = cell;
212 275 };
213 276 });
214 277 return result;
215 278 };
216 279
217 280
218 281 Notebook.prototype.selected_cell = function () {
219 282 return this.cell_elements().eq(this.selected_index()).data("cell");
220 283 }
221 284
222 285
223 286 // Cell insertion, deletion and moving.
224 287
225 288
226 289 Notebook.prototype.delete_cell = function (index) {
227 290 var i = index || this.selected_index();
228 291 if (i !== null && i >= 0 && i < this.ncells()) {
229 292 this.cell_elements().eq(i).remove();
230 293 if (i === (this.ncells())) {
231 294 this.select(i-1);
232 295 } else {
233 296 this.select(i);
234 297 };
235 298 };
236 299 this.dirty = true;
237 300 return this;
238 301 };
239 302
240 303
241 304 Notebook.prototype.append_cell = function (cell) {
242 305 this.element.find('div.end_space').before(cell.element);
243 306 this.dirty = true;
244 307 return this;
245 308 };
246 309
247 310
248 311 Notebook.prototype.insert_cell_after = function (cell, index) {
249 312 var ncells = this.ncells();
250 313 if (ncells === 0) {
251 314 this.append_cell(cell);
252 315 return this;
253 316 };
254 317 if (index >= 0 && index < ncells) {
255 318 this.cell_elements().eq(index).after(cell.element);
256 319 };
257 320 this.dirty = true;
258 321 return this
259 322 };
260 323
261 324
262 325 Notebook.prototype.insert_cell_before = function (cell, index) {
263 326 var ncells = this.ncells();
264 327 if (ncells === 0) {
265 328 this.append_cell(cell);
266 329 return this;
267 330 };
268 331 if (index >= 0 && index < ncells) {
269 332 this.cell_elements().eq(index).before(cell.element);
270 333 };
271 334 this.dirty = true;
272 335 return this;
273 336 };
274 337
275 338
276 339 Notebook.prototype.move_cell_up = function (index) {
277 340 var i = index || this.selected_index();
278 341 if (i !== null && i < this.ncells() && i > 0) {
279 342 var pivot = this.cell_elements().eq(i-1);
280 343 var tomove = this.cell_elements().eq(i);
281 344 if (pivot !== null && tomove !== null) {
282 345 tomove.detach();
283 346 pivot.before(tomove);
284 347 this.select(i-1);
285 348 };
286 349 };
287 350 this.dirty = true;
288 351 return this;
289 352 }
290 353
291 354
292 355 Notebook.prototype.move_cell_down = function (index) {
293 356 var i = index || this.selected_index();
294 357 if (i !== null && i < (this.ncells()-1) && i >= 0) {
295 358 var pivot = this.cell_elements().eq(i+1)
296 359 var tomove = this.cell_elements().eq(i)
297 360 if (pivot !== null && tomove !== null) {
298 361 tomove.detach();
299 362 pivot.after(tomove);
300 363 this.select(i+1);
301 364 };
302 365 };
303 366 this.dirty = true;
304 367 return this;
305 368 }
306 369
307 370
308 371 Notebook.prototype.sort_cells = function () {
309 372 var ncells = this.ncells();
310 373 var sindex = this.selected_index();
311 374 var swapped;
312 375 do {
313 376 swapped = false
314 377 for (var i=1; i<ncells; i++) {
315 378 current = this.cell_elements().eq(i).data("cell");
316 379 previous = this.cell_elements().eq(i-1).data("cell");
317 380 if (previous.input_prompt_number > current.input_prompt_number) {
318 381 this.move_cell_up(i);
319 382 swapped = true;
320 383 };
321 384 };
322 385 } while (swapped);
323 386 this.select(sindex);
324 387 return this;
325 388 };
326 389
327 390
328 391 Notebook.prototype.insert_code_cell_before = function (index) {
329 392 // TODO: Bounds check for i
330 393 var i = this.index_or_selected(index);
331 394 var cell = new IPython.CodeCell(this);
332 395 cell.set_input_prompt();
333 396 this.insert_cell_before(cell, i);
334 397 this.select(this.find_cell_index(cell));
335 398 return cell;
336 399 }
337 400
338 401
339 402 Notebook.prototype.insert_code_cell_after = function (index) {
340 403 // TODO: Bounds check for i
341 404 var i = this.index_or_selected(index);
342 405 var cell = new IPython.CodeCell(this);
343 406 cell.set_input_prompt();
344 407 this.insert_cell_after(cell, i);
345 408 this.select(this.find_cell_index(cell));
346 409 return cell;
347 410 }
348 411
349 412
350 413 Notebook.prototype.insert_html_cell_before = function (index) {
351 414 // TODO: Bounds check for i
352 415 var i = this.index_or_selected(index);
353 416 var cell = new IPython.HTMLCell(this);
354 417 cell.config_mathjax();
355 418 this.insert_cell_before(cell, i);
356 419 this.select(this.find_cell_index(cell));
357 420 return cell;
358 421 }
359 422
360 423
361 424 Notebook.prototype.insert_html_cell_after = function (index) {
362 425 // TODO: Bounds check for i
363 426 var i = this.index_or_selected(index);
364 427 var cell = new IPython.HTMLCell(this);
365 428 cell.config_mathjax();
366 429 this.insert_cell_after(cell, i);
367 430 this.select(this.find_cell_index(cell));
368 431 return cell;
369 432 }
370 433
371 434
372 435 Notebook.prototype.insert_markdown_cell_before = function (index) {
373 436 // TODO: Bounds check for i
374 437 var i = this.index_or_selected(index);
375 438 var cell = new IPython.MarkdownCell(this);
376 439 cell.config_mathjax();
377 440 this.insert_cell_before(cell, i);
378 441 this.select(this.find_cell_index(cell));
379 442 return cell;
380 443 }
381 444
382 445
383 446 Notebook.prototype.insert_markdown_cell_after = function (index) {
384 447 // TODO: Bounds check for i
385 448 var i = this.index_or_selected(index);
386 449 var cell = new IPython.MarkdownCell(this);
387 450 cell.config_mathjax();
388 451 this.insert_cell_after(cell, i);
389 452 this.select(this.find_cell_index(cell));
390 453 return cell;
391 454 }
392 455
393 456
394 457 Notebook.prototype.to_code = function (index) {
395 458 // TODO: Bounds check for i
396 459 var i = this.index_or_selected(index);
397 460 var source_element = this.cell_elements().eq(i);
398 461 var source_cell = source_element.data("cell");
399 462 if (source_cell instanceof IPython.HTMLCell ||
400 463 source_cell instanceof IPython.MarkdownCell) {
401 464 this.insert_code_cell_after(i);
402 465 var target_cell = this.cells()[i+1];
403 466 target_cell.set_code(source_cell.get_source());
404 467 source_element.remove();
405 468 target_cell.select();
406 469 };
407 470 this.dirty = true;
408 471 };
409 472
410 473
411 474 Notebook.prototype.to_markdown = function (index) {
412 475 // TODO: Bounds check for i
413 476 var i = this.index_or_selected(index);
414 477 var source_element = this.cell_elements().eq(i);
415 478 var source_cell = source_element.data("cell");
416 479 var target_cell = null;
417 480 if (source_cell instanceof IPython.CodeCell) {
418 481 this.insert_markdown_cell_after(i);
419 482 var target_cell = this.cells()[i+1];
420 483 var text = source_cell.get_code();
421 484 } else if (source_cell instanceof IPython.HTMLCell) {
422 485 this.insert_markdown_cell_after(i);
423 486 var target_cell = this.cells()[i+1];
424 487 var text = source_cell.get_source();
425 488 if (text === source_cell.placeholder) {
426 489 text = target_cell.placeholder;
427 490 }
428 491 }
429 492 if (target_cell !== null) {
430 493 if (text === "") {text = target_cell.placeholder;};
431 494 target_cell.set_source(text);
432 495 source_element.remove();
433 496 target_cell.edit();
434 497 }
435 498 this.dirty = true;
436 499 };
437 500
438 501
439 502 Notebook.prototype.to_html = function (index) {
440 503 // TODO: Bounds check for i
441 504 var i = this.index_or_selected(index);
442 505 var source_element = this.cell_elements().eq(i);
443 506 var source_cell = source_element.data("cell");
444 507 var target_cell = null;
445 508 if (source_cell instanceof IPython.CodeCell) {
446 509 this.insert_html_cell_after(i);
447 510 var target_cell = this.cells()[i+1];
448 511 var text = source_cell.get_code();
449 512 } else if (source_cell instanceof IPython.MarkdownCell) {
450 513 this.insert_html_cell_after(i);
451 514 var target_cell = this.cells()[i+1];
452 515 var text = source_cell.get_source();
453 516 if (text === source_cell.placeholder) {
454 517 text = target_cell.placeholder;
455 518 }
456 519 }
457 520 if (target_cell !== null) {
458 521 if (text === "") {text = target_cell.placeholder;};
459 522 target_cell.set_source(text);
460 523 source_element.remove();
461 524 target_cell.edit();
462 525 }
463 526 this.dirty = true;
464 527 };
465 528
466 529
467 530 // Cell collapsing and output clearing
468 531
469 532 Notebook.prototype.collapse = function (index) {
470 533 var i = this.index_or_selected(index);
471 534 this.cells()[i].collapse();
472 535 this.dirty = true;
473 536 };
474 537
475 538
476 539 Notebook.prototype.expand = function (index) {
477 540 var i = this.index_or_selected(index);
478 541 this.cells()[i].expand();
479 542 this.dirty = true;
480 543 };
481 544
482 545
483 546 Notebook.prototype.toggle_output = function (index) {
484 547 var i = this.index_or_selected(index);
485 548 this.cells()[i].toggle_output();
486 549 this.dirty = true;
487 550 };
488 551
489 552
490 553 Notebook.prototype.set_autoindent = function (state) {
491 554 var cells = this.cells();
492 555 len = cells.length;
493 556 for (var i=0; i<len; i++) {
494 557 cells[i].set_autoindent(state)
495 558 };
496 559 };
497 560
498 561
499 562 Notebook.prototype.clear_all_output = function () {
500 563 var ncells = this.ncells();
501 564 var cells = this.cells();
502 565 for (var i=0; i<ncells; i++) {
503 566 if (cells[i] instanceof IPython.CodeCell) {
504 567 cells[i].clear_output();
505 568 }
506 569 };
507 570 this.dirty = true;
508 571 };
509 572
510 573
511 574 // Kernel related things
512 575
513 576 Notebook.prototype.start_kernel = function () {
514 577 this.kernel = new IPython.Kernel();
515 578 var notebook_id = IPython.save_widget.get_notebook_id();
516 579 this.kernel.start(notebook_id, $.proxy(this.kernel_started, this));
517 580 };
518 581
519 582
520 583 Notebook.prototype.restart_kernel = function () {
521 584 var notebook_id = IPython.save_widget.get_notebook_id();
522 585 this.kernel.restart($.proxy(this.kernel_started, this));
523 586 };
524 587
525 588
526 589 Notebook.prototype.kernel_started = function () {
527 590 console.log("Kernel started: ", this.kernel.kernel_id);
528 591 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
529 592 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
530 593 };
531 594
532 595
533 596 Notebook.prototype.handle_shell_reply = function (e) {
534 597 reply = $.parseJSON(e.data);
535 598 var header = reply.header;
536 599 var content = reply.content;
537 600 var msg_type = header.msg_type;
538 601 // console.log(reply);
539 602 var cell = this.cell_for_msg(reply.parent_header.msg_id);
540 603 if (msg_type === "execute_reply") {
541 604 cell.set_input_prompt(content.execution_count);
542 605 this.dirty = true;
543 606 } else if (msg_type === "complete_reply") {
544 607 cell.finish_completing(content.matched_text, content.matches);
545 608 };
546 609 var payload = content.payload || [];
547 610 this.handle_payload(cell, payload);
548 611 };
549 612
550 613
551 614 Notebook.prototype.handle_payload = function (cell, payload) {
552 615 var l = payload.length;
553 616 for (var i=0; i<l; i++) {
554 617 if (payload[i].source === 'IPython.zmq.page.page') {
555 618 if (payload[i].text.trim() !== '') {
556 619 IPython.pager.clear();
557 620 IPython.pager.expand();
558 621 IPython.pager.append_text(payload[i].text);
559 622 }
560 623 } else if (payload[i].source === 'IPython.zmq.zmqshell.ZMQInteractiveShell.set_next_input') {
561 624 var index = this.find_cell_index(cell);
562 625 var new_cell = this.insert_code_cell_after(index);
563 626 new_cell.set_code(payload[i].text);
564 627 this.dirty = true;
565 628 }
566 629 };
567 630 };
568 631
569 632
570 633 Notebook.prototype.handle_iopub_reply = function (e) {
571 634 reply = $.parseJSON(e.data);
572 635 var content = reply.content;
573 636 // console.log(reply);
574 637 var msg_type = reply.header.msg_type;
575 638 var cell = this.cell_for_msg(reply.parent_header.msg_id);
576 639 var output_types = ['stream','display_data','pyout','pyerr'];
577 640 if (output_types.indexOf(msg_type) >= 0) {
578 641 this.handle_output(cell, msg_type, content);
579 642 } else if (msg_type === 'status') {
580 643 if (content.execution_state === 'busy') {
581 644 IPython.kernel_status_widget.status_busy();
582 645 } else if (content.execution_state === 'idle') {
583 646 IPython.kernel_status_widget.status_idle();
584 647 } else if (content.execution_state === 'dead') {
585 648 this.handle_status_dead();
586 649 };
587 650 }
588 651 };
589 652
590 653
591 654 Notebook.prototype.handle_status_dead = function () {
592 655 var that = this;
593 656 this.kernel.stop_channels();
594 657 var dialog = $('<div/>');
595 658 dialog.html('The kernel has died, would you like to restart it? If you do not restart the kernel, you will be able to save the notebook, but running code will not work until the notebook is reopened.');
596 659 $(document).append(dialog);
597 660 dialog.dialog({
598 661 resizable: false,
599 662 modal: true,
600 663 title: "Dead kernel",
601 664 buttons : {
602 665 "Yes": function () {
603 666 that.start_kernel();
604 667 $(this).dialog('close');
605 668 },
606 669 "No": function () {
607 670 $(this).dialog('close');
608 671 }
609 672 }
610 673 });
611 674 };
612 675
613 676
614 677 Notebook.prototype.handle_output = function (cell, msg_type, content) {
615 678 var json = {};
616 679 json.output_type = msg_type;
617 680 if (msg_type === "stream") {
618 681 json.text = utils.fixConsole(content.data + '\n');
619 682 } else if (msg_type === "display_data") {
620 683 json = this.convert_mime_types(json, content.data);
621 684 } else if (msg_type === "pyout") {
622 685 json.prompt_number = content.execution_count;
623 686 json = this.convert_mime_types(json, content.data);
624 687 } else if (msg_type === "pyerr") {
625 688 json.ename = content.ename;
626 689 json.evalue = content.evalue;
627 690 var traceback = [];
628 691 for (var i=0; i<content.traceback.length; i++) {
629 692 traceback.push(utils.fixConsole(content.traceback[i]));
630 693 }
631 694 json.traceback = traceback;
632 695 };
633 696 cell.append_output(json);
634 697 this.dirty = true;
635 698 };
636 699
637 700
638 701 Notebook.prototype.convert_mime_types = function (json, data) {
639 702 if (data['text/plain'] !== undefined) {
640 703 json.text = utils.fixConsole(data['text/plain']);
641 704 };
642 705 if (data['text/html'] !== undefined) {
643 706 json.html = data['text/html'];
644 707 };
645 708 if (data['image/svg+xml'] !== undefined) {
646 709 json.svg = data['image/svg+xml'];
647 710 };
648 711 if (data['image/png'] !== undefined) {
649 712 json.png = data['image/png'];
650 713 };
651 714 if (data['image/jpeg'] !== undefined) {
652 715 json.jpeg = data['image/jpeg'];
653 716 };
654 717 if (data['text/latex'] !== undefined) {
655 718 json.latex = data['text/latex'];
656 719 };
657 720 if (data['application/json'] !== undefined) {
658 721 json.json = data['application/json'];
659 722 };
660 723 if (data['application/javascript'] !== undefined) {
661 724 json.javascript = data['application/javascript'];
662 725 }
663 726 return json;
664 727 };
665 728
666 729
667 730 Notebook.prototype.execute_selected_cell = function (options) {
668 731 // add_new: should a new cell be added if we are at the end of the nb
669 732 // terminal: execute in terminal mode, which stays in the current cell
670 733 default_options = {terminal: false, add_new: true}
671 734 $.extend(default_options, options)
672 735 var that = this;
673 736 var cell = that.selected_cell();
674 737 var cell_index = that.find_cell_index(cell);
675 738 if (cell instanceof IPython.CodeCell) {
676 739 cell.clear_output();
677 740 var code = cell.get_code();
678 741 var msg_id = that.kernel.execute(cell.get_code());
679 742 that.msg_cell_map[msg_id] = cell.cell_id;
680 743 } else if (cell instanceof IPython.HTMLCell) {
681 744 cell.render();
682 745 }
683 746 if (default_options.terminal) {
684 747 cell.clear_input();
685 748 } else {
686 749 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
687 750 that.insert_code_cell_after();
688 751 // If we are adding a new cell at the end, scroll down to show it.
689 752 that.scroll_to_bottom();
690 753 } else {
691 754 that.select(cell_index+1);
692 755 };
693 756 };
694 757 this.dirty = true;
695 758 };
696 759
697 760
698 761 Notebook.prototype.execute_all_cells = function () {
699 762 var ncells = this.ncells();
700 763 for (var i=0; i<ncells; i++) {
701 764 this.select(i);
702 765 this.execute_selected_cell({add_new:false});
703 766 };
704 767 this.scroll_to_bottom();
705 768 };
706 769
707 770
708 771 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
709 772 var msg_id = this.kernel.complete(line, cursor_pos);
710 773 this.msg_cell_map[msg_id] = cell.cell_id;
711 774 };
712 775
713 776 // Persistance and loading
714 777
715 778
716 779 Notebook.prototype.fromJSON = function (data) {
717 780 var ncells = this.ncells();
718 781 for (var i=0; i<ncells; i++) {
719 782 // Always delete cell 0 as they get renumbered as they are deleted.
720 783 this.delete_cell(0);
721 784 };
722 785 // Save the metadata
723 786 this.metadata = data.metadata;
724 787 // Only handle 1 worksheet for now.
725 788 var worksheet = data.worksheets[0];
726 789 if (worksheet !== undefined) {
727 790 var new_cells = worksheet.cells;
728 791 ncells = new_cells.length;
729 792 var cell_data = null;
730 793 var new_cell = null;
731 794 for (var i=0; i<ncells; i++) {
732 795 cell_data = new_cells[i];
733 796 if (cell_data.cell_type == 'code') {
734 797 new_cell = this.insert_code_cell_after();
735 798 new_cell.fromJSON(cell_data);
736 799 } else if (cell_data.cell_type === 'html') {
737 800 new_cell = this.insert_html_cell_after();
738 801 new_cell.fromJSON(cell_data);
739 802 } else if (cell_data.cell_type === 'markdown') {
740 803 new_cell = this.insert_markdown_cell_after();
741 804 new_cell.fromJSON(cell_data);
742 805 };
743 806 };
744 807 };
745 808 };
746 809
747 810
748 811 Notebook.prototype.toJSON = function () {
749 812 var cells = this.cells();
750 813 var ncells = cells.length;
751 814 cell_array = new Array(ncells);
752 815 for (var i=0; i<ncells; i++) {
753 816 cell_array[i] = cells[i].toJSON();
754 817 };
755 818 data = {
756 819 // Only handle 1 worksheet for now.
757 820 worksheets : [{cells:cell_array}],
758 821 metadata : this.metadata
759 822 }
760 823 return data
761 824 };
762 825
763 826 Notebook.prototype.save_notebook = function () {
764 827 if (IPython.save_widget.test_notebook_name()) {
765 828 var notebook_id = IPython.save_widget.get_notebook_id();
766 829 var nbname = IPython.save_widget.get_notebook_name();
767 830 // We may want to move the name/id/nbformat logic inside toJSON?
768 831 var data = this.toJSON();
769 832 data.metadata.name = nbname;
770 833 data.nbformat = 2;
771 834 // We do the call with settings so we can set cache to false.
772 835 var settings = {
773 836 processData : false,
774 837 cache : false,
775 838 type : "PUT",
776 839 data : JSON.stringify(data),
777 840 headers : {'Content-Type': 'application/json'},
778 841 success : $.proxy(this.notebook_saved,this)
779 842 };
780 843 IPython.save_widget.status_saving();
781 844 $.ajax("/notebooks/" + notebook_id, settings);
782 845 };
783 846 };
784 847
785 848
786 849 Notebook.prototype.notebook_saved = function (data, status, xhr) {
787 850 this.dirty = false;
788 851 setTimeout($.proxy(IPython.save_widget.status_save,IPython.save_widget),500);
789 852 }
790 853
791 854
792 855 Notebook.prototype.load_notebook = function (callback) {
793 856 var that = this;
794 857 var notebook_id = IPython.save_widget.get_notebook_id();
795 858 // We do the call with settings so we can set cache to false.
796 859 var settings = {
797 860 processData : false,
798 861 cache : false,
799 862 type : "GET",
800 863 dataType : "json",
801 864 success : function (data, status, xhr) {
802 865 that.notebook_loaded(data, status, xhr);
803 866 if (callback !== undefined) {
804 867 callback();
805 868 };
806 869 }
807 870 };
808 871 IPython.save_widget.status_loading();
809 872 $.ajax("/notebooks/" + notebook_id, settings);
810 873 }
811 874
812 875
813 876 Notebook.prototype.notebook_loaded = function (data, status, xhr) {
814 877 this.fromJSON(data);
815 878 if (this.ncells() === 0) {
816 879 this.insert_code_cell_after();
817 880 };
818 881 IPython.save_widget.status_save();
819 882 IPython.save_widget.set_notebook_name(data.metadata.name);
820 883 this.start_kernel();
821 884 this.dirty = false;
822 885 // fromJSON always selects the last cell inserted. We need to wait
823 886 // until that is done before scrolling to the top.
824 887 setTimeout(function () {
825 888 IPython.notebook.select(0);
826 889 IPython.notebook.scroll_to_top();
827 890 }, 50);
828 891 };
829 892
830 893 IPython.Notebook = Notebook;
831 894
832 895 return IPython;
833 896
834 897 }(IPython));
835 898
@@ -1,142 +1,147
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // SaveWidget
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13
14 14 var utils = IPython.utils;
15 15
16 16 var SaveWidget = function (selector) {
17 17 this.selector = selector;
18 18 this.notebook_name_re = /[^/\\]+/
19 19 this.last_saved_name = '';
20 20 if (this.selector !== undefined) {
21 21 this.element = $(selector);
22 22 this.style();
23 23 this.bind_events();
24 24 }
25 25 };
26 26
27 27
28 28 SaveWidget.prototype.style = function () {
29 29 this.element.find('input#notebook_name').addClass('ui-widget ui-widget-content');
30 30 this.element.find('input#notebook_name').attr('tabindex','1');
31 31 this.element.find('button#save_notebook').button();
32 32 var left_panel_width = $('div#left_panel').outerWidth();
33 33 var left_panel_splitter_width = $('div#left_panel_splitter').outerWidth();
34 34 $('span#save_widget').css({marginLeft:left_panel_width+left_panel_splitter_width});
35 35
36 36 };
37 37
38 38
39 39 SaveWidget.prototype.bind_events = function () {
40 40 var that = this;
41 41 this.element.find('button#save_notebook').click(function () {
42 IPython.notebook.save_notebook();
43 that.set_document_title();
44 that.last_saved_name = that.get_notebook_name();
42 that.save_notebook();
45 43 });
46 44 this.element.find('input#notebook_name').keyup(function () {
47 45 that.is_renaming();
48 46 });
49 47 };
50 48
51 49
50 SaveWidget.prototype.save_notebook = function () {
51 IPython.notebook.save_notebook();
52 this.set_document_title();
53 this.last_saved_name = this.get_notebook_name();
54 };
55
56
52 57 SaveWidget.prototype.is_renaming = function () {
53 58 if (this.get_notebook_name() !== this.last_saved_name) {
54 59 this.status_rename();
55 60 } else {
56 61 this.status_save();
57 62 };
58 63 };
59 64
60 65
61 66 SaveWidget.prototype.get_notebook_name = function () {
62 67 return this.element.find('input#notebook_name').attr('value');
63 68 }
64 69
65 70
66 71 SaveWidget.prototype.set_notebook_name = function (nbname) {
67 72 this.element.find('input#notebook_name').attr('value',nbname);
68 73 this.set_document_title();
69 74 this.last_saved_name = nbname;
70 75 }
71 76
72 77
73 78 SaveWidget.prototype.set_document_title = function () {
74 79 nbname = this.get_notebook_name();
75 80 document.title = 'IPy: ' + nbname;
76 81 };
77 82
78 83
79 84 SaveWidget.prototype.get_notebook_id = function () {
80 85 return this.element.find('span#notebook_id').text()
81 86 };
82 87
83 88
84 89 SaveWidget.prototype.update_url = function () {
85 90 var notebook_id = this.get_notebook_id();
86 91 if (notebook_id !== '') {
87 92 window.history.replaceState({}, '', notebook_id);
88 93 };
89 94 };
90 95
91 96
92 97 SaveWidget.prototype.test_notebook_name = function () {
93 98 var nbname = this.get_notebook_name();
94 99 if (this.notebook_name_re.test(nbname)) {
95 100 return true;
96 101 } else {
97 102 var bad_name = $('<div/>');
98 103 bad_name.html(
99 104 "The notebook name you entered (" +
100 105 nbname +
101 106 ") is not valid. Notebook names can contain any characters except / and \\"
102 107 );
103 108 bad_name.dialog({title: 'Invalid name', modal: true});
104 109 return false;
105 110 };
106 111 };
107 112
108 113
109 114 SaveWidget.prototype.status_save = function () {
110 115 this.element.find('button#save_notebook').button('option', 'label', 'Save');
111 116 this.element.find('button#save_notebook').button('enable');
112 117 IPython.print_widget.enable();
113 118 };
114 119
115 120
116 121 SaveWidget.prototype.status_saving = function () {
117 122 this.element.find('button#save_notebook').button('option', 'label', 'Saving');
118 123 this.element.find('button#save_notebook').button('disable');
119 124 IPython.print_widget.disable();
120 125 };
121 126
122 127
123 128 SaveWidget.prototype.status_loading = function () {
124 129 this.element.find('button#save_notebook').button('option', 'label', 'Loading');
125 130 this.element.find('button#save_notebook').button('disable');
126 131 IPython.print_widget.disable();
127 132 };
128 133
129 134
130 135 SaveWidget.prototype.status_rename = function () {
131 136 this.element.find('button#save_notebook').button('option', 'label', 'Rename');
132 137 this.element.find('button#save_notebook').button('enable');
133 138 IPython.print_widget.enable();
134 139 };
135 140
136 141
137 142 IPython.SaveWidget = SaveWidget;
138 143
139 144 return IPython;
140 145
141 146 }(IPython));
142 147
General Comments 0
You need to be logged in to leave comments. Login now