##// END OF EJS Templates
Merge pull request #3232 from ellisonbg/codemirror3...
Merge pull request #3232 from ellisonbg/codemirror3 Update to CodeMirror 3 and start to ship our components The main purpose of this branch is the update to CodeMirror 3 and ship it as a bower component. Because CodeMirror doesn't use proper semantic version tags, we have created a CodeMirror ipython fork. The sole purpose of this fork is for us to maintain properly named tags that bower can install from. As a side effect of these changes, we are now shipping in place all bower components that we are using (bootstrap, less. codemirror, jquery). BUT, the version of jquery in components is not yet being used as we are still stuck with the old version attached to the nasty jquery.ui branch we ship. In a later PR, we will need to get rid of that jquery.ui branch (replace those capabilties with bootstrap) and then move to a stable version of jquery.ui that matches that of the jquery component depended upon by bootstrap.

File last commit:

r10165:c345888b
r10441:60a66d42 merge
Show More
cell.js
345 lines | 10.1 KiB | application/javascript | JavascriptLexer
//----------------------------------------------------------------------------
// 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.
//----------------------------------------------------------------------------
//============================================================================
// Cell
//============================================================================
/**
* An extendable module that provide base functionnality to create cell for notebook.
* @module IPython
* @namespace IPython
* @submodule Cell
*/
var IPython = (function (IPython) {
var utils = IPython.utils;
/**
* The Base `Cell` class from which to inherit
* @class Cell
**/
/*
* @constructor
*
* @param {object|undefined} [options]
* @param [options.cm_config] {object} config to pass to CodeMirror, will extend default parameters
*/
var Cell = function (options) {
options = this.mergeopt(Cell, options)
// superclass default overwrite our default
this.placeholder = options.placeholder || '';
this.read_only = options.cm_config.readOnly;
this.selected = false;
this.element = null;
this.metadata = {};
// load this from metadata later ?
this.user_highlight = 'auto';
this.cm_config = options.cm_config;
this.create_element();
if (this.element !== null) {
this.element.data("cell", this);
this.bind_events();
}
this.cell_id = utils.uuid();
this._options = options;
};
Cell.options_default = {
cm_config : {
indentUnit : 4,
readOnly: false,
theme: "default"
}
};
Cell.prototype.mergeopt = function(_class, options, overwrite){
overwrite = overwrite || {};
return $.extend(true, {}, _class.options_default, options, overwrite)
}
/**
* Empty. Subclasses must implement create_element.
* This should contain all the code to create the DOM element in notebook
* and will be called by Base Class constructor.
* @method create_element
*/
Cell.prototype.create_element = function () {
};
/**
* 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.
* @method bind_events
*/
Cell.prototype.bind_events = function () {
var that = this;
// We trigger events so that Cell doesn't have to depend on Notebook.
that.element.click(function (event) {
if (that.selected === false) {
$([IPython.events]).trigger('select.Cell', {'cell':that});
}
});
that.element.focusin(function (event) {
if (that.selected === false) {
$([IPython.events]).trigger('select.Cell', {'cell':that});
}
});
};
/**
* Triger typsetting of math by mathjax on current cell element
* @method typeset
*/
Cell.prototype.typeset = function () {
if (window.MathJax){
var cell_math = this.element.get(0);
MathJax.Hub.Queue(["Typeset", MathJax.Hub, cell_math]);
}
};
/**
* should be triggerd when cell is selected
* @method select
*/
Cell.prototype.select = function () {
this.element.addClass('selected');
this.selected = true;
};
/**
* should be triggerd when cell is unselected
* @method unselect
*/
Cell.prototype.unselect = function () {
this.element.removeClass('selected');
this.selected = false;
};
/**
* should be overritten by subclass
* @method get_text
*/
Cell.prototype.get_text = function () {
};
/**
* should be overritten by subclass
* @method set_text
* @param {string} text
*/
Cell.prototype.set_text = function (text) {
};
/**
* Refresh codemirror instance
* @method refresh
*/
Cell.prototype.refresh = function () {
this.code_mirror.refresh();
};
/**
* should be overritten by subclass
* @method edit
**/
Cell.prototype.edit = function () {
};
/**
* should be overritten by subclass
* @method render
**/
Cell.prototype.render = function () {
};
/**
* should be overritten by subclass
* serialise cell to json.
* @method toJSON
**/
Cell.prototype.toJSON = function () {
var data = {};
data.metadata = this.metadata;
return data;
};
/**
* should be overritten by subclass
* @method fromJSON
**/
Cell.prototype.fromJSON = function (data) {
if (data.metadata !== undefined) {
this.metadata = data.metadata;
}
this.celltoolbar.rebuild();
};
/**
* can the cell be splitted in 2 cells.
* @method is_splittable
**/
Cell.prototype.is_splittable = function () {
return true;
};
/**
* @return {String} - the text before the cursor
* @method get_pre_cursor
**/
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;
}
/**
* @return {String} - the text after the cursor
* @method get_post_cursor
**/
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;
};
/** 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
**/
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;
}
};
/**
* Show/Hide CodeMirror LineNumber
* @method show_line_numbers
*
* @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();
};
/**
* Toggle CodeMirror LineNumber
* @method toggle_line_numbers
**/
Cell.prototype.toggle_line_numbers = function () {
var val = this.code_mirror.getOption('lineNumbers');
this.show_line_numbers(!val);
};
/**
* Force codemirror highlight mode
* @method force_highlight
* @param {object} - CodeMirror mode
**/
Cell.prototype.force_highlight = function(mode) {
this.user_highlight = mode;
this.auto_highlight();
};
/**
* Try to autodetect cell highlight mode, or use selected mode
* @methods _auto_highlight
* @private
* @param {String|object|undefined} - CodeMirror mode | 'auto'
**/
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);
};
IPython.Cell = Cell;
return IPython;
}(IPython));