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