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