textcell.js
415 lines
| 12.4 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 | |||
//============================================================================ | ||||
Brian Granger
|
r4508 | // TextCell | ||
Brian E. Granger
|
r4349 | //============================================================================ | ||
Brian E. Granger
|
r4352 | var IPython = (function (IPython) { | ||
Brian E. Granger
|
r4349 | |||
Brian Granger
|
r4508 | // TextCell base class | ||
Bradley M. Froehle
|
r8168 | var key = IPython.utils.keycodes; | ||
Brian Granger
|
r4508 | |||
Brian Granger
|
r7168 | var TextCell = function () { | ||
Brian Granger
|
r4508 | this.code_mirror_mode = this.code_mirror_mode || 'htmlmixed'; | ||
Brian E. Granger
|
r4352 | IPython.Cell.apply(this, arguments); | ||
this.rendered = false; | ||||
Brian Granger
|
r4508 | this.cell_type = this.cell_type || 'text'; | ||
Brian E. Granger
|
r4352 | }; | ||
Brian Granger
|
r4508 | TextCell.prototype = new IPython.Cell(); | ||
Brian E. Granger
|
r4352 | |||
Brian E. Granger
|
r4499 | |||
Brian Granger
|
r4508 | TextCell.prototype.create_element = function () { | ||
var cell = $("<div>").addClass('cell text_cell border-box-sizing'); | ||||
Brian E. Granger
|
r4629 | cell.attr('tabindex','2'); | ||
Brian Granger
|
r5946 | var input_area = $('<div/>').addClass('text_cell_input border-box-sizing'); | ||
Brian E. Granger
|
r4499 | this.code_mirror = CodeMirror(input_area.get(0), { | ||
indentUnit : 4, | ||||
Brian Granger
|
r4508 | mode: this.code_mirror_mode, | ||
Brian E. Granger
|
r4504 | theme: 'default', | ||
MinRK
|
r5200 | value: this.placeholder, | ||
Brian Granger
|
r5942 | readOnly: this.read_only, | ||
Fernando Perez
|
r5978 | lineWrapping : true, | ||
Matthias BUSSONNIER
|
r8057 | extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"}, | ||
Brian Granger
|
r5942 | onKeyEvent: $.proxy(this.handle_codemirror_keyevent,this) | ||
Brian E. Granger
|
r4499 | }); | ||
// The tabindex=-1 makes this div focusable. | ||||
Brian Granger
|
r5946 | var render_area = $('<div/>').addClass('text_cell_render border-box-sizing'). | ||
Brian E. Granger
|
r4507 | addClass('rendered_html').attr('tabindex','-1'); | ||
Brian E. Granger
|
r4499 | cell.append(input_area).append(render_area); | ||
Brian E. Granger
|
r4352 | this.element = cell; | ||
}; | ||||
Brian Granger
|
r4508 | TextCell.prototype.bind_events = function () { | ||
Brian E. Granger
|
r4352 | IPython.Cell.prototype.bind_events.apply(this); | ||
var that = this; | ||||
this.element.keydown(function (event) { | ||||
Brian Granger
|
r6050 | if (event.which === 13 && !event.shiftKey) { | ||
Brian E. Granger
|
r4352 | if (that.rendered) { | ||
that.edit(); | ||||
Brian Granger
|
r5942 | return false; | ||
Brian Granger
|
r5946 | }; | ||
}; | ||||
}); | ||||
this.element.dblclick(function () { | ||||
that.edit(); | ||||
Brian E. Granger
|
r4352 | }); | ||
}; | ||||
Brian E. Granger
|
r4349 | |||
Brian Granger
|
r5942 | TextCell.prototype.handle_codemirror_keyevent = function (editor, event) { | ||
// This method gets called in CodeMirror's onKeyDown/onKeyPress | ||||
// handlers and is used to provide custom key handling. Its return | ||||
// value is used to determine if CodeMirror should ignore the event: | ||||
// true = ignore, false = don't ignore. | ||||
fawce
|
r5990 | |||
Brian Granger
|
r5942 | if (event.keyCode === 13 && (event.shiftKey || event.ctrlKey)) { | ||
// Always ignore shift-enter in CodeMirror as we handle it. | ||||
return true; | ||||
} | ||||
return false; | ||||
}; | ||||
Brian Granger
|
r4508 | TextCell.prototype.select = function () { | ||
Brian E. Granger
|
r4352 | IPython.Cell.prototype.select.apply(this); | ||
Brian Granger
|
r4508 | var output = this.element.find("div.text_cell_render"); | ||
Brian E. Granger
|
r4352 | output.trigger('focus'); | ||
}; | ||||
Brian E. Granger
|
r4349 | |||
MinRK
|
r5833 | TextCell.prototype.unselect = function() { | ||
// render on selection of another cell | ||||
this.render(); | ||||
IPython.Cell.prototype.unselect.apply(this); | ||||
}; | ||||
Brian Granger
|
r4508 | TextCell.prototype.edit = function () { | ||
MinRK
|
r5200 | if ( this.read_only ) return; | ||
Brian E. Granger
|
r4352 | if (this.rendered === true) { | ||
Brian Granger
|
r4508 | var text_cell = this.element; | ||
var output = text_cell.find("div.text_cell_render"); | ||||
Brian E. Granger
|
r4352 | output.hide(); | ||
Brian Granger
|
r4508 | text_cell.find('div.text_cell_input').show(); | ||
Brian Granger
|
r5946 | this.code_mirror.refresh(); | ||
Brian E. Granger
|
r4499 | this.code_mirror.focus(); | ||
Brian Granger
|
r5971 | // We used to need an additional refresh() after the focus, but | ||
// it appears that this has been fixed in CM. This bug would show | ||||
// up on FF when a newly loaded markdown cell was edited. | ||||
Brian E. Granger
|
r4352 | this.rendered = false; | ||
Brian Granger
|
r5943 | if (this.get_text() === this.placeholder) { | ||
this.set_text(''); | ||||
Brian Granger
|
r5946 | this.refresh(); | ||
Stefan van der Walt
|
r5479 | } | ||
} | ||||
Brian E. Granger
|
r4349 | }; | ||
Brian Granger
|
r4508 | // Subclasses must define render. | ||
TextCell.prototype.render = function () {}; | ||||
Brian E. Granger
|
r4349 | |||
Brian Granger
|
r5943 | TextCell.prototype.get_text = function() { | ||
Brian E. Granger
|
r4499 | return this.code_mirror.getValue(); | ||
Brian E. Granger
|
r4352 | }; | ||
Brian E. Granger
|
r4349 | |||
Brian Granger
|
r5943 | TextCell.prototype.set_text = function(text) { | ||
Brian E. Granger
|
r4499 | this.code_mirror.setValue(text); | ||
this.code_mirror.refresh(); | ||||
Brian E. Granger
|
r4352 | }; | ||
Brian E. Granger
|
r4349 | |||
Brian E. Granger
|
r4513 | TextCell.prototype.get_rendered = function() { | ||
Brian Granger
|
r4508 | return this.element.find('div.text_cell_render').html(); | ||
}; | ||||
TextCell.prototype.set_rendered = function(text) { | ||||
this.element.find('div.text_cell_render').html(text); | ||||
Brian E. Granger
|
r4352 | }; | ||
Brian E. Granger
|
r4349 | |||
Brian Granger
|
r4508 | TextCell.prototype.at_top = function () { | ||
Brian E. Granger
|
r4352 | if (this.rendered) { | ||
return true; | ||||
} else { | ||||
return false; | ||||
} | ||||
}; | ||||
Brian Granger
|
r4508 | TextCell.prototype.at_bottom = function () { | ||
Brian E. Granger
|
r4352 | if (this.rendered) { | ||
return true; | ||||
} else { | ||||
return false; | ||||
} | ||||
}; | ||||
Brian E. Granger
|
r4349 | |||
Brian Granger
|
r4508 | TextCell.prototype.fromJSON = function (data) { | ||
MinRK
|
r7523 | IPython.Cell.prototype.fromJSON.apply(this, arguments); | ||
Brian Granger
|
r4508 | if (data.cell_type === this.cell_type) { | ||
Brian E. Granger
|
r4499 | if (data.source !== undefined) { | ||
Brian Granger
|
r5943 | this.set_text(data.source); | ||
Paul Ivanov
|
r7587 | // make this value the starting point, so that we can only undo | ||
// to this state, instead of a blank cell | ||||
this.code_mirror.clearHistory(); | ||||
Brian E. Granger
|
r4513 | this.set_rendered(data.rendered || ''); | ||
this.rendered = false; | ||||
this.render(); | ||||
Stefan van der Walt
|
r5479 | } | ||
} | ||||
Brian E. Granger
|
r4513 | }; | ||
Brian E. Granger
|
r4349 | |||
Brian Granger
|
r4508 | TextCell.prototype.toJSON = function () { | ||
MinRK
|
r7523 | var data = IPython.Cell.prototype.toJSON.apply(this); | ||
Brian Granger
|
r4508 | data.cell_type = this.cell_type; | ||
Brian Granger
|
r5943 | data.source = this.get_text(); | ||
Brian E. Granger
|
r4484 | return data; | ||
Brian E. Granger
|
r4349 | }; | ||
Brian Granger
|
r4508 | |||
// HTMLCell | ||||
Brian Granger
|
r7168 | var HTMLCell = function () { | ||
Brian Granger
|
r5944 | this.placeholder = "Type <strong>HTML</strong> and LaTeX: $\\alpha^2$"; | ||
Brian Granger
|
r4508 | IPython.TextCell.apply(this, arguments); | ||
this.cell_type = 'html'; | ||||
}; | ||||
HTMLCell.prototype = new TextCell(); | ||||
HTMLCell.prototype.render = function () { | ||||
if (this.rendered === false) { | ||||
Brian Granger
|
r5943 | var text = this.get_text(); | ||
Stefan van der Walt
|
r5479 | if (text === "") { text = this.placeholder; } | ||
Brian Granger
|
r4508 | this.set_rendered(text); | ||
MinRK
|
r5547 | this.typeset(); | ||
Brian Granger
|
r4508 | this.element.find('div.text_cell_input').hide(); | ||
this.element.find("div.text_cell_render").show(); | ||||
this.rendered = true; | ||||
Stefan van der Walt
|
r5479 | } | ||
Brian Granger
|
r4508 | }; | ||
// MarkdownCell | ||||
Brian Granger
|
r7168 | var MarkdownCell = function () { | ||
Brian Granger
|
r5944 | this.placeholder = "Type *Markdown* and LaTeX: $\\alpha^2$"; | ||
Brian Granger
|
r4508 | IPython.TextCell.apply(this, arguments); | ||
this.cell_type = 'markdown'; | ||||
}; | ||||
MarkdownCell.prototype = new TextCell(); | ||||
MarkdownCell.prototype.render = function () { | ||||
if (this.rendered === false) { | ||||
Brian Granger
|
r5943 | var text = this.get_text(); | ||
Stefan van der Walt
|
r5479 | if (text === "") { text = this.placeholder; } | ||
Brian Granger
|
r4508 | var html = IPython.markdown_converter.makeHtml(text); | ||
MinRK
|
r8052 | try { | ||
this.set_rendered(html); | ||||
} catch (e) { | ||||
MinRK
|
r8068 | console.log("Error running Javascript in Markdown:"); | ||
MinRK
|
r8052 | console.log(e); | ||
MinRK
|
r8068 | this.set_rendered($("<div/>").addClass("js-error").html( | ||
MinRK
|
r8052 | "Error rendering Markdown!<br/>" + e.toString()) | ||
); | ||||
} | ||||
MinRK
|
r5547 | this.typeset() | ||
Brian Granger
|
r4508 | this.element.find('div.text_cell_input').hide(); | ||
this.element.find("div.text_cell_render").show(); | ||||
Stefan van der Walt
|
r4655 | var code_snippets = this.element.find("pre > code"); | ||
code_snippets.replaceWith(function () { | ||||
var code = $(this).html(); | ||||
/* Substitute br for newlines and for spaces | ||||
before highlighting, since prettify doesn't | ||||
preserve those on all browsers */ | ||||
code = code.replace(/(\r\n|\n|\r)/gm, "<br/>"); | ||||
code = code.replace(/ /gm, ' '); | ||||
code = prettyPrintOne(code); | ||||
return '<code class="prettyprint">' + code + '</code>'; | ||||
}); | ||||
Brian Granger
|
r4508 | this.rendered = true; | ||
Stefan van der Walt
|
r5479 | } | ||
Brian Granger
|
r4508 | }; | ||
MinRK
|
r6248 | // RawCell | ||
Brian Granger
|
r4508 | |||
Brian Granger
|
r7168 | var RawCell = function () { | ||
Brian Granger
|
r6027 | this.placeholder = "Type plain text and LaTeX: $\\alpha^2$"; | ||
Brian Granger
|
r6017 | this.code_mirror_mode = 'rst'; | ||
Brian Granger
|
r4508 | IPython.TextCell.apply(this, arguments); | ||
MinRK
|
r6248 | this.cell_type = 'raw'; | ||
Matthias BUSSONNIER
|
r8202 | var that = this | ||
this.element.focusout( | ||||
function() { that.auto_highlight(); } | ||||
); | ||||
Brian Granger
|
r4508 | }; | ||
MinRK
|
r6248 | RawCell.prototype = new TextCell(); | ||
Brian Granger
|
r4508 | |||
Matthias BUSSONNIER
|
r8202 | RawCell.prototype.auto_highlight = function () { | ||
this._auto_highlight(IPython.config.raw_cell_highlight); | ||||
}; | ||||
Brian Granger
|
r4508 | |||
MinRK
|
r6248 | RawCell.prototype.render = function () { | ||
Brian Granger
|
r6017 | this.rendered = true; | ||
this.edit(); | ||||
}; | ||||
Bradley M. Froehle
|
r8168 | RawCell.prototype.handle_codemirror_keyevent = function (editor, event) { | ||
// This method gets called in CodeMirror's onKeyDown/onKeyPress | ||||
// handlers and is used to provide custom key handling. Its return | ||||
// value is used to determine if CodeMirror should ignore the event: | ||||
// true = ignore, false = don't ignore. | ||||
var that = this; | ||||
if (event.which === key.UPARROW && event.type === 'keydown') { | ||||
// If we are not at the top, let CM handle the up arrow and | ||||
// prevent the global keydown handler from handling it. | ||||
if (!that.at_top()) { | ||||
event.stop(); | ||||
return false; | ||||
} else { | ||||
return true; | ||||
}; | ||||
} else if (event.which === key.DOWNARROW && event.type === 'keydown') { | ||||
// If we are not at the bottom, let CM handle the down arrow and | ||||
// prevent the global keydown handler from handling it. | ||||
if (!that.at_bottom()) { | ||||
event.stop(); | ||||
return false; | ||||
} else { | ||||
return true; | ||||
}; | ||||
}; | ||||
return false; | ||||
}; | ||||
MinRK
|
r6248 | RawCell.prototype.select = function () { | ||
Brian Granger
|
r6017 | IPython.Cell.prototype.select.apply(this); | ||
this.code_mirror.refresh(); | ||||
this.code_mirror.focus(); | ||||
}; | ||||
MinRK
|
r6248 | RawCell.prototype.at_top = function () { | ||
Brian Granger
|
r6017 | var cursor = this.code_mirror.getCursor(); | ||
Bradley M. Froehle
|
r8168 | if (cursor.line === 0 && cursor.ch === 0) { | ||
Brian Granger
|
r6017 | return true; | ||
} else { | ||||
return false; | ||||
Stefan van der Walt
|
r5479 | } | ||
Brian Granger
|
r4508 | }; | ||
MinRK
|
r6248 | RawCell.prototype.at_bottom = function () { | ||
Brian Granger
|
r6017 | var cursor = this.code_mirror.getCursor(); | ||
Bradley M. Froehle
|
r8168 | if (cursor.line === (this.code_mirror.lineCount()-1) && cursor.ch === this.code_mirror.getLine(cursor.line).length) { | ||
Brian Granger
|
r6017 | return true; | ||
} else { | ||||
return false; | ||||
} | ||||
Brian Granger
|
r4508 | }; | ||
Brian Granger
|
r6018 | // HTMLCell | ||
Brian Granger
|
r7168 | var HeadingCell = function () { | ||
Brian Granger
|
r6018 | this.placeholder = "Type Heading Here"; | ||
IPython.TextCell.apply(this, arguments); | ||||
this.cell_type = 'heading'; | ||||
this.level = 1; | ||||
}; | ||||
HeadingCell.prototype = new TextCell(); | ||||
MinRK
|
r6156 | HeadingCell.prototype.fromJSON = function (data) { | ||
if (data.level != undefined){ | ||||
this.level = data.level; | ||||
} | ||||
IPython.TextCell.prototype.fromJSON.apply(this, arguments); | ||||
}; | ||||
HeadingCell.prototype.toJSON = function () { | ||||
var data = IPython.TextCell.prototype.toJSON.apply(this); | ||||
data.level = this.get_level(); | ||||
return data; | ||||
}; | ||||
Brian Granger
|
r6019 | HeadingCell.prototype.set_level = function (level) { | ||
this.level = level; | ||||
if (this.rendered) { | ||||
this.rendered = false; | ||||
this.render(); | ||||
}; | ||||
}; | ||||
HeadingCell.prototype.get_level = function () { | ||||
return this.level; | ||||
}; | ||||
Brian Granger
|
r6018 | HeadingCell.prototype.set_rendered = function (text) { | ||
var r = this.element.find("div.text_cell_render"); | ||||
r.empty(); | ||||
Brian Granger
|
r6019 | r.append($('<h'+this.level+'/>').html(text)); | ||
}; | ||||
Brian Granger
|
r6018 | |||
HeadingCell.prototype.get_rendered = function () { | ||||
var r = this.element.find("div.text_cell_render"); | ||||
return r.children().first().html(); | ||||
Brian Granger
|
r6019 | }; | ||
Brian Granger
|
r6018 | |||
HeadingCell.prototype.render = function () { | ||||
if (this.rendered === false) { | ||||
var text = this.get_text(); | ||||
if (text === "") { text = this.placeholder; } | ||||
this.set_rendered(text); | ||||
this.typeset(); | ||||
this.element.find('div.text_cell_input').hide(); | ||||
this.element.find("div.text_cell_render").show(); | ||||
this.rendered = true; | ||||
Brian Granger
|
r6019 | }; | ||
Brian Granger
|
r6018 | }; | ||
Brian Granger
|
r4508 | IPython.TextCell = TextCell; | ||
Brian E. Granger
|
r4499 | IPython.HTMLCell = HTMLCell; | ||
Brian Granger
|
r4508 | IPython.MarkdownCell = MarkdownCell; | ||
MinRK
|
r6248 | IPython.RawCell = RawCell; | ||
Brian Granger
|
r6019 | IPython.HeadingCell = HeadingCell; | ||
Brian Granger
|
r4508 | |||
Brian E. Granger
|
r4349 | |||
Brian E. Granger
|
r4352 | return IPython; | ||
Brian E. Granger
|
r4349 | |||
Brian E. Granger
|
r4352 | }(IPython)); | ||
Brian E. Granger
|
r4349 | |||