cell.js
312 lines
| 9.2 KiB
| application/javascript
|
JavascriptLexer
|
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. | ||||
//---------------------------------------------------------------------------- | ||||
|
r4349 | |||
//============================================================================ | ||||
// Cell | ||||
//============================================================================ | ||||
|
r8713 | /** | ||
* An extendable module that provide base functionnality to create cell for notebook. | ||||
|
r8739 | * @module IPython | ||
* @namespace IPython | ||||
* @submodule Cell | ||||
|
r8713 | */ | ||
|
r4349 | |||
|
r4352 | var IPython = (function (IPython) { | ||
|
r4349 | |||
|
r4352 | var utils = IPython.utils; | ||
|
r4349 | |||
|
r8713 | /** | ||
|
r8714 | * The Base `Cell` class from which to inherit | ||
|
r8713 | * @class Cell | ||
*/ | ||||
|
r5959 | |||
|
r8713 | /* | ||
* @constructor | ||||
*/ | ||||
|
r7168 | var Cell = function () { | ||
|
r5944 | this.placeholder = this.placeholder || ''; | ||
|
r5200 | this.read_only = false; | ||
|
r4352 | this.selected = false; | ||
|
r4529 | this.element = null; | ||
|
r7523 | this.metadata = {}; | ||
|
r8202 | // load this from metadata later ? | ||
|
r8839 | this.user_highlight = 'auto'; | ||
|
r4352 | this.create_element(); | ||
|
r4529 | if (this.element !== null) { | ||
|
r4352 | this.element.data("cell", this); | ||
this.bind_events(); | ||||
} | ||||
this.cell_id = utils.uuid(); | ||||
}; | ||||
|
r5959 | |||
|
r8713 | /** | ||
* Empty. Subclasses must implement create_element. | ||||
|
r8714 | * This should contain all the code to create the DOM element in notebook | ||
|
r8713 | * and will be called by Base Class constructor. | ||
* @method create_element | ||||
*/ | ||||
|
r5943 | Cell.prototype.create_element = function () {}; | ||
|
r4352 | |||
|
r8281 | |||
|
r8713 | /** | ||
|
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. | ||||
|
r8713 | * @method bind_events | ||
*/ | ||||
|
r4352 | Cell.prototype.bind_events = function () { | ||
var that = this; | ||||
|
r7168 | // We trigger events so that Cell doesn't have to depend on Notebook. | ||
|
r4352 | that.element.click(function (event) { | ||
if (that.selected === false) { | ||||
|
r7168 | $([IPython.events]).trigger('select.Cell', {'cell':that}); | ||
|
r5479 | } | ||
|
r4352 | }); | ||
that.element.focusin(function (event) { | ||||
if (that.selected === false) { | ||||
|
r7168 | $([IPython.events]).trigger('select.Cell', {'cell':that}); | ||
|
r5479 | } | ||
|
r4352 | }); | ||
}; | ||||
|
r8714 | /** | ||
* Triger typsetting of math by mathjax on current cell element | ||||
* @method typeset | ||||
*/ | ||||
|
r8660 | Cell.prototype.typeset = function () { | ||
|
r8662 | if (window.MathJax){ | ||
var cell_math = this.element.get(0); | ||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub,cell_math]); | ||||
} | ||||
|
r8660 | }; | ||
|
r5959 | |||
|
r8714 | /** | ||
* should be triggerd when cell is selected | ||||
* @method select | ||||
*/ | ||||
|
r5943 | Cell.prototype.select = function () { | ||
this.element.addClass('ui-widget-content ui-corner-all'); | ||||
this.selected = true; | ||||
}; | ||||
|
r8714 | /** | ||
* should be triggerd when cell is unselected | ||||
* @method unselect | ||||
*/ | ||||
|
r5943 | Cell.prototype.unselect = function () { | ||
this.element.removeClass('ui-widget-content ui-corner-all'); | ||||
this.selected = false; | ||||
}; | ||||
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method get_text | ||||
*/ | ||||
|
r5943 | Cell.prototype.get_text = function () { | ||
}; | ||||
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method set_text | ||||
* @param {string} text | ||||
*/ | ||||
|
r5943 | Cell.prototype.set_text = function (text) { | ||
}; | ||||
|
r8739 | /** | ||
* Refresh codemirror instance | ||||
* @method refresh | ||||
*/ | ||||
|
r5943 | Cell.prototype.refresh = function () { | ||
this.code_mirror.refresh(); | ||||
}; | ||||
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method edit | ||||
**/ | ||||
|
r5943 | Cell.prototype.edit = function () { | ||
}; | ||||
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method render | ||||
**/ | ||||
|
r5943 | Cell.prototype.render = function () { | ||
}; | ||||
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* serialise cell to json. | ||||
* @method toJSON | ||||
**/ | ||||
|
r5943 | Cell.prototype.toJSON = function () { | ||
|
r7523 | var data = {}; | ||
data.metadata = this.metadata; | ||||
return data; | ||||
|
r5943 | }; | ||
|
r8739 | /** | ||
* should be overritten by subclass | ||||
* @method fromJSON | ||||
**/ | ||||
|
r5943 | Cell.prototype.fromJSON = function (data) { | ||
|
r7523 | if (data.metadata !== undefined) { | ||
this.metadata = data.metadata; | ||||
} | ||||
|
r5943 | }; | ||
|
r8739 | /** | ||
* can the cell be splitted in 2 cells. | ||||
* @method is_splittable | ||||
**/ | ||||
|
r5946 | Cell.prototype.is_splittable = function () { | ||
return true; | ||||
}; | ||||
|
r8739 | /** | ||
* @return {String} - the text before the cursor | ||||
* @method get_pre_cursor | ||||
**/ | ||||
|
r5946 | Cell.prototype.get_pre_cursor = function () { | ||
var cursor = this.code_mirror.getCursor(); | ||||
var text = this.code_mirror.getRange({line:0,ch:0}, cursor); | ||||
text = text.replace(/^\n+/, '').replace(/\n+$/, ''); | ||||
return text; | ||||
} | ||||
|
r8739 | /** | ||
* @return {String} - the text after the cursor | ||||
* @method get_post_cursor | ||||
**/ | ||||
|
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; | ||||
}; | ||||
|
r5959 | |||
|
r8739 | /** Grow the cell by hand. This is used upon reloading from JSON, when the | ||
* autogrow handler is not called. | ||||
* | ||||
* could be made static | ||||
* | ||||
* @param {Dom element} - element | ||||
* @method grow | ||||
**/ | ||||
|
r4400 | Cell.prototype.grow = function(element) { | ||
var dom = element.get(0); | ||||
var lines_count = 0; | ||||
// modified split rule from | ||||
// http://stackoverflow.com/questions/2035910/how-to-get-the-number-of-lines-in-a-textarea/2036424#2036424 | ||||
var lines = dom.value.split(/\r|\r\n|\n/); | ||||
lines_count = lines.length; | ||||
if (lines_count >= 1) { | ||||
dom.rows = lines_count; | ||||
} else { | ||||
dom.rows = 1; | ||||
} | ||||
}; | ||||
|
r8739 | /** | ||
* Toggle CodeMirror LineNumber | ||||
* @method toggle_line_numbers | ||||
**/ | ||||
|
r6059 | Cell.prototype.toggle_line_numbers = function () { | ||
if (this.code_mirror.getOption('lineNumbers') == false) { | ||||
this.code_mirror.setOption('lineNumbers', true); | ||||
} else { | ||||
this.code_mirror.setOption('lineNumbers', false); | ||||
} | ||||
this.code_mirror.refresh(); | ||||
}; | ||||
|
r8739 | /** | ||
* force codemirror highlight mode | ||||
* @method force_highlight | ||||
* @param {object} - CodeMirror mode | ||||
**/ | ||||
|
r8202 | Cell.prototype.force_highlight = function(mode) { | ||
this.user_highlight = mode; | ||||
this.auto_highlight(); | ||||
}; | ||||
|
r8739 | /** | ||
* Try to autodetect cell highlight mode, or use selected mode | ||||
* @methods _auto_highlight | ||||
* @private | ||||
* @param {String|object|undefined} - CodeMirror mode | 'auto' | ||||
**/ | ||||
|
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) { | ||||
this.code_mirror.setOption('mode',mode); | ||||
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; | ||||
} | ||||
} | ||||
} | ||||
// fallback on default (python) | ||||
var default_mode = this.default_mode || 'text/plain'; | ||||
this.code_mirror.setOption('mode', default_mode); | ||||
}; | ||||
|
r6059 | |||
|
r4352 | IPython.Cell = Cell; | ||
return IPython; | ||||
}(IPython)); | ||||
|
r4349 | |||