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