##// END OF EJS Templates
Removing default input prompt number....
Brian E. Granger -
Show More
@@ -1,616 +1,618
1 1
2 2 //============================================================================
3 3 // Notebook
4 4 //============================================================================
5 5
6 6 var IPython = (function (IPython) {
7 7
8 8 var utils = IPython.utils;
9 9
10 10 var Notebook = function (selector) {
11 11 this.element = $(selector);
12 12 this.element.scroll();
13 13 this.element.data("notebook", this);
14 14 this.next_prompt_number = 1;
15 15 this.kernel = null;
16 16 this.msg_cell_map = {};
17 17 this.filename = null;
18 18 this.notebook_load_re = /%notebook load/
19 19 this.notebook_save_re = /%notebook save/
20 20 this.notebook_filename_re = /(\w)+.ipynb/
21 21 this.style();
22 22 this.create_elements();
23 23 this.bind_events();
24 24 this.start_kernel();
25 25 };
26 26
27 27
28 28 Notebook.prototype.style = function () {
29 29 $('div#notebook').addClass('border-box-sizing');
30 30 };
31 31
32 32
33 33 Notebook.prototype.create_elements = function () {
34 34 // We add this end_space div to the end of the notebook div to:
35 35 // i) provide a margin between the last cell and the end of the notebook
36 36 // ii) to prevent the div from scrolling up when the last cell is being
37 37 // edited, but is too low on the page, which browsers will do automatically.
38 38 this.element.append($('<div class="end_space"></div>').height(50));
39 39 $('div#notebook').addClass('border-box-sizing');
40 40 };
41 41
42 42
43 43 Notebook.prototype.bind_events = function () {
44 44 var that = this;
45 45 $(document).keydown(function (event) {
46 46 // console.log(event);
47 47 if (event.which === 38) {
48 48 var cell = that.selected_cell();
49 49 if (cell.at_top()) {
50 50 event.preventDefault();
51 51 that.select_prev();
52 52 };
53 53 } else if (event.which === 40) {
54 54 var cell = that.selected_cell();
55 55 if (cell.at_bottom()) {
56 56 event.preventDefault();
57 57 that.select_next();
58 58 };
59 59 } else if (event.which === 13 && event.shiftKey) {
60 60 that.execute_selected_cell(true);
61 61 return false;
62 62 } else if (event.which === 13 && event.ctrlKey) {
63 63 that.execute_selected_cell(false);
64 64 that.selected_cell().clear_input();
65 65 return false;
66 66 };
67 67 });
68 68
69 69 this.element.bind('collapse_pager', function () {
70 70 var app_height = $('div#notebook_app').height(); // content height
71 71 var splitter_height = $('div#pager_splitter').outerHeight(true);
72 72 var new_height = app_height - splitter_height;
73 73 that.element.animate({height : new_height + 'px'}, 'fast');
74 74 });
75 75
76 76 this.element.bind('expand_pager', function () {
77 77 var app_height = $('div#notebook_app').height(); // content height
78 78 var splitter_height = $('div#pager_splitter').outerHeight(true);
79 79 var pager_height = $('div#pager').outerHeight(true);
80 80 var new_height = app_height - pager_height - splitter_height;
81 81 that.element.animate({height : new_height + 'px'}, 'fast');
82 82 });
83 83
84 84 this.element.bind('collapse_left_panel', function () {
85 85 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
86 86 var new_margin = splitter_width;
87 87 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
88 88 });
89 89
90 90 this.element.bind('expand_left_panel', function () {
91 91 var splitter_width = $('div#left_panel_splitter').outerWidth(true);
92 92 var left_panel_width = IPython.left_panel.width;
93 93 var new_margin = splitter_width + left_panel_width;
94 94 $('div#notebook_panel').animate({marginLeft : new_margin + 'px'}, 'fast');
95 95 });
96 96 };
97 97
98 98
99 99 Notebook.prototype.scroll_to_bottom = function () {
100 100 this.element.animate({scrollTop:this.element.get(0).scrollHeight}, 0);
101 101 };
102 102
103 103 // Cell indexing, retrieval, etc.
104 104
105 105
106 106 Notebook.prototype.cell_elements = function () {
107 107 return this.element.children("div.cell");
108 108 }
109 109
110 110
111 111 Notebook.prototype.ncells = function (cell) {
112 112 return this.cell_elements().length;
113 113 }
114 114
115 115
116 116 // TODO: we are often calling cells as cells()[i], which we should optimize
117 117 // to cells(i) or a new method.
118 118 Notebook.prototype.cells = function () {
119 119 return this.cell_elements().toArray().map(function (e) {
120 120 return $(e).data("cell");
121 121 });
122 122 }
123 123
124 124
125 125 Notebook.prototype.find_cell_index = function (cell) {
126 126 var result = null;
127 127 this.cell_elements().filter(function (index) {
128 128 if ($(this).data("cell") === cell) {
129 129 result = index;
130 130 };
131 131 });
132 132 return result;
133 133 };
134 134
135 135
136 136 Notebook.prototype.index_or_selected = function (index) {
137 137 return index || this.selected_index() || 0;
138 138 }
139 139
140 140
141 141 Notebook.prototype.select = function (index) {
142 142 if (index !== undefined && index >= 0 && index < this.ncells()) {
143 143 if (this.selected_index() !== null) {
144 144 this.selected_cell().unselect();
145 145 };
146 146 this.cells()[index].select();
147 147 if (index === (this.ncells()-1)) {
148 148 this.scroll_to_bottom();
149 149 };
150 150 };
151 151 return this;
152 152 };
153 153
154 154
155 155 Notebook.prototype.select_next = function () {
156 156 var index = this.selected_index();
157 157 if (index !== null && index >= 0 && (index+1) < this.ncells()) {
158 158 this.select(index+1);
159 159 };
160 160 return this;
161 161 };
162 162
163 163
164 164 Notebook.prototype.select_prev = function () {
165 165 var index = this.selected_index();
166 166 if (index !== null && index >= 0 && (index-1) < this.ncells()) {
167 167 this.select(index-1);
168 168 };
169 169 return this;
170 170 };
171 171
172 172
173 173 Notebook.prototype.selected_index = function () {
174 174 var result = null;
175 175 this.cell_elements().filter(function (index) {
176 176 if ($(this).data("cell").selected === true) {
177 177 result = index;
178 178 };
179 179 });
180 180 return result;
181 181 };
182 182
183 183
184 184 Notebook.prototype.cell_for_msg = function (msg_id) {
185 185 var cell_id = this.msg_cell_map[msg_id];
186 186 var result = null;
187 187 this.cell_elements().filter(function (index) {
188 188 cell = $(this).data("cell");
189 189 if (cell.cell_id === cell_id) {
190 190 result = cell;
191 191 };
192 192 });
193 193 return result;
194 194 };
195 195
196 196
197 197 Notebook.prototype.selected_cell = function () {
198 198 return this.cell_elements().eq(this.selected_index()).data("cell");
199 199 }
200 200
201 201
202 202 // Cell insertion, deletion and moving.
203 203
204 204
205 205 Notebook.prototype.delete_cell = function (index) {
206 206 var i = index || this.selected_index();
207 207 if (i !== null && i >= 0 && i < this.ncells()) {
208 208 this.cell_elements().eq(i).remove();
209 209 if (i === (this.ncells())) {
210 210 this.select(i-1);
211 211 } else {
212 212 this.select(i);
213 213 };
214 214 };
215 215 return this;
216 216 };
217 217
218 218
219 219 Notebook.prototype.append_cell = function (cell) {
220 220 this.element.find('div.end_space').before(cell.element);
221 221 return this;
222 222 };
223 223
224 224
225 225 Notebook.prototype.insert_cell_after = function (cell, index) {
226 226 var ncells = this.ncells();
227 227 if (ncells === 0) {
228 228 this.append_cell(cell);
229 229 return this;
230 230 };
231 231 if (index >= 0 && index < ncells) {
232 232 this.cell_elements().eq(index).after(cell.element);
233 233 };
234 234 return this
235 235 };
236 236
237 237
238 238 Notebook.prototype.insert_cell_before = function (cell, index) {
239 239 var ncells = this.ncells();
240 240 if (ncells === 0) {
241 241 this.append_cell(cell);
242 242 return this;
243 243 };
244 244 if (index >= 0 && index < ncells) {
245 245 this.cell_elements().eq(index).before(cell.element);
246 246 };
247 247 return this;
248 248 };
249 249
250 250
251 251 Notebook.prototype.move_cell_up = function (index) {
252 252 var i = index || this.selected_index();
253 253 if (i !== null && i < this.ncells() && i > 0) {
254 254 var pivot = this.cell_elements().eq(i-1);
255 255 var tomove = this.cell_elements().eq(i);
256 256 if (pivot !== null && tomove !== null) {
257 257 tomove.detach();
258 258 pivot.before(tomove);
259 259 this.select(i-1);
260 260 };
261 261 };
262 262 return this;
263 263 }
264 264
265 265
266 266 Notebook.prototype.move_cell_down = function (index) {
267 267 var i = index || this.selected_index();
268 268 if (i !== null && i < (this.ncells()-1) && i >= 0) {
269 269 var pivot = this.cell_elements().eq(i+1)
270 270 var tomove = this.cell_elements().eq(i)
271 271 if (pivot !== null && tomove !== null) {
272 272 tomove.detach();
273 273 pivot.after(tomove);
274 274 this.select(i+1);
275 275 };
276 276 };
277 277 return this;
278 278 }
279 279
280 280
281 281 Notebook.prototype.sort_cells = function () {
282 282 var ncells = this.ncells();
283 283 var sindex = this.selected_index();
284 284 var swapped;
285 285 do {
286 286 swapped = false
287 287 for (var i=1; i<ncells; i++) {
288 288 current = this.cell_elements().eq(i).data("cell");
289 289 previous = this.cell_elements().eq(i-1).data("cell");
290 290 if (previous.input_prompt_number > current.input_prompt_number) {
291 291 this.move_cell_up(i);
292 292 swapped = true;
293 293 };
294 294 };
295 295 } while (swapped);
296 296 this.select(sindex);
297 297 return this;
298 298 };
299 299
300 300
301 301 Notebook.prototype.insert_code_cell_before = function (index) {
302 302 // TODO: Bounds check for i
303 303 var i = this.index_or_selected(index);
304 304 var cell = new IPython.CodeCell(this);
305 cell.set_input_prompt(this.next_prompt_number);
305 // cell.set_input_prompt(this.next_prompt_number);
306 cell.set_input_prompt();
306 307 this.next_prompt_number = this.next_prompt_number + 1;
307 308 this.insert_cell_before(cell, i);
308 309 this.select(this.find_cell_index(cell));
309 310 return this;
310 311 }
311 312
312 313
313 314 Notebook.prototype.insert_code_cell_after = function (index) {
314 315 // TODO: Bounds check for i
315 316 var i = this.index_or_selected(index);
316 317 var cell = new IPython.CodeCell(this);
317 cell.set_input_prompt(this.next_prompt_number);
318 // cell.set_input_prompt(this.next_prompt_number);
319 cell.set_input_prompt();
318 320 this.next_prompt_number = this.next_prompt_number + 1;
319 321 this.insert_cell_after(cell, i);
320 322 this.select(this.find_cell_index(cell));
321 323 return this;
322 324 }
323 325
324 326
325 327 Notebook.prototype.insert_text_cell_before = function (index) {
326 328 // TODO: Bounds check for i
327 329 var i = this.index_or_selected(index);
328 330 var cell = new IPython.TextCell(this);
329 331 cell.config_mathjax();
330 332 this.insert_cell_before(cell, i);
331 333 this.select(this.find_cell_index(cell));
332 334 return this;
333 335 }
334 336
335 337
336 338 Notebook.prototype.insert_text_cell_after = function (index) {
337 339 // TODO: Bounds check for i
338 340 var i = this.index_or_selected(index);
339 341 var cell = new IPython.TextCell(this);
340 342 cell.config_mathjax();
341 343 this.insert_cell_after(cell, i);
342 344 this.select(this.find_cell_index(cell));
343 345 return this;
344 346 }
345 347
346 348
347 349 Notebook.prototype.text_to_code = function (index) {
348 350 // TODO: Bounds check for i
349 351 var i = this.index_or_selected(index);
350 352 var source_element = this.cell_elements().eq(i);
351 353 var source_cell = source_element.data("cell");
352 354 if (source_cell instanceof IPython.TextCell) {
353 355 this.insert_code_cell_after(i);
354 356 var target_cell = this.cells()[i+1];
355 357 target_cell.set_code(source_cell.get_text());
356 358 source_element.remove();
357 359 };
358 360 };
359 361
360 362
361 363 Notebook.prototype.code_to_text = function (index) {
362 364 // TODO: Bounds check for i
363 365 var i = this.index_or_selected(index);
364 366 var source_element = this.cell_elements().eq(i);
365 367 var source_cell = source_element.data("cell");
366 368 if (source_cell instanceof IPython.CodeCell) {
367 369 this.insert_text_cell_after(i);
368 370 var target_cell = this.cells()[i+1];
369 371 var text = source_cell.get_code();
370 372 if (text === "") {text = target_cell.placeholder;};
371 373 target_cell.set_text(text);
372 374 source_element.remove();
373 375 target_cell.edit();
374 376 };
375 377 };
376 378
377 379
378 380 // Cell collapsing
379 381
380 382 Notebook.prototype.collapse = function (index) {
381 383 var i = this.index_or_selected(index);
382 384 this.cells()[i].collapse();
383 385 };
384 386
385 387
386 388 Notebook.prototype.expand = function (index) {
387 389 var i = this.index_or_selected(index);
388 390 this.cells()[i].expand();
389 391 };
390 392
391 393
392 394 // Kernel related things
393 395
394 396 Notebook.prototype.start_kernel = function () {
395 397 this.kernel = new IPython.Kernel();
396 398 this.kernel.start_kernel($.proxy(this.kernel_started, this));
397 399 };
398 400
399 401
400 402 Notebook.prototype.handle_shell_reply = function (e) {
401 403 reply = $.parseJSON(e.data);
402 404 var header = reply.header;
403 405 var content = reply.content;
404 406 var msg_type = header.msg_type;
405 407 // console.log(reply);
406 408 var cell = this.cell_for_msg(reply.parent_header.msg_id);
407 409 if (msg_type === "execute_reply") {
408 410 cell.set_input_prompt(content.execution_count);
409 411 } else if (msg_type === "complete_reply") {
410 412 cell.finish_completing(content.matched_text, content.matches);
411 413 };
412 414 var payload = content.payload || [];
413 415 this.handle_payload(payload);
414 416 };
415 417
416 418
417 419 Notebook.prototype.handle_payload = function (payload) {
418 420 var l = payload.length;
419 421 if (l > 0) {
420 422 IPython.pager.clear();
421 423 IPython.pager.expand();
422 424 };
423 425 for (var i=0; i<l; i++) {
424 426 IPython.pager.append_text(payload[i].text);
425 427 };
426 428 };
427 429
428 430
429 431 Notebook.prototype.handle_iopub_reply = function (e) {
430 432 reply = $.parseJSON(e.data);
431 433 var content = reply.content;
432 434 // console.log(reply);
433 435 var msg_type = reply.header.msg_type;
434 436 var cell = this.cell_for_msg(reply.parent_header.msg_id);
435 437 if (msg_type === "stream") {
436 438 cell.expand();
437 439 cell.append_stream(content.data + "\n");
438 440 } else if (msg_type === "display_data") {
439 441 cell.expand();
440 442 cell.append_display_data(content.data);
441 443 } else if (msg_type === "pyout") {
442 444 cell.expand();
443 445 cell.append_pyout(content.data, content.execution_count)
444 446 } else if (msg_type === "pyerr") {
445 447 cell.expand();
446 448 cell.append_pyerr(content.ename, content.evalue, content.traceback);
447 449 } else if (msg_type === "status") {
448 450 if (content.execution_state === "busy") {
449 451 IPython.kernel_status_widget.status_busy();
450 452 } else if (content.execution_state === "idle") {
451 453 IPython.kernel_status_widget.status_idle();
452 454 };
453 455 }
454 456 };
455 457
456 458
457 459 Notebook.prototype.kernel_started = function () {
458 460 console.log("Kernel started: ", this.kernel.kernel_id);
459 461 this.kernel.shell_channel.onmessage = $.proxy(this.handle_shell_reply,this);
460 462 this.kernel.iopub_channel.onmessage = $.proxy(this.handle_iopub_reply,this);
461 463 };
462 464
463 465
464 466 Notebook.prototype.execute_selected_cell = function (add_new) {
465 467 if (add_new === undefined) {add_new = true;};
466 468 var that = this;
467 469 var cell = that.selected_cell();
468 470 var cell_index = that.find_cell_index(cell);
469 471 // TODO: the logic here needs to be moved into appropriate
470 472 // methods of Notebook.
471 473 if (cell instanceof IPython.CodeCell) {
472 474 cell.clear_output();
473 475 var code = cell.get_code();
474 476 if (that.notebook_load_re.test(code)) {
475 477 var code_parts = code.split(' ');
476 478 if (code_parts.length === 3) {
477 479 that.load_notebook(code_parts[2]);
478 480 };
479 481 } else if (that.notebook_save_re.test(code)) {
480 482 var code_parts = code.split(' ');
481 483 if (code_parts.length === 3) {
482 484 that.save_notebook(code_parts[2]);
483 485 } else {
484 486 that.save_notebook()
485 487 };
486 488 } else {
487 489 var msg_id = that.kernel.execute(cell.get_code());
488 490 that.msg_cell_map[msg_id] = cell.cell_id;
489 491 };
490 492 } else if (cell instanceof IPython.TextCell) {
491 493 cell.render();
492 494 }
493 495 if ((cell_index === (that.ncells()-1)) && add_new) {
494 496 that.insert_code_cell_after();
495 497 // If we are adding a new cell at the end, scroll down to show it.
496 498 that.scroll_to_bottom();
497 499 } else {
498 500 that.select(cell_index+1);
499 501 };
500 502 };
501 503
502 504
503 505 Notebook.prototype.execute_all_cells = function () {
504 506 var ncells = this.ncells();
505 507 for (var i=0; i<ncells; i++) {
506 508 this.select(i);
507 509 this.execute_selected_cell(false);
508 510 };
509 511 this.scroll_to_bottom();
510 512 };
511 513
512 514
513 515 Notebook.prototype.complete_cell = function (cell, line, cursor_pos) {
514 516 var msg_id = this.kernel.complete(line, cursor_pos);
515 517 this.msg_cell_map[msg_id] = cell.cell_id;
516 518 };
517 519
518 520 // Persistance and loading
519 521
520 522
521 523 Notebook.prototype.fromJSON = function (data) {
522 524 var ncells = this.ncells();
523 525 for (var i=0; i<ncells; i++) {
524 526 // Always delete cell 0 as they get renumbered as they are deleted.
525 527 this.delete_cell(0);
526 528 };
527 529 var new_cells = data.cells;
528 530 ncells = new_cells.length;
529 531 var cell_data = null;
530 532 for (var i=0; i<ncells; i++) {
531 533 cell_data = new_cells[i];
532 534 if (cell_data.cell_type == 'code') {
533 535 this.insert_code_cell_after();
534 536 this.selected_cell().fromJSON(cell_data);
535 537 } else if (cell_data.cell_type === 'text') {
536 538 this.insert_text_cell_after();
537 539 this.selected_cell().fromJSON(cell_data);
538 540 };
539 541 };
540 542 };
541 543
542 544
543 545 Notebook.prototype.toJSON = function () {
544 546 var cells = this.cells();
545 547 var ncells = cells.length;
546 548 cell_array = new Array(ncells);
547 549 for (var i=0; i<ncells; i++) {
548 550 cell_array[i] = cells[i].toJSON();
549 551 };
550 552 json = {
551 553 cells : cell_array
552 554 };
553 555 return json
554 556 };
555 557
556 558
557 559 Notebook.prototype.test_filename = function (filename) {
558 560 if (this.notebook_filename_re.test(filename)) {
559 561 return true;
560 562 } else {
561 563 var bad_filename = $('<div/>');
562 564 bad_filename.html(
563 565 "The filename you entered (" + filename + ") is not valid. Notebook filenames must have the following form: foo.ipynb"
564 566 );
565 567 bad_filename.dialog({title: 'Invalid filename', modal: true});
566 568 return false;
567 569 };
568 570 };
569 571
570 572 Notebook.prototype.save_notebook = function (filename) {
571 573 this.filename = filename || this.filename || '';
572 574 if (this.filename === '') {
573 575 var no_filename = $('<div/>');
574 576 no_filename.html(
575 577 "This notebook has no filename, please specify a filename of the form: foo.ipynb"
576 578 );
577 579 no_filename.dialog({title: 'Missing filename', modal: true});
578 580 return;
579 581 }
580 582 if (!this.test_filename(this.filename)) {return;}
581 583 var thedata = this.toJSON();
582 584 var settings = {
583 585 processData : false,
584 586 cache : false,
585 587 type : "PUT",
586 588 data : JSON.stringify(thedata),
587 589 success : function (data, status, xhr) {console.log(data);}
588 590 };
589 591 $.ajax("/notebooks/" + this.filename, settings);
590 592 };
591 593
592 594
593 595 Notebook.prototype.load_notebook = function (filename) {
594 596 if (!this.test_filename(filename)) {return;}
595 597 var that = this;
596 598 // We do the call with settings so we can set cache to false.
597 599 var settings = {
598 600 processData : false,
599 601 cache : false,
600 602 type : "GET",
601 603 dataType : "json",
602 604 success : function (data, status, xhr) {
603 605 that.fromJSON(data);
604 606 that.filename = filename;
605 607 that.kernel.restart();
606 608 }
607 609 };
608 610 $.ajax("/notebooks/" + filename, settings);
609 611 }
610 612
611 613 IPython.Notebook = Notebook;
612 614
613 615 return IPython;
614 616
615 617 }(IPython));
616 618
General Comments 0
You need to be logged in to leave comments. Login now