textcell.js
459 lines
| 13.7 KiB
| application/javascript
|
JavascriptLexer
Brian E. Granger
|
r4609 | //---------------------------------------------------------------------------- | ||
Aron Ahmadia
|
r8565 | // Copyright (C) 2008-2012 The IPython Development Team | ||
Brian E. Granger
|
r4609 | // | ||
// 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 | //============================================================================ | ||
Matthias BUSSONNIER
|
r10165 | |||
Matthias BUSSONNIER
|
r8709 | /** | ||
A module that allow to create different type of Text Cell | ||||
Matthias BUSSONNIER
|
r8739 | @module IPython | ||
@namespace IPython | ||||
Matthias BUSSONNIER
|
r8709 | */ | ||
Brian E. Granger
|
r4352 | var IPython = (function (IPython) { | ||
Matthias BUSSONNIER
|
r11526 | "use strict"; | ||
Brian E. Granger
|
r4349 | |||
Brian Granger
|
r4508 | // TextCell base class | ||
Brian E. Granger
|
r15619 | var keycodes = IPython.keyboard.keycodes; | ||
Brian E. Granger
|
r15632 | var security = IPython.security; | ||
Brian Granger
|
r4508 | |||
Matthias BUSSONNIER
|
r8709 | /** | ||
Matthias BUSSONNIER
|
r8711 | * Construct a new TextCell, codemirror mode is by default 'htmlmixed', and cell type is 'text' | ||
* cell start as not redered. | ||||
* | ||||
Matthias BUSSONNIER
|
r8709 | * @class TextCell | ||
Matthias BUSSONNIER
|
r8711 | * @constructor TextCell | ||
MinRK
|
r11288 | * @extend IPython.Cell | ||
Matthias BUSSONNIER
|
r9537 | * @param {object|undefined} [options] | ||
* @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config | ||||
Matthias BUSSONNIER
|
r10165 | * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass) | ||
Matthias BUSSONNIER
|
r8709 | */ | ||
Matthias BUSSONNIER
|
r9537 | var TextCell = function (options) { | ||
Matthias BUSSONNIER
|
r10165 | // in all TextCell/Cell subclasses | ||
// do not assign most of members here, just pass it down | ||||
// in the options dict potentially overwriting what you wish. | ||||
// they will be assigned in the base class. | ||||
Matthias BUSSONNIER
|
r9537 | |||
Matthias BUSSONNIER
|
r10165 | // we cannot put this as a class key as it has handle to "this". | ||
Matthias BUSSONNIER
|
r9537 | var cm_overwrite_options = { | ||
Brian E. Granger
|
r14021 | onKeyEvent: $.proxy(this.handle_keyevent,this) | ||
Matthias BUSSONNIER
|
r9537 | }; | ||
Matthias BUSSONNIER
|
r10165 | options = this.mergeopt(TextCell,options,{cm_config:cm_overwrite_options}); | ||
Matthias BUSSONNIER
|
r9537 | |||
MinRK
|
r13668 | this.cell_type = this.cell_type || 'text'; | ||
Matthias BUSSONNIER
|
r9537 | |||
MinRK
|
r13668 | IPython.Cell.apply(this, [options]); | ||
Matthias BUSSONNIER
|
r9537 | |||
Brian E. Granger
|
r4352 | this.rendered = false; | ||
}; | ||||
Matthias BUSSONNIER
|
r10165 | TextCell.prototype = new IPython.Cell(); | ||
TextCell.options_default = { | ||||
cm_config : { | ||||
extraKeys: {"Tab": "indentMore","Shift-Tab" : "indentLess"}, | ||||
mode: 'htmlmixed', | ||||
Matthias BUSSONNIER
|
r9537 | lineWrapping : true, | ||
} | ||||
Matthias BUSSONNIER
|
r10165 | }; | ||
Matthias BUSSONNIER
|
r9537 | |||
Matthias BUSSONNIER
|
r8711 | /** | ||
* Create the DOM element of the TextCell | ||||
Matthias BUSSONNIER
|
r8709 | * @method create_element | ||
* @private | ||||
*/ | ||||
Brian Granger
|
r4508 | TextCell.prototype.create_element = function () { | ||
Matthias BUSSONNIER
|
r9073 | IPython.Cell.prototype.create_element.apply(this, arguments); | ||
Brian E. Granger
|
r13776 | |||
Matthias BUSSONNIER
|
r10217 | var cell = $("<div>").addClass('cell text_cell border-box-sizing'); | ||
Brian E. Granger
|
r4629 | cell.attr('tabindex','2'); | ||
Brian Granger
|
r9144 | |||
Brian E. Granger
|
r13776 | var prompt = $('<div/>').addClass('prompt input_prompt'); | ||
cell.append(prompt); | ||||
var inner_cell = $('<div/>').addClass('inner_cell'); | ||||
Brian E. Granger
|
r9142 | this.celltoolbar = new IPython.CellToolbar(this); | ||
Brian E. Granger
|
r13776 | inner_cell.append(this.celltoolbar.element); | ||
MinRK
|
r15552 | var input_area = $('<div/>').addClass('input_area'); | ||
Jonathan Frederic
|
r15497 | this.code_mirror = new CodeMirror(input_area.get(0), this.cm_config); | ||
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'). | ||
Aron Ahmadia
|
r8661 | addClass('rendered_html').attr('tabindex','-1'); | ||
Brian E. Granger
|
r13776 | inner_cell.append(input_area).append(render_area); | ||
cell.append(inner_cell); | ||||
Brian E. Granger
|
r4352 | this.element = cell; | ||
}; | ||||
Matthias BUSSONNIER
|
r8711 | /** | ||
* Bind the DOM evet to cell actions | ||||
Matthias BUSSONNIER
|
r8709 | * Need to be called after TextCell.create_element | ||
* @private | ||||
* @method bind_event | ||||
*/ | ||||
Brian Granger
|
r4508 | TextCell.prototype.bind_events = function () { | ||
Brian E. Granger
|
r4352 | IPython.Cell.prototype.bind_events.apply(this); | ||
var that = this; | ||||
Brian E. Granger
|
r14015 | |||
Brian Granger
|
r5946 | this.element.dblclick(function () { | ||
Brian E. Granger
|
r14016 | if (that.selected === false) { | ||
$([IPython.events]).trigger('select.Cell', {'cell':that}); | ||||
Jonathan Frederic
|
r15497 | } | ||
Jonathan Frederic
|
r15531 | var cont = that.unrender(); | ||
if (cont) { | ||||
that.focus_editor(); | ||||
} | ||||
Brian E. Granger
|
r4352 | }); | ||
}; | ||||
Brian E. Granger
|
r4349 | |||
Brian E. Granger
|
r14014 | // Cell level actions | ||
Brian Granger
|
r4508 | TextCell.prototype.select = function () { | ||
Brian E. Granger
|
r14015 | var cont = IPython.Cell.prototype.select.apply(this); | ||
if (cont) { | ||||
Brian E. Granger
|
r14014 | if (this.mode === 'edit') { | ||
this.code_mirror.refresh(); | ||||
} | ||||
Jonathan Frederic
|
r15497 | } | ||
Brian E. Granger
|
r14015 | return cont; | ||
MinRK
|
r5833 | }; | ||
Brian E. Granger
|
r14014 | TextCell.prototype.unrender = function () { | ||
if (this.read_only) return; | ||||
Brian E. Granger
|
r14015 | var cont = IPython.Cell.prototype.unrender.apply(this); | ||
if (cont) { | ||||
Brian Granger
|
r4508 | var text_cell = this.element; | ||
Matthias BUSSONNIER
|
r8709 | var output = text_cell.find("div.text_cell_render"); | ||
Brian E. Granger
|
r4352 | output.hide(); | ||
MinRK
|
r15552 | text_cell.find('div.input_area').show(); | ||
Brian Granger
|
r5943 | if (this.get_text() === this.placeholder) { | ||
this.set_text(''); | ||||
Stefan van der Walt
|
r5479 | } | ||
Brian E. Granger
|
r15502 | this.refresh(); | ||
Jonathan Frederic
|
r15497 | } | ||
Brian E. Granger
|
r14015 | return cont; | ||
}; | ||||
TextCell.prototype.execute = function () { | ||||
this.render(); | ||||
Brian E. Granger
|
r14014 | }; | ||
Brian E. Granger
|
r4349 | |||
Matthias BUSSONNIER
|
r8709 | /** | ||
* setter: {{#crossLink "TextCell/set_text"}}{{/crossLink}} | ||||
* @method get_text | ||||
* @retrun {string} CodeMirror current text value | ||||
*/ | ||||
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 | |||
Matthias BUSSONNIER
|
r8709 | /** | ||
* @param {string} text - Codemiror text value | ||||
* @see TextCell#get_text | ||||
* @method set_text | ||||
* */ | ||||
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 | |||
Matthias BUSSONNIER
|
r8709 | /** | ||
* setter :{{#crossLink "TextCell/set_rendered"}}{{/crossLink}} | ||||
* @method get_rendered | ||||
* @return {html} html of rendered element | ||||
* */ | ||||
Brian E. Granger
|
r4513 | TextCell.prototype.get_rendered = function() { | ||
Brian Granger
|
r4508 | return this.element.find('div.text_cell_render').html(); | ||
}; | ||||
Matthias BUSSONNIER
|
r8709 | /** | ||
* @method set_rendered | ||||
*/ | ||||
Brian Granger
|
r4508 | TextCell.prototype.set_rendered = function(text) { | ||
this.element.find('div.text_cell_render').html(text); | ||||
Brian E. Granger
|
r4352 | }; | ||
Brian E. Granger
|
r4349 | |||
Matthias BUSSONNIER
|
r8711 | /** | ||
* Create Text cell from JSON | ||||
Matthias BUSSONNIER
|
r8709 | * @param {json} data - JSON serialized text-cell | ||
Matthias BUSSONNIER
|
r8711 | * @method fromJSON | ||
Matthias BUSSONNIER
|
r8709 | */ | ||
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(); | ||||
Jonathan Frederic
|
r15407 | // TODO: This HTML needs to be treated as potentially dangerous | ||
// user input and should be handled before set_rendered. | ||||
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 | |||
Matthias BUSSONNIER
|
r8711 | /** Generate JSON from cell | ||
* @return {object} cell data serialised to json | ||||
*/ | ||||
Brian Granger
|
r4508 | TextCell.prototype.toJSON = function () { | ||
MinRK
|
r7523 | var data = IPython.Cell.prototype.toJSON.apply(this); | ||
Brian Granger
|
r5943 | data.source = this.get_text(); | ||
MinRK
|
r13668 | if (data.source == this.placeholder) { | ||
data.source = ""; | ||||
} | ||||
Brian E. Granger
|
r4484 | return data; | ||
Brian E. Granger
|
r4349 | }; | ||
Brian Granger
|
r4508 | |||
Matthias BUSSONNIER
|
r8709 | /** | ||
Matthias BUSSONNIER
|
r8711 | * @class MarkdownCell | ||
* @constructor MarkdownCell | ||||
MinRK
|
r11288 | * @extends IPython.HTMLCell | ||
Matthias BUSSONNIER
|
r8709 | */ | ||
Matthias BUSSONNIER
|
r10165 | var MarkdownCell = function (options) { | ||
MinRK
|
r13668 | options = this.mergeopt(MarkdownCell, options); | ||
Matthias BUSSONNIER
|
r10165 | |||
Brian Granger
|
r4508 | this.cell_type = 'markdown'; | ||
MinRK
|
r13668 | TextCell.apply(this, [options]); | ||
Brian Granger
|
r4508 | }; | ||
Matthias BUSSONNIER
|
r10165 | MarkdownCell.options_default = { | ||
cm_config: { | ||||
MinRK
|
r10539 | mode: 'gfm' | ||
Matthias BUSSONNIER
|
r10165 | }, | ||
placeholder: "Type *Markdown* and LaTeX: $\\alpha^2$" | ||||
Jonathan Frederic
|
r15497 | }; | ||
Matthias BUSSONNIER
|
r10165 | |||
Brian Granger
|
r4508 | MarkdownCell.prototype = new TextCell(); | ||
Matthias BUSSONNIER
|
r8711 | /** | ||
* @method render | ||||
*/ | ||||
Brian Granger
|
r4508 | MarkdownCell.prototype.render = function () { | ||
Brian E. Granger
|
r14015 | var cont = IPython.TextCell.prototype.render.apply(this); | ||
if (cont) { | ||||
Brian Granger
|
r5943 | var text = this.get_text(); | ||
Jessica B. Hamrick
|
r11843 | var math = null; | ||
Stefan van der Walt
|
r5479 | if (text === "") { text = this.placeholder; } | ||
Jessica B. Hamrick
|
r11843 | var text_and_math = IPython.mathjaxutils.remove_math(text); | ||
text = text_and_math[0]; | ||||
math = text_and_math[1]; | ||||
MinRK
|
r10536 | var html = marked.parser(marked.lexer(text)); | ||
MinRK
|
r15637 | html = IPython.mathjaxutils.replace_math(html, math); | ||
html = security.sanitize_html(html); | ||||
html = $(html); | ||||
// links in markdown cells should open in new tabs | ||||
html.find("a[href]").not('[href^="#"]').attr("target", "_blank"); | ||||
this.set_rendered(html); | ||||
MinRK
|
r15552 | this.element.find('div.input_area').hide(); | ||
Aron Ahmadia
|
r8662 | this.element.find("div.text_cell_render").show(); | ||
MinRK
|
r15637 | this.typeset(); | ||
Jonathan Frederic
|
r15497 | } | ||
Brian E. Granger
|
r14015 | return cont; | ||
Brian Granger
|
r4508 | }; | ||
MinRK
|
r6248 | // RawCell | ||
Brian Granger
|
r4508 | |||
Matthias BUSSONNIER
|
r8711 | /** | ||
* @class RawCell | ||||
* @constructor RawCell | ||||
MinRK
|
r11288 | * @extends IPython.TextCell | ||
Matthias BUSSONNIER
|
r8709 | */ | ||
Matthias BUSSONNIER
|
r10165 | var RawCell = function (options) { | ||
Brian E. Granger
|
r14016 | |||
Jonathan Frederic
|
r15497 | options = this.mergeopt(RawCell,options); | ||
MinRK
|
r13668 | TextCell.apply(this, [options]); | ||
Brian E. Granger
|
r14016 | this.cell_type = 'raw'; | ||
// RawCell should always hide its rendered div | ||||
this.element.find('div.text_cell_render').hide(); | ||||
Brian Granger
|
r4508 | }; | ||
Matthias BUSSONNIER
|
r10165 | RawCell.options_default = { | ||
Paul Ivanov
|
r15859 | placeholder : "Write raw LaTeX or other formats here, for use with nbconvert. " + | ||
"It will not be rendered in the notebook. " + | ||||
MinRK
|
r13668 | "When passing through nbconvert, a Raw Cell's content is added to the output unmodified." | ||
Matthias BUSSONNIER
|
r10165 | }; | ||
MinRK
|
r6248 | RawCell.prototype = new TextCell(); | ||
Brian Granger
|
r4508 | |||
Brian E. Granger
|
r14015 | /** @method bind_events **/ | ||
RawCell.prototype.bind_events = function () { | ||||
TextCell.prototype.bind_events.apply(this); | ||||
Jonathan Frederic
|
r15497 | var that = this; | ||
Brian E. Granger
|
r14015 | this.element.focusout(function() { | ||
that.auto_highlight(); | ||||
Paul Ivanov
|
r15858 | that.render(); | ||
Brian E. Granger
|
r14015 | }); | ||
Paul Ivanov
|
r15768 | |||
this.code_mirror.on('focus', function() { that.unrender(); }); | ||||
Brian E. Granger
|
r14015 | }; | ||
Matthias BUSSONNIER
|
r8711 | /** | ||
* Trigger autodetection of highlight scheme for current cell | ||||
* @method auto_highlight | ||||
*/ | ||||
Matthias BUSSONNIER
|
r8202 | RawCell.prototype.auto_highlight = function () { | ||
this._auto_highlight(IPython.config.raw_cell_highlight); | ||||
}; | ||||
Brian Granger
|
r4508 | |||
Matthias BUSSONNIER
|
r8711 | /** @method render **/ | ||
MinRK
|
r6248 | RawCell.prototype.render = function () { | ||
Paul Ivanov
|
r15768 | var cont = IPython.TextCell.prototype.render.apply(this); | ||
if (cont){ | ||||
var text = this.get_text(); | ||||
if (text === "") { text = this.placeholder; } | ||||
this.set_text(text); | ||||
Paul Ivanov
|
r15858 | this.element.removeClass('rendered'); | ||
Brian E. Granger
|
r14015 | } | ||
Paul Ivanov
|
r15768 | return cont; | ||
Brian Granger
|
r6017 | }; | ||
Matthias BUSSONNIER
|
r8714 | /** | ||
Matthias BUSSONNIER
|
r8711 | * @class HeadingCell | ||
MinRK
|
r11288 | * @extends IPython.TextCell | ||
Matthias BUSSONNIER
|
r8711 | */ | ||
/** | ||||
* @constructor HeadingCell | ||||
MinRK
|
r11288 | * @extends IPython.TextCell | ||
Matthias BUSSONNIER
|
r8709 | */ | ||
Matthias BUSSONNIER
|
r10165 | var HeadingCell = function (options) { | ||
MinRK
|
r13668 | options = this.mergeopt(HeadingCell, options); | ||
Matthias BUSSONNIER
|
r10165 | |||
MinRK
|
r13668 | this.level = 1; | ||
this.cell_type = 'heading'; | ||||
Matthias BUSSONNIER
|
r10165 | TextCell.apply(this, [options]); | ||
Matthias BUSSONNIER
|
r8711 | /** | ||
* heading level of the cell, use getter and setter to access | ||||
* @property level | ||||
*/ | ||||
Brian Granger
|
r6018 | }; | ||
Matthias BUSSONNIER
|
r10165 | HeadingCell.options_default = { | ||
placeholder: "Type Heading Here" | ||||
}; | ||||
Brian Granger
|
r6018 | |||
HeadingCell.prototype = new TextCell(); | ||||
Matthias BUSSONNIER
|
r8711 | /** @method fromJSON */ | ||
MinRK
|
r6156 | HeadingCell.prototype.fromJSON = function (data) { | ||
Jonathan Frederic
|
r15497 | if (data.level !== undefined){ | ||
MinRK
|
r6156 | this.level = data.level; | ||
} | ||||
Matthias BUSSONNIER
|
r10165 | TextCell.prototype.fromJSON.apply(this, arguments); | ||
MinRK
|
r6156 | }; | ||
Matthias BUSSONNIER
|
r8711 | /** @method toJSON */ | ||
MinRK
|
r6156 | HeadingCell.prototype.toJSON = function () { | ||
Matthias BUSSONNIER
|
r10165 | var data = TextCell.prototype.toJSON.apply(this); | ||
MinRK
|
r6156 | data.level = this.get_level(); | ||
return data; | ||||
}; | ||||
MinRK
|
r12510 | /** | ||
* can the cell be split into two cells | ||||
* @method is_splittable | ||||
**/ | ||||
HeadingCell.prototype.is_splittable = function () { | ||||
return false; | ||||
}; | ||||
/** | ||||
* can the cell be merged with other cells | ||||
* @method is_mergeable | ||||
**/ | ||||
HeadingCell.prototype.is_mergeable = function () { | ||||
return false; | ||||
}; | ||||
MinRK
|
r6156 | |||
Matthias BUSSONNIER
|
r8711 | /** | ||
* Change heading level of cell, and re-render | ||||
* @method set_level | ||||
*/ | ||||
Brian Granger
|
r6019 | HeadingCell.prototype.set_level = function (level) { | ||
this.level = level; | ||||
if (this.rendered) { | ||||
this.rendered = false; | ||||
this.render(); | ||||
Jonathan Frederic
|
r15497 | } | ||
Brian Granger
|
r6019 | }; | ||
Matthias BUSSONNIER
|
r8709 | /** The depth of header cell, based on html (h1 to h6) | ||
Matthias BUSSONNIER
|
r8711 | * @method get_level | ||
Matthias BUSSONNIER
|
r8709 | * @return {integer} level - for 1 to 6 | ||
*/ | ||||
Brian Granger
|
r6019 | HeadingCell.prototype.get_level = function () { | ||
return this.level; | ||||
}; | ||||
MinRK
|
r11288 | HeadingCell.prototype.set_rendered = function (html) { | ||
this.element.find("div.text_cell_render").html(html); | ||||
Brian Granger
|
r6019 | }; | ||
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 () { | ||||
Brian E. Granger
|
r14015 | var cont = IPython.TextCell.prototype.render.apply(this); | ||
if (cont) { | ||||
Brian Granger
|
r6018 | var text = this.get_text(); | ||
Jessica B. Hamrick
|
r11843 | var math = null; | ||
MinRK
|
r11290 | // Markdown headings must be a single line | ||
text = text.replace(/\n/g, ' '); | ||||
Brian Granger
|
r6018 | if (text === "") { text = this.placeholder; } | ||
MinRK
|
r11288 | text = Array(this.level + 1).join("#") + " " + text; | ||
Matthias BUSSONNIER
|
r11526 | var text_and_math = IPython.mathjaxutils.remove_math(text); | ||
Jessica B. Hamrick
|
r11843 | text = text_and_math[0]; | ||
math = text_and_math[1]; | ||||
MinRK
|
r11288 | var html = marked.parser(marked.lexer(text)); | ||
MinRK
|
r15637 | html = IPython.mathjaxutils.replace_math(html, math); | ||
html = security.sanitize_html(html); | ||||
var h = $(html); | ||||
// add id and linkback anchor | ||||
var hash = h.text().replace(/ /g, '-'); | ||||
h.attr('id', hash); | ||||
h.append( | ||||
$('<a/>') | ||||
.addClass('anchor-link') | ||||
.attr('href', '#' + hash) | ||||
.text('ΒΆ') | ||||
); | ||||
this.set_rendered(h); | ||||
MinRK
|
r15698 | this.element.find('div.input_area').hide(); | ||
Brian Granger
|
r6018 | this.element.find("div.text_cell_render").show(); | ||
MinRK
|
r15637 | this.typeset(); | ||
} | ||||
Brian E. Granger
|
r14015 | return cont; | ||
Brian Granger
|
r6018 | }; | ||
Brian Granger
|
r4508 | IPython.TextCell = TextCell; | ||
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 | |||