##// END OF EJS Templates
Merge pull request #6857 from jdfreder/celltoolbar_metadata_fix...
Thomas Kluyver -
r18858:9932fd3f merge
parent child Browse files
Show More
@@ -1,588 +1,597 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 /**
4 /**
5 *
5 *
6 *
6 *
7 * @module cell
7 * @module cell
8 * @namespace cell
8 * @namespace cell
9 * @class Cell
9 * @class Cell
10 */
10 */
11
11
12
12
13 define([
13 define([
14 'base/js/namespace',
14 'base/js/namespace',
15 'jquery',
15 'jquery',
16 'base/js/utils',
16 'base/js/utils',
17 'codemirror/lib/codemirror',
17 'codemirror/lib/codemirror',
18 'codemirror/addon/edit/matchbrackets',
18 'codemirror/addon/edit/matchbrackets',
19 'codemirror/addon/edit/closebrackets',
19 'codemirror/addon/edit/closebrackets',
20 'codemirror/addon/comment/comment'
20 'codemirror/addon/comment/comment'
21 ], function(IPython, $, utils, CodeMirror, cm_match, cm_closeb, cm_comment) {
21 ], function(IPython, $, utils, CodeMirror, cm_match, cm_closeb, cm_comment) {
22 // TODO: remove IPython dependency here
22 // TODO: remove IPython dependency here
23 "use strict";
23 "use strict";
24
24
25 var Cell = function (options) {
25 var Cell = function (options) {
26 /* Constructor
26 /* Constructor
27 *
27 *
28 * The Base `Cell` class from which to inherit.
28 * The Base `Cell` class from which to inherit.
29 * @constructor
29 * @constructor
30 * @param:
30 * @param:
31 * options: dictionary
31 * options: dictionary
32 * Dictionary of keyword arguments.
32 * Dictionary of keyword arguments.
33 * events: $(Events) instance
33 * events: $(Events) instance
34 * config: dictionary
34 * config: dictionary
35 * keyboard_manager: KeyboardManager instance
35 * keyboard_manager: KeyboardManager instance
36 */
36 */
37 options = options || {};
37 options = options || {};
38 this.keyboard_manager = options.keyboard_manager;
38 this.keyboard_manager = options.keyboard_manager;
39 this.events = options.events;
39 this.events = options.events;
40 var config = utils.mergeopt(Cell, options.config);
40 var config = utils.mergeopt(Cell, options.config);
41 // superclass default overwrite our default
41 // superclass default overwrite our default
42
42
43 this.placeholder = config.placeholder || '';
43 this.placeholder = config.placeholder || '';
44 this.read_only = config.cm_config.readOnly;
44 this.read_only = config.cm_config.readOnly;
45 this.selected = false;
45 this.selected = false;
46 this.rendered = false;
46 this.rendered = false;
47 this.mode = 'command';
47 this.mode = 'command';
48 this.metadata = {};
48
49 // Metadata property
50 var that = this;
51 this._metadata = {};
52 Object.defineProperty(this, 'metadata', {
53 get: function() { return that._metadata; },
54 set: function(value) {
55 that._metadata = value;
56 that.celltoolbar.rebuild();
57 }
58 });
59
49 // load this from metadata later ?
60 // load this from metadata later ?
50 this.user_highlight = 'auto';
61 this.user_highlight = 'auto';
51 this.cm_config = config.cm_config;
62 this.cm_config = config.cm_config;
52 this.cell_id = utils.uuid();
63 this.cell_id = utils.uuid();
53 this._options = config;
64 this._options = config;
54
65
55 // For JS VM engines optimization, attributes should be all set (even
66 // For JS VM engines optimization, attributes should be all set (even
56 // to null) in the constructor, and if possible, if different subclass
67 // to null) in the constructor, and if possible, if different subclass
57 // have new attributes with same name, they should be created in the
68 // have new attributes with same name, they should be created in the
58 // same order. Easiest is to create and set to null in parent class.
69 // same order. Easiest is to create and set to null in parent class.
59
70
60 this.element = null;
71 this.element = null;
61 this.cell_type = this.cell_type || null;
72 this.cell_type = this.cell_type || null;
62 this.code_mirror = null;
73 this.code_mirror = null;
63
74
64 this.create_element();
75 this.create_element();
65 if (this.element !== null) {
76 if (this.element !== null) {
66 this.element.data("cell", this);
77 this.element.data("cell", this);
67 this.bind_events();
78 this.bind_events();
68 this.init_classes();
79 this.init_classes();
69 }
80 }
70 };
81 };
71
82
72 Cell.options_default = {
83 Cell.options_default = {
73 cm_config : {
84 cm_config : {
74 indentUnit : 4,
85 indentUnit : 4,
75 readOnly: false,
86 readOnly: false,
76 theme: "default",
87 theme: "default",
77 extraKeys: {
88 extraKeys: {
78 "Cmd-Right":"goLineRight",
89 "Cmd-Right":"goLineRight",
79 "End":"goLineRight",
90 "End":"goLineRight",
80 "Cmd-Left":"goLineLeft"
91 "Cmd-Left":"goLineLeft"
81 }
92 }
82 }
93 }
83 };
94 };
84
95
85 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
96 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
86 // by disabling drag/drop altogether on Safari
97 // by disabling drag/drop altogether on Safari
87 // https://github.com/codemirror/CodeMirror/issues/332
98 // https://github.com/codemirror/CodeMirror/issues/332
88 if (utils.browser[0] == "Safari") {
99 if (utils.browser[0] == "Safari") {
89 Cell.options_default.cm_config.dragDrop = false;
100 Cell.options_default.cm_config.dragDrop = false;
90 }
101 }
91
102
92 /**
103 /**
93 * Empty. Subclasses must implement create_element.
104 * Empty. Subclasses must implement create_element.
94 * This should contain all the code to create the DOM element in notebook
105 * This should contain all the code to create the DOM element in notebook
95 * and will be called by Base Class constructor.
106 * and will be called by Base Class constructor.
96 * @method create_element
107 * @method create_element
97 */
108 */
98 Cell.prototype.create_element = function () {
109 Cell.prototype.create_element = function () {
99 };
110 };
100
111
101 Cell.prototype.init_classes = function () {
112 Cell.prototype.init_classes = function () {
102 // Call after this.element exists to initialize the css classes
113 // Call after this.element exists to initialize the css classes
103 // related to selected, rendered and mode.
114 // related to selected, rendered and mode.
104 if (this.selected) {
115 if (this.selected) {
105 this.element.addClass('selected');
116 this.element.addClass('selected');
106 } else {
117 } else {
107 this.element.addClass('unselected');
118 this.element.addClass('unselected');
108 }
119 }
109 if (this.rendered) {
120 if (this.rendered) {
110 this.element.addClass('rendered');
121 this.element.addClass('rendered');
111 } else {
122 } else {
112 this.element.addClass('unrendered');
123 this.element.addClass('unrendered');
113 }
124 }
114 if (this.mode === 'edit') {
125 if (this.mode === 'edit') {
115 this.element.addClass('edit_mode');
126 this.element.addClass('edit_mode');
116 } else {
127 } else {
117 this.element.addClass('command_mode');
128 this.element.addClass('command_mode');
118 }
129 }
119 };
130 };
120
131
121 /**
132 /**
122 * Subclasses can implement override bind_events.
133 * Subclasses can implement override bind_events.
123 * Be carefull to call the parent method when overwriting as it fires event.
134 * Be carefull to call the parent method when overwriting as it fires event.
124 * this will be triggerd after create_element in constructor.
135 * this will be triggerd after create_element in constructor.
125 * @method bind_events
136 * @method bind_events
126 */
137 */
127 Cell.prototype.bind_events = function () {
138 Cell.prototype.bind_events = function () {
128 var that = this;
139 var that = this;
129 // We trigger events so that Cell doesn't have to depend on Notebook.
140 // We trigger events so that Cell doesn't have to depend on Notebook.
130 that.element.click(function (event) {
141 that.element.click(function (event) {
131 if (!that.selected) {
142 if (!that.selected) {
132 that.events.trigger('select.Cell', {'cell':that});
143 that.events.trigger('select.Cell', {'cell':that});
133 }
144 }
134 });
145 });
135 that.element.focusin(function (event) {
146 that.element.focusin(function (event) {
136 if (!that.selected) {
147 if (!that.selected) {
137 that.events.trigger('select.Cell', {'cell':that});
148 that.events.trigger('select.Cell', {'cell':that});
138 }
149 }
139 });
150 });
140 if (this.code_mirror) {
151 if (this.code_mirror) {
141 this.code_mirror.on("change", function(cm, change) {
152 this.code_mirror.on("change", function(cm, change) {
142 that.events.trigger("set_dirty.Notebook", {value: true});
153 that.events.trigger("set_dirty.Notebook", {value: true});
143 });
154 });
144 }
155 }
145 if (this.code_mirror) {
156 if (this.code_mirror) {
146 this.code_mirror.on('focus', function(cm, change) {
157 this.code_mirror.on('focus', function(cm, change) {
147 that.events.trigger('edit_mode.Cell', {cell: that});
158 that.events.trigger('edit_mode.Cell', {cell: that});
148 });
159 });
149 }
160 }
150 if (this.code_mirror) {
161 if (this.code_mirror) {
151 this.code_mirror.on('blur', function(cm, change) {
162 this.code_mirror.on('blur', function(cm, change) {
152 that.events.trigger('command_mode.Cell', {cell: that});
163 that.events.trigger('command_mode.Cell', {cell: that});
153 });
164 });
154 }
165 }
155
166
156 this.element.dblclick(function () {
167 this.element.dblclick(function () {
157 if (that.selected === false) {
168 if (that.selected === false) {
158 this.events.trigger('select.Cell', {'cell':that});
169 this.events.trigger('select.Cell', {'cell':that});
159 }
170 }
160 var cont = that.unrender();
171 var cont = that.unrender();
161 if (cont) {
172 if (cont) {
162 that.focus_editor();
173 that.focus_editor();
163 }
174 }
164 });
175 });
165 };
176 };
166
177
167 /**
178 /**
168 * This method gets called in CodeMirror's onKeyDown/onKeyPress
179 * This method gets called in CodeMirror's onKeyDown/onKeyPress
169 * handlers and is used to provide custom key handling.
180 * handlers and is used to provide custom key handling.
170 *
181 *
171 * To have custom handling, subclasses should override this method, but still call it
182 * To have custom handling, subclasses should override this method, but still call it
172 * in order to process the Edit mode keyboard shortcuts.
183 * in order to process the Edit mode keyboard shortcuts.
173 *
184 *
174 * @method handle_codemirror_keyevent
185 * @method handle_codemirror_keyevent
175 * @param {CodeMirror} editor - The codemirror instance bound to the cell
186 * @param {CodeMirror} editor - The codemirror instance bound to the cell
176 * @param {event} event - key press event which either should or should not be handled by CodeMirror
187 * @param {event} event - key press event which either should or should not be handled by CodeMirror
177 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
188 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
178 */
189 */
179 Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
190 Cell.prototype.handle_codemirror_keyevent = function (editor, event) {
180 var shortcuts = this.keyboard_manager.edit_shortcuts;
191 var shortcuts = this.keyboard_manager.edit_shortcuts;
181
192
182 var cur = editor.getCursor();
193 var cur = editor.getCursor();
183 if((cur.line !== 0 || cur.ch !==0) && event.keyCode === 38){
194 if((cur.line !== 0 || cur.ch !==0) && event.keyCode === 38){
184 event._ipkmIgnore = true;
195 event._ipkmIgnore = true;
185 }
196 }
186 var nLastLine = editor.lastLine()
197 var nLastLine = editor.lastLine()
187 if( ( event.keyCode === 40)
198 if( ( event.keyCode === 40)
188 && (( cur.line !== nLastLine)
199 && (( cur.line !== nLastLine)
189 || ( cur.ch !== editor.getLineHandle(nLastLine).text.length))
200 || ( cur.ch !== editor.getLineHandle(nLastLine).text.length))
190 ){
201 ){
191 event._ipkmIgnore = true;
202 event._ipkmIgnore = true;
192 }
203 }
193 // if this is an edit_shortcuts shortcut, the global keyboard/shortcut
204 // if this is an edit_shortcuts shortcut, the global keyboard/shortcut
194 // manager will handle it
205 // manager will handle it
195 if (shortcuts.handles(event)) {
206 if (shortcuts.handles(event)) {
196 return true;
207 return true;
197 }
208 }
198
209
199 return false;
210 return false;
200 };
211 };
201
212
202
213
203 /**
214 /**
204 * Triger typsetting of math by mathjax on current cell element
215 * Triger typsetting of math by mathjax on current cell element
205 * @method typeset
216 * @method typeset
206 */
217 */
207 Cell.prototype.typeset = function () {
218 Cell.prototype.typeset = function () {
208 if (window.MathJax) {
219 if (window.MathJax) {
209 var cell_math = this.element.get(0);
220 var cell_math = this.element.get(0);
210 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
221 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
211 }
222 }
212 };
223 };
213
224
214 /**
225 /**
215 * handle cell level logic when a cell is selected
226 * handle cell level logic when a cell is selected
216 * @method select
227 * @method select
217 * @return is the action being taken
228 * @return is the action being taken
218 */
229 */
219 Cell.prototype.select = function () {
230 Cell.prototype.select = function () {
220 if (!this.selected) {
231 if (!this.selected) {
221 this.element.addClass('selected');
232 this.element.addClass('selected');
222 this.element.removeClass('unselected');
233 this.element.removeClass('unselected');
223 this.selected = true;
234 this.selected = true;
224 return true;
235 return true;
225 } else {
236 } else {
226 return false;
237 return false;
227 }
238 }
228 };
239 };
229
240
230 /**
241 /**
231 * handle cell level logic when a cell is unselected
242 * handle cell level logic when a cell is unselected
232 * @method unselect
243 * @method unselect
233 * @return is the action being taken
244 * @return is the action being taken
234 */
245 */
235 Cell.prototype.unselect = function () {
246 Cell.prototype.unselect = function () {
236 if (this.selected) {
247 if (this.selected) {
237 this.element.addClass('unselected');
248 this.element.addClass('unselected');
238 this.element.removeClass('selected');
249 this.element.removeClass('selected');
239 this.selected = false;
250 this.selected = false;
240 return true;
251 return true;
241 } else {
252 } else {
242 return false;
253 return false;
243 }
254 }
244 };
255 };
245
256
246 /**
257 /**
247 * handle cell level logic when a cell is rendered
258 * handle cell level logic when a cell is rendered
248 * @method render
259 * @method render
249 * @return is the action being taken
260 * @return is the action being taken
250 */
261 */
251 Cell.prototype.render = function () {
262 Cell.prototype.render = function () {
252 if (!this.rendered) {
263 if (!this.rendered) {
253 this.element.addClass('rendered');
264 this.element.addClass('rendered');
254 this.element.removeClass('unrendered');
265 this.element.removeClass('unrendered');
255 this.rendered = true;
266 this.rendered = true;
256 return true;
267 return true;
257 } else {
268 } else {
258 return false;
269 return false;
259 }
270 }
260 };
271 };
261
272
262 /**
273 /**
263 * handle cell level logic when a cell is unrendered
274 * handle cell level logic when a cell is unrendered
264 * @method unrender
275 * @method unrender
265 * @return is the action being taken
276 * @return is the action being taken
266 */
277 */
267 Cell.prototype.unrender = function () {
278 Cell.prototype.unrender = function () {
268 if (this.rendered) {
279 if (this.rendered) {
269 this.element.addClass('unrendered');
280 this.element.addClass('unrendered');
270 this.element.removeClass('rendered');
281 this.element.removeClass('rendered');
271 this.rendered = false;
282 this.rendered = false;
272 return true;
283 return true;
273 } else {
284 } else {
274 return false;
285 return false;
275 }
286 }
276 };
287 };
277
288
278 /**
289 /**
279 * Delegates keyboard shortcut handling to either IPython keyboard
290 * Delegates keyboard shortcut handling to either IPython keyboard
280 * manager when in command mode, or CodeMirror when in edit mode
291 * manager when in command mode, or CodeMirror when in edit mode
281 *
292 *
282 * @method handle_keyevent
293 * @method handle_keyevent
283 * @param {CodeMirror} editor - The codemirror instance bound to the cell
294 * @param {CodeMirror} editor - The codemirror instance bound to the cell
284 * @param {event} - key event to be handled
295 * @param {event} - key event to be handled
285 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
296 * @return {Boolean} `true` if CodeMirror should ignore the event, `false` Otherwise
286 */
297 */
287 Cell.prototype.handle_keyevent = function (editor, event) {
298 Cell.prototype.handle_keyevent = function (editor, event) {
288 if (this.mode === 'command') {
299 if (this.mode === 'command') {
289 return true;
300 return true;
290 } else if (this.mode === 'edit') {
301 } else if (this.mode === 'edit') {
291 return this.handle_codemirror_keyevent(editor, event);
302 return this.handle_codemirror_keyevent(editor, event);
292 }
303 }
293 };
304 };
294
305
295 /**
306 /**
296 * @method at_top
307 * @method at_top
297 * @return {Boolean}
308 * @return {Boolean}
298 */
309 */
299 Cell.prototype.at_top = function () {
310 Cell.prototype.at_top = function () {
300 var cm = this.code_mirror;
311 var cm = this.code_mirror;
301 var cursor = cm.getCursor();
312 var cursor = cm.getCursor();
302 if (cursor.line === 0 && cursor.ch === 0) {
313 if (cursor.line === 0 && cursor.ch === 0) {
303 return true;
314 return true;
304 }
315 }
305 return false;
316 return false;
306 };
317 };
307
318
308 /**
319 /**
309 * @method at_bottom
320 * @method at_bottom
310 * @return {Boolean}
321 * @return {Boolean}
311 * */
322 * */
312 Cell.prototype.at_bottom = function () {
323 Cell.prototype.at_bottom = function () {
313 var cm = this.code_mirror;
324 var cm = this.code_mirror;
314 var cursor = cm.getCursor();
325 var cursor = cm.getCursor();
315 if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
326 if (cursor.line === (cm.lineCount()-1) && cursor.ch === cm.getLine(cursor.line).length) {
316 return true;
327 return true;
317 }
328 }
318 return false;
329 return false;
319 };
330 };
320
331
321 /**
332 /**
322 * enter the command mode for the cell
333 * enter the command mode for the cell
323 * @method command_mode
334 * @method command_mode
324 * @return is the action being taken
335 * @return is the action being taken
325 */
336 */
326 Cell.prototype.command_mode = function () {
337 Cell.prototype.command_mode = function () {
327 if (this.mode !== 'command') {
338 if (this.mode !== 'command') {
328 this.element.addClass('command_mode');
339 this.element.addClass('command_mode');
329 this.element.removeClass('edit_mode');
340 this.element.removeClass('edit_mode');
330 this.mode = 'command';
341 this.mode = 'command';
331 return true;
342 return true;
332 } else {
343 } else {
333 return false;
344 return false;
334 }
345 }
335 };
346 };
336
347
337 /**
348 /**
338 * enter the edit mode for the cell
349 * enter the edit mode for the cell
339 * @method command_mode
350 * @method command_mode
340 * @return is the action being taken
351 * @return is the action being taken
341 */
352 */
342 Cell.prototype.edit_mode = function () {
353 Cell.prototype.edit_mode = function () {
343 if (this.mode !== 'edit') {
354 if (this.mode !== 'edit') {
344 this.element.addClass('edit_mode');
355 this.element.addClass('edit_mode');
345 this.element.removeClass('command_mode');
356 this.element.removeClass('command_mode');
346 this.mode = 'edit';
357 this.mode = 'edit';
347 return true;
358 return true;
348 } else {
359 } else {
349 return false;
360 return false;
350 }
361 }
351 };
362 };
352
363
353 /**
364 /**
354 * Focus the cell in the DOM sense
365 * Focus the cell in the DOM sense
355 * @method focus_cell
366 * @method focus_cell
356 */
367 */
357 Cell.prototype.focus_cell = function () {
368 Cell.prototype.focus_cell = function () {
358 this.element.focus();
369 this.element.focus();
359 };
370 };
360
371
361 /**
372 /**
362 * Focus the editor area so a user can type
373 * Focus the editor area so a user can type
363 *
374 *
364 * NOTE: If codemirror is focused via a mouse click event, you don't want to
375 * NOTE: If codemirror is focused via a mouse click event, you don't want to
365 * call this because it will cause a page jump.
376 * call this because it will cause a page jump.
366 * @method focus_editor
377 * @method focus_editor
367 */
378 */
368 Cell.prototype.focus_editor = function () {
379 Cell.prototype.focus_editor = function () {
369 this.refresh();
380 this.refresh();
370 this.code_mirror.focus();
381 this.code_mirror.focus();
371 };
382 };
372
383
373 /**
384 /**
374 * Refresh codemirror instance
385 * Refresh codemirror instance
375 * @method refresh
386 * @method refresh
376 */
387 */
377 Cell.prototype.refresh = function () {
388 Cell.prototype.refresh = function () {
378 this.code_mirror.refresh();
389 this.code_mirror.refresh();
379 };
390 };
380
391
381 /**
392 /**
382 * should be overritten by subclass
393 * should be overritten by subclass
383 * @method get_text
394 * @method get_text
384 */
395 */
385 Cell.prototype.get_text = function () {
396 Cell.prototype.get_text = function () {
386 };
397 };
387
398
388 /**
399 /**
389 * should be overritten by subclass
400 * should be overritten by subclass
390 * @method set_text
401 * @method set_text
391 * @param {string} text
402 * @param {string} text
392 */
403 */
393 Cell.prototype.set_text = function (text) {
404 Cell.prototype.set_text = function (text) {
394 };
405 };
395
406
396 /**
407 /**
397 * should be overritten by subclass
408 * should be overritten by subclass
398 * serialise cell to json.
409 * serialise cell to json.
399 * @method toJSON
410 * @method toJSON
400 **/
411 **/
401 Cell.prototype.toJSON = function () {
412 Cell.prototype.toJSON = function () {
402 var data = {};
413 var data = {};
403 // deepcopy the metadata so copied cells don't share the same object
414 // deepcopy the metadata so copied cells don't share the same object
404 data.metadata = JSON.parse(JSON.stringify(this.metadata));
415 data.metadata = JSON.parse(JSON.stringify(this.metadata));
405 data.cell_type = this.cell_type;
416 data.cell_type = this.cell_type;
406 return data;
417 return data;
407 };
418 };
408
419
409
410 /**
420 /**
411 * should be overritten by subclass
421 * should be overritten by subclass
412 * @method fromJSON
422 * @method fromJSON
413 **/
423 **/
414 Cell.prototype.fromJSON = function (data) {
424 Cell.prototype.fromJSON = function (data) {
415 if (data.metadata !== undefined) {
425 if (data.metadata !== undefined) {
416 this.metadata = data.metadata;
426 this.metadata = data.metadata;
417 }
427 }
418 this.celltoolbar.rebuild();
419 };
428 };
420
429
421
430
422 /**
431 /**
423 * can the cell be split into two cells (false if not deletable)
432 * can the cell be split into two cells (false if not deletable)
424 * @method is_splittable
433 * @method is_splittable
425 **/
434 **/
426 Cell.prototype.is_splittable = function () {
435 Cell.prototype.is_splittable = function () {
427 return this.is_deletable();
436 return this.is_deletable();
428 };
437 };
429
438
430
439
431 /**
440 /**
432 * can the cell be merged with other cells (false if not deletable)
441 * can the cell be merged with other cells (false if not deletable)
433 * @method is_mergeable
442 * @method is_mergeable
434 **/
443 **/
435 Cell.prototype.is_mergeable = function () {
444 Cell.prototype.is_mergeable = function () {
436 return this.is_deletable();
445 return this.is_deletable();
437 };
446 };
438
447
439 /**
448 /**
440 * is the cell deletable? only false (undeletable) if
449 * is the cell deletable? only false (undeletable) if
441 * metadata.deletable is explicitly false -- everything else
450 * metadata.deletable is explicitly false -- everything else
442 * counts as true
451 * counts as true
443 *
452 *
444 * @method is_deletable
453 * @method is_deletable
445 **/
454 **/
446 Cell.prototype.is_deletable = function () {
455 Cell.prototype.is_deletable = function () {
447 if (this.metadata.deletable === false) {
456 if (this.metadata.deletable === false) {
448 return false;
457 return false;
449 }
458 }
450 return true;
459 return true;
451 };
460 };
452
461
453 /**
462 /**
454 * @return {String} - the text before the cursor
463 * @return {String} - the text before the cursor
455 * @method get_pre_cursor
464 * @method get_pre_cursor
456 **/
465 **/
457 Cell.prototype.get_pre_cursor = function () {
466 Cell.prototype.get_pre_cursor = function () {
458 var cursor = this.code_mirror.getCursor();
467 var cursor = this.code_mirror.getCursor();
459 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
468 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
460 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
469 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
461 return text;
470 return text;
462 };
471 };
463
472
464
473
465 /**
474 /**
466 * @return {String} - the text after the cursor
475 * @return {String} - the text after the cursor
467 * @method get_post_cursor
476 * @method get_post_cursor
468 **/
477 **/
469 Cell.prototype.get_post_cursor = function () {
478 Cell.prototype.get_post_cursor = function () {
470 var cursor = this.code_mirror.getCursor();
479 var cursor = this.code_mirror.getCursor();
471 var last_line_num = this.code_mirror.lineCount()-1;
480 var last_line_num = this.code_mirror.lineCount()-1;
472 var last_line_len = this.code_mirror.getLine(last_line_num).length;
481 var last_line_len = this.code_mirror.getLine(last_line_num).length;
473 var end = {line:last_line_num, ch:last_line_len};
482 var end = {line:last_line_num, ch:last_line_len};
474 var text = this.code_mirror.getRange(cursor, end);
483 var text = this.code_mirror.getRange(cursor, end);
475 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
484 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
476 return text;
485 return text;
477 };
486 };
478
487
479 /**
488 /**
480 * Show/Hide CodeMirror LineNumber
489 * Show/Hide CodeMirror LineNumber
481 * @method show_line_numbers
490 * @method show_line_numbers
482 *
491 *
483 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
492 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
484 **/
493 **/
485 Cell.prototype.show_line_numbers = function (value) {
494 Cell.prototype.show_line_numbers = function (value) {
486 this.code_mirror.setOption('lineNumbers', value);
495 this.code_mirror.setOption('lineNumbers', value);
487 this.code_mirror.refresh();
496 this.code_mirror.refresh();
488 };
497 };
489
498
490 /**
499 /**
491 * Toggle CodeMirror LineNumber
500 * Toggle CodeMirror LineNumber
492 * @method toggle_line_numbers
501 * @method toggle_line_numbers
493 **/
502 **/
494 Cell.prototype.toggle_line_numbers = function () {
503 Cell.prototype.toggle_line_numbers = function () {
495 var val = this.code_mirror.getOption('lineNumbers');
504 var val = this.code_mirror.getOption('lineNumbers');
496 this.show_line_numbers(!val);
505 this.show_line_numbers(!val);
497 };
506 };
498
507
499 /**
508 /**
500 * Force codemirror highlight mode
509 * Force codemirror highlight mode
501 * @method force_highlight
510 * @method force_highlight
502 * @param {object} - CodeMirror mode
511 * @param {object} - CodeMirror mode
503 **/
512 **/
504 Cell.prototype.force_highlight = function(mode) {
513 Cell.prototype.force_highlight = function(mode) {
505 this.user_highlight = mode;
514 this.user_highlight = mode;
506 this.auto_highlight();
515 this.auto_highlight();
507 };
516 };
508
517
509 /**
518 /**
510 * Try to autodetect cell highlight mode, or use selected mode
519 * Try to autodetect cell highlight mode, or use selected mode
511 * @methods _auto_highlight
520 * @methods _auto_highlight
512 * @private
521 * @private
513 * @param {String|object|undefined} - CodeMirror mode | 'auto'
522 * @param {String|object|undefined} - CodeMirror mode | 'auto'
514 **/
523 **/
515 Cell.prototype._auto_highlight = function (modes) {
524 Cell.prototype._auto_highlight = function (modes) {
516 //Here we handle manually selected modes
525 //Here we handle manually selected modes
517 var that = this;
526 var that = this;
518 var mode;
527 var mode;
519 if( this.user_highlight !== undefined && this.user_highlight != 'auto' )
528 if( this.user_highlight !== undefined && this.user_highlight != 'auto' )
520 {
529 {
521 mode = this.user_highlight;
530 mode = this.user_highlight;
522 CodeMirror.autoLoadMode(this.code_mirror, mode);
531 CodeMirror.autoLoadMode(this.code_mirror, mode);
523 this.code_mirror.setOption('mode', mode);
532 this.code_mirror.setOption('mode', mode);
524 return;
533 return;
525 }
534 }
526 var current_mode = this.code_mirror.getOption('mode', mode);
535 var current_mode = this.code_mirror.getOption('mode', mode);
527 var first_line = this.code_mirror.getLine(0);
536 var first_line = this.code_mirror.getLine(0);
528 // loop on every pairs
537 // loop on every pairs
529 for(mode in modes) {
538 for(mode in modes) {
530 var regs = modes[mode].reg;
539 var regs = modes[mode].reg;
531 // only one key every time but regexp can't be keys...
540 // only one key every time but regexp can't be keys...
532 for(var i=0; i<regs.length; i++) {
541 for(var i=0; i<regs.length; i++) {
533 // here we handle non magic_modes
542 // here we handle non magic_modes
534 if(first_line.match(regs[i]) !== null) {
543 if(first_line.match(regs[i]) !== null) {
535 if(current_mode == mode){
544 if(current_mode == mode){
536 return;
545 return;
537 }
546 }
538 if (mode.search('magic_') !== 0) {
547 if (mode.search('magic_') !== 0) {
539 utils.requireCodeMirrorMode(mode, function () {
548 utils.requireCodeMirrorMode(mode, function () {
540 that.code_mirror.setOption('mode', mode);
549 that.code_mirror.setOption('mode', mode);
541 });
550 });
542 return;
551 return;
543 }
552 }
544 var open = modes[mode].open || "%%";
553 var open = modes[mode].open || "%%";
545 var close = modes[mode].close || "%%end";
554 var close = modes[mode].close || "%%end";
546 var magic_mode = mode;
555 var magic_mode = mode;
547 mode = magic_mode.substr(6);
556 mode = magic_mode.substr(6);
548 if(current_mode == magic_mode){
557 if(current_mode == magic_mode){
549 return;
558 return;
550 }
559 }
551 utils.requireCodeMirrorMode(mode, function () {
560 utils.requireCodeMirrorMode(mode, function () {
552 // create on the fly a mode that switch between
561 // create on the fly a mode that switch between
553 // plain/text and something else, otherwise `%%` is
562 // plain/text and something else, otherwise `%%` is
554 // source of some highlight issues.
563 // source of some highlight issues.
555 CodeMirror.defineMode(magic_mode, function(config) {
564 CodeMirror.defineMode(magic_mode, function(config) {
556 return CodeMirror.multiplexingMode(
565 return CodeMirror.multiplexingMode(
557 CodeMirror.getMode(config, 'text/plain'),
566 CodeMirror.getMode(config, 'text/plain'),
558 // always set something on close
567 // always set something on close
559 {open: open, close: close,
568 {open: open, close: close,
560 mode: CodeMirror.getMode(config, mode),
569 mode: CodeMirror.getMode(config, mode),
561 delimStyle: "delimit"
570 delimStyle: "delimit"
562 }
571 }
563 );
572 );
564 });
573 });
565 that.code_mirror.setOption('mode', magic_mode);
574 that.code_mirror.setOption('mode', magic_mode);
566 });
575 });
567 return;
576 return;
568 }
577 }
569 }
578 }
570 }
579 }
571 // fallback on default
580 // fallback on default
572 var default_mode;
581 var default_mode;
573 try {
582 try {
574 default_mode = this._options.cm_config.mode;
583 default_mode = this._options.cm_config.mode;
575 } catch(e) {
584 } catch(e) {
576 default_mode = 'text/plain';
585 default_mode = 'text/plain';
577 }
586 }
578 if( current_mode === default_mode){
587 if( current_mode === default_mode){
579 return;
588 return;
580 }
589 }
581 this.code_mirror.setOption('mode', default_mode);
590 this.code_mirror.setOption('mode', default_mode);
582 };
591 };
583
592
584 // Backwards compatibility.
593 // Backwards compatibility.
585 IPython.Cell = Cell;
594 IPython.Cell = Cell;
586
595
587 return {'Cell': Cell};
596 return {'Cell': Cell};
588 });
597 });
General Comments 0
You need to be logged in to leave comments. Login now