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