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