##// END OF EJS Templates
Replacing a for-in loop by an index loop on an array, to avoid enumerating inherited properties
Sylvain Corlay -
Show More
@@ -1,372 +1,372
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
22
23 /**
23 /**
24 * The Base `Cell` class from which to inherit
24 * The Base `Cell` class from which to inherit
25 * @class Cell
25 * @class Cell
26 **/
26 **/
27
27
28 /*
28 /*
29 * @constructor
29 * @constructor
30 *
30 *
31 * @param {object|undefined} [options]
31 * @param {object|undefined} [options]
32 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
32 * @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
33 */
33 */
34 var Cell = function (options) {
34 var Cell = function (options) {
35
35
36 options = this.mergeopt(Cell, options);
36 options = this.mergeopt(Cell, options);
37 // superclass default overwrite our default
37 // superclass default overwrite our default
38
38
39 this.placeholder = options.placeholder || '';
39 this.placeholder = options.placeholder || '';
40 this.read_only = options.cm_config.readOnly;
40 this.read_only = options.cm_config.readOnly;
41 this.selected = false;
41 this.selected = false;
42 this.metadata = {};
42 this.metadata = {};
43 // load this from metadata later ?
43 // load this from metadata later ?
44 this.user_highlight = 'auto';
44 this.user_highlight = 'auto';
45 this.cm_config = options.cm_config;
45 this.cm_config = options.cm_config;
46 this.cell_id = utils.uuid();
46 this.cell_id = utils.uuid();
47 this._options = options;
47 this._options = options;
48
48
49 // For JS VM engines optimisation, attributes should be all set (even
49 // For JS VM engines optimisation, attributes should be all set (even
50 // to null) in the constructor, and if possible, if different subclass
50 // to null) in the constructor, and if possible, if different subclass
51 // have new attributes with same name, they should be created in the
51 // have new attributes with same name, they should be created in the
52 // same order. Easiest is to create and set to null in parent class.
52 // same order. Easiest is to create and set to null in parent class.
53
53
54 this.element = null;
54 this.element = null;
55 this.cell_type = this.cell_type || null;
55 this.cell_type = this.cell_type || null;
56 this.code_mirror = null;
56 this.code_mirror = null;
57
57
58
58
59 this.create_element();
59 this.create_element();
60 if (this.element !== null) {
60 if (this.element !== null) {
61 this.element.data("cell", this);
61 this.element.data("cell", this);
62 this.bind_events();
62 this.bind_events();
63 }
63 }
64 };
64 };
65
65
66 Cell.options_default = {
66 Cell.options_default = {
67 cm_config : {
67 cm_config : {
68 indentUnit : 4,
68 indentUnit : 4,
69 readOnly: false,
69 readOnly: false,
70 theme: "default"
70 theme: "default"
71 }
71 }
72 };
72 };
73
73
74 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
74 // FIXME: Workaround CM Bug #332 (Safari segfault on drag)
75 // by disabling drag/drop altogether on Safari
75 // by disabling drag/drop altogether on Safari
76 // https://github.com/marijnh/CodeMirror/issues/332
76 // https://github.com/marijnh/CodeMirror/issues/332
77
77
78 if (utils.browser[0] == "Safari") {
78 if (utils.browser[0] == "Safari") {
79 Cell.options_default.cm_config.dragDrop = false;
79 Cell.options_default.cm_config.dragDrop = false;
80 }
80 }
81
81
82 Cell.prototype.mergeopt = function(_class, options, overwrite){
82 Cell.prototype.mergeopt = function(_class, options, overwrite){
83 options = options || {};
83 options = options || {};
84 overwrite = overwrite || {};
84 overwrite = overwrite || {};
85 return $.extend(true, {}, _class.options_default, options, overwrite)
85 return $.extend(true, {}, _class.options_default, options, overwrite)
86
86
87 }
87 }
88
88
89
89
90
90
91 /**
91 /**
92 * Empty. Subclasses must implement create_element.
92 * Empty. Subclasses must implement create_element.
93 * This should contain all the code to create the DOM element in notebook
93 * This should contain all the code to create the DOM element in notebook
94 * and will be called by Base Class constructor.
94 * and will be called by Base Class constructor.
95 * @method create_element
95 * @method create_element
96 */
96 */
97 Cell.prototype.create_element = function () {
97 Cell.prototype.create_element = function () {
98 };
98 };
99
99
100
100
101 /**
101 /**
102 * Subclasses can implement override bind_events.
102 * Subclasses can implement override bind_events.
103 * Be carefull to call the parent method when overwriting as it fires event.
103 * Be carefull to call the parent method when overwriting as it fires event.
104 * this will be triggerd after create_element in constructor.
104 * this will be triggerd after create_element in constructor.
105 * @method bind_events
105 * @method bind_events
106 */
106 */
107 Cell.prototype.bind_events = function () {
107 Cell.prototype.bind_events = function () {
108 var that = this;
108 var that = this;
109 // We trigger events so that Cell doesn't have to depend on Notebook.
109 // We trigger events so that Cell doesn't have to depend on Notebook.
110 that.element.click(function (event) {
110 that.element.click(function (event) {
111 if (that.selected === false) {
111 if (that.selected === false) {
112 $([IPython.events]).trigger('select.Cell', {'cell':that});
112 $([IPython.events]).trigger('select.Cell', {'cell':that});
113 }
113 }
114 });
114 });
115 that.element.focusin(function (event) {
115 that.element.focusin(function (event) {
116 if (that.selected === false) {
116 if (that.selected === false) {
117 $([IPython.events]).trigger('select.Cell', {'cell':that});
117 $([IPython.events]).trigger('select.Cell', {'cell':that});
118 }
118 }
119 });
119 });
120 if (this.code_mirror) {
120 if (this.code_mirror) {
121 this.code_mirror.on("change", function(cm, change) {
121 this.code_mirror.on("change", function(cm, change) {
122 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
122 $([IPython.events]).trigger("set_dirty.Notebook", {value: true});
123 });
123 });
124 }
124 }
125 };
125 };
126
126
127 /**
127 /**
128 * Triger typsetting of math by mathjax on current cell element
128 * Triger typsetting of math by mathjax on current cell element
129 * @method typeset
129 * @method typeset
130 */
130 */
131 Cell.prototype.typeset = function () {
131 Cell.prototype.typeset = function () {
132 if (window.MathJax){
132 if (window.MathJax){
133 var cell_math = this.element.get(0);
133 var cell_math = this.element.get(0);
134 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
134 MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
135 }
135 }
136 };
136 };
137
137
138 /**
138 /**
139 * should be triggerd when cell is selected
139 * should be triggerd when cell is selected
140 * @method select
140 * @method select
141 */
141 */
142 Cell.prototype.select = function () {
142 Cell.prototype.select = function () {
143 this.element.addClass('selected');
143 this.element.addClass('selected');
144 this.selected = true;
144 this.selected = true;
145 };
145 };
146
146
147
147
148 /**
148 /**
149 * should be triggerd when cell is unselected
149 * should be triggerd when cell is unselected
150 * @method unselect
150 * @method unselect
151 */
151 */
152 Cell.prototype.unselect = function () {
152 Cell.prototype.unselect = function () {
153 this.element.removeClass('selected');
153 this.element.removeClass('selected');
154 this.selected = false;
154 this.selected = false;
155 };
155 };
156
156
157 /**
157 /**
158 * should be overritten by subclass
158 * should be overritten by subclass
159 * @method get_text
159 * @method get_text
160 */
160 */
161 Cell.prototype.get_text = function () {
161 Cell.prototype.get_text = function () {
162 };
162 };
163
163
164 /**
164 /**
165 * should be overritten by subclass
165 * should be overritten by subclass
166 * @method set_text
166 * @method set_text
167 * @param {string} text
167 * @param {string} text
168 */
168 */
169 Cell.prototype.set_text = function (text) {
169 Cell.prototype.set_text = function (text) {
170 };
170 };
171
171
172 /**
172 /**
173 * Refresh codemirror instance
173 * Refresh codemirror instance
174 * @method refresh
174 * @method refresh
175 */
175 */
176 Cell.prototype.refresh = function () {
176 Cell.prototype.refresh = function () {
177 this.code_mirror.refresh();
177 this.code_mirror.refresh();
178 };
178 };
179
179
180
180
181 /**
181 /**
182 * should be overritten by subclass
182 * should be overritten by subclass
183 * @method edit
183 * @method edit
184 **/
184 **/
185 Cell.prototype.edit = function () {
185 Cell.prototype.edit = function () {
186 };
186 };
187
187
188
188
189 /**
189 /**
190 * should be overritten by subclass
190 * should be overritten by subclass
191 * @method render
191 * @method render
192 **/
192 **/
193 Cell.prototype.render = function () {
193 Cell.prototype.render = function () {
194 };
194 };
195
195
196 /**
196 /**
197 * should be overritten by subclass
197 * should be overritten by subclass
198 * serialise cell to json.
198 * serialise cell to json.
199 * @method toJSON
199 * @method toJSON
200 **/
200 **/
201 Cell.prototype.toJSON = function () {
201 Cell.prototype.toJSON = function () {
202 var data = {};
202 var data = {};
203 data.metadata = this.metadata;
203 data.metadata = this.metadata;
204 data.cell_type = this.cell_type;
204 data.cell_type = this.cell_type;
205 return data;
205 return data;
206 };
206 };
207
207
208
208
209 /**
209 /**
210 * should be overritten by subclass
210 * should be overritten by subclass
211 * @method fromJSON
211 * @method fromJSON
212 **/
212 **/
213 Cell.prototype.fromJSON = function (data) {
213 Cell.prototype.fromJSON = function (data) {
214 if (data.metadata !== undefined) {
214 if (data.metadata !== undefined) {
215 this.metadata = data.metadata;
215 this.metadata = data.metadata;
216 }
216 }
217 this.celltoolbar.rebuild();
217 this.celltoolbar.rebuild();
218 };
218 };
219
219
220
220
221 /**
221 /**
222 * can the cell be split into two cells
222 * can the cell be split into two cells
223 * @method is_splittable
223 * @method is_splittable
224 **/
224 **/
225 Cell.prototype.is_splittable = function () {
225 Cell.prototype.is_splittable = function () {
226 return true;
226 return true;
227 };
227 };
228
228
229
229
230 /**
230 /**
231 * can the cell be merged with other cells
231 * can the cell be merged with other cells
232 * @method is_mergeable
232 * @method is_mergeable
233 **/
233 **/
234 Cell.prototype.is_mergeable = function () {
234 Cell.prototype.is_mergeable = function () {
235 return true;
235 return true;
236 };
236 };
237
237
238
238
239 /**
239 /**
240 * @return {String} - the text before the cursor
240 * @return {String} - the text before the cursor
241 * @method get_pre_cursor
241 * @method get_pre_cursor
242 **/
242 **/
243 Cell.prototype.get_pre_cursor = function () {
243 Cell.prototype.get_pre_cursor = function () {
244 var cursor = this.code_mirror.getCursor();
244 var cursor = this.code_mirror.getCursor();
245 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
245 var text = this.code_mirror.getRange({line:0, ch:0}, cursor);
246 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
246 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
247 return text;
247 return text;
248 }
248 }
249
249
250
250
251 /**
251 /**
252 * @return {String} - the text after the cursor
252 * @return {String} - the text after the cursor
253 * @method get_post_cursor
253 * @method get_post_cursor
254 **/
254 **/
255 Cell.prototype.get_post_cursor = function () {
255 Cell.prototype.get_post_cursor = function () {
256 var cursor = this.code_mirror.getCursor();
256 var cursor = this.code_mirror.getCursor();
257 var last_line_num = this.code_mirror.lineCount()-1;
257 var last_line_num = this.code_mirror.lineCount()-1;
258 var last_line_len = this.code_mirror.getLine(last_line_num).length;
258 var last_line_len = this.code_mirror.getLine(last_line_num).length;
259 var end = {line:last_line_num, ch:last_line_len}
259 var end = {line:last_line_num, ch:last_line_len}
260 var text = this.code_mirror.getRange(cursor, end);
260 var text = this.code_mirror.getRange(cursor, end);
261 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
261 text = text.replace(/^\n+/, '').replace(/\n+$/, '');
262 return text;
262 return text;
263 };
263 };
264
264
265 /**
265 /**
266 * Show/Hide CodeMirror LineNumber
266 * Show/Hide CodeMirror LineNumber
267 * @method show_line_numbers
267 * @method show_line_numbers
268 *
268 *
269 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
269 * @param value {Bool} show (true), or hide (false) the line number in CodeMirror
270 **/
270 **/
271 Cell.prototype.show_line_numbers = function (value) {
271 Cell.prototype.show_line_numbers = function (value) {
272 this.code_mirror.setOption('lineNumbers', value);
272 this.code_mirror.setOption('lineNumbers', value);
273 this.code_mirror.refresh();
273 this.code_mirror.refresh();
274 };
274 };
275
275
276 /**
276 /**
277 * Toggle CodeMirror LineNumber
277 * Toggle CodeMirror LineNumber
278 * @method toggle_line_numbers
278 * @method toggle_line_numbers
279 **/
279 **/
280 Cell.prototype.toggle_line_numbers = function () {
280 Cell.prototype.toggle_line_numbers = function () {
281 var val = this.code_mirror.getOption('lineNumbers');
281 var val = this.code_mirror.getOption('lineNumbers');
282 this.show_line_numbers(!val);
282 this.show_line_numbers(!val);
283 };
283 };
284
284
285 /**
285 /**
286 * Force codemirror highlight mode
286 * Force codemirror highlight mode
287 * @method force_highlight
287 * @method force_highlight
288 * @param {object} - CodeMirror mode
288 * @param {object} - CodeMirror mode
289 **/
289 **/
290 Cell.prototype.force_highlight = function(mode) {
290 Cell.prototype.force_highlight = function(mode) {
291 this.user_highlight = mode;
291 this.user_highlight = mode;
292 this.auto_highlight();
292 this.auto_highlight();
293 };
293 };
294
294
295 /**
295 /**
296 * Try to autodetect cell highlight mode, or use selected mode
296 * Try to autodetect cell highlight mode, or use selected mode
297 * @methods _auto_highlight
297 * @methods _auto_highlight
298 * @private
298 * @private
299 * @param {String|object|undefined} - CodeMirror mode | 'auto'
299 * @param {String|object|undefined} - CodeMirror mode | 'auto'
300 **/
300 **/
301 Cell.prototype._auto_highlight = function (modes) {
301 Cell.prototype._auto_highlight = function (modes) {
302 //Here we handle manually selected modes
302 //Here we handle manually selected modes
303 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
303 if( this.user_highlight != undefined && this.user_highlight != 'auto' )
304 {
304 {
305 var mode = this.user_highlight;
305 var mode = this.user_highlight;
306 CodeMirror.autoLoadMode(this.code_mirror, mode);
306 CodeMirror.autoLoadMode(this.code_mirror, mode);
307 this.code_mirror.setOption('mode', mode);
307 this.code_mirror.setOption('mode', mode);
308 return;
308 return;
309 }
309 }
310 var current_mode = this.code_mirror.getOption('mode', mode);
310 var current_mode = this.code_mirror.getOption('mode', mode);
311 var first_line = this.code_mirror.getLine(0);
311 var first_line = this.code_mirror.getLine(0);
312 // loop on every pairs
312 // loop on every pairs
313 for( var mode in modes) {
313 for( var mode in modes) {
314 var regs = modes[mode]['reg'];
314 var regs = modes[mode]['reg'];
315 // only one key every time but regexp can't be keys...
315 // only one key every time but regexp can't be keys...
316 for(var reg in regs ) {
316 for(var i=0; i<regs.length; i++) {
317 // here we handle non magic_modes
317 // here we handle non magic_modes
318 if(first_line.match(regs[reg]) != null) {
318 if(first_line.match(regs[i]) != null) {
319 if(current_mode == mode){
319 if(current_mode == mode){
320 return;
320 return;
321 }
321 }
322 if (mode.search('magic_') != 0) {
322 if (mode.search('magic_') != 0) {
323 this.code_mirror.setOption('mode', mode);
323 this.code_mirror.setOption('mode', mode);
324 CodeMirror.autoLoadMode(this.code_mirror, mode);
324 CodeMirror.autoLoadMode(this.code_mirror, mode);
325 return;
325 return;
326 }
326 }
327 var open = modes[mode]['open']|| "%%";
327 var open = modes[mode]['open']|| "%%";
328 var close = modes[mode]['close']|| "%%end";
328 var close = modes[mode]['close']|| "%%end";
329 var mmode = mode;
329 var mmode = mode;
330 mode = mmode.substr(6);
330 mode = mmode.substr(6);
331 if(current_mode == mode){
331 if(current_mode == mode){
332 return;
332 return;
333 }
333 }
334 CodeMirror.autoLoadMode(this.code_mirror, mode);
334 CodeMirror.autoLoadMode(this.code_mirror, mode);
335 // create on the fly a mode that swhitch between
335 // create on the fly a mode that swhitch between
336 // plain/text and smth else otherwise `%%` is
336 // plain/text and smth else otherwise `%%` is
337 // source of some highlight issues.
337 // source of some highlight issues.
338 // we use patchedGetMode to circumvent a bug in CM
338 // we use patchedGetMode to circumvent a bug in CM
339 CodeMirror.defineMode(mmode , function(config) {
339 CodeMirror.defineMode(mmode , function(config) {
340 return CodeMirror.multiplexingMode(
340 return CodeMirror.multiplexingMode(
341 CodeMirror.patchedGetMode(config, 'text/plain'),
341 CodeMirror.patchedGetMode(config, 'text/plain'),
342 // always set someting on close
342 // always set someting on close
343 {open: open, close: close,
343 {open: open, close: close,
344 mode: CodeMirror.patchedGetMode(config, mode),
344 mode: CodeMirror.patchedGetMode(config, mode),
345 delimStyle: "delimit"
345 delimStyle: "delimit"
346 }
346 }
347 );
347 );
348 });
348 });
349 this.code_mirror.setOption('mode', mmode);
349 this.code_mirror.setOption('mode', mmode);
350 return;
350 return;
351 }
351 }
352 }
352 }
353 }
353 }
354 // fallback on default
354 // fallback on default
355 var default_mode
355 var default_mode
356 try {
356 try {
357 default_mode = this._options.cm_config.mode;
357 default_mode = this._options.cm_config.mode;
358 } catch(e) {
358 } catch(e) {
359 default_mode = 'text/plain';
359 default_mode = 'text/plain';
360 }
360 }
361 if( current_mode === default_mode){
361 if( current_mode === default_mode){
362 return
362 return
363 }
363 }
364 this.code_mirror.setOption('mode', default_mode);
364 this.code_mirror.setOption('mode', default_mode);
365 };
365 };
366
366
367 IPython.Cell = Cell;
367 IPython.Cell = Cell;
368
368
369 return IPython;
369 return IPython;
370
370
371 }(IPython));
371 }(IPython));
372
372
General Comments 0
You need to be logged in to leave comments. Login now