From ae04a0df43b9166aa138c448b585ecc2c4b81a5f 2013-07-16 20:00:32 From: Min RK Date: 2013-07-16 20:00:32 Subject: [PATCH] Merge pull request #3601 from minrk/headingmath2 Markdown in heading cells (take 2) closes #3053 closes #3597 --- diff --git a/IPython/html/static/notebook/js/textcell.js b/IPython/html/static/notebook/js/textcell.js index 848d92e..c9ed7c3 100644 --- a/IPython/html/static/notebook/js/textcell.js +++ b/IPython/html/static/notebook/js/textcell.js @@ -27,7 +27,7 @@ var IPython = (function (IPython) { * * @class TextCell * @constructor TextCell - * @extend Ipython.Cell + * @extend IPython.Cell * @param {object|undefined} [options] * @param [options.cm_config] {object} config to pass to CodeMirror, will extend/overwrite default config * @param [options.placeholder] {string} default string to use when souce in empty for rendering (only use in some TextCell subclass) @@ -285,7 +285,7 @@ var IPython = (function (IPython) { /** * @class MarkdownCell * @constructor MarkdownCell - * @extends Ipython.HtmlCell + * @extends IPython.HTMLCell */ var MarkdownCell = function (options) { var options = options || {}; @@ -342,7 +342,7 @@ var IPython = (function (IPython) { /** * @class RawCell * @constructor RawCell - * @extends Ipython.TextCell + * @extends IPython.TextCell */ var RawCell = function (options) { @@ -437,12 +437,12 @@ var IPython = (function (IPython) { /** * @class HeadingCell - * @extends Ipython.TextCell + * @extends IPython.TextCell */ /** * @constructor HeadingCell - * @extends Ipython.TextCell + * @extends IPython.TextCell */ var HeadingCell = function (options) { @@ -501,24 +501,8 @@ var IPython = (function (IPython) { }; - HeadingCell.prototype.set_rendered = function (text) { - var r = this.element.find("div.text_cell_render"); - r.empty(); - var link = text.replace(/ /g, '_'); - r.append( - $('') - .append( - $('') - .addClass('heading-anchor') - .attr('id', link) - .html(text) - ).append( - $('') - .addClass('anchor-link') - .attr('href', '#' + link) - .text('¶') - ) - ); + HeadingCell.prototype.set_rendered = function (html) { + this.element.find("div.text_cell_render").html(html); }; @@ -531,8 +515,24 @@ var IPython = (function (IPython) { HeadingCell.prototype.render = function () { if (this.rendered === false) { var text = this.get_text(); + // Markdown headings must be a single line + text = text.replace(/\n/g, ' '); if (text === "") { text = this.placeholder; } - this.set_rendered(text); + text = Array(this.level + 1).join("#") + " " + text; + text = IPython.mathjaxutils.remove_math(text); + var html = marked.parser(marked.lexer(text)); + var h = $(IPython.mathjaxutils.replace_math(html)); + // add id and linkback anchor + var hash = h.text().replace(/ /g, '-'); + h.attr('id', hash); + h.append( + $('') + .addClass('anchor-link') + .attr('href', '#' + hash) + .text('¶') + ); + + this.set_rendered(h); this.typeset(); this.element.find('div.text_cell_input').hide(); this.element.find("div.text_cell_render").show(); diff --git a/IPython/html/static/notebook/less/textcell.less b/IPython/html/static/notebook/less/textcell.less index e104e9c..67382b4 100644 --- a/IPython/html/static/notebook/less/textcell.less +++ b/IPython/html/static/notebook/less/textcell.less @@ -19,11 +19,6 @@ div.text_cell_render { color: @textColor; } -a.heading-anchor { - text-decoration: none; - color: inherit; -} - a.anchor-link:link { text-decoration: none; padding: 0px 20px; diff --git a/IPython/html/static/style/style.min.css b/IPython/html/static/style/style.min.css index 08581b0..ed96732 100644 --- a/IPython/html/static/style/style.min.css +++ b/IPython/html/static/style/style.min.css @@ -1559,7 +1559,6 @@ span#checkpoint_status,span#autosave_status{font-size:small;} @media (max-width:767px){span#save_widget{font-size:small;} span#checkpoint_status,span#autosave_status{font-size:x-small;}}@media (max-width:767px){span#checkpoint_status,span#autosave_status{display:none;}}@media (min-width:768px) and (max-width:979px){span#checkpoint_status{display:none;} span#autosave_status{font-size:x-small;}}div.text_cell{padding:5px 5px 5px 5px;} div.text_cell_input{color:#000000;border:1px solid #cfcfcf;border-radius:4px;background:#f7f7f7;} div.text_cell_render{outline:none;resize:none;width:inherit;border-style:none;padding:5px;color:#000000;} -a.heading-anchor{text-decoration:none;color:inherit;} a.anchor-link:link{text-decoration:none;padding:0px 20px;visibility:hidden;} h1:hover .anchor-link,h2:hover .anchor-link,h3:hover .anchor-link,h4:hover .anchor-link,h5:hover .anchor-link,h6:hover .anchor-link{visibility:visible;} .toolbar{padding:0px 10px;margin-top:-5px;}.toolbar select,.toolbar label{width:auto;height:26px;vertical-align:middle;margin-right:2px;margin-bottom:0px;display:inline;font-size:92%;margin-left:0.3em;margin-right:0.3em;padding:0px;padding-top:3px;} diff --git a/IPython/nbconvert/exporters/exporter.py b/IPython/nbconvert/exporters/exporter.py index cc19ee5..6c9475b 100755 --- a/IPython/nbconvert/exporters/exporter.py +++ b/IPython/nbconvert/exporters/exporter.py @@ -57,6 +57,8 @@ default_filters = { 'rm_ansi': filters.remove_ansi, 'rm_dollars': filters.strip_dollars, 'rm_fake': filters.rm_fake, + 'html_text' : filters.html_text, + 'add_anchor': filters.add_anchor, 'ansi2latex': filters.ansi2latex, 'rm_math_space': filters.rm_math_space, 'wrap': filters.wrap diff --git a/IPython/nbconvert/filters/markdown.py b/IPython/nbconvert/filters/markdown.py index eb842bd..cd4b5a2 100755 --- a/IPython/nbconvert/filters/markdown.py +++ b/IPython/nbconvert/filters/markdown.py @@ -52,7 +52,7 @@ def markdown2latex(source): def markdown2html(source): """Convert a markdown string to HTML via pandoc""" - return pandoc(source, 'markdown', 'html') + return pandoc(source, 'markdown', 'html', extra_args=['--mathjax']) def markdown2rst(source): """Convert a markdown string to LaTeX via pandoc. diff --git a/IPython/nbconvert/filters/strings.py b/IPython/nbconvert/filters/strings.py index 5464e7d..c886804 100755 --- a/IPython/nbconvert/filters/strings.py +++ b/IPython/nbconvert/filters/strings.py @@ -1,3 +1,4 @@ +# coding: utf-8 """String filters. Contains a collection of useful string manipulation filters for use in Jinja @@ -17,6 +18,9 @@ templates. import re import textwrap +from xml.etree import ElementTree + +from IPython.utils import py3compat #----------------------------------------------------------------------------- # Functions @@ -24,6 +28,8 @@ import textwrap __all__ = [ 'wrap', + 'html_text', + 'add_anchor', 'strip_dollars', 'rm_fake', 'python_comment', @@ -50,6 +56,35 @@ def wrap(text, width=100): return '\n'.join(wrpd) +def html_text(element): + """extract inner text from html + + Analog of jQuery's $(element).text() + """ + if not isinstance(element, (ElementTree.ElementTree, ElementTree.Element)): + element = ElementTree.fromstring(element) + + text = element.text or "" + for child in element: + text += html_text(child) + text += (element.tail or "") + return text + + +def add_anchor(html): + """Add an anchor-link to an html header tag + + For use in heading cells + """ + h = ElementTree.fromstring(py3compat.cast_bytes_py2(html)) + link = html_text(h).replace(' ', '-') + h.set('id', link) + a = ElementTree.Element("a", {"class" : "anchor-link", "href" : "#" + link}) + a.text = u'¶' + h.append(a) + return ElementTree.tostring(h) + + def strip_dollars(text): """ Remove all dollar symbols from text diff --git a/IPython/nbconvert/templates/basichtml.tpl b/IPython/nbconvert/templates/basichtml.tpl index 1785cd4..2fd6f0e 100644 --- a/IPython/nbconvert/templates/basichtml.tpl +++ b/IPython/nbconvert/templates/basichtml.tpl @@ -59,12 +59,7 @@ Out[{{cell.prompt_number}}]: {% block headingcell scoped %}
- -{% set source = cell.source | replace(' ','_') %} - - {{cell.source | markdown| rm_fake}} - - + {{("#" * cell.level + cell.source) | replace('\n', ' ') | markdown | rm_fake | add_anchor }}
{% endblock headingcell %} diff --git a/IPython/nbconvert/templates/latex/base.tplx b/IPython/nbconvert/templates/latex/base.tplx index 4b6cae0..a7223d7 100644 --- a/IPython/nbconvert/templates/latex/base.tplx +++ b/IPython/nbconvert/templates/latex/base.tplx @@ -87,22 +87,7 @@ it introduces a new line ((* endblock markdowncell *)) ((* block headingcell scoped -*)) - \ - ((*- if cell.level == 1 -*)) - ((* block h1 -*))section((* endblock h1 -*)) - ((*- elif cell.level == 2 -*)) - ((* block h2 -*))subsection((* endblock h2 -*)) - ((*- elif cell.level == 3 -*)) - ((* block h3 -*))subsubsection((* endblock h3 -*)) - ((*- elif cell.level == 4 -*)) - ((* block h4 -*))paragraph((* endblock h4 -*)) - ((*- elif cell.level == 5 -*)) - ((* block h5 -*))subparagraph((* endblock h5 -*)) - ((*- elif cell.level == 6 -*)) - ((* block h6 -*))subparagraph((* endblock h6 -*)) - ((= 6th level not available in standard latex =)) - - ((*- endif -*)){((( cell.source | markdown2latex )))} + ((( ('#' * cell.level + cell.source) | replace('\n', ' ') | markdown2latex ))) ((* endblock headingcell *)) ((* block rawcell scoped *)) diff --git a/IPython/nbconvert/templates/markdown.tpl b/IPython/nbconvert/templates/markdown.tpl index 732192a..9c12c4e 100644 --- a/IPython/nbconvert/templates/markdown.tpl +++ b/IPython/nbconvert/templates/markdown.tpl @@ -61,7 +61,7 @@ $$ {% block headingcell scoped %} -{{ '#' * cell.level }} {{ cell.source }} +{{ '#' * cell.level }} {{ cell.source | replace('\n', ' ') }} {% endblock headingcell %} diff --git a/IPython/nbconvert/templates/python.tpl b/IPython/nbconvert/templates/python.tpl index 49eaa43..484a64d 100644 --- a/IPython/nbconvert/templates/python.tpl +++ b/IPython/nbconvert/templates/python.tpl @@ -44,7 +44,7 @@ it introduces a new line {% endblock markdowncell %} {% block headingcell scoped %} -{{ '#' * cell.level }}{{ cell.source | pycomment}} +{{ '#' * cell.level }}{{ cell.source | replace('\n', ' ') | pycomment}} {% endblock headingcell %} {% block rawcell scoped %} diff --git a/IPython/nbconvert/templates/rst.tpl b/IPython/nbconvert/templates/rst.tpl index 7bc1150..d791cfb 100644 --- a/IPython/nbconvert/templates/rst.tpl +++ b/IPython/nbconvert/templates/rst.tpl @@ -63,21 +63,7 @@ Out[{{cell.prompt_number}}]:{% endif %}{% endblock output_prompt %} {% endblock markdowncell %} {% block headingcell scoped %} -{%- set len = cell.source|length -%} -{{ cell.source | markdown2rst }} -{% if cell.level == 1 %} -{{- '=' * len }} -{%- elif cell.level == 2 %} -{{- '-' * len }} -{%- elif cell.level == 3 %} -{{- '~' * len }} -{%- elif cell.level == 4 %} -{{- '.' * len }} -{%- elif cell.level == 5 %} -{{- '\\' * len }} -{%- elif cell.level == 6 %} -{{- '`' * len }} -{% endif %} +{{ ("#" * cell.level + cell.source) | replace('\n', ' ') | markdown2rst }} {% endblock headingcell %} diff --git a/IPython/nbconvert/utils/pandoc.py b/IPython/nbconvert/utils/pandoc.py index c914cee..3823d80 100644 --- a/IPython/nbconvert/utils/pandoc.py +++ b/IPython/nbconvert/utils/pandoc.py @@ -24,7 +24,7 @@ from IPython.utils.py3compat import cast_bytes # Classes and functions #----------------------------------------------------------------------------- -def pandoc(source, fmt, to, encoding='utf-8'): +def pandoc(source, fmt, to, extra_args=None, encoding='utf-8'): """Convert an input string in format `from` to format `to` via pandoc. This function will raise an error if pandoc is not installed. @@ -44,7 +44,10 @@ def pandoc(source, fmt, to, encoding='utf-8'): out : unicode Output as returned by pandoc. """ - p = subprocess.Popen(['pandoc', '-f', fmt, '-t', to], + command = ['pandoc', '-f', fmt, '-t', to] + if extra_args: + command.extend(extra_args) + p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE ) out, _ = p.communicate(cast_bytes(source, encoding))