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