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