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