##// END OF EJS Templates
go to the beginning of the line when entering cell
Paul Ivanov -
Show More
@@ -1,567 +1,566
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2008-2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Cell
10 10 //============================================================================
11 11 /**
12 12 * An extendable module that provide base functionnality to create cell for notebook.
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule Cell
16 16 */
17 17
18 18 var IPython = (function (IPython) {
19 19 "use strict";
20 20
21 21 var utils = IPython.utils;
22 22 var keycodes = IPython.keyboard.keycodes;
23 23
24 24 /**
25 25 * The Base `Cell` class from which to inherit
26 26 * @class Cell
27 27 **/
28 28
29 29 /*
30 30 * @constructor
31 31 *
32 32 * @param {object|undefined} [options]
33 33 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
34 34 */
35 35 var Cell = function (options) {
36 36
37 37 options = this.mergeopt(Cell, options);
38 38 // superclass default overwrite our default
39 39
40 40 this.placeholder = options.placeholder || '';
41 41 this.read_only = options.cm_config.readOnly;
42 42 this.selected = false;
43 43 this.rendered = false;
44 44 this.mode = 'command';
45 45 this.metadata = {};
46 46 // load this from metadata later ?
47 47 this.user_highlight = 'auto';
48 48 this.cm_config = options.cm_config;
49 49 this.cell_id = utils.uuid();
50 50 this._options = options;
51 51
52 52 // For JS VM engines optimization, attributes should be all set (even
53 53 // to null) in the constructor, and if possible, if different subclass
54 54 // have new attributes with same name, they should be created in the
55 55 // same order. Easiest is to create and set to null in parent class.
56 56
57 57 this.element = null;
58 58 this.cell_type = this.cell_type || null;
59 59 this.code_mirror = null;
60 60
61 61 this.create_element();
62 62 if (this.element !== null) {
63 63 this.element.data("cell", this);
64 64 this.bind_events();
65 65 this.init_classes();
66 66 }
67 67 };
68 68
69 69 Cell.options_default = {
70 70 cm_config : {
71 71 indentUnit : 4,
72 72 readOnly: false,
73 73 theme: "default"
74 74 }
75 75 };
76 76
77 77 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
78 78 // by disabling drag/drop altogether on Safari
79 79 // https://github.com/marijnh/CodeMirror/issues/332
80 80 if (utils.browser[0] == "Safari") {
81 81 Cell.options_default.cm_config.dragDrop = false;
82 82 }
83 83
84 84 Cell.prototype.mergeopt = function(_class, options, overwrite){
85 85 options = options || {};
86 86 overwrite = overwrite || {};
87 87 return $.extend(true, {}, _class.options_default, options, overwrite);
88 88 };
89 89
90 90 /**
91 91 * Empty. Subclasses must implement create_element.
92 92 * This should contain all the code to create the DOM element in notebook
93 93 * and will be called by Base Class constructor.
94 94 * @method create_element
95 95 */
96 96 Cell.prototype.create_element = function () {
97 97 };
98 98
99 99 Cell.prototype.init_classes = function () {
100 100 // Call after this.element exists to initialize the css classes
101 101 // related to selected, rendered and mode.
102 102 if (this.selected) {
103 103 this.element.addClass('selected');
104 104 } else {
105 105 this.element.addClass('unselected');
106 106 }
107 107 if (this.rendered) {
108 108 this.element.addClass('rendered');
109 109 } else {
110 110 this.element.addClass('unrendered');
111 111 }
112 112 if (this.mode === 'edit') {
113 113 this.element.addClass('edit_mode');
114 114 } else {
115 115 this.element.addClass('command_mode');
116 116 }
117 117 };
118 118
119 119 /**
120 120 * Subclasses can implement override bind_events.
121 121 * Be carefull to call the parent method when overwriting as it fires event.
122 122 * this will be triggerd after create_element in constructor.
123 123 * @method bind_events
124 124 */
125 125 Cell.prototype.bind_events = function () {
126 126 var that = this;
127 127 // We trigger events so that Cell doesn't have to depend on Notebook.
128 128 that.element.click(function (event) {
129 129 if (!that.selected) {
130 130 $([IPython.events]).trigger('select.Cell', {'cell':that});
131 131 }
132 132 });
133 133 that.element.focusin(function (event) {
134 134 if (!that.selected) {
135 135 $([IPython.events]).trigger('select.Cell', {'cell':that});
136 136 }
137 137 });
138 138 if (this.code_mirror) {
139 139 this.code_mirror.on("change", function(cm, change) {
140 140 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
141 141 });
142 142 }
143 143 if (this.code_mirror) {
144 144 this.code_mirror.on('focus', function(cm, change) {
145 145 $([IPython.events]).trigger('edit_mode.Cell', {cell: that});
146 146 });
147 147 }
148 148 if (this.code_mirror) {
149 149 this.code_mirror.on('blur', function(cm, change) {
150 150 // Check if this unfocus event is legit.
151 151 if (!that.should_cancel_blur()) {
152 152 $([IPython.events]).trigger('command_mode.Cell', {cell: that});
153 153 }
154 154 });
155 155 }
156 156 };
157 157
158 158 /**
159 159 * This method gets called in CodeMirror's onKeyDown/onKeyPress
160 160 * handlers and is used to provide custom key handling.
161 161 *
162 162 * To have custom handling, subclasses should override this method, but still call it
163 163 * in order to process the Edit mode keyboard shortcuts.
164 164 *
165 165 * @method handle_codemirror_keyevent
166 166 * @param {CodeMirror} editor - The codemirror instance bound to the cell
167 167 * @param {event} event - key press event which either should or should not be handled by CodeMirror
168 168 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
169 169 */
170 170 Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
171 171 var that = this;
172 172 var shortcuts = IPython.keyboard_manager.edit_shortcuts;
173 173
174 174 // if this is an edit_shortcuts shortcut, the global keyboard/shortcut
175 175 // manager will handle it
176 176 if (shortcuts.handles(event)) { return true; }
177 177
178 178 return false;
179 179 };
180 180
181 181
182 182 /**
183 183 * Triger typsetting of math by mathjax on current cell element
184 184 * @method typeset
185 185 */
186 186 Cell.prototype.typeset = function () {
187 187 if (window.MathJax) {
188 188 var cell_math = this.element.get(0);
189 189 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
190 190 }
191 191 };
192 192
193 193 /**
194 194 * handle cell level logic when a cell is selected
195 195 * @method select
196 196 * @return is the action being taken
197 197 */
198 198 Cell.prototype.select = function () {
199 199 if (!this.selected) {
200 200 this.element.addClass('selected');
201 201 this.element.removeClass('unselected');
202 202 this.selected = true;
203 203 return true;
204 204 } else {
205 205 return false;
206 206 }
207 207 };
208 208
209 209 /**
210 210 * handle cell level logic when a cell is unselected
211 211 * @method unselect
212 212 * @return is the action being taken
213 213 */
214 214 Cell.prototype.unselect = function () {
215 215 if (this.selected) {
216 216 this.element.addClass('unselected');
217 217 this.element.removeClass('selected');
218 218 this.selected = false;
219 219 return true;
220 220 } else {
221 221 return false;
222 222 }
223 223 };
224 224
225 225 /**
226 226 * handle cell level logic when a cell is rendered
227 227 * @method render
228 228 * @return is the action being taken
229 229 */
230 230 Cell.prototype.render = function () {
231 231 if (!this.rendered) {
232 232 this.element.addClass('rendered');
233 233 this.element.removeClass('unrendered');
234 234 this.rendered = true;
235 235 return true;
236 236 } else {
237 237 return false;
238 238 }
239 239 };
240 240
241 241 /**
242 242 * handle cell level logic when a cell is unrendered
243 243 * @method unrender
244 244 * @return is the action being taken
245 245 */
246 246 Cell.prototype.unrender = function () {
247 247 if (this.rendered) {
248 248 this.element.addClass('unrendered');
249 249 this.element.removeClass('rendered');
250 250 this.rendered = false;
251 251 return true;
252 252 } else {
253 253 return false;
254 254 }
255 255 };
256 256
257 257 /**
258 258 * Delegates keyboard shortcut handling to either IPython keyboard
259 259 * manager when in command mode, or CodeMirror when in edit mode
260 260 *
261 261 * @method handle_keyevent
262 262 * @param {CodeMirror} editor - The codemirror instance bound to the cell
263 263 * @param {event} event -
264 264 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
265 265 */
266 266 Cell.prototype.handle_keyevent = function (editor, event) {
267 267
268 268 // console.log('CM', this.mode, event.which, event.type)
269 269
270 270 if (this.mode === 'command') {
271 271 return true;
272 272 } else if (this.mode === 'edit') {
273 273 return this.handle_codemirror_keyevent(editor, event);
274 274 }
275 275 };
276 276
277 277 /**
278 278 * @method at_top
279 279 * @return {Boolean}
280 280 */
281 281 Cell.prototype.at_top = function () {
282 282 var cm = this.code_mirror;
283 283 var cursor = cm.getCursor();
284 284 if (cursor.line === 0 && cursor.ch === 0) {
285 285 return true;
286 } else {
287 return false;
288 286 }
287 return false;
289 288 };
290 289
291 290 /**
292 291 * @method at_bottom
293 292 * @return {Boolean}
294 293 * */
295 294 Cell.prototype.at_bottom = function () {
296 295 var cm = this.code_mirror;
297 296 var cursor = cm.getCursor();
298 297 if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
299 298 return true;
300 } else {
301 return false;
302 299 }
300 return false;
303 301 };
302
304 303 /**
305 304 * enter the command mode for the cell
306 305 * @method command_mode
307 306 * @return is the action being taken
308 307 */
309 308 Cell.prototype.command_mode = function () {
310 309 if (this.mode !== 'command') {
311 310 this.element.addClass('command_mode');
312 311 this.element.removeClass('edit_mode');
313 312 this.mode = 'command';
314 313 return true;
315 314 } else {
316 315 return false;
317 316 }
318 317 };
319 318
320 319 /**
321 320 * enter the edit mode for the cell
322 321 * @method command_mode
323 322 * @return is the action being taken
324 323 */
325 324 Cell.prototype.edit_mode = function () {
326 325 if (this.mode !== 'edit') {
327 326 this.element.addClass('edit_mode');
328 327 this.element.removeClass('command_mode');
329 328 this.mode = 'edit';
330 329 return true;
331 330 } else {
332 331 return false;
333 332 }
334 333 };
335 334
336 335 /**
337 336 * Determine whether or not the unfocus event should be aknowledged.
338 337 *
339 338 * @method should_cancel_blur
340 339 *
341 340 * @return results {bool} Whether or not to ignore the cell's blur event.
342 341 **/
343 342 Cell.prototype.should_cancel_blur = function () {
344 343 return false;
345 344 };
346 345
347 346 /**
348 347 * Focus the cell in the DOM sense
349 348 * @method focus_cell
350 349 */
351 350 Cell.prototype.focus_cell = function () {
352 351 this.element.focus();
353 352 };
354 353
355 354 /**
356 355 * Focus the editor area so a user can type
357 356 *
358 357 * NOTE: If codemirror is focused via a mouse click event, you don't want to
359 358 * call this because it will cause a page jump.
360 359 * @method focus_editor
361 360 */
362 361 Cell.prototype.focus_editor = function () {
363 362 this.refresh();
364 363 this.code_mirror.focus();
365 364 };
366 365
367 366 /**
368 367 * Refresh codemirror instance
369 368 * @method refresh
370 369 */
371 370 Cell.prototype.refresh = function () {
372 371 this.code_mirror.refresh();
373 372 };
374 373
375 374 /**
376 375 * should be overritten by subclass
377 376 * @method get_text
378 377 */
379 378 Cell.prototype.get_text = function () {
380 379 };
381 380
382 381 /**
383 382 * should be overritten by subclass
384 383 * @method set_text
385 384 * @param {string} text
386 385 */
387 386 Cell.prototype.set_text = function (text) {
388 387 };
389 388
390 389 /**
391 390 * should be overritten by subclass
392 391 * serialise cell to json.
393 392 * @method toJSON
394 393 **/
395 394 Cell.prototype.toJSON = function () {
396 395 var data = {};
397 396 data.metadata = this.metadata;
398 397 data.cell_type = this.cell_type;
399 398 return data;
400 399 };
401 400
402 401
403 402 /**
404 403 * should be overritten by subclass
405 404 * @method fromJSON
406 405 **/
407 406 Cell.prototype.fromJSON = function (data) {
408 407 if (data.metadata !== undefined) {
409 408 this.metadata = data.metadata;
410 409 }
411 410 this.celltoolbar.rebuild();
412 411 };
413 412
414 413
415 414 /**
416 415 * can the cell be split into two cells
417 416 * @method is_splittable
418 417 **/
419 418 Cell.prototype.is_splittable = function () {
420 419 return true;
421 420 };
422 421
423 422
424 423 /**
425 424 * can the cell be merged with other cells
426 425 * @method is_mergeable
427 426 **/
428 427 Cell.prototype.is_mergeable = function () {
429 428 return true;
430 429 };
431 430
432 431
433 432 /**
434 433 * @return {String} - the text before the cursor
435 434 * @method get_pre_cursor
436 435 **/
437 436 Cell.prototype.get_pre_cursor = function () {
438 437 var cursor = this.code_mirror.getCursor();
439 438 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
440 439 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
441 440 return text;
442 441 };
443 442
444 443
445 444 /**
446 445 * @return {String} - the text after the cursor
447 446 * @method get_post_cursor
448 447 **/
449 448 Cell.prototype.get_post_cursor = function () {
450 449 var cursor = this.code_mirror.getCursor();
451 450 var last_line_num = this.code_mirror.lineCount()-1;
452 451 var last_line_len = this.code_mirror.getLine(last_line_num).length;
453 452 var end = {line:last_line_num, ch:last_line_len};
454 453 var text = this.code_mirror.getRange(cursor, end);
455 454 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
456 455 return text;
457 456 };
458 457
459 458 /**
460 459 * Show/Hide CodeMirror LineNumber
461 460 * @method show_line_numbers
462 461 *
463 462 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
464 463 **/
465 464 Cell.prototype.show_line_numbers = function (value) {
466 465 this.code_mirror.setOption('lineNumbers', value);
467 466 this.code_mirror.refresh();
468 467 };
469 468
470 469 /**
471 470 * Toggle CodeMirror LineNumber
472 471 * @method toggle_line_numbers
473 472 **/
474 473 Cell.prototype.toggle_line_numbers = function () {
475 474 var val = this.code_mirror.getOption('lineNumbers');
476 475 this.show_line_numbers(!val);
477 476 };
478 477
479 478 /**
480 479 * Force codemirror highlight mode
481 480 * @method force_highlight
482 481 * @param {object} - CodeMirror mode
483 482 **/
484 483 Cell.prototype.force_highlight = function(mode) {
485 484 this.user_highlight = mode;
486 485 this.auto_highlight();
487 486 };
488 487
489 488 /**
490 489 * Try to autodetect cell highlight mode, or use selected mode
491 490 * @methods _auto_highlight
492 491 * @private
493 492 * @param {String|object|undefined} - CodeMirror mode | 'auto'
494 493 **/
495 494 Cell.prototype._auto_highlight = function (modes) {
496 495 //Here we handle manually selected modes
497 496 var mode;
498 497 if( this.user_highlight !== undefined && this.user_highlight != 'auto' )
499 498 {
500 499 mode = this.user_highlight;
501 500 CodeMirror.autoLoadMode(this.code_mirror, mode);
502 501 this.code_mirror.setOption('mode', mode);
503 502 return;
504 503 }
505 504 var current_mode = this.code_mirror.getOption('mode', mode);
506 505 var first_line = this.code_mirror.getLine(0);
507 506 // loop on every pairs
508 507 for(mode in modes) {
509 508 var regs = modes[mode].reg;
510 509 // only one key every time but regexp can't be keys...
511 510 for(var i=0; i<regs.length; i++) {
512 511 // here we handle non magic_modes
513 512 if(first_line.match(regs[i]) !== null) {
514 513 if(current_mode == mode){
515 514 return;
516 515 }
517 516 if (mode.search('magic_') !== 0) {
518 517 this.code_mirror.setOption('mode', mode);
519 518 CodeMirror.autoLoadMode(this.code_mirror, mode);
520 519 return;
521 520 }
522 521 var open = modes[mode].open || "%%";
523 522 var close = modes[mode].close || "%%end";
524 523 var mmode = mode;
525 524 mode = mmode.substr(6);
526 525 if(current_mode == mode){
527 526 return;
528 527 }
529 528 CodeMirror.autoLoadMode(this.code_mirror, mode);
530 529 // create on the fly a mode that swhitch between
531 530 // plain/text and smth else otherwise `%%` is
532 531 // source of some highlight issues.
533 532 // we use patchedGetMode to circumvent a bug in CM
534 533 CodeMirror.defineMode(mmode , function(config) {
535 534 return CodeMirror.multiplexingMode(
536 535 CodeMirror.patchedGetMode(config, 'text/plain'),
537 536 // always set someting on close
538 537 {open: open, close: close,
539 538 mode: CodeMirror.patchedGetMode(config, mode),
540 539 delimStyle: "delimit"
541 540 }
542 541 );
543 542 });
544 543 this.code_mirror.setOption('mode', mmode);
545 544 return;
546 545 }
547 546 }
548 547 }
549 548 // fallback on default
550 549 var default_mode;
551 550 try {
552 551 default_mode = this._options.cm_config.mode;
553 552 } catch(e) {
554 553 default_mode = 'text/plain';
555 554 }
556 555 if( current_mode === default_mode){
557 556 return;
558 557 }
559 558 this.code_mirror.setOption('mode', default_mode);
560 559 };
561 560
562 561 IPython.Cell = Cell;
563 562
564 563 return IPython;
565 564
566 565 }(IPython));
567 566
@@ -1,614 +1,612
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2011 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Keyboard management
10 10 //============================================================================
11 11
12 12 var IPython = (function (IPython) {
13 13 "use strict";
14 14
15 15 var browser = IPython.utils.browser[0];
16 16 var platform = IPython.utils.platform;
17 17
18 18 // Default keyboard shortcuts
19 19
20 20 var default_common_shortcuts = {
21 21 'shift' : {
22 22 help : '',
23 23 help_index : '',
24 24 handler : function (event) {
25 25 // ignore shift keydown
26 26 return true;
27 27 }
28 28 },
29 29 'shift+enter' : {
30 30 help : 'run cell, select below',
31 31 help_index : 'ba',
32 32 handler : function (event) {
33 33 IPython.notebook.execute_cell_and_select_below();
34 34 return false;
35 35 }
36 36 },
37 37 'ctrl+enter' : {
38 38 help : 'run cell',
39 39 help_index : 'bb',
40 40 handler : function (event) {
41 41 IPython.notebook.execute_cell();
42 42 return false;
43 43 }
44 44 },
45 45 'alt+enter' : {
46 46 help : 'run cell, insert below',
47 47 help_index : 'bc',
48 48 handler : function (event) {
49 49 IPython.notebook.execute_cell_and_insert_below();
50 50 return false;
51 51 }
52 52 }
53 53 };
54 54
55 55 if (platform === 'MacOS') {
56 56 default_common_shortcuts['cmd+s'] =
57 57 {
58 58 help : 'save notebook',
59 59 help_index : 'fb',
60 60 handler : function (event) {
61 61 IPython.notebook.save_checkpoint();
62 62 event.preventDefault();
63 63 return false;
64 64 }
65 65 };
66 66 } else {
67 67 default_common_shortcuts['ctrl+s'] =
68 68 {
69 69 help : 'save notebook',
70 70 help_index : 'fb',
71 71 handler : function (event) {
72 72 IPython.notebook.save_checkpoint();
73 73 event.preventDefault();
74 74 return false;
75 75 }
76 76 };
77 77 }
78 78
79 79 // Edit mode defaults
80 80
81 81 var default_edit_shortcuts = {
82 82 'esc' : {
83 83 help : 'command mode',
84 84 help_index : 'aa',
85 85 handler : function (event) {
86 86 IPython.notebook.command_mode();
87 87 return false;
88 88 }
89 89 },
90 90 'ctrl+m' : {
91 91 help : 'command mode',
92 92 help_index : 'ab',
93 93 handler : function (event) {
94 94 IPython.notebook.command_mode();
95 95 return false;
96 96 }
97 97 },
98 98 'up' : {
99 99 help : '',
100 100 help_index : '',
101 101 handler : function (event) {
102 102 var index = IPython.notebook.get_selected_index();
103 103 if (index !== null && index !== 0) {
104 104 var cell = IPython.notebook.get_cell(index);
105 105 if (cell && cell.at_top()) {
106 106 event.preventDefault();
107 107 IPython.notebook.command_mode();
108 108 IPython.notebook.select_prev();
109 109 IPython.notebook.edit_mode();
110 110 var cm = IPython.notebook.get_selected_cell().code_mirror;
111 var prev_cursor = cell.code_mirror.getCursor();
112 cm.setCursor(cm.lastLine(), prev_cursor.ch)
111 cm.setCursor(cm.lastLine(), 0);
113 112 return false;
114 113 } else if (cell) {
115 114 var cm = cell.code_mirror;
116 115 var cursor = cm.getCursor();
117 116 cursor.line -= 1;
118 117 cm.setCursor(cursor);
119 118 return false;
120 119 }
121 120 }
122 121 }
123 122 },
124 123 'down' : {
125 124 help : '',
126 125 help_index : '',
127 126 handler : function (event) {
128 127 var index = IPython.notebook.get_selected_index();
129 128 if (index !== null && index !== (IPython.notebook.ncells()-1)) {
130 129 var cell = IPython.notebook.get_cell(index);
131 130 if (cell && cell.at_bottom()) {
132 131 event.preventDefault();
133 132 IPython.notebook.command_mode();
134 133 IPython.notebook.select_next();
135 134 IPython.notebook.edit_mode();
136 135 var cm = IPython.notebook.get_selected_cell().code_mirror;
137 var prev_cursor = cell.code_mirror.getCursor();
138 cm.setCursor(0, prev_cursor.ch);
136 cm.setCursor(0, 0);
139 137 return false;
140 138 } else if (cell) {
141 139 var cm = cell.code_mirror;
142 140 var cursor = cm.getCursor();
143 141 cursor.line += 1;
144 142 cm.setCursor(cursor);
145 143 return false;
146 144 }
147 145 }
148 146 }
149 147 },
150 148 'alt+-' : {
151 149 help : 'split cell',
152 150 help_index : 'ea',
153 151 handler : function (event) {
154 152 IPython.notebook.split_cell();
155 153 return false;
156 154 }
157 155 },
158 156 'alt+subtract' : {
159 157 help : '',
160 158 help_index : 'eb',
161 159 handler : function (event) {
162 160 IPython.notebook.split_cell();
163 161 return false;
164 162 }
165 163 },
166 164 'tab' : {
167 165 help : 'indent or complete',
168 166 help_index : 'ec',
169 167 },
170 168 'shift+tab' : {
171 169 help : 'tooltip',
172 170 help_index : 'ed',
173 171 },
174 172 };
175 173
176 174 if (platform === 'MacOS') {
177 175 default_edit_shortcuts['cmd+/'] =
178 176 {
179 177 help : 'toggle comment',
180 178 help_index : 'ee'
181 179 };
182 180 default_edit_shortcuts['cmd+]'] =
183 181 {
184 182 help : 'indent',
185 183 help_index : 'ef'
186 184 };
187 185 default_edit_shortcuts['cmd+['] =
188 186 {
189 187 help : 'dedent',
190 188 help_index : 'eg'
191 189 };
192 190 } else {
193 191 default_edit_shortcuts['ctrl+/'] =
194 192 {
195 193 help : 'toggle comment',
196 194 help_index : 'ee'
197 195 };
198 196 default_edit_shortcuts['ctrl+]'] =
199 197 {
200 198 help : 'indent',
201 199 help_index : 'ef'
202 200 };
203 201 default_edit_shortcuts['ctrl+['] =
204 202 {
205 203 help : 'dedent',
206 204 help_index : 'eg'
207 205 };
208 206 }
209 207
210 208 // Command mode defaults
211 209
212 210 var default_command_shortcuts = {
213 211 'enter' : {
214 212 help : 'edit mode',
215 213 help_index : 'aa',
216 214 handler : function (event) {
217 215 IPython.notebook.edit_mode();
218 216 return false;
219 217 }
220 218 },
221 219 'up' : {
222 220 help : 'select previous cell',
223 221 help_index : 'da',
224 222 handler : function (event) {
225 223 var index = IPython.notebook.get_selected_index();
226 224 if (index !== 0 && index !== null) {
227 225 IPython.notebook.select_prev();
228 226 IPython.notebook.focus_cell();
229 227 }
230 228 return false;
231 229 }
232 230 },
233 231 'down' : {
234 232 help : 'select next cell',
235 233 help_index : 'db',
236 234 handler : function (event) {
237 235 var index = IPython.notebook.get_selected_index();
238 236 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
239 237 IPython.notebook.select_next();
240 238 IPython.notebook.focus_cell();
241 239 }
242 240 return false;
243 241 }
244 242 },
245 243 'k' : {
246 244 help : 'select previous cell',
247 245 help_index : 'dc',
248 246 handler : function (event) {
249 247 var index = IPython.notebook.get_selected_index();
250 248 if (index !== 0 && index !== null) {
251 249 IPython.notebook.select_prev();
252 250 IPython.notebook.focus_cell();
253 251 }
254 252 return false;
255 253 }
256 254 },
257 255 'j' : {
258 256 help : 'select next cell',
259 257 help_index : 'dd',
260 258 handler : function (event) {
261 259 var index = IPython.notebook.get_selected_index();
262 260 if (index !== (IPython.notebook.ncells()-1) && index !== null) {
263 261 IPython.notebook.select_next();
264 262 IPython.notebook.focus_cell();
265 263 }
266 264 return false;
267 265 }
268 266 },
269 267 'x' : {
270 268 help : 'cut cell',
271 269 help_index : 'ee',
272 270 handler : function (event) {
273 271 IPython.notebook.cut_cell();
274 272 return false;
275 273 }
276 274 },
277 275 'c' : {
278 276 help : 'copy cell',
279 277 help_index : 'ef',
280 278 handler : function (event) {
281 279 IPython.notebook.copy_cell();
282 280 return false;
283 281 }
284 282 },
285 283 'shift+v' : {
286 284 help : 'paste cell above',
287 285 help_index : 'eg',
288 286 handler : function (event) {
289 287 IPython.notebook.paste_cell_above();
290 288 return false;
291 289 }
292 290 },
293 291 'v' : {
294 292 help : 'paste cell below',
295 293 help_index : 'eh',
296 294 handler : function (event) {
297 295 IPython.notebook.paste_cell_below();
298 296 return false;
299 297 }
300 298 },
301 299 'd' : {
302 300 help : 'delete cell (press twice)',
303 301 help_index : 'ej',
304 302 count: 2,
305 303 handler : function (event) {
306 304 IPython.notebook.delete_cell();
307 305 return false;
308 306 }
309 307 },
310 308 'a' : {
311 309 help : 'insert cell above',
312 310 help_index : 'ec',
313 311 handler : function (event) {
314 312 IPython.notebook.insert_cell_above('code');
315 313 IPython.notebook.select_prev();
316 314 IPython.notebook.focus_cell();
317 315 return false;
318 316 }
319 317 },
320 318 'b' : {
321 319 help : 'insert cell below',
322 320 help_index : 'ed',
323 321 handler : function (event) {
324 322 IPython.notebook.insert_cell_below('code');
325 323 IPython.notebook.select_next();
326 324 IPython.notebook.focus_cell();
327 325 return false;
328 326 }
329 327 },
330 328 'y' : {
331 329 help : 'to code',
332 330 help_index : 'ca',
333 331 handler : function (event) {
334 332 IPython.notebook.to_code();
335 333 return false;
336 334 }
337 335 },
338 336 'm' : {
339 337 help : 'to markdown',
340 338 help_index : 'cb',
341 339 handler : function (event) {
342 340 IPython.notebook.to_markdown();
343 341 return false;
344 342 }
345 343 },
346 344 'r' : {
347 345 help : 'to raw',
348 346 help_index : 'cc',
349 347 handler : function (event) {
350 348 IPython.notebook.to_raw();
351 349 return false;
352 350 }
353 351 },
354 352 '1' : {
355 353 help : 'to heading 1',
356 354 help_index : 'cd',
357 355 handler : function (event) {
358 356 IPython.notebook.to_heading(undefined, 1);
359 357 return false;
360 358 }
361 359 },
362 360 '2' : {
363 361 help : 'to heading 2',
364 362 help_index : 'ce',
365 363 handler : function (event) {
366 364 IPython.notebook.to_heading(undefined, 2);
367 365 return false;
368 366 }
369 367 },
370 368 '3' : {
371 369 help : 'to heading 3',
372 370 help_index : 'cf',
373 371 handler : function (event) {
374 372 IPython.notebook.to_heading(undefined, 3);
375 373 return false;
376 374 }
377 375 },
378 376 '4' : {
379 377 help : 'to heading 4',
380 378 help_index : 'cg',
381 379 handler : function (event) {
382 380 IPython.notebook.to_heading(undefined, 4);
383 381 return false;
384 382 }
385 383 },
386 384 '5' : {
387 385 help : 'to heading 5',
388 386 help_index : 'ch',
389 387 handler : function (event) {
390 388 IPython.notebook.to_heading(undefined, 5);
391 389 return false;
392 390 }
393 391 },
394 392 '6' : {
395 393 help : 'to heading 6',
396 394 help_index : 'ci',
397 395 handler : function (event) {
398 396 IPython.notebook.to_heading(undefined, 6);
399 397 return false;
400 398 }
401 399 },
402 400 'o' : {
403 401 help : 'toggle output',
404 402 help_index : 'gb',
405 403 handler : function (event) {
406 404 IPython.notebook.toggle_output();
407 405 return false;
408 406 }
409 407 },
410 408 'shift+o' : {
411 409 help : 'toggle output scrolling',
412 410 help_index : 'gc',
413 411 handler : function (event) {
414 412 IPython.notebook.toggle_output_scroll();
415 413 return false;
416 414 }
417 415 },
418 416 's' : {
419 417 help : 'save notebook',
420 418 help_index : 'fa',
421 419 handler : function (event) {
422 420 IPython.notebook.save_checkpoint();
423 421 return false;
424 422 }
425 423 },
426 424 'ctrl+j' : {
427 425 help : 'move cell down',
428 426 help_index : 'eb',
429 427 handler : function (event) {
430 428 IPython.notebook.move_cell_down();
431 429 return false;
432 430 }
433 431 },
434 432 'ctrl+k' : {
435 433 help : 'move cell up',
436 434 help_index : 'ea',
437 435 handler : function (event) {
438 436 IPython.notebook.move_cell_up();
439 437 return false;
440 438 }
441 439 },
442 440 'l' : {
443 441 help : 'toggle line numbers',
444 442 help_index : 'ga',
445 443 handler : function (event) {
446 444 IPython.notebook.cell_toggle_line_numbers();
447 445 return false;
448 446 }
449 447 },
450 448 'i' : {
451 449 help : 'interrupt kernel (press twice)',
452 450 help_index : 'ha',
453 451 count: 2,
454 452 handler : function (event) {
455 453 IPython.notebook.kernel.interrupt();
456 454 return false;
457 455 }
458 456 },
459 457 '0' : {
460 458 help : 'restart kernel (press twice)',
461 459 help_index : 'hb',
462 460 count: 2,
463 461 handler : function (event) {
464 462 IPython.notebook.restart_kernel();
465 463 return false;
466 464 }
467 465 },
468 466 'h' : {
469 467 help : 'keyboard shortcuts',
470 468 help_index : 'ge',
471 469 handler : function (event) {
472 470 IPython.quick_help.show_keyboard_shortcuts();
473 471 return false;
474 472 }
475 473 },
476 474 'z' : {
477 475 help : 'undo last delete',
478 476 help_index : 'ei',
479 477 handler : function (event) {
480 478 IPython.notebook.undelete_cell();
481 479 return false;
482 480 }
483 481 },
484 482 'shift+m' : {
485 483 help : 'merge cell below',
486 484 help_index : 'ek',
487 485 handler : function (event) {
488 486 IPython.notebook.merge_cell_below();
489 487 return false;
490 488 }
491 489 },
492 490 'q' : {
493 491 help : 'close pager',
494 492 help_index : 'gd',
495 493 handler : function (event) {
496 494 IPython.pager.collapse();
497 495 return false;
498 496 }
499 497 },
500 498 };
501 499
502 500
503 501 // Main keyboard manager for the notebook
504 502
505 503 var ShortcutManager = IPython.keyboard.ShortcutManager;
506 504 var keycodes = IPython.keyboard.keycodes;
507 505
508 506 var KeyboardManager = function () {
509 507 this.mode = 'command';
510 508 this.enabled = true;
511 509 this.bind_events();
512 510 this.command_shortcuts = new ShortcutManager();
513 511 this.command_shortcuts.add_shortcuts(default_common_shortcuts);
514 512 this.command_shortcuts.add_shortcuts(default_command_shortcuts);
515 513 this.edit_shortcuts = new ShortcutManager();
516 514 this.edit_shortcuts.add_shortcuts(default_common_shortcuts);
517 515 this.edit_shortcuts.add_shortcuts(default_edit_shortcuts);
518 516 };
519 517
520 518 KeyboardManager.prototype.bind_events = function () {
521 519 var that = this;
522 520 $(document).keydown(function (event) {
523 521 return that.handle_keydown(event);
524 522 });
525 523 };
526 524
527 525 KeyboardManager.prototype.handle_keydown = function (event) {
528 526 var notebook = IPython.notebook;
529 527
530 528 if (event.which === keycodes.esc) {
531 529 // Intercept escape at highest level to avoid closing
532 530 // websocket connection with firefox
533 531 event.preventDefault();
534 532 }
535 533
536 534 if (!this.enabled) {
537 535 if (event.which === keycodes.esc) {
538 536 // ESC
539 537 notebook.command_mode();
540 538 return false;
541 539 }
542 540 return true;
543 541 }
544 542
545 543 if (this.mode === 'edit') {
546 544 return this.edit_shortcuts.call_handler(event);
547 545 } else if (this.mode === 'command') {
548 546 return this.command_shortcuts.call_handler(event);
549 547 }
550 548 return true;
551 549 };
552 550
553 551 KeyboardManager.prototype.edit_mode = function () {
554 552 this.last_mode = this.mode;
555 553 this.mode = 'edit';
556 554 };
557 555
558 556 KeyboardManager.prototype.command_mode = function () {
559 557 this.last_mode = this.mode;
560 558 this.mode = 'command';
561 559 };
562 560
563 561 KeyboardManager.prototype.enable = function () {
564 562 this.enabled = true;
565 563 };
566 564
567 565 KeyboardManager.prototype.disable = function () {
568 566 this.enabled = false;
569 567 };
570 568
571 569 KeyboardManager.prototype.register_events = function (e) {
572 570 var that = this;
573 571 var handle_focus = function () {
574 572 that.disable();
575 573 };
576 574 var handle_blur = function () {
577 575 that.enable();
578 576 };
579 577 e.on('focusin', handle_focus);
580 578 e.on('focusout', handle_blur);
581 579 // TODO: Very strange. The focusout event does not seem fire for the
582 580 // bootstrap textboxes on FF25&26... This works around that by
583 581 // registering focus and blur events recursively on all inputs within
584 582 // registered element.
585 583 e.find('input').blur(handle_blur);
586 584 e.on('DOMNodeInserted', function (event) {
587 585 var target = $(event.target);
588 586 if (target.is('input')) {
589 587 target.blur(handle_blur);
590 588 } else {
591 589 target.find('input').blur(handle_blur);
592 590 }
593 591 });
594 592 // There are times (raw_input) where we remove the element from the DOM before
595 593 // focusout is called. In this case we bind to the remove event of jQueryUI,
596 594 // which gets triggered upon removal, iff it is focused at the time.
597 595 // is_focused must be used to check for the case where an element within
598 596 // the element being removed is focused.
599 597 e.on('remove', function () {
600 598 if (IPython.utils.is_focused(e[0])) {
601 599 that.enable();
602 600 }
603 601 });
604 602 };
605 603
606 604
607 605 IPython.default_common_shortcuts = default_common_shortcuts;
608 606 IPython.default_edit_shortcuts = default_edit_shortcuts;
609 607 IPython.default_command_shortcuts = default_command_shortcuts;
610 608 IPython.KeyboardManager = KeyboardManager;
611 609
612 610 return IPython;
613 611
614 612 }(IPython));
General Comments 0
You need to be logged in to leave comments. Login now