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