##// END OF EJS Templates
Merge pull request #3605 from ellisonbg/newux...
Brian E. Granger -
r14101:59235449 merge
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (683 lines changed) Show them Hide them
@@ -0,0 +1,683 b''
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2011 The IPython Development Team
3 //
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
7
8 //============================================================================
9 // Keyboard management
10 //============================================================================
11
12 var IPython = (function (IPython) {
13 "use strict";
14
15 // Setup global keycodes and inverse keycodes.
16
17 // See http://unixpapa.com/js/key.html for a complete description. The short of
18 // it is that there are different keycode sets. Firefox uses the "Mozilla keycodes"
19 // and Webkit/IE use the "IE keycodes". These keycode sets are mostly the same
20 // but have minor differences.
21
22 // These apply to Firefox, (Webkit and IE)
23 var _keycodes = {
24 'a': 65, 'b': 66, 'c': 67, 'd': 68, 'e': 69, 'f': 70, 'g': 71, 'h': 72, 'i': 73,
25 'j': 74, 'k': 75, 'l': 76, 'm': 77, 'n': 78, 'o': 79, 'p': 80, 'q': 81, 'r': 82,
26 's': 83, 't': 84, 'u': 85, 'v': 86, 'w': 87, 'x': 88, 'y': 89, 'z': 90,
27 '1 !': 49, '2 @': 50, '3 #': 51, '4 $': 52, '5 %': 53, '6 ^': 54,
28 '7 &': 55, '8 *': 56, '9 (': 57, '0 )': 48,
29 '[ {': 219, '] }': 221, '` ~': 192, ', <': 188, '. >': 190, '/ ?': 191,
30 '\\ |': 220, '\' "': 222,
31 'numpad0': 96, 'numpad1': 97, 'numpad2': 98, 'numpad3': 99, 'numpad4': 100,
32 'numpad5': 101, 'numpad6': 102, 'numpad7': 103, 'numpad8': 104, 'numpad9': 105,
33 'multiply': 106, 'add': 107, 'subtract': 109, 'decimal': 110, 'divide': 111,
34 'f1': 112, 'f2': 113, 'f3': 114, 'f4': 115, 'f5': 116, 'f6': 117, 'f7': 118,
35 'f8': 119, 'f9': 120, 'f11': 122, 'f12': 123, 'f13': 124, 'f14': 125, 'f15': 126,
36 'backspace': 8, 'tab': 9, 'enter': 13, 'shift': 16, 'ctrl': 17, 'alt': 18,
37 'meta': 91, 'capslock': 20, 'esc': 27, 'space': 32, 'pageup': 33, 'pagedown': 34,
38 'end': 35, 'home': 36, 'left': 37, 'up': 38, 'right': 39, 'down': 40,
39 'insert': 45, 'delete': 46, 'numlock': 144,
40 };
41
42 // These apply to Firefox and Opera
43 var _mozilla_keycodes = {
44 '; :': 59, '= +': 61, '- _': 173, 'meta': 224
45 }
46
47 // This apply to Webkit and IE
48 var _ie_keycodes = {
49 '; :': 186, '= +': 187, '- _': 189,
50 }
51
52 var browser = IPython.utils.browser[0];
53
54 if (browser === 'Firefox' || browser === 'Opera') {
55 $.extend(_keycodes, _mozilla_keycodes);
56 } else if (browser === 'Safari' || browser === 'Chrome' || browser === 'MSIE') {
57 $.extend(_keycodes, _ie_keycodes);
58 }
59
60 var keycodes = {};
61 var inv_keycodes = {};
62 for (var name in _keycodes) {
63 var names = name.split(' ');
64 if (names.length === 1) {
65 var n = names[0]
66 keycodes[n] = _keycodes[n]
67 inv_keycodes[_keycodes[n]] = n
68 } else {
69 var primary = names[0];
70 var secondary = names[1];
71 keycodes[primary] = _keycodes[name]
72 keycodes[secondary] = _keycodes[name]
73 inv_keycodes[_keycodes[name]] = primary
74 }
75 }
76
77
78 // Default keyboard shortcuts
79
80 var default_common_shortcuts = {
81 'meta+s' : {
82 help : 'save notebook',
83 help_index : 'fb',
84 handler : function (event) {
85 IPython.notebook.save_checkpoint();
86 event.preventDefault();
87 return false;
88 }
89 },
90 'ctrl+s' : {
91 help : 'save notebook',
92 help_index : 'fc',
93 handler : function (event) {
94 IPython.notebook.save_checkpoint();
95 event.preventDefault();
96 return false;
97 }
98 },
99 'shift' : {
100 help : '',
101 help_index : '',
102 handler : function (event) {
103 // ignore shift keydown
104 return true;
105 }
106 },
107 'shift+enter' : {
108 help : 'run cell',
109 help_index : 'ba',
110 handler : function (event) {
111 IPython.notebook.execute_cell();
112 return false;
113 }
114 },
115 'ctrl+enter' : {
116 help : 'run cell, select below',
117 help_index : 'bb',
118 handler : function (event) {
119 IPython.notebook.execute_cell_and_select_below();
120 return false;
121 }
122 },
123 'alt+enter' : {
124 help : 'run cell, insert below',
125 help_index : 'bc',
126 handler : function (event) {
127 IPython.notebook.execute_cell_and_insert_below();
128 return false;
129 }
130 }
131 }
132
133 // Edit mode defaults
134
135 var default_edit_shortcuts = {
136 'esc' : {
137 help : 'command mode',
138 help_index : 'aa',
139 handler : function (event) {
140 IPython.notebook.command_mode();
141 IPython.notebook.focus_cell();
142 return false;
143 }
144 },
145 'ctrl+m' : {
146 help : 'command mode',
147 help_index : 'ab',
148 handler : function (event) {
149 IPython.notebook.command_mode();
150 IPython.notebook.focus_cell();
151 return false;
152 }
153 },
154 'up' : {
155 help : '',
156 help_index : '',
157 handler : function (event) {
158 var cell = IPython.notebook.get_selected_cell();
159 if (cell && cell.at_top()) {
160 event.preventDefault();
161 IPython.notebook.command_mode()
162 IPython.notebook.select_prev();
163 IPython.notebook.edit_mode();
164 return false;
165 };
166 }
167 },
168 'down' : {
169 help : '',
170 help_index : '',
171 handler : function (event) {
172 var cell = IPython.notebook.get_selected_cell();
173 if (cell && cell.at_bottom()) {
174 event.preventDefault();
175 IPython.notebook.command_mode()
176 IPython.notebook.select_next();
177 IPython.notebook.edit_mode();
178 return false;
179 };
180 }
181 },
182 'alt+-' : {
183 help : 'split cell',
184 help_index : 'ea',
185 handler : function (event) {
186 IPython.notebook.split_cell();
187 return false;
188 }
189 },
190 'alt+subtract' : {
191 help : '',
192 help_index : 'eb',
193 handler : function (event) {
194 IPython.notebook.split_cell();
195 return false;
196 }
197 },
198 }
199
200 // Command mode defaults
201
202 var default_command_shortcuts = {
203 'enter' : {
204 help : 'edit mode',
205 help_index : 'aa',
206 handler : function (event) {
207 IPython.notebook.edit_mode();
208 return false;
209 }
210 },
211 'up' : {
212 help : 'select previous cell',
213 help_index : 'da',
214 handler : function (event) {
215 var index = IPython.notebook.get_selected_index();
216 if (index !== 0 && index !== null) {
217 IPython.notebook.select_prev();
218 var cell = IPython.notebook.get_selected_cell();
219 cell.focus_cell();
220 };
221 return false;
222 }
223 },
224 'down' : {
225 help : 'select next cell',
226 help_index : 'db',
227 handler : function (event) {
228 var index = IPython.notebook.get_selected_index();
229 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
230 IPython.notebook.select_next();
231 var cell = IPython.notebook.get_selected_cell();
232 cell.focus_cell();
233 };
234 return false;
235 }
236 },
237 'k' : {
238 help : 'select previous cell',
239 help_index : 'dc',
240 handler : function (event) {
241 var index = IPython.notebook.get_selected_index();
242 if (index !== 0 && index !== null) {
243 IPython.notebook.select_prev();
244 var cell = IPython.notebook.get_selected_cell();
245 cell.focus_cell();
246 };
247 return false;
248 }
249 },
250 'j' : {
251 help : 'select next cell',
252 help_index : 'dd',
253 handler : function (event) {
254 var index = IPython.notebook.get_selected_index();
255 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
256 IPython.notebook.select_next();
257 var cell = IPython.notebook.get_selected_cell();
258 cell.focus_cell();
259 };
260 return false;
261 }
262 },
263 'x' : {
264 help : 'cut cell',
265 help_index : 'ee',
266 handler : function (event) {
267 IPython.notebook.cut_cell();
268 return false;
269 }
270 },
271 'c' : {
272 help : 'copy cell',
273 help_index : 'ef',
274 handler : function (event) {
275 IPython.notebook.copy_cell();
276 return false;
277 }
278 },
279 'v' : {
280 help : 'paste cell below',
281 help_index : 'eg',
282 handler : function (event) {
283 IPython.notebook.paste_cell_below();
284 return false;
285 }
286 },
287 'd' : {
288 help : 'delete cell (press twice)',
289 help_index : 'ei',
290 handler : function (event) {
291 var dc = IPython.keyboard_manager._delete_count;
292 if (dc === 0) {
293 IPython.keyboard_manager._delete_count = 1;
294 setTimeout(function () {
295 IPython.keyboard_manager._delete_count = 0;
296 }, 800);
297 } else if (dc === 1) {
298 IPython.notebook.delete_cell();
299 IPython.keyboard_manager._delete_count = 0;
300 }
301 return false;
302 }
303 },
304 'a' : {
305 help : 'insert cell above',
306 help_index : 'ec',
307 handler : function (event) {
308 IPython.notebook.insert_cell_above('code');
309 IPython.notebook.select_prev();
310 IPython.notebook.focus_cell();
311 return false;
312 }
313 },
314 'b' : {
315 help : 'insert cell below',
316 help_index : 'ed',
317 handler : function (event) {
318 IPython.notebook.insert_cell_below('code');
319 IPython.notebook.select_next();
320 IPython.notebook.focus_cell();
321 return false;
322 }
323 },
324 'y' : {
325 help : 'to code',
326 help_index : 'ca',
327 handler : function (event) {
328 IPython.notebook.to_code();
329 return false;
330 }
331 },
332 'm' : {
333 help : 'to markdown',
334 help_index : 'cb',
335 handler : function (event) {
336 IPython.notebook.to_markdown();
337 return false;
338 }
339 },
340 't' : {
341 help : 'to raw',
342 help_index : 'cc',
343 handler : function (event) {
344 IPython.notebook.to_raw();
345 return false;
346 }
347 },
348 '1' : {
349 help : 'to heading 1',
350 help_index : 'cd',
351 handler : function (event) {
352 IPython.notebook.to_heading(undefined, 1);
353 return false;
354 }
355 },
356 '2' : {
357 help : 'to heading 2',
358 help_index : 'ce',
359 handler : function (event) {
360 IPython.notebook.to_heading(undefined, 2);
361 return false;
362 }
363 },
364 '3' : {
365 help : 'to heading 3',
366 help_index : 'cf',
367 handler : function (event) {
368 IPython.notebook.to_heading(undefined, 3);
369 return false;
370 }
371 },
372 '4' : {
373 help : 'to heading 4',
374 help_index : 'cg',
375 handler : function (event) {
376 IPython.notebook.to_heading(undefined, 4);
377 return false;
378 }
379 },
380 '5' : {
381 help : 'to heading 5',
382 help_index : 'ch',
383 handler : function (event) {
384 IPython.notebook.to_heading(undefined, 5);
385 return false;
386 }
387 },
388 '6' : {
389 help : 'to heading 6',
390 help_index : 'ci',
391 handler : function (event) {
392 IPython.notebook.to_heading(undefined, 6);
393 return false;
394 }
395 },
396 'o' : {
397 help : 'toggle output',
398 help_index : 'gb',
399 handler : function (event) {
400 IPython.notebook.toggle_output();
401 return false;
402 }
403 },
404 'shift+o' : {
405 help : 'toggle output',
406 help_index : 'gc',
407 handler : function (event) {
408 IPython.notebook.toggle_output_scroll();
409 return false;
410 }
411 },
412 's' : {
413 help : 'save notebook',
414 help_index : 'fa',
415 handler : function (event) {
416 IPython.notebook.save_checkpoint();
417 return false;
418 }
419 },
420 'ctrl+j' : {
421 help : 'move cell down',
422 help_index : 'eb',
423 handler : function (event) {
424 IPython.notebook.move_cell_down();
425 return false;
426 }
427 },
428 'ctrl+k' : {
429 help : 'move cell up',
430 help_index : 'ea',
431 handler : function (event) {
432 IPython.notebook.move_cell_up();
433 return false;
434 }
435 },
436 'l' : {
437 help : 'toggle line numbers',
438 help_index : 'ga',
439 handler : function (event) {
440 IPython.notebook.cell_toggle_line_numbers();
441 return false;
442 }
443 },
444 'i' : {
445 help : 'interrupt kernel',
446 help_index : 'ha',
447 handler : function (event) {
448 IPython.notebook.kernel.interrupt();
449 return false;
450 }
451 },
452 '.' : {
453 help : 'restart kernel',
454 help_index : 'hb',
455 handler : function (event) {
456 IPython.notebook.restart_kernel();
457 return false;
458 }
459 },
460 'h' : {
461 help : 'keyboard shortcuts',
462 help_index : 'gd',
463 handler : function (event) {
464 IPython.quick_help.show_keyboard_shortcuts();
465 return false;
466 }
467 },
468 'z' : {
469 help : 'undo last delete',
470 help_index : 'eh',
471 handler : function (event) {
472 IPython.notebook.undelete_cell();
473 return false;
474 }
475 },
476 'shift+=' : {
477 help : 'merge cell below',
478 help_index : 'ej',
479 handler : function (event) {
480 IPython.notebook.merge_cell_below();
481 return false;
482 }
483 },
484 }
485
486
487 // Shortcut manager class
488
489 var ShortcutManager = function () {
490 this._shortcuts = {}
491 }
492
493 ShortcutManager.prototype.help = function () {
494 var help = [];
495 for (var shortcut in this._shortcuts) {
496 var help_string = this._shortcuts[shortcut]['help'];
497 var help_index = this._shortcuts[shortcut]['help_index'];
498 if (help_string) {
499 help.push({
500 shortcut: shortcut,
501 help: help_string,
502 help_index: help_index}
503 );
504 }
505 }
506 help.sort(function (a, b) {
507 if (a.help_index > b.help_index)
508 return 1;
509 if (a.help_index < b.help_index)
510 return -1;
511 return 0;
512 });
513 return help;
514 }
515
516 ShortcutManager.prototype.normalize_key = function (key) {
517 return inv_keycodes[keycodes[key]];
518 }
519
520 ShortcutManager.prototype.normalize_shortcut = function (shortcut) {
521 // Sort a sequence of + separated modifiers into the order alt+ctrl+meta+shift
522 var values = shortcut.split("+");
523 if (values.length === 1) {
524 return this.normalize_key(values[0])
525 } else {
526 var modifiers = values.slice(0,-1);
527 var key = this.normalize_key(values[values.length-1]);
528 modifiers.sort();
529 return modifiers.join('+') + '+' + key;
530 }
531 }
532
533 ShortcutManager.prototype.event_to_shortcut = function (event) {
534 // Convert a jQuery keyboard event to a strong based keyboard shortcut
535 var shortcut = '';
536 var key = inv_keycodes[event.which]
537 if (event.altKey && key !== 'alt') {shortcut += 'alt+';}
538 if (event.ctrlKey && key !== 'ctrl') {shortcut += 'ctrl+';}
539 if (event.metaKey && key !== 'meta') {shortcut += 'meta+';}
540 if (event.shiftKey && key !== 'shift') {shortcut += 'shift+';}
541 shortcut += key;
542 return shortcut
543 }
544
545 ShortcutManager.prototype.clear_shortcuts = function () {
546 this._shortcuts = {};
547 }
548
549 ShortcutManager.prototype.add_shortcut = function (shortcut, data) {
550 if (typeof(data) === 'function') {
551 data = {help: '', help_index: '', handler: data}
552 }
553 data.help_index = data.help_index || '';
554 data.help = data.help || '';
555 if (data.help_index === '') {
556 data.help_index = 'zz';
557 }
558 shortcut = this.normalize_shortcut(shortcut);
559 this._shortcuts[shortcut] = data;
560 }
561
562 ShortcutManager.prototype.add_shortcuts = function (data) {
563 for (var shortcut in data) {
564 this.add_shortcut(shortcut, data[shortcut]);
565 }
566 }
567
568 ShortcutManager.prototype.remove_shortcut = function (shortcut) {
569 shortcut = this.normalize_shortcut(shortcut);
570 delete this._shortcuts[shortcut];
571 }
572
573 ShortcutManager.prototype.call_handler = function (event) {
574 var shortcut = this.event_to_shortcut(event);
575 var data = this._shortcuts[shortcut];
576 if (data !== undefined) {
577 var handler = data['handler'];
578 if (handler !== undefined) {
579 return handler(event);
580 }
581 }
582 return true;
583 }
584
585
586
587 // Main keyboard manager for the notebook
588
589 var KeyboardManager = function () {
590 this.mode = 'command';
591 this.enabled = true;
592 this._delete_count = 0;
593 this.bind_events();
594 this.command_shortcuts = new ShortcutManager();
595 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
596 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
597 this.edit_shortcuts = new ShortcutManager();
598 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
599 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
600 };
601
602 KeyboardManager.prototype.bind_events = function () {
603 var that = this;
604 $(document).keydown(function (event) {
605 return that.handle_keydown(event);
606 });
607 };
608
609 KeyboardManager.prototype.handle_keydown = function (event) {
610 var notebook = IPython.notebook;
611
612 if (event.which === keycodes['esc']) {
613 // Intercept escape at highest level to avoid closing
614 // websocket connection with firefox
615 event.preventDefault();
616 }
617
618 if (!this.enabled) {
619 if (event.which === keycodes['esc']) {
620 // ESC
621 notebook.command_mode();
622 return false;
623 }
624 return true;
625 }
626
627 if (this.mode === 'edit') {
628 return this.edit_shortcuts.call_handler(event);
629 } else if (this.mode === 'command') {
630 return this.command_shortcuts.call_handler(event);
631 }
632 return true;
633 }
634
635 KeyboardManager.prototype.edit_mode = function () {
636 this.last_mode = this.mode;
637 this.mode = 'edit';
638 }
639
640 KeyboardManager.prototype.command_mode = function () {
641 this.last_mode = this.mode;
642 this.mode = 'command';
643 }
644
645 KeyboardManager.prototype.enable = function () {
646 this.enabled = true;
647 }
648
649 KeyboardManager.prototype.disable = function () {
650 this.enabled = false;
651 }
652
653 KeyboardManager.prototype.register_events = function (e) {
654 var that = this;
655 e.on('focusin', function () {
656 that.command_mode();
657 that.disable();
658 });
659 e.on('focusout', function () {
660 that.command_mode();
661 that.enable();
662 });
663 // There are times (raw_input) where we remove the element from the DOM before
664 // focusout is called. In this case we bind to the remove event of jQueryUI,
665 // which gets triggered upon removal.
666 e.on('remove', function () {
667 that.command_mode();
668 that.enable();
669 });
670 }
671
672
673 IPython.keycodes = keycodes;
674 IPython.inv_keycodes = inv_keycodes;
675 IPython.default_common_shortcuts = default_common_shortcuts;
676 IPython.default_edit_shortcuts = default_edit_shortcuts;
677 IPython.default_command_shortcuts = default_command_shortcuts;
678 IPython.ShortcutManager = ShortcutManager;
679 IPython.KeyboardManager = KeyboardManager;
680
681 return IPython;
682
683 }(IPython));
@@ -0,0 +1,421 b''
1 {
2 "metadata": {
3 "name": ""
4 },
5 "nbformat": 3,
6 "nbformat_minor": 0,
7 "worksheets": [
8 {
9 "cells": [
10 {
11 "cell_type": "heading",
12 "level": 1,
13 "metadata": {},
14 "source": [
15 "User Interface"
16 ]
17 },
18 {
19 "cell_type": "markdown",
20 "metadata": {},
21 "source": [
22 "This notebook describes the user interface of the IPython Notebook. This includes both mouse and keyboard based navigation and interaction.\n",
23 "\n",
24 "<div class=\"alert\" style=\"margin: 10px\">\n",
25 "As of IPython 2.0, the user interface has changed significantly. Because of this we highly recommend existing users to review this information after upgrading to IPython 2.0. All new users of IPython should review this information as well.\n",
26 "</div>"
27 ]
28 },
29 {
30 "cell_type": "heading",
31 "level": 2,
32 "metadata": {},
33 "source": [
34 "Modal editor"
35 ]
36 },
37 {
38 "cell_type": "markdown",
39 "metadata": {},
40 "source": [
41 "Starting with IPython 2.0, the IPython Notebook has a modal user interface. This means that the keyboard does different things depending on which mode the Notebook is in. There are two modes: edit mode and command mode."
42 ]
43 },
44 {
45 "cell_type": "heading",
46 "level": 3,
47 "metadata": {},
48 "source": [
49 "Edit mode"
50 ]
51 },
52 {
53 "cell_type": "markdown",
54 "metadata": {},
55 "source": [
56 "Edit mode is indicated by a green cell border and a prompt showing in the editor area:\n",
57 "\n",
58 "<img src=\"images/edit_mode.png\">\n",
59 "\n",
60 "When a cell is in edit mode, you can type into the cell, like a normal text editor.\n",
61 "\n",
62 "<div class=\"alert alert-success\" style=\"margin: 10px\">\n",
63 "Enter edit mode by pressing `enter` or using the mouse to click on a cell's editor area.\n",
64 "</div>"
65 ]
66 },
67 {
68 "cell_type": "heading",
69 "level": 3,
70 "metadata": {},
71 "source": [
72 "Command mode"
73 ]
74 },
75 {
76 "cell_type": "markdown",
77 "metadata": {},
78 "source": [
79 "Command mode is indicated by a grey cell border:\n",
80 "\n",
81 "<img src=\"images/command_mode.png\">\n",
82 "\n",
83 "When you are in command mode, you are able to edit the notebook as a whole, but not type into individual cells. Most importantly, in command mode, the keyboard is mapped to a set of shortcuts that let you perform notebook and cell actions efficiently. For example, if you are in command mode and you press `c`, you will copy the current cell - no modifier is needed.\n",
84 "\n",
85 "<div class=\"alert alert-error\" style=\"margin: 10px\">\n",
86 "Don't try to type into a cell in command mode; unexpected things will happen!\n",
87 "</div>\n",
88 "\n",
89 "<div class=\"alert alert-success\" style=\"margin: 10px\">\n",
90 "Enter command mode by pressing `esc` or using the mouse to click *outside* a cell's editor area.\n",
91 "</div>"
92 ]
93 },
94 {
95 "cell_type": "heading",
96 "level": 2,
97 "metadata": {},
98 "source": [
99 "Mouse navigation"
100 ]
101 },
102 {
103 "cell_type": "markdown",
104 "metadata": {},
105 "source": [
106 "All navigation and actions in the Notebook are available using the mouse through the menubar and toolbar, which are both above the main Notebook area:\n",
107 "\n",
108 "<img src=\"images/menubar_toolbar.png\">"
109 ]
110 },
111 {
112 "cell_type": "markdown",
113 "metadata": {},
114 "source": [
115 "The first idea of mouse based navigation is that **cells can be selected by clicking on them.** The currently selected cell gets a grey or green border depending on whether the notebook is in edit or command mode. If you click inside a cell's editor area, you will enter edit mode. If you click on the prompt or output area of a cell you will enter command mode.\n",
116 "\n",
117 "If you are running this notebook in a live session (not on http://nbviewer.ipython.org) try selecting different cells and going between edit and command mode. Try typing into a cell."
118 ]
119 },
120 {
121 "cell_type": "markdown",
122 "metadata": {},
123 "source": [
124 "The second idea of mouse based navigation is that **cell actions usually apply to the currently selected cell**. Thus if you want to run the code in a cell, you would select it and click the \"Play\" button in the toolbar or the \"Cell:Run\" menu item. Similarly, to copy a cell you would select it and click the \"Copy\" button in the toolbar or the \"Edit:Copy\" menu item. With this simple pattern, you should be able to do most everything you need with the mouse.\n",
125 "\n",
126 "Markdown and heading cells have one other state that can be modified with the mouse. These cells can either be rendered or unrendered. When they are rendered, you will see a nice formatted representation of the cell's contents. When they are unrendered, you will see the raw text source of the cell. To render the selected cell with the mouse, click the \"Play\" button in the toolbar or the \"Cell:Run\" menu item. To unrender the selected cell, double click on the cell."
127 ]
128 },
129 {
130 "cell_type": "heading",
131 "level": 2,
132 "metadata": {},
133 "source": [
134 "Keyboard Navigation"
135 ]
136 },
137 {
138 "cell_type": "markdown",
139 "metadata": {},
140 "source": [
141 "The modal user interface of the IPython Notebook has been optimized for efficient keyboard usage. This is made possible by having two different sets of keyboard shortcuts: one set that is active in edit mode and another in command mode.\n",
142 "\n",
143 "The most important keyboard shortcuts are `enter`, which enters edit mode, and `esc`, which enters command mode.\n",
144 "\n",
145 "In edit mode, most of the keyboard is dedicated to typing into the cell's editor. Thus, in edit mode there are relatively few shortcuts:"
146 ]
147 },
148 {
149 "cell_type": "markdown",
150 "metadata": {},
151 "source": [
152 "The `display_edit_shortcuts()` function used here is defined in the [Utilities section](#Utilities) at the bottom of this notebook."
153 ]
154 },
155 {
156 "cell_type": "code",
157 "collapsed": false,
158 "input": [
159 "display_edit_shortcuts()"
160 ],
161 "language": "python",
162 "metadata": {},
163 "outputs": [
164 {
165 "html": [
166 "<div class=\"hbox\"><div class=\"box-flex0\"><div class=\"quickhelp\"><span class=\"shortcut_key\">esc</span><span class=\"shortcut_descr\"> : command mode</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+m</span><span class=\"shortcut_descr\"> : command mode</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">shift+enter</span><span class=\"shortcut_descr\"> : run cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+enter</span><span class=\"shortcut_descr\"> : run cell, select below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">alt+enter</span><span class=\"shortcut_descr\"> : run cell, insert below</span></div></div><div class=\"box-flex0\"><div class=\"quickhelp\"><span class=\"shortcut_key\">up</span><span class=\"shortcut_descr\"> : select previous cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">down</span><span class=\"shortcut_descr\"> : select next cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">alt+-</span><span class=\"shortcut_descr\"> : split cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">meta+s</span><span class=\"shortcut_descr\"> : save notebook</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+s</span><span class=\"shortcut_descr\"> : save notebook</span></div></div></div>"
167 ],
168 "output_type": "display_data"
169 },
170 {
171 "javascript": [
172 "var help = IPython.quick_help.build_edit_help();\n",
173 "help.children().first().remove();\n",
174 "this.append_output({output_type: 'display_data', html: help.html()});"
175 ],
176 "metadata": {},
177 "output_type": "display_data",
178 "text": [
179 "<IPython.core.display.Javascript at 0x10e441250>"
180 ]
181 }
182 ],
183 "prompt_number": 14
184 },
185 {
186 "cell_type": "markdown",
187 "metadata": {},
188 "source": [
189 "There are two other keyboard shortcuts in edit mode that are not listed here:\n",
190 "\n",
191 "* `tab`: trigger \"tab\" completion\n",
192 "* `shift+tab`: open the tooltip"
193 ]
194 },
195 {
196 "cell_type": "markdown",
197 "metadata": {},
198 "source": [
199 "In command mode, the entire keyboard is available for shortcuts:"
200 ]
201 },
202 {
203 "cell_type": "code",
204 "collapsed": false,
205 "input": [
206 "display_command_shortcuts()"
207 ],
208 "language": "python",
209 "metadata": {},
210 "outputs": [
211 {
212 "html": [
213 "<div class=\"hbox\"><div class=\"box-flex0\"><div class=\"quickhelp\"><span class=\"shortcut_key\">enter</span><span class=\"shortcut_descr\"> : edit mode</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">shift+enter</span><span class=\"shortcut_descr\"> : run cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+enter</span><span class=\"shortcut_descr\"> : run cell, select below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">alt+enter</span><span class=\"shortcut_descr\"> : run cell, insert below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">y</span><span class=\"shortcut_descr\"> : to code</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">m</span><span class=\"shortcut_descr\"> : to markdown</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">t</span><span class=\"shortcut_descr\"> : to raw</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">1</span><span class=\"shortcut_descr\"> : to heading 1</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">2</span><span class=\"shortcut_descr\"> : to heading 2</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">3</span><span class=\"shortcut_descr\"> : to heading 3</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">4</span><span class=\"shortcut_descr\"> : to heading 4</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">5</span><span class=\"shortcut_descr\"> : to heading 5</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">6</span><span class=\"shortcut_descr\"> : to heading 6</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">up</span><span class=\"shortcut_descr\"> : select previous cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">down</span><span class=\"shortcut_descr\"> : select next cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">k</span><span class=\"shortcut_descr\"> : select previous cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">j</span><span class=\"shortcut_descr\"> : select next cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+k</span><span class=\"shortcut_descr\"> : move cell up</span></div></div><div class=\"box-flex0\"><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+j</span><span class=\"shortcut_descr\"> : move cell down</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">a</span><span class=\"shortcut_descr\"> : insert cell above</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">b</span><span class=\"shortcut_descr\"> : insert cell below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">x</span><span class=\"shortcut_descr\"> : cut cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">c</span><span class=\"shortcut_descr\"> : copy cell</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">v</span><span class=\"shortcut_descr\"> : paste cell below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">z</span><span class=\"shortcut_descr\"> : undo last delete</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">d</span><span class=\"shortcut_descr\"> : delete cell (press twice)</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">shift+=</span><span class=\"shortcut_descr\"> : merge cell below</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">s</span><span class=\"shortcut_descr\"> : save notebook</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">meta+s</span><span class=\"shortcut_descr\"> : save notebook</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">ctrl+s</span><span class=\"shortcut_descr\"> : save notebook</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">l</span><span class=\"shortcut_descr\"> : toggle line numbers</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">o</span><span class=\"shortcut_descr\"> : toggle output</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">shift+o</span><span class=\"shortcut_descr\"> : toggle output</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">h</span><span class=\"shortcut_descr\"> : keyboard shortcuts</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">i</span><span class=\"shortcut_descr\"> : interrupt kernel</span></div><div class=\"quickhelp\"><span class=\"shortcut_key\">.</span><span class=\"shortcut_descr\"> : restart kernel</span></div></div></div>"
214 ],
215 "output_type": "display_data"
216 },
217 {
218 "javascript": [
219 "var help = IPython.quick_help.build_command_help();\n",
220 "help.children().first().remove();\n",
221 "this.append_output({output_type: 'display_data', html: help.html()});"
222 ],
223 "metadata": {},
224 "output_type": "display_data",
225 "text": [
226 "<IPython.core.display.Javascript at 0x10e441410>"
227 ]
228 }
229 ],
230 "prompt_number": 15
231 },
232 {
233 "cell_type": "markdown",
234 "metadata": {},
235 "source": [
236 "Here the rough order in which we recommend learning the command mode shortcuts:\n",
237 "\n",
238 "1. Basic navigation: `enter`, `shift-enter`, `up/k`, `down/j`\n",
239 "2. Saving the notebook: `s`\n",
240 "2. Cell types: `y`, `m`, `1-6`, `t`\n",
241 "3. Cell creation and movement: `a`, `b`, `ctrl+k`, `ctrl+j`\n",
242 "4. Cell editing: `x`, `c`, `v`, `d`, `z`, `shift+=`\n",
243 "5. Kernel operations: `i`, `.`"
244 ]
245 },
246 {
247 "cell_type": "heading",
248 "level": 2,
249 "metadata": {},
250 "source": [
251 "Keyboard shortcut customization"
252 ]
253 },
254 {
255 "cell_type": "markdown",
256 "metadata": {},
257 "source": [
258 "Starting with IPython 2.0 keyboard shortcuts in command and edit mode are fully customizable. These customizations are made using the IPython JavaScript API. Here is an example that makes the `r` key available for running a cell:"
259 ]
260 },
261 {
262 "cell_type": "code",
263 "collapsed": false,
264 "input": [
265 "%%javascript\n",
266 "\n",
267 "IPython.keyboard_manager.command_shortcuts.add_shortcut('r', {\n",
268 " help : 'run cell',\n",
269 " help_index : 'zz',\n",
270 " handler : function (event) {\n",
271 " IPython.notebook.execute_cell();\n",
272 " return false;\n",
273 " }}\n",
274 ");"
275 ],
276 "language": "python",
277 "metadata": {},
278 "outputs": [
279 {
280 "javascript": [
281 "\n",
282 "IPython.keyboard_manager.command_shortcuts.add_shortcut('r', {\n",
283 " help : 'run cell',\n",
284 " help_index : 'aa',\n",
285 " handler : function (event) {\n",
286 " IPython.notebook.execute_cell();\n",
287 " return false;\n",
288 " }}\n",
289 ");"
290 ],
291 "metadata": {},
292 "output_type": "display_data",
293 "text": [
294 "<IPython.core.display.Javascript at 0x1019ba990>"
295 ]
296 }
297 ],
298 "prompt_number": 6
299 },
300 {
301 "cell_type": "markdown",
302 "metadata": {},
303 "source": [
304 "There are a couple of points to mention about this API:\n",
305 "\n",
306 "* The `help_index` field is used to sort the shortcuts in the Keyboard Shortcuts help dialog. It defaults to `zz`.\n",
307 "* When a handler returns `false` it indicates that the event should stop propagating and the default action should not be performed. For further details about the `event` object or event handling, see the jQuery docs.\n",
308 "* If you don't need a `help` or `help_index` field, you can simply pass a function as the second argument to `add_shortcut`."
309 ]
310 },
311 {
312 "cell_type": "code",
313 "collapsed": false,
314 "input": [
315 "%%javascript\n",
316 "\n",
317 "IPython.keyboard_manager.command_shortcuts.add_shortcut('r', function (event) {\n",
318 " IPython.notebook.execute_cell();\n",
319 " return false;\n",
320 "});"
321 ],
322 "language": "python",
323 "metadata": {},
324 "outputs": [
325 {
326 "javascript": [
327 "\n",
328 "IPython.keyboard_manager.command_shortcuts.add_shortcut('r', function (event) {\n",
329 " IPython.notebook.execute_cell();\n",
330 " return false;\n",
331 "});"
332 ],
333 "metadata": {},
334 "output_type": "display_data",
335 "text": [
336 "<IPython.core.display.Javascript at 0x1019baf90>"
337 ]
338 }
339 ],
340 "prompt_number": 11
341 },
342 {
343 "cell_type": "markdown",
344 "metadata": {},
345 "source": [
346 "Likewise, to remove a shortcut, use `remove_shortcut`:"
347 ]
348 },
349 {
350 "cell_type": "code",
351 "collapsed": false,
352 "input": [
353 "%%javascript\n",
354 "\n",
355 "IPython.keyboard_manager.command_shortcuts.remove_shortcut('r');"
356 ],
357 "language": "python",
358 "metadata": {},
359 "outputs": [
360 {
361 "javascript": [
362 "\n",
363 "IPython.keyboard_manager.command_shortcuts.remove_shortcut('r');"
364 ],
365 "metadata": {},
366 "output_type": "display_data",
367 "text": [
368 "<IPython.core.display.Javascript at 0x1019ba950>"
369 ]
370 }
371 ],
372 "prompt_number": 8
373 },
374 {
375 "cell_type": "markdown",
376 "metadata": {},
377 "source": [
378 "If you want your keyboard shortcuts to be active for all of your notebooks, put the above API calls into your `custom.js` file."
379 ]
380 },
381 {
382 "cell_type": "heading",
383 "level": 2,
384 "metadata": {},
385 "source": [
386 "Utilities"
387 ]
388 },
389 {
390 "cell_type": "markdown",
391 "metadata": {},
392 "source": [
393 "We use the following functions to generate the keyboard shortcut listings above."
394 ]
395 },
396 {
397 "cell_type": "code",
398 "collapsed": false,
399 "input": [
400 "from IPython.display import Javascript, display\n",
401 "\n",
402 "t = \"\"\"var help = IPython.quick_help.build_{0}_help();\n",
403 "help.children().first().remove();\n",
404 "this.append_output({{output_type: 'display_data', html: help.html()}});\"\"\"\n",
405 "\n",
406 "def display_command_shortcuts():\n",
407 " display(Javascript(t.format('command')))\n",
408 "\n",
409 "def display_edit_shortcuts():\n",
410 " display(Javascript(t.format('edit'))) "
411 ],
412 "language": "python",
413 "metadata": {},
414 "outputs": [],
415 "prompt_number": 2
416 }
417 ],
418 "metadata": {}
419 }
420 ]
421 } No newline at end of file
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
1 NO CONTENT: new file 100644, binary diff hidden
NO CONTENT: new file 100644, binary diff hidden
@@ -65,13 +65,17 b' IPython.dialog = (function (IPython) {'
65 dialog.remove();
65 dialog.remove();
66 });
66 });
67 }
67 }
68 if (options.reselect_cell !== false) {
69 dialog.on("hidden", function () {
68 dialog.on("hidden", function () {
70 if (IPython.notebook) {
69 if (IPython.notebook) {
71 var cell = IPython.notebook.get_selected_cell();
70 var cell = IPython.notebook.get_selected_cell();
72 if (cell) cell.select();
71 if (cell) cell.select();
72 IPython.keyboard_manager.enable();
73 IPython.keyboard_manager.command_mode();
73 }
74 }
74 });
75 });
76
77 if (IPython.keyboard_manager) {
78 IPython.keyboard_manager.disable();
75 }
79 }
76
80
77 return dialog.modal(options);
81 return dialog.modal(options);
@@ -455,6 +455,26 b' IPython.utils = (function (IPython) {'
455 return M;
455 return M;
456 })();
456 })();
457
457
458 var is_or_has = function (a, b) {
459 // Is b a child of a or a itself?
460 return a.has(b).length !==0 || a.is(b);
461 }
462
463 var is_focused = function (e) {
464 // Is element e, or one of its children focused?
465 e = $(e);
466 var target = $(document.activeElement);
467 if (target.length > 0) {
468 if (is_or_has(e, target)) {
469 return true;
470 } else {
471 return false;
472 }
473 } else {
474 return false;
475 }
476 }
477
458
478
459 return {
479 return {
460 regex_split : regex_split,
480 regex_split : regex_split,
@@ -475,7 +495,9 b' IPython.utils = (function (IPython) {'
475 encode_uri_components : encode_uri_components,
495 encode_uri_components : encode_uri_components,
476 splitext : splitext,
496 splitext : splitext,
477 always_new : always_new,
497 always_new : always_new,
478 browser : browser
498 browser : browser,
499 is_or_has : is_or_has,
500 is_focused : is_focused
479 };
501 };
480
502
481 }(IPython));
503 }(IPython));
@@ -39,6 +39,8 b' var IPython = (function (IPython) {'
39 this.placeholder = options.placeholder || '';
39 this.placeholder = options.placeholder || '';
40 this.read_only = options.cm_config.readOnly;
40 this.read_only = options.cm_config.readOnly;
41 this.selected = false;
41 this.selected = false;
42 this.rendered = false;
43 this.mode = 'command';
42 this.metadata = {};
44 this.metadata = {};
43 // load this from metadata later ?
45 // load this from metadata later ?
44 this.user_highlight = 'auto';
46 this.user_highlight = 'auto';
@@ -60,6 +62,7 b' var IPython = (function (IPython) {'
60 if (this.element !== null) {
62 if (this.element !== null) {
61 this.element.data("cell", this);
63 this.element.data("cell", this);
62 this.bind_events();
64 this.bind_events();
65 this.init_classes();
63 }
66 }
64 };
67 };
65
68
@@ -97,6 +100,26 b' var IPython = (function (IPython) {'
97 Cell.prototype.create_element = function () {
100 Cell.prototype.create_element = function () {
98 };
101 };
99
102
103 Cell.prototype.init_classes = function () {
104 // Call after this.element exists to initialize the css classes
105 // related to selected, rendered and mode.
106 if (this.selected) {
107 this.element.addClass('selected');
108 } else {
109 this.element.addClass('unselected');
110 }
111 if (this.rendered) {
112 this.element.addClass('rendered');
113 } else {
114 this.element.addClass('unrendered');
115 }
116 if (this.mode === 'edit') {
117 this.element.addClass('edit_mode');
118 } else {
119 this.element.addClass('command_mode');
120 }
121 }
122
100
123
101 /**
124 /**
102 * Subclasses can implement override bind_events.
125 * Subclasses can implement override bind_events.
@@ -108,20 +131,41 b' var IPython = (function (IPython) {'
108 var that = this;
131 var that = this;
109 // We trigger events so that Cell doesn't have to depend on Notebook.
132 // We trigger events so that Cell doesn't have to depend on Notebook.
110 that.element.click(function (event) {
133 that.element.click(function (event) {
111 if (that.selected === false) {
134 if (!that.selected) {
112 $([IPython.events]).trigger('select.Cell', {'cell':that});
135 $([IPython.events]).trigger('select.Cell', {'cell':that});
113 }
136 };
114 });
137 });
115 that.element.focusin(function (event) {
138 that.element.focusin(function (event) {
116 if (that.selected === false) {
139 if (!that.selected) {
117 $([IPython.events]).trigger('select.Cell', {'cell':that});
140 $([IPython.events]).trigger('select.Cell', {'cell':that});
118 }
141 };
119 });
142 });
120 if (this.code_mirror) {
143 if (this.code_mirror) {
121 this.code_mirror.on("change", function(cm, change) {
144 this.code_mirror.on("change", function(cm, change) {
122 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
145 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
123 });
146 });
124 }
147 }
148 if (this.code_mirror) {
149 this.code_mirror.on('focus', function(cm, change) {
150 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
151 });
152 }
153 if (this.code_mirror) {
154 this.code_mirror.on('blur', function(cm, change) {
155 if (that.mode === 'edit') {
156 setTimeout(function () {
157 var isf = IPython.utils.is_focused;
158 var trigger = true;
159 if (isf('div#tooltip') || isf('div.completions')) {
160 trigger = false;
161 }
162 if (trigger) {
163 $([IPython.events]).trigger('command_mode.Cell', {cell: that});
164 }
165 }, 1);
166 }
167 });
168 }
125 };
169 };
126
170
127 /**
171 /**
@@ -136,40 +180,119 b' var IPython = (function (IPython) {'
136 };
180 };
137
181
138 /**
182 /**
139 * should be triggerd when cell is selected
183 * handle cell level logic when a cell is selected
140 * @method select
184 * @method select
185 * @return is the action being taken
141 */
186 */
142 Cell.prototype.select = function () {
187 Cell.prototype.select = function () {
188 if (!this.selected) {
143 this.element.addClass('selected');
189 this.element.addClass('selected');
190 this.element.removeClass('unselected');
144 this.selected = true;
191 this.selected = true;
192 return true;
193 } else {
194 return false;
195 }
145 };
196 };
146
197
147
148 /**
198 /**
149 * should be triggerd when cell is unselected
199 * handle cell level logic when a cell is unselected
150 * @method unselect
200 * @method unselect
201 * @return is the action being taken
151 */
202 */
152 Cell.prototype.unselect = function () {
203 Cell.prototype.unselect = function () {
204 if (this.selected) {
205 this.element.addClass('unselected');
153 this.element.removeClass('selected');
206 this.element.removeClass('selected');
154 this.selected = false;
207 this.selected = false;
208 return true;
209 } else {
210 return false;
211 }
155 };
212 };
156
213
157 /**
214 /**
158 * should be overritten by subclass
215 * handle cell level logic when a cell is rendered
159 * @method get_text
216 * @method render
217 * @return is the action being taken
160 */
218 */
161 Cell.prototype.get_text = function () {
219 Cell.prototype.render = function () {
220 if (!this.rendered) {
221 this.element.addClass('rendered');
222 this.element.removeClass('unrendered');
223 this.rendered = true;
224 return true;
225 } else {
226 return false;
227 }
162 };
228 };
163
229
164 /**
230 /**
165 * should be overritten by subclass
231 * handle cell level logic when a cell is unrendered
166 * @method set_text
232 * @method unrender
167 * @param {string} text
233 * @return is the action being taken
168 */
234 */
169 Cell.prototype.set_text = function (text) {
235 Cell.prototype.unrender = function () {
236 if (this.rendered) {
237 this.element.addClass('unrendered');
238 this.element.removeClass('rendered');
239 this.rendered = false;
240 return true;
241 } else {
242 return false;
243 }
170 };
244 };
171
245
172 /**
246 /**
247 * enter the command mode for the cell
248 * @method command_mode
249 * @return is the action being taken
250 */
251 Cell.prototype.command_mode = function () {
252 if (this.mode !== 'command') {
253 this.element.addClass('command_mode');
254 this.element.removeClass('edit_mode');
255 this.mode = 'command';
256 return true;
257 } else {
258 return false;
259 }
260 };
261
262 /**
263 * enter the edit mode for the cell
264 * @method command_mode
265 * @return is the action being taken
266 */
267 Cell.prototype.edit_mode = function () {
268 if (this.mode !== 'edit') {
269 this.element.addClass('edit_mode');
270 this.element.removeClass('command_mode');
271 this.mode = 'edit';
272 return true;
273 } else {
274 return false;
275 }
276 }
277
278 /**
279 * Focus the cell in the DOM sense
280 * @method focus_cell
281 */
282 Cell.prototype.focus_cell = function () {
283 this.element.focus();
284 }
285
286 /**
287 * Focus the editor area so a user can type
288 * @method focus_editor
289 */
290 Cell.prototype.focus_editor = function () {
291 this.refresh();
292 this.code_mirror.focus();
293 }
294
295 /**
173 * Refresh codemirror instance
296 * Refresh codemirror instance
174 * @method refresh
297 * @method refresh
175 */
298 */
@@ -177,20 +300,19 b' var IPython = (function (IPython) {'
177 this.code_mirror.refresh();
300 this.code_mirror.refresh();
178 };
301 };
179
302
180
181 /**
303 /**
182 * should be overritten by subclass
304 * should be overritten by subclass
183 * @method edit
305 * @method get_text
184 **/
306 */
185 Cell.prototype.edit = function () {
307 Cell.prototype.get_text = function () {
186 };
308 };
187
309
188
189 /**
310 /**
190 * should be overritten by subclass
311 * should be overritten by subclass
191 * @method render
312 * @method set_text
192 **/
313 * @param {string} text
193 Cell.prototype.render = function () {
314 */
315 Cell.prototype.set_text = function (text) {
194 };
316 };
195
317
196 /**
318 /**
@@ -74,7 +74,7 b' var IPython = (function (IPython) {'
74
74
75
75
76 var cm_overwrite_options = {
76 var cm_overwrite_options = {
77 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
77 onKeyEvent: $.proxy(this.handle_keyevent,this)
78 };
78 };
79
79
80 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
80 options = this.mergeopt(CodeCell, options, {cm_config:cm_overwrite_options});
@@ -139,6 +139,27 b' var IPython = (function (IPython) {'
139 this.completer = new IPython.Completer(this);
139 this.completer = new IPython.Completer(this);
140 };
140 };
141
141
142 /** @method bind_events */
143 CodeCell.prototype.bind_events = function () {
144 IPython.Cell.prototype.bind_events.apply(this);
145 var that = this;
146
147 this.element.focusout(
148 function() { that.auto_highlight(); }
149 );
150 };
151
152 CodeCell.prototype.handle_keyevent = function (editor, event) {
153
154 // console.log('CM', this.mode, event.which, event.type)
155
156 if (this.mode === 'command') {
157 return true;
158 } else if (this.mode === 'edit') {
159 return this.handle_codemirror_keyevent(editor, event);
160 }
161 };
162
142 /**
163 /**
143 * This method gets called in CodeMirror's onKeyDown/onKeyPress
164 * This method gets called in CodeMirror's onKeyDown/onKeyPress
144 * handlers and is used to provide custom key handling. Its return
165 * handlers and is used to provide custom key handling. Its return
@@ -151,8 +172,9 b' var IPython = (function (IPython) {'
151 var that = this;
172 var that = this;
152 // whatever key is pressed, first, cancel the tooltip request before
173 // whatever key is pressed, first, cancel the tooltip request before
153 // they are sent, and remove tooltip if any, except for tab again
174 // they are sent, and remove tooltip if any, except for tab again
175 var tooltip_closed = null;
154 if (event.type === 'keydown' && event.which != key.TAB ) {
176 if (event.type === 'keydown' && event.which != key.TAB ) {
155 IPython.tooltip.remove_and_cancel_tooltip();
177 tooltip_closed = IPython.tooltip.remove_and_cancel_tooltip();
156 }
178 }
157
179
158 var cur = editor.getCursor();
180 var cur = editor.getCursor();
@@ -160,7 +182,7 b' var IPython = (function (IPython) {'
160 this.auto_highlight();
182 this.auto_highlight();
161 }
183 }
162
184
163 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey)) {
185 if (event.keyCode === key.ENTER && (event.shiftKey || event.ctrlKey || event.altKey)) {
164 // Always ignore shift-enter in CodeMirror as we handle it.
186 // Always ignore shift-enter in CodeMirror as we handle it.
165 return true;
187 return true;
166 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
188 } else if (event.which === 40 && event.type === 'keypress' && IPython.tooltip.time_before_tooltip >= 0) {
@@ -179,8 +201,32 b' var IPython = (function (IPython) {'
179 } else {
201 } else {
180 return true;
202 return true;
181 }
203 }
182 } else if (event.which === key.ESC) {
204 } else if (event.which === key.ESC && event.type === 'keydown') {
183 return IPython.tooltip.remove_and_cancel_tooltip(true);
205 // First see if the tooltip is active and if so cancel it.
206 if (tooltip_closed) {
207 // The call to remove_and_cancel_tooltip above in L177 doesn't pass
208 // force=true. Because of this it won't actually close the tooltip
209 // if it is in sticky mode. Thus, we have to check again if it is open
210 // and close it with force=true.
211 if (!IPython.tooltip._hidden) {
212 IPython.tooltip.remove_and_cancel_tooltip(true);
213 }
214 // If we closed the tooltip, don't let CM or the global handlers
215 // handle this event.
216 event.stop();
217 return true;
218 }
219 if (that.code_mirror.options.keyMap === "vim-insert") {
220 // vim keyMap is active and in insert mode. In this case we leave vim
221 // insert mode, but remain in notebook edit mode.
222 // Let' CM handle this event and prevent global handling.
223 event.stop();
224 return false;
225 } else {
226 // vim keyMap is not active. Leave notebook edit mode.
227 // Don't let CM handle the event, defer to global handling.
228 return true;
229 }
184 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
230 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
185 // If we are not at the bottom, let CM handle the down arrow and
231 // If we are not at the bottom, let CM handle the down arrow and
186 // prevent the global keydown handler from handling it.
232 // prevent the global keydown handler from handling it.
@@ -190,7 +236,7 b' var IPython = (function (IPython) {'
190 } else {
236 } else {
191 return true;
237 return true;
192 }
238 }
193 } else if (event.keyCode === key.TAB && event.type == 'keydown' && event.shiftKey) {
239 } else if (event.keyCode === key.TAB && event.type === 'keydown' && event.shiftKey) {
194 if (editor.somethingSelected()){
240 if (editor.somethingSelected()){
195 var anchor = editor.getCursor("anchor");
241 var anchor = editor.getCursor("anchor");
196 var head = editor.getCursor("head");
242 var head = editor.getCursor("head");
@@ -225,7 +271,6 b' var IPython = (function (IPython) {'
225 return false;
271 return false;
226 };
272 };
227
273
228
229 // Kernel related calls.
274 // Kernel related calls.
230
275
231 CodeCell.prototype.set_kernel = function (kernel) {
276 CodeCell.prototype.set_kernel = function (kernel) {
@@ -304,15 +349,32 b' var IPython = (function (IPython) {'
304 // Basic cell manipulation.
349 // Basic cell manipulation.
305
350
306 CodeCell.prototype.select = function () {
351 CodeCell.prototype.select = function () {
307 IPython.Cell.prototype.select.apply(this);
352 var cont = IPython.Cell.prototype.select.apply(this);
353 if (cont) {
308 this.code_mirror.refresh();
354 this.code_mirror.refresh();
309 this.code_mirror.focus();
310 this.auto_highlight();
355 this.auto_highlight();
311 // We used to need an additional refresh() after the focus, but
356 }
312 // it appears that this has been fixed in CM. This bug would show
357 return cont;
313 // up on FF when a newly loaded markdown cell was edited.
358 };
359
360 CodeCell.prototype.render = function () {
361 var cont = IPython.Cell.prototype.render.apply(this);
362 // Always execute, even if we are already in the rendered state
363 return cont;
364 };
365
366 CodeCell.prototype.unrender = function () {
367 // CodeCell is always rendered
368 return false;
314 };
369 };
315
370
371 CodeCell.prototype.edit_mode = function () {
372 var cont = IPython.Cell.prototype.edit_mode.apply(this);
373 if (cont) {
374 this.focus_editor();
375 }
376 return cont;
377 }
316
378
317 CodeCell.prototype.select_all = function () {
379 CodeCell.prototype.select_all = function () {
318 var start = {line: 0, ch: 0};
380 var start = {line: 0, ch: 0};
@@ -218,6 +218,8 b' var IPython = (function (IPython) {'
218 this.complete = $('<div/>').addClass('completions');
218 this.complete = $('<div/>').addClass('completions');
219 this.complete.attr('id', 'complete');
219 this.complete.attr('id', 'complete');
220
220
221 // Currently webkit doesn't use the size attr correctly. See:
222 // https://code.google.com/p/chromium/issues/detail?id=4579
221 this.sel = $('<select style="width: auto"/>')
223 this.sel = $('<select style="width: auto"/>')
222 .attr('multiple', 'true')
224 .attr('multiple', 'true')
223 .attr('size', Math.min(10, this.raw_result.length));
225 .attr('size', Math.min(10, this.raw_result.length));
@@ -255,6 +257,7 b' var IPython = (function (IPython) {'
255 this.build_gui_list(this.raw_result);
257 this.build_gui_list(this.raw_result);
256
258
257 this.sel.focus();
259 this.sel.focus();
260 IPython.keyboard_manager.disable();
258 // Opera sometimes ignores focusing a freshly created node
261 // Opera sometimes ignores focusing a freshly created node
259 if (window.opera) setTimeout(function () {
262 if (window.opera) setTimeout(function () {
260 if (!this.done) this.sel.focus();
263 if (!this.done) this.sel.focus();
@@ -279,6 +282,7 b' var IPython = (function (IPython) {'
279 if (this.done) return;
282 if (this.done) return;
280 this.done = true;
283 this.done = true;
281 $('.completions').remove();
284 $('.completions').remove();
285 IPython.keyboard_manager.enable();
282 }
286 }
283
287
284 Completer.prototype.pick = function () {
288 Completer.prototype.pick = function () {
@@ -62,6 +62,7 b' function (marked) {'
62 IPython.quick_help = new IPython.QuickHelp();
62 IPython.quick_help = new IPython.QuickHelp();
63 IPython.login_widget = new IPython.LoginWidget('span#login_widget',{baseProjectUrl:baseProjectUrl});
63 IPython.login_widget = new IPython.LoginWidget('span#login_widget',{baseProjectUrl:baseProjectUrl});
64 IPython.notebook = new IPython.Notebook('div#notebook',{baseProjectUrl:baseProjectUrl, notebookPath:notebookPath, notebookName:notebookName});
64 IPython.notebook = new IPython.Notebook('div#notebook',{baseProjectUrl:baseProjectUrl, notebookPath:notebookPath, notebookName:notebookName});
65 IPython.keyboard_manager = new IPython.KeyboardManager();
65 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
66 IPython.save_widget = new IPython.SaveWidget('span#save_widget');
66 IPython.menubar = new IPython.MenuBar('#menubar',{baseProjectUrl:baseProjectUrl, notebookPath: notebookPath})
67 IPython.menubar = new IPython.MenuBar('#menubar',{baseProjectUrl:baseProjectUrl, notebookPath: notebookPath})
67 IPython.toolbar = new IPython.MainToolBar('#maintoolbar-container')
68 IPython.toolbar = new IPython.MainToolBar('#maintoolbar-container')
@@ -41,6 +41,8 b' var IPython = (function (IPython) {'
41 icon : 'icon-plus-sign',
41 icon : 'icon-plus-sign',
42 callback : function () {
42 callback : function () {
43 IPython.notebook.insert_cell_below('code');
43 IPython.notebook.insert_cell_below('code');
44 IPython.notebook.select_next();
45 IPython.notebook.focus_cell();
44 }
46 }
45 }
47 }
46 ],'insert_above_below');
48 ],'insert_above_below');
@@ -98,7 +100,7 b' var IPython = (function (IPython) {'
98 label : 'Run Cell',
100 label : 'Run Cell',
99 icon : 'icon-play',
101 icon : 'icon-play',
100 callback : function () {
102 callback : function () {
101 IPython.notebook.execute_selected_cell();
103 IPython.notebook.execute_cell();
102 }
104 }
103 },
105 },
104 {
106 {
@@ -161,7 +161,7 b' var IPython = (function (IPython) {'
161 IPython.notebook.delete_cell();
161 IPython.notebook.delete_cell();
162 });
162 });
163 this.element.find('#undelete_cell').click(function () {
163 this.element.find('#undelete_cell').click(function () {
164 IPython.notebook.undelete();
164 IPython.notebook.undelete_cell();
165 });
165 });
166 this.element.find('#split_cell').click(function () {
166 this.element.find('#split_cell').click(function () {
167 IPython.notebook.split_cell();
167 IPython.notebook.split_cell();
@@ -200,16 +200,21 b' var IPython = (function (IPython) {'
200 // Insert
200 // Insert
201 this.element.find('#insert_cell_above').click(function () {
201 this.element.find('#insert_cell_above').click(function () {
202 IPython.notebook.insert_cell_above('code');
202 IPython.notebook.insert_cell_above('code');
203 IPython.notebook.select_prev();
203 });
204 });
204 this.element.find('#insert_cell_below').click(function () {
205 this.element.find('#insert_cell_below').click(function () {
205 IPython.notebook.insert_cell_below('code');
206 IPython.notebook.insert_cell_below('code');
207 IPython.notebook.select_next();
206 });
208 });
207 // Cell
209 // Cell
208 this.element.find('#run_cell').click(function () {
210 this.element.find('#run_cell').click(function () {
209 IPython.notebook.execute_selected_cell();
211 IPython.notebook.execute_cell();
212 });
213 this.element.find('#run_cell_select_below').click(function () {
214 IPython.notebook.execute_cell_and_select_below();
210 });
215 });
211 this.element.find('#run_cell_in_place').click(function () {
216 this.element.find('#run_cell_insert_below').click(function () {
212 IPython.notebook.execute_selected_cell({terminal:true});
217 IPython.notebook.execute_cell_and_insert_below();
213 });
218 });
214 this.element.find('#run_all_cells').click(function () {
219 this.element.find('#run_all_cells').click(function () {
215 IPython.notebook.execute_all_cells();
220 IPython.notebook.execute_all_cells();
This diff has been collapsed as it changes many lines, (516 lines changed) Show them Hide them
@@ -13,7 +13,6 b' var IPython = (function (IPython) {'
13 "use strict";
13 "use strict";
14
14
15 var utils = IPython.utils;
15 var utils = IPython.utils;
16 var key = IPython.utils.keycodes;
17
16
18 /**
17 /**
19 * A notebook contains and manages cells.
18 * A notebook contains and manages cells.
@@ -39,6 +38,9 b' var IPython = (function (IPython) {'
39 this.undelete_index = null;
38 this.undelete_index = null;
40 this.undelete_below = false;
39 this.undelete_below = false;
41 this.paste_enabled = false;
40 this.paste_enabled = false;
41 // It is important to start out in command mode to match the intial mode
42 // of the KeyboardManager.
43 this.mode = 'command';
42 this.set_dirty(false);
44 this.set_dirty(false);
43 this.metadata = {};
45 this.metadata = {};
44 this._checkpoint_after_save = false;
46 this._checkpoint_after_save = false;
@@ -50,7 +52,6 b' var IPython = (function (IPython) {'
50 this.minimum_autosave_interval = 120000;
52 this.minimum_autosave_interval = 120000;
51 // single worksheet for now
53 // single worksheet for now
52 this.worksheet_metadata = {};
54 this.worksheet_metadata = {};
53 this.control_key_active = false;
54 this.notebook_name_blacklist_re = /[\/\\:]/;
55 this.notebook_name_blacklist_re = /[\/\\:]/;
55 this.nbformat = 3 // Increment this when changing the nbformat
56 this.nbformat = 3 // Increment this when changing the nbformat
56 this.nbformat_minor = 0 // Increment this when changing the nbformat
57 this.nbformat_minor = 0 // Increment this when changing the nbformat
@@ -92,12 +93,13 b' var IPython = (function (IPython) {'
92 * @method create_elements
93 * @method create_elements
93 */
94 */
94 Notebook.prototype.create_elements = function () {
95 Notebook.prototype.create_elements = function () {
96 var that = this;
97 this.element.attr('tabindex','-1');
98 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
95 // We add this end_space div to the end of the notebook div to:
99 // We add this end_space div to the end of the notebook div to:
96 // i) provide a margin between the last cell and the end of the notebook
100 // i) provide a margin between the last cell and the end of the notebook
97 // ii) to prevent the div from scrolling up when the last cell is being
101 // ii) to prevent the div from scrolling up when the last cell is being
98 // edited, but is too low on the page, which browsers will do automatically.
102 // edited, but is too low on the page, which browsers will do automatically.
99 var that = this;
100 this.container = $("<div/>").addClass("container").attr("id", "notebook-container");
101 var end_space = $('<div/>').addClass('end_space');
103 var end_space = $('<div/>').addClass('end_space');
102 end_space.dblclick(function (e) {
104 end_space.dblclick(function (e) {
103 var ncells = that.ncells();
105 var ncells = that.ncells();
@@ -105,7 +107,6 b' var IPython = (function (IPython) {'
105 });
107 });
106 this.element.append(this.container);
108 this.element.append(this.container);
107 this.container.append(end_space);
109 this.container.append(end_space);
108 $('div#notebook').addClass('border-box-sizing');
109 };
110 };
110
111
111 /**
112 /**
@@ -132,6 +133,16 b' var IPython = (function (IPython) {'
132 that.select(index);
133 that.select(index);
133 });
134 });
134
135
136 $([IPython.events]).on('edit_mode.Cell', function (event, data) {
137 var index = that.find_cell_index(data.cell);
138 that.select(index);
139 that.edit_mode();
140 });
141
142 $([IPython.events]).on('command_mode.Cell', function (event, data) {
143 that.command_mode();
144 });
145
135 $([IPython.events]).on('status_autorestarting.Kernel', function () {
146 $([IPython.events]).on('status_autorestarting.Kernel', function () {
136 IPython.dialog.modal({
147 IPython.dialog.modal({
137 title: "Kernel Restarting",
148 title: "Kernel Restarting",
@@ -144,207 +155,12 b' var IPython = (function (IPython) {'
144 });
155 });
145 });
156 });
146
157
147
148 $(document).keydown(function (event) {
149
150 // Save (CTRL+S) or (AppleKey+S)
151 //metaKey = applekey on mac
152 if ((event.ctrlKey || event.metaKey) && event.keyCode==83) {
153 that.save_checkpoint();
154 event.preventDefault();
155 return false;
156 } else if (event.which === key.ESC) {
157 // Intercept escape at highest level to avoid closing
158 // websocket connection with firefox
159 IPython.pager.collapse();
160 event.preventDefault();
161 } else if (event.which === key.SHIFT) {
162 // ignore shift keydown
163 return true;
164 }
165 if (event.which === key.UPARROW && !event.shiftKey) {
166 var cell = that.get_selected_cell();
167 if (cell && cell.at_top()) {
168 event.preventDefault();
169 that.select_prev();
170 };
171 } else if (event.which === key.DOWNARROW && !event.shiftKey) {
172 var cell = that.get_selected_cell();
173 if (cell && cell.at_bottom()) {
174 event.preventDefault();
175 that.select_next();
176 };
177 } else if (event.which === key.ENTER && event.shiftKey) {
178 that.execute_selected_cell();
179 return false;
180 } else if (event.which === key.ENTER && event.altKey) {
181 // Execute code cell, and insert new in place
182 that.execute_selected_cell();
183 // Only insert a new cell, if we ended up in an already populated cell
184 if (/\S/.test(that.get_selected_cell().get_text()) == true) {
185 that.insert_cell_above('code');
186 }
187 return false;
188 } else if (event.which === key.ENTER && event.ctrlKey) {
189 that.execute_selected_cell({terminal:true});
190 return false;
191 } else if (event.which === 77 && event.ctrlKey && that.control_key_active == false) {
192 that.control_key_active = true;
193 return false;
194 } else if (event.which === 88 && that.control_key_active) {
195 // Cut selected cell = x
196 that.cut_cell();
197 that.control_key_active = false;
198 return false;
199 } else if (event.which === 67 && that.control_key_active) {
200 // Copy selected cell = c
201 that.copy_cell();
202 that.control_key_active = false;
203 return false;
204 } else if (event.which === 86 && that.control_key_active) {
205 // Paste below selected cell = v
206 that.paste_cell_below();
207 that.control_key_active = false;
208 return false;
209 } else if (event.which === 68 && that.control_key_active) {
210 // Delete selected cell = d
211 that.delete_cell();
212 that.control_key_active = false;
213 return false;
214 } else if (event.which === 65 && that.control_key_active) {
215 // Insert code cell above selected = a
216 that.insert_cell_above('code');
217 that.control_key_active = false;
218 return false;
219 } else if (event.which === 66 && that.control_key_active) {
220 // Insert code cell below selected = b
221 that.insert_cell_below('code');
222 that.control_key_active = false;
223 return false;
224 } else if (event.which === 89 && that.control_key_active) {
225 // To code = y
226 that.to_code();
227 that.control_key_active = false;
228 return false;
229 } else if (event.which === 77 && that.control_key_active) {
230 // To markdown = m
231 that.to_markdown();
232 that.control_key_active = false;
233 return false;
234 } else if (event.which === 84 && that.control_key_active) {
235 // To Raw = t
236 that.to_raw();
237 that.control_key_active = false;
238 return false;
239 } else if (event.which === 49 && that.control_key_active) {
240 // To Heading 1 = 1
241 that.to_heading(undefined, 1);
242 that.control_key_active = false;
243 return false;
244 } else if (event.which === 50 && that.control_key_active) {
245 // To Heading 2 = 2
246 that.to_heading(undefined, 2);
247 that.control_key_active = false;
248 return false;
249 } else if (event.which === 51 && that.control_key_active) {
250 // To Heading 3 = 3
251 that.to_heading(undefined, 3);
252 that.control_key_active = false;
253 return false;
254 } else if (event.which === 52 && that.control_key_active) {
255 // To Heading 4 = 4
256 that.to_heading(undefined, 4);
257 that.control_key_active = false;
258 return false;
259 } else if (event.which === 53 && that.control_key_active) {
260 // To Heading 5 = 5
261 that.to_heading(undefined, 5);
262 that.control_key_active = false;
263 return false;
264 } else if (event.which === 54 && that.control_key_active) {
265 // To Heading 6 = 6
266 that.to_heading(undefined, 6);
267 that.control_key_active = false;
268 return false;
269 } else if (event.which === 79 && that.control_key_active) {
270 // Toggle output = o
271 if (event.shiftKey){
272 that.toggle_output_scroll();
273 } else {
274 that.toggle_output();
275 }
276 that.control_key_active = false;
277 return false;
278 } else if (event.which === 83 && that.control_key_active) {
279 // Save notebook = s
280 that.save_checkpoint();
281 that.control_key_active = false;
282 return false;
283 } else if (event.which === 74 && that.control_key_active) {
284 // Move cell down = j
285 that.move_cell_down();
286 that.control_key_active = false;
287 return false;
288 } else if (event.which === 75 && that.control_key_active) {
289 // Move cell up = k
290 that.move_cell_up();
291 that.control_key_active = false;
292 return false;
293 } else if (event.which === 80 && that.control_key_active) {
294 // Select previous = p
295 that.select_prev();
296 that.control_key_active = false;
297 return false;
298 } else if (event.which === 78 && that.control_key_active) {
299 // Select next = n
300 that.select_next();
301 that.control_key_active = false;
302 return false;
303 } else if (event.which === 76 && that.control_key_active) {
304 // Toggle line numbers = l
305 that.cell_toggle_line_numbers();
306 that.control_key_active = false;
307 return false;
308 } else if (event.which === 73 && that.control_key_active) {
309 // Interrupt kernel = i
310 that.session.interrupt_kernel();
311 that.control_key_active = false;
312 return false;
313 } else if (event.which === 190 && that.control_key_active) {
314 // Restart kernel = . # matches qt console
315 that.restart_kernel();
316 that.control_key_active = false;
317 return false;
318 } else if (event.which === 72 && that.control_key_active) {
319 // Show keyboard shortcuts = h
320 IPython.quick_help.show_keyboard_shortcuts();
321 that.control_key_active = false;
322 return false;
323 } else if (event.which === 90 && that.control_key_active) {
324 // Undo last cell delete = z
325 that.undelete();
326 that.control_key_active = false;
327 return false;
328 } else if ((event.which === 189 || event.which === 173) &&
329 that.control_key_active) {
330 // how fun! '-' is 189 in Chrome, but 173 in FF and Opera
331 // Split cell = -
332 that.split_cell();
333 that.control_key_active = false;
334 return false;
335 } else if (that.control_key_active) {
336 that.control_key_active = false;
337 return true;
338 }
339 return true;
340 });
341
342 var collapse_time = function(time){
158 var collapse_time = function (time) {
343 var app_height = $('#ipython-main-app').height(); // content height
159 var app_height = $('#ipython-main-app').height(); // content height
344 var splitter_height = $('div#pager_splitter').outerHeight(true);
160 var splitter_height = $('div#pager_splitter').outerHeight(true);
345 var new_height = app_height - splitter_height;
161 var new_height = app_height - splitter_height;
346 that.element.animate({height : new_height + 'px'}, time);
162 that.element.animate({height : new_height + 'px'}, time);
347 }
163 };
348
164
349 this.element.bind('collapse_pager', function (event,extrap) {
165 this.element.bind('collapse_pager', function (event, extrap) {
350 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
166 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
@@ -357,7 +173,7 b' var IPython = (function (IPython) {'
357 var pager_height = $('div#pager').outerHeight(true);
173 var pager_height = $('div#pager').outerHeight(true);
358 var new_height = app_height - pager_height - splitter_height;
174 var new_height = app_height - pager_height - splitter_height;
359 that.element.animate({height : new_height + 'px'}, time);
175 that.element.animate({height : new_height + 'px'}, time);
360 }
176 };
361
177
362 this.element.bind('expand_pager', function (event, extrap) {
178 this.element.bind('expand_pager', function (event, extrap) {
363 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
179 var time = (extrap != undefined) ? ((extrap.duration != undefined ) ? extrap.duration : 'fast') : 'fast';
@@ -650,6 +466,7 b' var IPython = (function (IPython) {'
650 if (this.is_valid_cell_index(index)) {
466 if (this.is_valid_cell_index(index)) {
651 var sindex = this.get_selected_index()
467 var sindex = this.get_selected_index()
652 if (sindex !== null && index !== sindex) {
468 if (sindex !== null && index !== sindex) {
469 this.command_mode();
653 this.get_cell(sindex).unselect();
470 this.get_cell(sindex).unselect();
654 };
471 };
655 var cell = this.get_cell(index);
472 var cell = this.get_cell(index);
@@ -692,6 +509,48 b' var IPython = (function (IPython) {'
692 };
509 };
693
510
694
511
512 // Edit/Command mode
513
514 Notebook.prototype.get_edit_index = function () {
515 var result = null;
516 this.get_cell_elements().filter(function (index) {
517 if ($(this).data("cell").mode === 'edit') {
518 result = index;
519 };
520 });
521 return result;
522 };
523
524 Notebook.prototype.command_mode = function () {
525 if (this.mode !== 'command') {
526 var index = this.get_edit_index();
527 var cell = this.get_cell(index);
528 if (cell) {
529 cell.command_mode();
530 };
531 this.mode = 'command';
532 IPython.keyboard_manager.command_mode();
533 };
534 };
535
536 Notebook.prototype.edit_mode = function () {
537 if (this.mode !== 'edit') {
538 var cell = this.get_selected_cell();
539 if (cell === null) {return;} // No cell is selected
540 // We need to set the mode to edit to prevent reentering this method
541 // when cell.edit_mode() is called below.
542 this.mode = 'edit';
543 IPython.keyboard_manager.edit_mode();
544 cell.edit_mode();
545 };
546 };
547
548 Notebook.prototype.focus_cell = function () {
549 var cell = this.get_selected_cell();
550 if (cell === null) {return;} // No cell is selected
551 cell.focus_cell();
552 };
553
695 // Cell movement
554 // Cell movement
696
555
697 /**
556 /**
@@ -710,6 +569,8 b' var IPython = (function (IPython) {'
710 tomove.detach();
569 tomove.detach();
711 pivot.before(tomove);
570 pivot.before(tomove);
712 this.select(i-1);
571 this.select(i-1);
572 var cell = this.get_selected_cell();
573 cell.focus_cell();
713 };
574 };
714 this.set_dirty(true);
575 this.set_dirty(true);
715 };
576 };
@@ -733,6 +594,8 b' var IPython = (function (IPython) {'
733 tomove.detach();
594 tomove.detach();
734 pivot.after(tomove);
595 pivot.after(tomove);
735 this.select(i+1);
596 this.select(i+1);
597 var cell = this.get_selected_cell();
598 cell.focus_cell();
736 };
599 };
737 };
600 };
738 this.set_dirty();
601 this.set_dirty();
@@ -755,9 +618,18 b' var IPython = (function (IPython) {'
755 this.undelete_backup = cell.toJSON();
618 this.undelete_backup = cell.toJSON();
756 $('#undelete_cell').removeClass('disabled');
619 $('#undelete_cell').removeClass('disabled');
757 if (this.is_valid_cell_index(i)) {
620 if (this.is_valid_cell_index(i)) {
621 var old_ncells = this.ncells();
758 var ce = this.get_cell_element(i);
622 var ce = this.get_cell_element(i);
759 ce.remove();
623 ce.remove();
760 if (i === (this.ncells())) {
624 if (i === 0) {
625 // Always make sure we have at least one cell.
626 if (old_ncells === 1) {
627 this.insert_cell_below('code');
628 }
629 this.select(0);
630 this.undelete_index = 0;
631 this.undelete_below = false;
632 } else if (i === old_ncells-1 && i !== 0) {
761 this.select(i-1);
633 this.select(i-1);
762 this.undelete_index = i - 1;
634 this.undelete_index = i - 1;
763 this.undelete_below = true;
635 this.undelete_below = true;
@@ -773,6 +645,42 b' var IPython = (function (IPython) {'
773 };
645 };
774
646
775 /**
647 /**
648 * Restore the most recently deleted cell.
649 *
650 * @method undelete
651 */
652 Notebook.prototype.undelete_cell = function() {
653 if (this.undelete_backup !== null && this.undelete_index !== null) {
654 var current_index = this.get_selected_index();
655 if (this.undelete_index < current_index) {
656 current_index = current_index + 1;
657 }
658 if (this.undelete_index >= this.ncells()) {
659 this.select(this.ncells() - 1);
660 }
661 else {
662 this.select(this.undelete_index);
663 }
664 var cell_data = this.undelete_backup;
665 var new_cell = null;
666 if (this.undelete_below) {
667 new_cell = this.insert_cell_below(cell_data.cell_type);
668 } else {
669 new_cell = this.insert_cell_above(cell_data.cell_type);
670 }
671 new_cell.fromJSON(cell_data);
672 if (this.undelete_below) {
673 this.select(current_index+1);
674 } else {
675 this.select(current_index);
676 }
677 this.undelete_backup = null;
678 this.undelete_index = null;
679 }
680 $('#undelete_cell').addClass('disabled');
681 }
682
683 /**
776 * Insert a cell so that after insertion the cell is at given index.
684 * Insert a cell so that after insertion the cell is at given index.
777 *
685 *
778 * Similar to insert_above, but index parameter is mandatory
686 * Similar to insert_above, but index parameter is mandatory
@@ -806,8 +714,12 b' var IPython = (function (IPython) {'
806
714
807 if(this._insert_element_at_index(cell.element,index)){
715 if(this._insert_element_at_index(cell.element,index)) {
808 cell.render();
716 cell.render();
809 this.select(this.find_cell_index(cell));
810 $([IPython.events]).trigger('create.Cell', {'cell': cell, 'index': index});
717 $([IPython.events]).trigger('create.Cell', {'cell': cell, 'index': index});
718 cell.refresh();
719 // We used to select the cell after we refresh it, but there
720 // are now cases were this method is called where select is
721 // not appropriate. The selection logic should be handled by the
722 // caller of the the top level insert_cell methods.
811 this.set_dirty(true);
723 this.set_dirty(true);
812 }
724 }
813 }
725 }
@@ -923,6 +835,8 b' var IPython = (function (IPython) {'
923 // to this state, instead of a blank cell
835 // to this state, instead of a blank cell
924 target_cell.code_mirror.clearHistory();
836 target_cell.code_mirror.clearHistory();
925 source_element.remove();
837 source_element.remove();
838 this.select(i);
839 this.edit_mode();
926 this.set_dirty(true);
840 this.set_dirty(true);
927 };
841 };
928 };
842 };
@@ -945,13 +859,15 b' var IPython = (function (IPython) {'
945 if (text === source_cell.placeholder) {
859 if (text === source_cell.placeholder) {
946 text = '';
860 text = '';
947 };
861 };
948 // The edit must come before the set_text.
862 // We must show the editor before setting its contents
949 target_cell.edit();
863 target_cell.unrender();
950 target_cell.set_text(text);
864 target_cell.set_text(text);
951 // make this value the starting point, so that we can only undo
865 // make this value the starting point, so that we can only undo
952 // to this state, instead of a blank cell
866 // to this state, instead of a blank cell
953 target_cell.code_mirror.clearHistory();
867 target_cell.code_mirror.clearHistory();
954 source_element.remove();
868 source_element.remove();
869 this.select(i);
870 this.edit_mode();
955 this.set_dirty(true);
871 this.set_dirty(true);
956 };
872 };
957 };
873 };
@@ -975,13 +891,15 b' var IPython = (function (IPython) {'
975 if (text === source_cell.placeholder) {
891 if (text === source_cell.placeholder) {
976 text = '';
892 text = '';
977 };
893 };
978 // The edit must come before the set_text.
894 // We must show the editor before setting its contents
979 target_cell.edit();
895 target_cell.unrender();
980 target_cell.set_text(text);
896 target_cell.set_text(text);
981 // make this value the starting point, so that we can only undo
897 // make this value the starting point, so that we can only undo
982 // to this state, instead of a blank cell
898 // to this state, instead of a blank cell
983 target_cell.code_mirror.clearHistory();
899 target_cell.code_mirror.clearHistory();
984 source_element.remove();
900 source_element.remove();
901 this.select(i);
902 this.edit_mode();
985 this.set_dirty(true);
903 this.set_dirty(true);
986 };
904 };
987 };
905 };
@@ -1009,16 +927,18 b' var IPython = (function (IPython) {'
1009 if (text === source_cell.placeholder) {
927 if (text === source_cell.placeholder) {
1010 text = '';
928 text = '';
1011 };
929 };
1012 // The edit must come before the set_text.
930 // We must show the editor before setting its contents
1013 target_cell.set_level(level);
931 target_cell.set_level(level);
1014 target_cell.edit();
932 target_cell.unrender();
1015 target_cell.set_text(text);
933 target_cell.set_text(text);
1016 // make this value the starting point, so that we can only undo
934 // make this value the starting point, so that we can only undo
1017 // to this state, instead of a blank cell
935 // to this state, instead of a blank cell
1018 target_cell.code_mirror.clearHistory();
936 target_cell.code_mirror.clearHistory();
1019 source_element.remove();
937 source_element.remove();
1020 this.set_dirty(true);
938 this.select(i);
1021 };
939 };
940 this.edit_mode();
941 this.set_dirty(true);
1022 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
942 $([IPython.events]).trigger('selected_cell_type_changed.Notebook',
1023 {'cell_type':'heading',level:level}
943 {'cell_type':'heading',level:level}
1024 );
944 );
@@ -1123,40 +1043,6 b' var IPython = (function (IPython) {'
1123 };
1043 };
1124 };
1044 };
1125
1045
1126 // Cell undelete
1127
1128 /**
1129 * Restore the most recently deleted cell.
1130 *
1131 * @method undelete
1132 */
1133 Notebook.prototype.undelete = function() {
1134 if (this.undelete_backup !== null && this.undelete_index !== null) {
1135 var current_index = this.get_selected_index();
1136 if (this.undelete_index < current_index) {
1137 current_index = current_index + 1;
1138 }
1139 if (this.undelete_index >= this.ncells()) {
1140 this.select(this.ncells() - 1);
1141 }
1142 else {
1143 this.select(this.undelete_index);
1144 }
1145 var cell_data = this.undelete_backup;
1146 var new_cell = null;
1147 if (this.undelete_below) {
1148 new_cell = this.insert_cell_below(cell_data.cell_type);
1149 } else {
1150 new_cell = this.insert_cell_above(cell_data.cell_type);
1151 }
1152 new_cell.fromJSON(cell_data);
1153 this.select(current_index);
1154 this.undelete_backup = null;
1155 this.undelete_index = null;
1156 }
1157 $('#undelete_cell').addClass('disabled');
1158 }
1159
1160 // Split/merge
1046 // Split/merge
1161
1047
1162 /**
1048 /**
@@ -1165,24 +1051,25 b' var IPython = (function (IPython) {'
1165 * @method split_cell
1051 * @method split_cell
1166 */
1052 */
1167 Notebook.prototype.split_cell = function () {
1053 Notebook.prototype.split_cell = function () {
1168 // Todo: implement spliting for other cell types.
1054 var mdc = IPython.MarkdownCell;
1055 var rc = IPython.RawCell;
1169 var cell = this.get_selected_cell();
1056 var cell = this.get_selected_cell();
1170 if (cell.is_splittable()) {
1057 if (cell.is_splittable()) {
1171 var texta = cell.get_pre_cursor();
1058 var texta = cell.get_pre_cursor();
1172 var textb = cell.get_post_cursor();
1059 var textb = cell.get_post_cursor();
1173 if (cell instanceof IPython.CodeCell) {
1060 if (cell instanceof IPython.CodeCell) {
1061 // In this case the operations keep the notebook in its existing mode
1062 // so we don't need to do any post-op mode changes.
1174 cell.set_text(textb);
1063 cell.set_text(textb);
1175 var new_cell = this.insert_cell_above('code');
1064 var new_cell = this.insert_cell_above('code');
1176 new_cell.set_text(texta);
1065 new_cell.set_text(texta);
1177 this.select_next();
1066 } else if ((cell instanceof mdc && !cell.rendered) || (cell instanceof rc)) {
1178 } else if (cell instanceof IPython.MarkdownCell) {
1067 // We know cell is !rendered so we can use set_text.
1179 cell.set_text(textb);
1068 cell.set_text(textb);
1180 cell.render();
1069 var new_cell = this.insert_cell_above(cell.cell_type);
1181 var new_cell = this.insert_cell_above('markdown');
1070 // Unrender the new cell so we can call set_text.
1182 new_cell.edit(); // editor must be visible to call set_text
1071 new_cell.unrender();
1183 new_cell.set_text(texta);
1072 new_cell.set_text(texta);
1184 new_cell.render();
1185 this.select_next();
1186 }
1073 }
1187 };
1074 };
1188 };
1075 };
@@ -1193,8 +1080,11 b' var IPython = (function (IPython) {'
1193 * @method merge_cell_above
1080 * @method merge_cell_above
1194 */
1081 */
1195 Notebook.prototype.merge_cell_above = function () {
1082 Notebook.prototype.merge_cell_above = function () {
1083 var mdc = IPython.MarkdownCell;
1084 var rc = IPython.RawCell;
1196 var index = this.get_selected_index();
1085 var index = this.get_selected_index();
1197 var cell = this.get_cell(index);
1086 var cell = this.get_cell(index);
1087 var render = cell.rendered;
1198 if (!cell.is_mergeable()) {
1088 if (!cell.is_mergeable()) {
1199 return;
1089 return;
1200 }
1090 }
@@ -1207,10 +1097,14 b' var IPython = (function (IPython) {'
1207 var text = cell.get_text();
1097 var text = cell.get_text();
1208 if (cell instanceof IPython.CodeCell) {
1098 if (cell instanceof IPython.CodeCell) {
1209 cell.set_text(upper_text+'\n'+text);
1099 cell.set_text(upper_text+'\n'+text);
1210 } else if (cell instanceof IPython.MarkdownCell) {
1100 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1211 cell.edit();
1101 cell.unrender(); // Must unrender before we set_text.
1212 cell.set_text(upper_text+'\n'+text);
1102 cell.set_text(upper_text+'\n\n'+text);
1103 if (render) {
1104 // The rendered state of the final cell should match
1105 // that of the original selected cell;
1213 cell.render();
1106 cell.render();
1107 }
1214 };
1108 };
1215 this.delete_cell(index-1);
1109 this.delete_cell(index-1);
1216 this.select(this.find_cell_index(cell));
1110 this.select(this.find_cell_index(cell));
@@ -1223,8 +1117,11 b' var IPython = (function (IPython) {'
1223 * @method merge_cell_below
1117 * @method merge_cell_below
1224 */
1118 */
1225 Notebook.prototype.merge_cell_below = function () {
1119 Notebook.prototype.merge_cell_below = function () {
1120 var mdc = IPython.MarkdownCell;
1121 var rc = IPython.RawCell;
1226 var index = this.get_selected_index();
1122 var index = this.get_selected_index();
1227 var cell = this.get_cell(index);
1123 var cell = this.get_cell(index);
1124 var render = cell.rendered;
1228 if (!cell.is_mergeable()) {
1125 if (!cell.is_mergeable()) {
1229 return;
1126 return;
1230 }
1127 }
@@ -1237,10 +1134,14 b' var IPython = (function (IPython) {'
1237 var text = cell.get_text();
1134 var text = cell.get_text();
1238 if (cell instanceof IPython.CodeCell) {
1135 if (cell instanceof IPython.CodeCell) {
1239 cell.set_text(text+'\n'+lower_text);
1136 cell.set_text(text+'\n'+lower_text);
1240 } else if (cell instanceof IPython.MarkdownCell) {
1137 } else if ((cell instanceof mdc) || (cell instanceof rc)) {
1241 cell.edit();
1138 cell.unrender(); // Must unrender before we set_text.
1242 cell.set_text(text+'\n'+lower_text);
1139 cell.set_text(text+'\n\n'+lower_text);
1140 if (render) {
1141 // The rendered state of the final cell should match
1142 // that of the original selected cell;
1243 cell.render();
1143 cell.render();
1144 }
1244 };
1145 };
1245 this.delete_cell(index+1);
1146 this.delete_cell(index+1);
1246 this.select(this.find_cell_index(cell));
1147 this.select(this.find_cell_index(cell));
@@ -1433,35 +1334,76 b' var IPython = (function (IPython) {'
1433 };
1334 };
1434
1335
1435 /**
1336 /**
1436 * Run the selected cell.
1337 * Execute or render cell outputs and go into command mode.
1437 *
1338 *
1438 * Execute or render cell outputs.
1339 * @method execute_cell
1340 */
1341 Notebook.prototype.execute_cell = function () {
1342 // mode = shift, ctrl, alt
1343 var cell = this.get_selected_cell();
1344 var cell_index = this.find_cell_index(cell);
1345
1346 cell.execute();
1347 this.command_mode();
1348 cell.focus_cell();
1349 this.set_dirty(true);
1350 }
1351
1352 /**
1353 * Execute or render cell outputs and insert a new cell below.
1439 *
1354 *
1440 * @method execute_selected_cell
1355 * @method execute_cell_and_insert_below
1441 * @param {Object} options Customize post-execution behavior
1442 */
1356 */
1443 Notebook.prototype.execute_selected_cell = function (options) {
1357 Notebook.prototype.execute_cell_and_insert_below = function () {
1444 // add_new: should a new cell be added if we are at the end of the nb
1358 var cell = this.get_selected_cell();
1445 // terminal: execute in terminal mode, which stays in the current cell
1359 var cell_index = this.find_cell_index(cell);
1446 var default_options = {terminal: false, add_new: true};
1360
1447 $.extend(default_options, options);
1448 var that = this;
1449 var cell = that.get_selected_cell();
1450 var cell_index = that.find_cell_index(cell);
1451 if (cell instanceof IPython.CodeCell) {
1452 cell.execute();
1361 cell.execute();
1362
1363 // If we are at the end always insert a new cell and return
1364 if (cell_index === (this.ncells()-1)) {
1365 this.insert_cell_below('code');
1366 this.select(cell_index+1);
1367 this.edit_mode();
1368 this.scroll_to_bottom();
1369 this.set_dirty(true);
1370 return;
1453 }
1371 }
1454 if (default_options.terminal) {
1372
1455 cell.select_all();
1373 // Only insert a new cell, if we ended up in an already populated cell
1456 } else {
1374 var next_text = this.get_cell(cell_index+1).get_text();
1457 if ((cell_index === (that.ncells()-1)) && default_options.add_new) {
1375 if (/\S/.test(next_text) === true) {
1458 that.insert_cell_below('code');
1376 this.insert_cell_below('code');
1459 // If we are adding a new cell at the end, scroll down to show it.
1377 }
1460 that.scroll_to_bottom();
1378 this.select(cell_index+1);
1461 } else {
1379 this.edit_mode();
1462 that.select(cell_index+1);
1380 this.set_dirty(true);
1463 };
1464 };
1381 };
1382
1383 /**
1384 * Execute or render cell outputs and select the next cell.
1385 *
1386 * @method execute_cell_and_select_below
1387 */
1388 Notebook.prototype.execute_cell_and_select_below = function () {
1389
1390 var cell = this.get_selected_cell();
1391 var cell_index = this.find_cell_index(cell);
1392
1393 cell.execute();
1394
1395 // If we are at the end always insert a new cell and return
1396 if (cell_index === (this.ncells()-1)) {
1397 this.insert_cell_below('code');
1398 this.select(cell_index+1);
1399 this.edit_mode();
1400 this.scroll_to_bottom();
1401 this.set_dirty(true);
1402 return;
1403 }
1404
1405 this.select(cell_index+1);
1406 this.get_cell(cell_index+1).focus_cell();
1465 this.set_dirty(true);
1407 this.set_dirty(true);
1466 };
1408 };
1467
1409
@@ -1504,7 +1446,7 b' var IPython = (function (IPython) {'
1504 Notebook.prototype.execute_cell_range = function (start, end) {
1446 Notebook.prototype.execute_cell_range = function (start, end) {
1505 for (var i=start; i<end; i++) {
1447 for (var i=start; i<end; i++) {
1506 this.select(i);
1448 this.select(i);
1507 this.execute_selected_cell({add_new:false});
1449 this.execute_cell();
1508 };
1450 };
1509 };
1451 };
1510
1452
@@ -1584,7 +1526,7 b' var IPython = (function (IPython) {'
1584 cell_data.cell_type = 'raw';
1526 cell_data.cell_type = 'raw';
1585 }
1527 }
1586
1528
1587 new_cell = this.insert_cell_at_bottom(cell_data.cell_type);
1529 new_cell = this.insert_cell_at_index(cell_data.cell_type, i);
1588 new_cell.fromJSON(cell_data);
1530 new_cell.fromJSON(cell_data);
1589 };
1531 };
1590 };
1532 };
@@ -1909,9 +1851,13 b' var IPython = (function (IPython) {'
1909 this.fromJSON(data);
1851 this.fromJSON(data);
1910 if (this.ncells() === 0) {
1852 if (this.ncells() === 0) {
1911 this.insert_cell_below('code');
1853 this.insert_cell_below('code');
1854 this.select(0);
1855 this.edit_mode();
1856 } else {
1857 this.select(0);
1858 this.command_mode();
1912 };
1859 };
1913 this.set_dirty(false);
1860 this.set_dirty(false);
1914 this.select(0);
1915 this.scroll_to_top();
1861 this.scroll_to_top();
1916 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1862 if (data.orig_nbformat !== undefined && data.nbformat !== data.orig_nbformat) {
1917 var msg = "This notebook has been converted from an older " +
1863 var msg = "This notebook has been converted from an older " +
@@ -509,6 +509,7 b' var IPython = (function (IPython) {'
509
509
510 OutputArea.prototype.append_html = function (html, md, element) {
510 OutputArea.prototype.append_html = function (html, md, element) {
511 var toinsert = this.create_output_subarea(md, "output_html rendered_html");
511 var toinsert = this.create_output_subarea(md, "output_html rendered_html");
512 IPython.keyboard_manager.register_events(toinsert);
512 toinsert.append(html);
513 toinsert.append(html);
513 element.append(toinsert);
514 element.append(toinsert);
514 };
515 };
@@ -517,6 +518,7 b' var IPython = (function (IPython) {'
517 OutputArea.prototype.append_javascript = function (js, md, container) {
518 OutputArea.prototype.append_javascript = function (js, md, container) {
518 // We just eval the JS code, element appears in the local scope.
519 // We just eval the JS code, element appears in the local scope.
519 var element = this.create_output_subarea(md, "output_javascript");
520 var element = this.create_output_subarea(md, "output_javascript");
521 IPython.keyboard_manager.register_events(element);
520 container.append(element);
522 container.append(element);
521 try {
523 try {
522 eval(js);
524 eval(js);
@@ -646,11 +648,18 b' var IPython = (function (IPython) {'
646 })
648 })
647 )
649 )
648 );
650 );
651
649 this.element.append(area);
652 this.element.append(area);
650 // weirdly need double-focus now,
653 var raw_input = area.find('input.raw_input');
651 // otherwise only the cell will be focused
654 // Register events that enable/disable the keyboard manager while raw
652 area.find("input.raw_input").focus().focus();
655 // input is focused.
656 IPython.keyboard_manager.register_events(raw_input);
657 // Note, the following line used to read raw_input.focus().focus().
658 // This seemed to be needed otherwise only the cell would be focused.
659 // But with the modal UI, this seems to work fine with one call to focus().
660 raw_input.focus();
653 }
661 }
662
654 OutputArea.prototype._submit_raw_input = function (evt) {
663 OutputArea.prototype._submit_raw_input = function (evt) {
655 var container = this.element.find("div.raw_input");
664 var container = this.element.find("div.raw_input");
656 var theprompt = container.find("span.input_prompt");
665 var theprompt = container.find("span.input_prompt");
@@ -23,44 +23,35 b' var IPython = (function (IPython) {'
23 $(this.shortcut_dialog).modal("toggle");
23 $(this.shortcut_dialog).modal("toggle");
24 return;
24 return;
25 }
25 }
26 var body = $('<div/>');
26 var command_shortcuts = IPython.keyboard_manager.command_shortcuts.help();
27 var shortcuts = [
27 var edit_shortcuts = IPython.keyboard_manager.edit_shortcuts.help();
28 {key: 'Shift-Enter', help: 'run cell'},
28 var help, shortcut;
29 {key: 'Ctrl-Enter', help: 'run cell in-place'},
29 var i, half, n;
30 {key: 'Alt-Enter', help: 'run cell, insert below'},
30 var element = $('<div/>');
31 {key: 'Ctrl-m x', help: 'cut cell'},
31
32 {key: 'Ctrl-m c', help: 'copy cell'},
32 // The documentation
33 {key: 'Ctrl-m v', help: 'paste cell'},
33 var doc = $('<div/>').addClass('alert');
34 {key: 'Ctrl-m d', help: 'delete cell'},
34 doc.append(
35 {key: 'Ctrl-m z', help: 'undo last cell deletion'},
35 $('<button/>').addClass('close').attr('data-dismiss','alert').html('&times')
36 {key: 'Ctrl-m -', help: 'split cell'},
36 ).append(
37 {key: 'Ctrl-m a', help: 'insert cell above'},
37 'The IPython Notebook has two different keyboard input modes. <b>Edit mode</b> '+
38 {key: 'Ctrl-m b', help: 'insert cell below'},
38 'allow you the type code/text into a cell and is indicated by a green cell '+
39 {key: 'Ctrl-m o', help: 'toggle output'},
39 'border. <b>Command mode</b> binds the keyboard to notebook level actions '+
40 {key: 'Ctrl-m O', help: 'toggle output scroll'},
40 'and is indicated by a grey cell border.'
41 {key: 'Ctrl-m l', help: 'toggle line numbers'},
41 )
42 {key: 'Ctrl-m s', help: 'save notebook'},
42 element.append(doc);
43 {key: 'Ctrl-m j', help: 'move cell down'},
43
44 {key: 'Ctrl-m k', help: 'move cell up'},
44 // Command mode
45 {key: 'Ctrl-m y', help: 'code cell'},
45 var cmd_div = this.build_command_help();
46 {key: 'Ctrl-m m', help: 'markdown cell'},
46 element.append(cmd_div);
47 {key: 'Ctrl-m t', help: 'raw cell'},
47
48 {key: 'Ctrl-m 1-6', help: 'heading 1-6 cell'},
48 // Edit mode
49 {key: 'Ctrl-m p', help: 'select previous'},
49 var edit_div = this.build_edit_help();
50 {key: 'Ctrl-m n', help: 'select next'},
50 element.append(edit_div);
51 {key: 'Ctrl-m i', help: 'interrupt kernel'},
51
52 {key: 'Ctrl-m .', help: 'restart kernel'},
53 {key: 'Ctrl-m h', help: 'show keyboard shortcuts'}
54 ];
55 for (var i=0; i<shortcuts.length; i++) {
56 body.append($('<div>').addClass('quickhelp').
57 append($('<span/>').addClass('shortcut_key').html(shortcuts[i].key)).
58 append($('<span/>').addClass('shortcut_descr').html(' : ' + shortcuts[i].help))
59 );
60 };
61 this.shortcut_dialog = IPython.dialog.modal({
52 this.shortcut_dialog = IPython.dialog.modal({
62 title : "Keyboard shortcuts",
53 title : "Keyboard shortcuts",
63 body : body,
54 body : element,
64 destroy : false,
55 destroy : false,
65 buttons : {
56 buttons : {
66 Close : {}
57 Close : {}
@@ -68,6 +59,72 b' var IPython = (function (IPython) {'
68 });
59 });
69 };
60 };
70
61
62 QuickHelp.prototype.build_command_help = function () {
63 var command_shortcuts = IPython.keyboard_manager.command_shortcuts.help();
64 var help, shortcut;
65 var i, half, n;
66
67 // Command mode
68 var cmd_div = $('<div/>').append($('<h4>Command Mode (press ESC to enable)</h4>'));
69 var cmd_sub_div = $('<div/>').addClass('hbox');
70 var cmd_col1 = $('<div/>').addClass('box-flex0');
71 var cmd_col2 = $('<div/>').addClass('box-flex0');
72 n = command_shortcuts.length;
73 half = ~~(n/2); // Truncate :)
74 for (i=0; i<half; i++) {
75 help = command_shortcuts[i]['help'];
76 shortcut = command_shortcuts[i]['shortcut'];
77 cmd_col1.append($('<div>').addClass('quickhelp').
78 append($('<span/>').addClass('shortcut_key').html(shortcut)).
79 append($('<span/>').addClass('shortcut_descr').html(' : ' + help))
80 );
81 };
82 for (i=half; i<n; i++) {
83 help = command_shortcuts[i]['help'];
84 shortcut = command_shortcuts[i]['shortcut'];
85 cmd_col2.append($('<div>').addClass('quickhelp').
86 append($('<span/>').addClass('shortcut_key').html(shortcut)).
87 append($('<span/>').addClass('shortcut_descr').html(' : ' + help))
88 );
89 };
90 cmd_sub_div.append(cmd_col1).append(cmd_col2);
91 cmd_div.append(cmd_sub_div);
92 return cmd_div;
93 }
94
95 QuickHelp.prototype.build_edit_help = function () {
96 var edit_shortcuts = IPython.keyboard_manager.edit_shortcuts.help();
97 var help, shortcut;
98 var i, half, n;
99
100 // Edit mode
101 var edit_div = $('<div/>').append($('<h4>Edit Mode (press ENTER to enable)</h4>'));
102 var edit_sub_div = $('<div/>').addClass('hbox');
103 var edit_col1 = $('<div/>').addClass('box-flex0');
104 var edit_col2 = $('<div/>').addClass('box-flex0');
105 n = edit_shortcuts.length;
106 half = ~~(n/2); // Truncate :)
107 for (i=0; i<half; i++) {
108 help = edit_shortcuts[i]['help'];
109 shortcut = edit_shortcuts[i]['shortcut'];
110 edit_col1.append($('<div>').addClass('quickhelp').
111 append($('<span/>').addClass('shortcut_key').html(shortcut)).
112 append($('<span/>').addClass('shortcut_descr').html(' : ' + help))
113 );
114 };
115 for (i=half; i<n; i++) {
116 help = edit_shortcuts[i]['help'];
117 shortcut = edit_shortcuts[i]['shortcut'];
118 edit_col2.append($('<div>').addClass('quickhelp').
119 append($('<span/>').addClass('shortcut_key').html(shortcut)).
120 append($('<span/>').addClass('shortcut_descr').html(' : ' + help))
121 );
122 };
123 edit_sub_div.append(edit_col1).append(edit_col2);
124 edit_div.append(edit_sub_div);
125 return edit_div;
126 }
127
71 // Set module variables
128 // Set module variables
72 IPython.QuickHelp = QuickHelp;
129 IPython.QuickHelp = QuickHelp;
73
130
@@ -41,7 +41,7 b' var IPython = (function (IPython) {'
41
41
42 // we cannot put this as a class key as it has handle to "this".
42 // we cannot put this as a class key as it has handle to "this".
43 var cm_overwrite_options = {
43 var cm_overwrite_options = {
44 onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this)
44 onKeyEvent: $.proxy(this.handle_keyevent,this)
45 };
45 };
46
46
47 options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
47 options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options});
@@ -64,7 +64,6 b' var IPython = (function (IPython) {'
64 };
64 };
65
65
66
66
67
68 /**
67 /**
69 * Create the DOM element of the TextCell
68 * Create the DOM element of the TextCell
70 * @method create_element
69 * @method create_element
@@ -101,19 +100,26 b' var IPython = (function (IPython) {'
101 TextCell.prototype.bind_events = function () {
100 TextCell.prototype.bind_events = function () {
102 IPython.Cell.prototype.bind_events.apply(this);
101 IPython.Cell.prototype.bind_events.apply(this);
103 var that = this;
102 var that = this;
104 this.element.keydown(function (event) {
103
105 if (event.which === 13 && !event.shiftKey) {
106 if (that.rendered) {
107 that.edit();
108 return false;
109 };
110 };
111 });
112 this.element.dblclick(function () {
104 this.element.dblclick(function () {
113 that.edit();
105 if (that.selected === false) {
106 $([IPython.events]).trigger('select.Cell', {'cell':that});
107 };
108 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
114 });
109 });
115 };
110 };
116
111
112 TextCell.prototype.handle_keyevent = function (editor, event) {
113
114 // console.log('CM', this.mode, event.which, event.type)
115
116 if (this.mode === 'command') {
117 return true;
118 } else if (this.mode === 'edit') {
119 return this.handle_codemirror_keyevent(editor, event);
120 }
121 };
122
117 /**
123 /**
118 * This method gets called in CodeMirror's onKeyDown/onKeyPress
124 * This method gets called in CodeMirror's onKeyDown/onKeyPress
119 * handlers and is used to provide custom key handling.
125 * handlers and is used to provide custom key handling.
@@ -126,65 +132,86 b' var IPython = (function (IPython) {'
126 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
132 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
127 */
133 */
128 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
134 TextCell.prototype.handle_codemirror_keyevent = function (editor, event) {
135 var that = this;
129
136
130 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) {
137 if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey || event.altKey)) {
131 // Always ignore shift-enter in CodeMirror as we handle it.
138 // Always ignore shift-enter in CodeMirror as we handle it.
132 return true;
139 return true;
140 } else if (event.which === key.UPARROW && event.type === 'keydown') {
141 // If we are not at the top, let CM handle the up arrow and
142 // prevent the global keydown handler from handling it.
143 if (!that.at_top()) {
144 event.stop();
145 return false;
146 } else {
147 return true;
148 };
149 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
150 // If we are not at the bottom, let CM handle the down arrow and
151 // prevent the global keydown handler from handling it.
152 if (!that.at_bottom()) {
153 event.stop();
154 return false;
155 } else {
156 return true;
157 };
158 } else if (event.which === key.ESC && event.type === 'keydown') {
159 if (that.code_mirror.options.keyMap === "vim-insert") {
160 // vim keyMap is active and in insert mode. In this case we leave vim
161 // insert mode, but remain in notebook edit mode.
162 // Let' CM handle this event and prevent global handling.
163 event.stop();
164 return false;
165 } else {
166 // vim keyMap is not active. Leave notebook edit mode.
167 // Don't let CM handle the event, defer to global handling.
168 return true;
169 }
133 }
170 }
134 return false;
171 return false;
135 };
172 };
136
173
137 /**
174 // Cell level actions
138 * Select the current cell and trigger 'focus'
175
139 * @method select
140 */
141 TextCell.prototype.select = function () {
176 TextCell.prototype.select = function () {
142 IPython.Cell.prototype.select.apply(this);
177 var cont = IPython.Cell.prototype.select.apply(this);
143 var output = this.element.find("div.text_cell_render");
178 if (cont) {
144 output.trigger('focus');
179 if (this.mode === 'edit') {
180 this.code_mirror.refresh();
181 }
145 };
182 };
146
183 return cont;
147 /**
148 * unselect the current cell and `render` it
149 * @method unselect
150 */
151 TextCell.prototype.unselect = function() {
152 // render on selection of another cell
153 this.render();
154 IPython.Cell.prototype.unselect.apply(this);
155 };
184 };
156
185
157 /**
186 TextCell.prototype.unrender = function () {
158 *
187 if (this.read_only) return;
159 * put the current cell in edition mode
188 var cont = IPython.Cell.prototype.unrender.apply(this);
160 * @method edit
189 if (cont) {
161 */
162 TextCell.prototype.edit = function () {
163 if (this.rendered === true) {
164 var text_cell = this.element;
190 var text_cell = this.element;
165 var output = text_cell.find("div.text_cell_render");
191 var output = text_cell.find("div.text_cell_render");
166 output.hide();
192 output.hide();
167 text_cell.find('div.text_cell_input').show();
193 text_cell.find('div.text_cell_input').show();
168 this.code_mirror.refresh();
169 this.code_mirror.focus();
170 // We used to need an additional refresh() after the focus, but
171 // it appears that this has been fixed in CM. This bug would show
172 // up on FF when a newly loaded markdown cell was edited.
173 this.rendered = false;
174 if (this.get_text() === this.placeholder) {
194 if (this.get_text() === this.placeholder) {
175 this.set_text('');
195 this.set_text('');
176 this.refresh();
196 this.refresh();
177 }
197 }
178 }
179 };
180
198
199 };
200 return cont;
201 };
181
202
182 /**
203 TextCell.prototype.execute = function () {
183 * Empty, Subclasses must define render.
204 this.render();
184 * @method render
205 };
185 */
186 TextCell.prototype.render = function () {};
187
206
207 TextCell.prototype.edit_mode = function () {
208 var cont = IPython.Cell.prototype.edit_mode.apply(this);
209 if (cont) {
210 this.unrender();
211 this.focus_editor();
212 };
213 return cont;
214 }
188
215
189 /**
216 /**
190 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
217 * setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}}
@@ -222,36 +249,37 b' var IPython = (function (IPython) {'
222 };
249 };
223
250
224 /**
251 /**
225 * not deprecated, but implementation wrong
226 * @method at_top
252 * @method at_top
227 * @deprecated
253 * @return {Boolean}
228 * @return {Boolean} true is cell rendered, false otherwise
229 * I doubt this is what it is supposed to do
230 * this implementation is completly false
231 */
254 */
232 TextCell.prototype.at_top = function () {
255 TextCell.prototype.at_top = function () {
233 if (this.rendered) {
256 if (this.rendered) {
234 return true;
257 return true;
235 } else {
258 } else {
259 var cursor = this.code_mirror.getCursor();
260 if (cursor.line === 0 && cursor.ch === 0) {
261 return true;
262 } else {
236 return false;
263 return false;
237 }
238 };
264 };
239
265 };
266 };
240
267
241 /**
268 /**
242 * not deprecated, but implementation wrong
243 * @method at_bottom
269 * @method at_bottom
244 * @deprecated
270 * @return {Boolean}
245 * @return {Boolean} true is cell rendered, false otherwise
246 * I doubt this is what it is supposed to do
247 * this implementation is completly false
248 * */
271 * */
249 TextCell.prototype.at_bottom = function () {
272 TextCell.prototype.at_bottom = function () {
250 if (this.rendered) {
273 if (this.rendered) {
251 return true;
274 return true;
252 } else {
275 } else {
276 var cursor = this.code_mirror.getCursor();
277 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
278 return true;
279 } else {
253 return false;
280 return false;
254 }
281 };
282 };
255 };
283 };
256
284
257 /**
285 /**
@@ -306,16 +334,14 b' var IPython = (function (IPython) {'
306 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
334 placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$"
307 }
335 }
308
336
309
310
311
312 MarkdownCell.prototype = new TextCell();
337 MarkdownCell.prototype = new TextCell();
313
338
314 /**
339 /**
315 * @method render
340 * @method render
316 */
341 */
317 MarkdownCell.prototype.render = function () {
342 MarkdownCell.prototype.render = function () {
318 if (this.rendered === false) {
343 var cont = IPython.TextCell.prototype.render.apply(this);
344 if (cont) {
319 var text = this.get_text();
345 var text = this.get_text();
320 var math = null;
346 var math = null;
321 if (text === "") { text = this.placeholder; }
347 if (text === "") { text = this.placeholder; }
@@ -337,9 +363,9 b' var IPython = (function (IPython) {'
337 }
363 }
338 this.element.find('div.text_cell_input').hide();
364 this.element.find('div.text_cell_input').hide();
339 this.element.find("div.text_cell_render").show();
365 this.element.find("div.text_cell_render").show();
340 this.typeset();
366 this.typeset()
341 this.rendered = true;
367 };
342 }
368 return cont;
343 };
369 };
344
370
345
371
@@ -351,15 +377,12 b' var IPython = (function (IPython) {'
351 * @extends IPython.TextCell
377 * @extends IPython.TextCell
352 */
378 */
353 var RawCell = function (options) {
379 var RawCell = function (options) {
354 options = this.mergeopt(RawCell, options);
355
380
356 this.cell_type = 'raw';
381 options = this.mergeopt(RawCell,options)
357 TextCell.apply(this, [options]);
382 TextCell.apply(this, [options]);
358
383 this.cell_type = 'raw';
359 var that = this;
384 // RawCell should always hide its rendered div
360 this.element.focusout(
385 this.element.find('div.text_cell_render').hide();
361 function() { that.auto_highlight(); }
362 );
363 };
386 };
364
387
365 RawCell.options_default = {
388 RawCell.options_default = {
@@ -368,10 +391,17 b' var IPython = (function (IPython) {'
368 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
391 "When passing through nbconvert, a Raw Cell's content is added to the output unmodified."
369 };
392 };
370
393
371
372
373 RawCell.prototype = new TextCell();
394 RawCell.prototype = new TextCell();
374
395
396 /** @method bind_events **/
397 RawCell.prototype.bind_events = function () {
398 TextCell.prototype.bind_events.apply(this);
399 var that = this
400 this.element.focusout(function() {
401 that.auto_highlight();
402 });
403 };
404
375 /**
405 /**
376 * Trigger autodetection of highlight scheme for current cell
406 * Trigger autodetection of highlight scheme for current cell
377 * @method auto_highlight
407 * @method auto_highlight
@@ -382,68 +412,16 b' var IPython = (function (IPython) {'
382
412
383 /** @method render **/
413 /** @method render **/
384 RawCell.prototype.render = function () {
414 RawCell.prototype.render = function () {
385 this.rendered = true;
415 // Make sure that this cell type can never be rendered
416 if (this.rendered) {
417 this.unrender();
418 }
386 var text = this.get_text();
419 var text = this.get_text();
387 if (text === "") { text = this.placeholder; }
420 if (text === "") { text = this.placeholder; }
388 console.log('rendering', text);
389 this.set_text(text);
421 this.set_text(text);
390 };
422 };
391
423
392
424
393 /** @method handle_codemirror_keyevent **/
394 RawCell.prototype.handle_codemirror_keyevent = function (editor, event) {
395
396 var that = this;
397 if (event.which === key.UPARROW && event.type === 'keydown') {
398 // If we are not at the top, let CM handle the up arrow and
399 // prevent the global keydown handler from handling it.
400 if (!that.at_top()) {
401 event.stop();
402 return false;
403 } else {
404 return true;
405 };
406 } else if (event.which === key.DOWNARROW && event.type === 'keydown') {
407 // If we are not at the bottom, let CM handle the down arrow and
408 // prevent the global keydown handler from handling it.
409 if (!that.at_bottom()) {
410 event.stop();
411 return false;
412 } else {
413 return true;
414 };
415 };
416 return false;
417 };
418
419 /** @method select **/
420 RawCell.prototype.select = function () {
421 IPython.Cell.prototype.select.apply(this);
422 this.edit();
423 };
424
425 /** @method at_top **/
426 RawCell.prototype.at_top = function () {
427 var cursor = this.code_mirror.getCursor();
428 if (cursor.line === 0 && cursor.ch === 0) {
429 return true;
430 } else {
431 return false;
432 }
433 };
434
435
436 /** @method at_bottom **/
437 RawCell.prototype.at_bottom = function () {
438 var cursor = this.code_mirror.getCursor();
439 if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) {
440 return true;
441 } else {
442 return false;
443 }
444 };
445
446
447 /**
425 /**
448 * @class HeadingCell
426 * @class HeadingCell
449 * @extends IPython.TextCell
427 * @extends IPython.TextCell
@@ -538,7 +516,8 b' var IPython = (function (IPython) {'
538
516
539
517
540 HeadingCell.prototype.render = function () {
518 HeadingCell.prototype.render = function () {
541 if (this.rendered === false) {
519 var cont = IPython.TextCell.prototype.render.apply(this);
520 if (cont) {
542 var text = this.get_text();
521 var text = this.get_text();
543 var math = null;
522 var math = null;
544 // Markdown headings must be a single line
523 // Markdown headings must be a single line
@@ -564,8 +543,9 b' var IPython = (function (IPython) {'
564 this.typeset();
543 this.typeset();
565 this.element.find('div.text_cell_input').hide();
544 this.element.find('div.text_cell_input').hide();
566 this.element.find("div.text_cell_render").show();
545 this.element.find("div.text_cell_render").show();
567 this.rendered = true;
546
568 };
547 };
548 return cont;
569 };
549 };
570
550
571 IPython.TextCell = TextCell;
551 IPython.TextCell = TextCell;
@@ -6,6 +6,14 b' div.cell {'
6 .corner-all;
6 .corner-all;
7 border : thin @border_color solid;
7 border : thin @border_color solid;
8 }
8 }
9
10 &.edit_mode {
11 .corner-all;
12 border : thin green solid;
13 }
14 }
15
16 div.cell {
9 width: 100%;
17 width: 100%;
10 padding: 5px 5px 5px 0px;
18 padding: 5px 5px 5px 0px;
11 /* This acts as a spacer between cells, that is outside the border */
19 /* This acts as a spacer between cells, that is outside the border */
@@ -28,6 +28,8 b' div#notebook {'
28 padding: 5px 5px 15px 5px;
28 padding: 5px 5px 15px 5px;
29 margin: 0px;
29 margin: 0px;
30 border-top: 1px solid @border_color;
30 border-top: 1px solid @border_color;
31 outline: none;
32 .border-box-sizing();
31 }
33 }
32
34
33 div.ui-widget-content {
35 div.ui-widget-content {
@@ -9,7 +9,3 b''
9 display: inline-block;
9 display: inline-block;
10 }
10 }
11
11
12 div.quickhelp {
13 float: left;
14 width: 50%;
15 }
@@ -58,7 +58,9 b' input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto'
58 .ansibgpurple{background-color:magenta;}
58 .ansibgpurple{background-color:magenta;}
59 .ansibgcyan{background-color:cyan;}
59 .ansibgcyan{background-color:cyan;}
60 .ansibggray{background-color:gray;}
60 .ansibggray{background-color:gray;}
61 div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
61 div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
62 div.cell.edit_mode{border-radius:4px;border:thin green solid;}
63 div.cell{width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}
62 div.prompt{min-width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
64 div.prompt{min-width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
63 div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
65 div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
64 div.prompt:empty{padding-top:0;padding-bottom:0;}
66 div.prompt:empty{padding-top:0;padding-bottom:0;}
@@ -1439,7 +1439,9 b' input.engine_num_input{height:20px;margin-bottom:2px;padding-top:0;padding-botto'
1439 .ansibgpurple{background-color:magenta;}
1439 .ansibgpurple{background-color:magenta;}
1440 .ansibgcyan{background-color:cyan;}
1440 .ansibgcyan{background-color:cyan;}
1441 .ansibggray{background-color:gray;}
1441 .ansibggray{background-color:gray;}
1442 div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
1442 div.cell{border:1px solid transparent;display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;}div.cell.selected{border-radius:4px;border:thin #ababab solid;}
1443 div.cell.edit_mode{border-radius:4px;border:thin green solid;}
1444 div.cell{width:100%;padding:5px 5px 5px 0px;margin:0px;outline:none;}
1443 div.prompt{min-width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
1445 div.prompt{min-width:11ex;padding:0.4em;margin:0px;font-family:monospace;text-align:right;line-height:1.231em;}
1444 div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
1446 div.inner_cell{display:-webkit-box;-webkit-box-orient:vertical;-webkit-box-align:stretch;display:-moz-box;-moz-box-orient:vertical;-moz-box-align:stretch;display:box;box-orient:vertical;box-align:stretch;width:100%;-webkit-box-flex:1;-moz-box-flex:1;box-flex:1;}
1445 div.prompt:empty{padding-top:0;padding-bottom:0;}
1447 div.prompt:empty{padding-top:0;padding-bottom:0;}
@@ -1535,7 +1537,7 b' body{background-color:#ffffff;}'
1535 body.notebook_app{overflow:hidden;}
1537 body.notebook_app{overflow:hidden;}
1536 span#notebook_name{height:1em;line-height:1em;padding:3px;border:none;font-size:146.5%;}
1538 span#notebook_name{height:1em;line-height:1em;padding:3px;border:none;font-size:146.5%;}
1537 div#notebook_panel{margin:0px 0px 0px 0px;padding:0px;-webkit-box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);}
1539 div#notebook_panel{margin:0px 0px 0px 0px;padding:0px;-webkit-box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);-moz-box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);box-shadow:0 -1px 10px rgba(0, 0, 0, 0.1);}
1538 div#notebook{overflow-y:scroll;overflow-x:auto;width:100%;padding:5px 5px 15px 5px;margin:0px;border-top:1px solid #ababab;}
1540 div#notebook{overflow-y:scroll;overflow-x:auto;width:100%;padding:5px 5px 15px 5px;margin:0px;border-top:1px solid #ababab;outline:none;box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;}
1539 div.ui-widget-content{border:1px solid #ababab;outline:none;}
1541 div.ui-widget-content{border:1px solid #ababab;outline:none;}
1540 pre.dialog{background-color:#f7f7f7;border:1px solid #ddd;border-radius:4px;padding:0.4em;padding-left:2em;}
1542 pre.dialog{background-color:#f7f7f7;border:1px solid #ddd;border-radius:4px;padding:0.4em;padding-left:2em;}
1541 p.dialog{padding:0.2em;}
1543 p.dialog{padding:0.2em;}
@@ -1567,7 +1569,6 b' div#pager_splitter{height:8px;}'
1567 div#pager{overflow:auto;display:none;}div#pager pre{font-size:13px;line-height:1.231em;color:#000000;background-color:#f7f7f7;padding:0.4em;}
1569 div#pager{overflow:auto;display:none;}div#pager pre{font-size:13px;line-height:1.231em;color:#000000;background-color:#f7f7f7;padding:0.4em;}
1568 .shortcut_key{display:inline-block;width:15ex;text-align:right;font-family:monospace;}
1570 .shortcut_key{display:inline-block;width:15ex;text-align:right;font-family:monospace;}
1569 .shortcut_descr{display:inline-block;}
1571 .shortcut_descr{display:inline-block;}
1570 div.quickhelp{float:left;width:50%;}
1571 span#save_widget{padding:0px 5px;margin-top:12px;}
1572 span#save_widget{padding:0px 5px;margin-top:12px;}
1572 span#checkpoint_status,span#autosave_status{font-size:small;}
1573 span#checkpoint_status,span#autosave_status{font-size:small;}
1573 @media (max-width:767px){span#save_widget{font-size:small;} span#checkpoint_status,span#autosave_status{font-size:x-small;}}@media (max-width:767px){span#checkpoint_status,span#autosave_status{display:none;}}@media (min-width:768px) and (max-width:979px){span#checkpoint_status{display:none;} span#autosave_status{font-size:x-small;}}.toolbar{padding:0px 10px;margin-top:-5px;}.toolbar select,.toolbar label{width:auto;height:26px;vertical-align:middle;margin-right:2px;margin-bottom:0px;display:inline;font-size:92%;margin-left:0.3em;margin-right:0.3em;padding:0px;padding-top:3px;}
1574 @media (max-width:767px){span#save_widget{font-size:small;} span#checkpoint_status,span#autosave_status{font-size:x-small;}}@media (max-width:767px){span#checkpoint_status,span#autosave_status{display:none;}}@media (min-width:768px) and (max-width:979px){span#checkpoint_status{display:none;} span#autosave_status{font-size:x-small;}}.toolbar{padding:0px 10px;margin-top:-5px;}.toolbar select,.toolbar label{width:auto;height:26px;vertical-align:middle;margin-right:2px;margin-bottom:0px;display:inline;font-size:92%;margin-left:0.3em;margin-right:0.3em;padding:0px;padding-top:3px;}
@@ -140,8 +140,10 b' class="notebook_app"'
140 <ul class="dropdown-menu">
140 <ul class="dropdown-menu">
141 <li id="run_cell" title="Run this cell, and move cursor to the next one">
141 <li id="run_cell" title="Run this cell, and move cursor to the next one">
142 <a href="#">Run</a></li>
142 <a href="#">Run</a></li>
143 <li id="run_cell_in_place" title="Run this cell, without moving to the next one">
143 <li id="run_cell_select_below" title="Run this cell, select below">
144 <a href="#">Run in Place</a></li>
144 <a href="#">Run and Select Below</a></li>
145 <li id="run_cell_insert_below" title="Run this cell, insert below">
146 <a href="#">Run and Insert Below</a></li>
145 <li id="run_all_cells" title="Run all cells in the notebook">
147 <li id="run_all_cells" title="Run all cells in the notebook">
146 <a href="#">Run All</a></li>
148 <a href="#">Run All</a></li>
147 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
149 <li id="run_all_cells_above" title="Run all cells above (but not including) this cell">
@@ -291,6 +293,7 b' class="notebook_app"'
291 <script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
293 <script src="{{ static_url("notebook/js/toolbar.js") }}" type="text/javascript" charset="utf-8"></script>
292 <script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
294 <script src="{{ static_url("notebook/js/maintoolbar.js") }}" type="text/javascript" charset="utf-8"></script>
293 <script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
295 <script src="{{ static_url("notebook/js/notebook.js") }}" type="text/javascript" charset="utf-8"></script>
296 <script src="{{ static_url("notebook/js/keyboardmanager.js") }}" type="text/javascript" charset="utf-8"></script>
294 <script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
297 <script src="{{ static_url("notebook/js/notificationwidget.js") }}" type="text/javascript" charset="utf-8"></script>
295 <script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
298 <script src="{{ static_url("notebook/js/notificationarea.js") }}" type="text/javascript" charset="utf-8"></script>
296 <script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
299 <script src="{{ static_url("notebook/js/tooltip.js") }}" type="text/javascript" charset="utf-8"></script>
@@ -31,11 +31,13 b' casper.notebook_test(function () {'
31 var result = this.get_output_cell(0);
31 var result = this.get_output_cell(0);
32 var num_cells = this.get_cells_length();
32 var num_cells = this.get_cells_length();
33 this.test.assertEquals(result.text, '11\n', 'cell execute (using ctrl-enter)');
33 this.test.assertEquals(result.text, '11\n', 'cell execute (using ctrl-enter)');
34 this.test.assertEquals(num_cells, 1, ' ^--- does not add a new cell')
34 this.test.assertEquals(num_cells, 2, 'ctrl-enter adds a new cell at the bottom')
35 });
35 });
36
36
37 // do it again with the keyboard shortcut
37 // do it again with the keyboard shortcut
38 this.thenEvaluate(function () {
38 this.thenEvaluate(function () {
39 IPython.notebook.select(1);
40 IPython.notebook.delete_cell();
39 var cell = IPython.notebook.get_cell(0);
41 var cell = IPython.notebook.get_cell(0);
40 cell.set_text('a=12; print(a)');
42 cell.set_text('a=12; print(a)');
41 cell.clear_output();
43 cell.clear_output();
@@ -48,7 +50,7 b' casper.notebook_test(function () {'
48 var result = this.get_output_cell(0);
50 var result = this.get_output_cell(0);
49 var num_cells = this.get_cells_length();
51 var num_cells = this.get_cells_length();
50 this.test.assertEquals(result.text, '12\n', 'cell execute (using shift-enter)');
52 this.test.assertEquals(result.text, '12\n', 'cell execute (using shift-enter)');
51 this.test.assertEquals(num_cells, 2, ' ^--- adds a new cell')
53 this.test.assertEquals(num_cells, 1, 'shift-enter adds no new cell at the bottom')
52 });
54 });
53
55
54 // press the "play" triangle button in the toolbar
56 // press the "play" triangle button in the toolbar
@@ -4,11 +4,12 b''
4 casper.notebook_test(function() {
4 casper.notebook_test(function() {
5 var output = this.evaluate(function () {
5 var output = this.evaluate(function () {
6 // Fill in test data.
6 // Fill in test data.
7 IPython.notebook.command_mode();
7 var set_cell_text = function () {
8 var set_cell_text = function () {
8 var cell_one = IPython.notebook.get_selected_cell();
9 var cell_one = IPython.notebook.get_selected_cell();
9 cell_one.set_text('a = 5');
10 cell_one.set_text('a = 5');
10
11
11 IPython.notebook.insert_cell_below('code');
12 IPython.utils.press(IPython.keycodes.b)
12 var cell_two = IPython.notebook.get_selected_cell();
13 var cell_two = IPython.notebook.get_selected_cell();
13 cell_two.set_text('print(a)');
14 cell_two.set_text('print(a)');
14 };
15 };
@@ -31,7 +32,7 b' casper.notebook_test(function() {'
31 });
32 });
32
33
33 this.test.assertEquals(output.above, 'a = 5\nprint(a)',
34 this.test.assertEquals(output.above, 'a = 5\nprint(a)',
34 'Successful insert_cell_above().');
35 'Successful merge_cell_above().');
35 this.test.assertEquals(output.below, 'a = 5\nprint(a)',
36 this.test.assertEquals(output.below, 'a = 5\nprint(a)',
36 'Successful insert_cell_below().');
37 'Successful merge_cell_below().');
37 });
38 });
@@ -3,10 +3,11 b''
3 //
3 //
4 casper.notebook_test(function () {
4 casper.notebook_test(function () {
5 var result = this.evaluate(function() {
5 var result = this.evaluate(function() {
6 IPython.notebook.command_mode();
6 pos0 = IPython.notebook.get_selected_index();
7 pos0 = IPython.notebook.get_selected_index();
7 IPython.notebook.insert_cell_below('code');
8 IPython.utils.press(IPython.keycodes.b)
8 pos1 = IPython.notebook.get_selected_index();
9 pos1 = IPython.notebook.get_selected_index();
9 IPython.notebook.insert_cell_below('code');
10 IPython.utils.press(IPython.keycodes.b)
10 pos2 = IPython.notebook.get_selected_index();
11 pos2 = IPython.notebook.get_selected_index();
11 // Simulate the "up arrow" and "down arrow" keys.
12 // Simulate the "up arrow" and "down arrow" keys.
12 IPython.utils.press_up();
13 IPython.utils.press_up();
General Comments 0
You need to be logged in to leave comments. Login now