cell.js
350 lines
| 10.2 KiB
| application/javascript
|
JavascriptLexer
Brian E. Granger
|
r4609 | //---------------------------------------------------------------------------- | ||
// Copyright (C) 2008-2011 The IPython Development Team | ||||
// | ||||
// Distributed under the terms of the BSD License. The full license is in | ||||
// the file COPYING, distributed as part of this software. | ||||
//---------------------------------------------------------------------------- | ||||
Brian E. Granger
|
r4349 | |||
//============================================================================ | ||||
// Cell | ||||
//============================================================================ | ||||
Matthias BUSSONNIER
|
r8713 | /** | ||
* An extendable module that provide base functionnality to create cell for notebook. | ||||
Matthias BUSSONNIER
|
r8739 | * @module IPython | ||
* @namespace IPython | ||||
* @submodule Cell | ||||
Matthias BUSSONNIER
|
r8713 | */ | ||
Brian E. Granger
|
r4349 | |||
Brian E. Granger
|
r4352 | var IPython = (function (IPython) { | ||
Matthias BUSSONNIER
|
r12103 | "use strict"; | ||
Brian E. Granger
|
r4349 | |||
Brian E. Granger
|
r4352 | var utils = IPython.utils; | ||
Brian E. Granger
|
r4349 | |||
Matthias BUSSONNIER
|
r8713 | /** | ||
Matthias BUSSONNIER
|
r8714 | * The Base `Cell` class from which to inherit | ||
Matthias BUSSONNIER
|
r8713 | * @class Cell | ||
Matthias BUSSONNIER
|
r9548 | **/ | ||
Brian Granger
|
r5959 | |||
Matthias BUSSONNIER
|
r8713 | /* | ||
* @constructor | ||||
Matthias BUSSONNIER
|
r9537 | * | ||
Matthias BUSSONNIER
|
r9548 | * @param {object|undefined} [options] | ||
* @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters | ||||
Matthias BUSSONNIER
|
r8713 | */ | ||
Matthias BUSSONNIER
|
r9537 | var Cell = function (options) { | ||
Matthias BUSSONNIER
|
r10165 | options = this.mergeopt(Cell, options) | ||
Matthias BUSSONNIER
|
r9537 | // superclass default overwrite our default | ||
Matthias BUSSONNIER
|
r10165 | this.placeholder = options.placeholder || ''; | ||
this.read_only = options.cm_config.readOnly; | ||||
Brian E. Granger
|
r4352 | this.selected = false; | ||
Brian E. Granger
|
r4529 | this.element = null; | ||
MinRK
|
r7523 | this.metadata = {}; | ||
Matthias BUSSONNIER
|
r8202 | // load this from metadata later ? | ||
Mikhail Korobov
|
r8839 | this.user_highlight = 'auto'; | ||
Matthias BUSSONNIER
|
r10165 | this.cm_config = options.cm_config; | ||
Brian E. Granger
|
r4352 | this.create_element(); | ||
Brian E. Granger
|
r4529 | if (this.element !== null) { | ||
Brian E. Granger
|
r4352 | this.element.data("cell", this); | ||
this.bind_events(); | ||||
} | ||||
this.cell_id = utils.uuid(); | ||||
Matthias BUSSONNIER
|
r10165 | this._options = options; | ||
Brian E. Granger
|
r4352 | }; | ||
Matthias BUSSONNIER
|
r10165 | Cell.options_default = { | ||
cm_config : { | ||||
Matthias BUSSONNIER
|
r9537 | indentUnit : 4, | ||
Matthias BUSSONNIER
|
r10165 | readOnly: false, | ||
theme: "default" | ||||
} | ||||
Matthias BUSSONNIER
|
r9537 | }; | ||
MinRK
|
r10791 | |||
// FIXME: Workaround CM Bug #332 (Safari segfault on drag) | ||||
// by disabling drag/drop altogether on Safari | ||||
// https://github.com/marijnh/CodeMirror/issues/332 | ||||
if (utils.browser[0] == "Safari") { | ||||
Cell.options_default.cm_config.dragDrop = false; | ||||
} | ||||
Matthias BUSSONNIER
|
r9537 | |||
Matthias BUSSONNIER
|
r10165 | Cell.prototype.mergeopt = function(_class, options, overwrite){ | ||
MinRK
|
r10791 | overwrite = overwrite || {}; | ||
Matthias BUSSONNIER
|
r10165 | return $.extend(true, {}, _class.options_default, options, overwrite) | ||
} | ||||
Brian Granger
|
r5959 | |||
Matthias BUSSONNIER
|
r8713 | /** | ||
* Empty. Subclasses must implement create_element. | ||||
Matthias BUSSONNIER
|
r8714 | * This should contain all the code to create the DOM element in notebook | ||
Matthias BUSSONNIER
|
r8713 | * and will be called by Base Class constructor. | ||
* @method create_element | ||||
*/ | ||||
Matthias BUSSONNIER
|
r9073 | Cell.prototype.create_element = function () { | ||
}; | ||||
Brian E. Granger
|
r4352 | |||
Matthias BUSSONNIER
|
r8281 | |||
Matthias BUSSONNIER
|
r8713 | /** | ||
Matthias BUSSONNIER
|
r8714 | * Subclasses can implement override bind_events. | ||
* Be carefull to call the parent method when overwriting as it fires event. | ||||
* this will be triggerd after create_element in constructor. | ||||
Matthias BUSSONNIER
|
r8713 | * @method bind_events | ||
*/ | ||||
Brian E. Granger
|
r4352 | Cell.prototype.bind_events = function () { | ||
var that = this; | ||||
Brian Granger
|
r7168 | // We trigger events so that Cell doesn't have to depend on Notebook. | ||
Brian E. Granger
|
r4352 | that.element.click(function (event) { | ||
if (that.selected === false) { | ||||
Brian Granger
|
r7168 | $([IPython.events]).trigger('select.Cell', {'cell':that}); | ||
Stefan van der Walt
|
r5479 | } | ||
Brian E. Granger
|
r4352 | }); | ||
that.element.focusin(function (event) { | ||||
if (that.selected === false) { | ||||
Brian Granger
|
r7168 | $([IPython.events]).trigger('select.Cell', {'cell':that}); | ||
Stefan van der Walt
|
r5479 | } | ||
Brian E. Granger
|
r4352 | }); | ||
MinRK
|
r10785 | if (this.code_mirror) { | ||
this.code_mirror.on("change", function(cm, change) { | ||||
$([IPython.events]).trigger("set_dirty.Notebook", {value: true}); | ||||
}); | ||||
} | ||||
Brian E. Granger
|
r4352 | }; | ||
Matthias BUSSONNIER
|
r8714 | /** | ||
* Triger typsetting of math by mathjax on current cell element | ||||
* @method typeset | ||||
*/ | ||||
Aron Ahmadia
|
r8660 | Cell.prototype.typeset = function () { | ||
Aron Ahmadia
|
r8662 | if (window.MathJax){ | ||
var cell_math = this.element.get(0); | ||||
Matthias BUSSONNIER
|
r10165 | MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]); | ||
Aron Ahmadia
|
r8662 | } | ||
Aron Ahmadia
|
r8660 | }; | ||
Brian Granger
|
r5959 | |||
Matthias BUSSONNIER
|
r8714 | /** | ||
* should be triggerd when cell is selected | ||||
* @method select | ||||
*/ | ||||
Brian Granger
|
r5943 | Cell.prototype.select = function () { | ||
Matthias BUSSONNIER
|
r9260 | this.element.addClass('selected'); | ||
Brian Granger
|
r5943 | this.selected = true; | ||
}; | ||||
Matthias BUSSONNIER
|
r8714 | /** | ||
* should be triggerd when cell is unselected | ||||
* @method unselect | ||||
*/ | ||||
Brian Granger
|
r5943 | Cell.prototype.unselect = function () { | ||
Matthias BUSSONNIER
|
r9260 | this.element.removeClass('selected'); | ||
Brian Granger
|
r5943 | this.selected = false; | ||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method get_text | ||||
*/ | ||||
Brian Granger
|
r5943 | Cell.prototype.get_text = function () { | ||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method set_text | ||||
* @param {string} text | ||||
*/ | ||||
Brian Granger
|
r5943 | Cell.prototype.set_text = function (text) { | ||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
* Refresh codemirror instance | ||||
* @method refresh | ||||
*/ | ||||
Brian Granger
|
r5943 | Cell.prototype.refresh = function () { | ||
this.code_mirror.refresh(); | ||||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method edit | ||||
**/ | ||||
Brian Granger
|
r5943 | Cell.prototype.edit = function () { | ||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method render | ||||
**/ | ||||
Brian Granger
|
r5943 | Cell.prototype.render = function () { | ||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* serialise cell to json. | ||||
* @method toJSON | ||||
**/ | ||||
Brian Granger
|
r5943 | Cell.prototype.toJSON = function () { | ||
MinRK
|
r7523 | var data = {}; | ||
data.metadata = this.metadata; | ||||
return data; | ||||
Brian Granger
|
r5943 | }; | ||
Matthias BUSSONNIER
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method fromJSON | ||||
**/ | ||||
Brian Granger
|
r5943 | Cell.prototype.fromJSON = function (data) { | ||
MinRK
|
r7523 | if (data.metadata !== undefined) { | ||
this.metadata = data.metadata; | ||||
} | ||||
Matthias BUSSONNIER
|
r9064 | this.celltoolbar.rebuild(); | ||
Brian Granger
|
r5943 | }; | ||
Matthias BUSSONNIER
|
r8739 | /** | ||
MinRK
|
r12509 | * can the cell be split into two cells | ||
Matthias BUSSONNIER
|
r8739 | * @method is_splittable | ||
**/ | ||||
Brian Granger
|
r5946 | Cell.prototype.is_splittable = function () { | ||
return true; | ||||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
MinRK
|
r12509 | * can the cell be merged with other cells | ||
* @method is_mergeable | ||||
**/ | ||||
Cell.prototype.is_mergeable = function () { | ||||
return true; | ||||
}; | ||||
/** | ||||
Matthias BUSSONNIER
|
r8739 | * @return {String} - the text before the cursor | ||
* @method get_pre_cursor | ||||
**/ | ||||
Brian Granger
|
r5946 | Cell.prototype.get_pre_cursor = function () { | ||
var cursor = this.code_mirror.getCursor(); | ||||
Matthias BUSSONNIER
|
r10165 | var text = this.code_mirror.getRange({line:0, ch:0}, cursor); | ||
Brian Granger
|
r5946 | text = text.replace(/^\n+/, '').replace(/\n+$/, ''); | ||
return text; | ||||
} | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
* @return {String} - the text after the cursor | ||||
* @method get_post_cursor | ||||
**/ | ||||
Brian Granger
|
r5946 | Cell.prototype.get_post_cursor = function () { | ||
var cursor = this.code_mirror.getCursor(); | ||||
var last_line_num = this.code_mirror.lineCount()-1; | ||||
var last_line_len = this.code_mirror.getLine(last_line_num).length; | ||||
var end = {line:last_line_num, ch:last_line_len} | ||||
var text = this.code_mirror.getRange(cursor, end); | ||||
text = text.replace(/^\n+/, '').replace(/\n+$/, ''); | ||||
return text; | ||||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
Matthias BUSSONNIER
|
r9542 | * Show/Hide CodeMirror LineNumber | ||
Matthias BUSSONNIER
|
r9690 | * @method show_line_numbers | ||
Matthias BUSSONNIER
|
r9542 | * | ||
* @param value {Bool} show (true), or hide (false) the line number in CodeMirror | ||||
**/ | ||||
Cell.prototype.show_line_numbers = function (value) { | ||||
this.code_mirror.setOption('lineNumbers', value); | ||||
this.code_mirror.refresh(); | ||||
}; | ||||
/** | ||||
Matthias BUSSONNIER
|
r8739 | * Toggle CodeMirror LineNumber | ||
* @method toggle_line_numbers | ||||
**/ | ||||
Brian Granger
|
r6059 | Cell.prototype.toggle_line_numbers = function () { | ||
Matthias BUSSONNIER
|
r9690 | var val = this.code_mirror.getOption('lineNumbers'); | ||
this.show_line_numbers(!val); | ||||
Brian Granger
|
r6059 | }; | ||
Matthias BUSSONNIER
|
r8739 | /** | ||
Matthias BUSSONNIER
|
r9548 | * Force codemirror highlight mode | ||
Matthias BUSSONNIER
|
r8739 | * @method force_highlight | ||
* @param {object} - CodeMirror mode | ||||
**/ | ||||
Matthias BUSSONNIER
|
r8202 | Cell.prototype.force_highlight = function(mode) { | ||
this.user_highlight = mode; | ||||
this.auto_highlight(); | ||||
}; | ||||
Matthias BUSSONNIER
|
r8739 | /** | ||
* Try to autodetect cell highlight mode, or use selected mode | ||||
* @methods _auto_highlight | ||||
* @private | ||||
* @param {String|object|undefined} - CodeMirror mode | 'auto' | ||||
**/ | ||||
Matthias BUSSONNIER
|
r8202 | Cell.prototype._auto_highlight = function (modes) { | ||
//Here we handle manually selected modes | ||||
if( this.user_highlight != undefined && this.user_highlight != 'auto' ) | ||||
{ | ||||
var mode = this.user_highlight; | ||||
CodeMirror.autoLoadMode(this.code_mirror, mode); | ||||
this.code_mirror.setOption('mode', mode); | ||||
return; | ||||
} | ||||
var first_line = this.code_mirror.getLine(0); | ||||
// loop on every pairs | ||||
for( var mode in modes) { | ||||
var regs = modes[mode]['reg']; | ||||
// only one key every time but regexp can't be keys... | ||||
for(var reg in regs ) { | ||||
// here we handle non magic_modes | ||||
if(first_line.match(regs[reg]) != null) { | ||||
if (mode.search('magic_') != 0) { | ||||
Matthias BUSSONNIER
|
r10165 | this.code_mirror.setOption('mode', mode); | ||
Matthias BUSSONNIER
|
r8202 | CodeMirror.autoLoadMode(this.code_mirror, mode); | ||
return; | ||||
} | ||||
var open = modes[mode]['open']|| "%%"; | ||||
var close = modes[mode]['close']|| "%%end"; | ||||
var mmode = mode; | ||||
mode = mmode.substr(6); | ||||
CodeMirror.autoLoadMode(this.code_mirror, mode); | ||||
// create on the fly a mode that swhitch between | ||||
// plain/text and smth else otherwise `%%` is | ||||
// source of some highlight issues. | ||||
// we use patchedGetMode to circumvent a bug in CM | ||||
CodeMirror.defineMode(mmode , function(config) { | ||||
return CodeMirror.multiplexingMode( | ||||
CodeMirror.patchedGetMode(config, 'text/plain'), | ||||
// always set someting on close | ||||
{open: open, close: close, | ||||
mode: CodeMirror.patchedGetMode(config, mode), | ||||
delimStyle: "delimit" | ||||
} | ||||
); | ||||
}); | ||||
this.code_mirror.setOption('mode', mmode); | ||||
return; | ||||
} | ||||
} | ||||
} | ||||
Matthias BUSSONNIER
|
r12233 | // fallback on default | ||
Matthias Bussonnier
|
r12237 | var default_mode | ||
try { | ||||
default_mode = this._options.cm_config.mode; | ||||
} catch(e) { | ||||
default_mode = 'text/plain'; | ||||
} | ||||
Matthias BUSSONNIER
|
r8202 | this.code_mirror.setOption('mode', default_mode); | ||
}; | ||||
Brian Granger
|
r6059 | |||
Brian E. Granger
|
r4352 | IPython.Cell = Cell; | ||
return IPython; | ||||
}(IPython)); | ||||
Brian E. Granger
|
r4349 | |||