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