From 07dbfbc23f336487f93cc81474d3d0eff8225244 2014-01-26 23:58:11 From: Min RK Date: 2014-01-26 23:58:11 Subject: [PATCH] Merge pull request #4655 from minrk/marked-nbconvert prefer marked to pandoc for markdown2html --- diff --git a/IPython/html/static/base/js/utils.js b/IPython/html/static/base/js/utils.js index b3f7b3a..90eca6e 100644 --- a/IPython/html/static/base/js/utils.js +++ b/IPython/html/static/base/js/utils.js @@ -448,6 +448,10 @@ IPython.utils = (function (IPython) { // http://stackoverflow.com/questions/2400935/browser-detection-in-javascript var browser = (function() { + if (typeof navigator === 'undefined') { + // navigator undefined in node + return 'None'; + } var N= navigator.appName, ua= navigator.userAgent, tem; var M= ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i); if (M && (tem= ua.match(/version\/([\.\d]+)/i))!= null) M[2]= tem[1]; diff --git a/IPython/html/static/notebook/js/mathjaxutils.js b/IPython/html/static/notebook/js/mathjaxutils.js index e076901..62044dc 100644 --- a/IPython/html/static/notebook/js/mathjaxutils.js +++ b/IPython/html/static/notebook/js/mathjaxutils.js @@ -106,12 +106,11 @@ IPython.mathjaxutils = (function (IPython) { // math, then push the math string onto the storage array. // The preProcess function is called on all blocks if it has been passed in var process_math = function (i, j, pre_process, math, blocks) { - var hub = MathJax.Hub; var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&") // use HTML entity for & .replace(//g, ">") // use HTML entity for > ; - if (hub.Browser.isMSIE) { + if (IPython.utils.browser === 'msie') { block = block.replace(/(%[^\n]*)\n/g, "$1
\n"); } while (j > i) { @@ -133,10 +132,6 @@ IPython.mathjaxutils = (function (IPython) { // (which will be a paragraph). // var remove_math = function (text) { - if (!window.MathJax) { - return [text, null]; - } - var math = []; // stores math strings for later var start; var end; @@ -241,9 +236,6 @@ IPython.mathjaxutils = (function (IPython) { // and clear the math array (no need to keep it around). // var replace_math = function (text, math) { - if (!window.MathJax) { - return text; - } text = text.replace(/@@(\d+)@@/g, function (match, n) { return math[n]; }); diff --git a/IPython/nbconvert/filters/markdown.py b/IPython/nbconvert/filters/markdown.py index cd4b5a2..5bfd4e6 100755 --- a/IPython/nbconvert/filters/markdown.py +++ b/IPython/nbconvert/filters/markdown.py @@ -16,21 +16,33 @@ markdown within Jinja templates. from __future__ import print_function # Stdlib imports -import sys +import os import subprocess +from io import TextIOWrapper, BytesIO +# IPython imports from IPython.nbconvert.utils.pandoc import pandoc +from IPython.nbconvert.utils.exceptions import ConversionException +from IPython.utils.process import find_cmd, FindCmdError +from IPython.utils.py3compat import cast_bytes #----------------------------------------------------------------------------- # Functions #----------------------------------------------------------------------------- +marked = os.path.join(os.path.dirname(__file__), "marked.js") __all__ = [ 'markdown2html', + 'markdown2html_pandoc', + 'markdown2html_marked', 'markdown2latex', - 'markdown2rst' + 'markdown2rst', ] +class NodeJSMissing(ConversionException): + """Exception raised when node.js is missing.""" + pass + def markdown2latex(source): """Convert a markdown string to LaTeX via pandoc. @@ -49,11 +61,26 @@ def markdown2latex(source): """ return pandoc(source, 'markdown', 'latex') - -def markdown2html(source): +def markdown2html_pandoc(source): """Convert a markdown string to HTML via pandoc""" return pandoc(source, 'markdown', 'html', extra_args=['--mathjax']) +def markdown2html_marked(source, encoding='utf-8'): + """Convert a markdown string to HTML via marked""" + command = ['node', marked] + try: + p = subprocess.Popen(command, + stdin=subprocess.PIPE, stdout=subprocess.PIPE + ) + except OSError as e: + raise NodeJSMissing( + "The command '%s' returned an error: %s.\n" % (" ".join(command), e) + + "Please check that Node.js is installed." + ) + out, _ = p.communicate(cast_bytes(source, encoding)) + out = TextIOWrapper(BytesIO(out), encoding, 'replace').read() + return out.rstrip('\n') + def markdown2rst(source): """Convert a markdown string to LaTeX via pandoc. @@ -72,3 +99,9 @@ def markdown2rst(source): """ return pandoc(source, 'markdown', 'rst') +try: + find_cmd('node') +except FindCmdError: + markdown2html = markdown2html_pandoc +else: + markdown2html = markdown2html_marked diff --git a/IPython/nbconvert/filters/marked.js b/IPython/nbconvert/filters/marked.js new file mode 100644 index 0000000..2816630 --- /dev/null +++ b/IPython/nbconvert/filters/marked.js @@ -0,0 +1,54 @@ +// Node.js script for markdown to html conversion +// This applies the same math extraction and marked settings +// that we use in the live notebook. + +// IPython static_path dir relative to here: +var static_path = __dirname + "/../../html/static/"; + +var fs = require('fs'); +var IPython; +// marked can be loaded with require, +// the others must be execfiled +var marked = require(static_path + 'components/marked/lib/marked.js'); + +eval(fs.readFileSync(static_path + "components/highlight.js/build/highlight.pack.js", 'utf8')); +eval(fs.readFileSync(static_path + "base/js/namespace.js", 'utf8')); + +eval(fs.readFileSync(static_path + "base/js/utils.js", 'utf8')); +eval(fs.readFileSync(static_path + "notebook/js/mathjaxutils.js", 'utf8')); + +// this is copied from notebook.main. Should it be moved somewhere we can reuse it? +marked.setOptions({ + gfm : true, + tables: true, + langPrefix: "language-", + highlight: function(code, lang) { + if (!lang) { + // no language, no highlight + return code; + } + var highlighted; + try { + highlighted = hljs.highlight(lang, code, false); + } catch(err) { + highlighted = hljs.highlightAuto(code); + } + return highlighted.value; + } +}); + +// read the markdown from stdin +var md=''; +process.stdin.on("data", function (data) { + md += data; +}); + +// perform the md2html transform once stdin is complete +process.stdin.on("end", function () { + var text_and_math = IPython.mathjaxutils.remove_math(md); + var text = text_and_math[0]; + var math = text_and_math[1]; + var html = marked.parser(marked.lexer(text)); + html = IPython.mathjaxutils.replace_math(html, math); + process.stdout.write(html); +}); diff --git a/setupbase.py b/setupbase.py index 5afbdfe..b5c9b20 100644 --- a/setupbase.py +++ b/setupbase.py @@ -164,6 +164,7 @@ def find_package_data(): 'IPython.qt.console' : ['resources/icon/*.svg'], 'IPython.nbconvert' : nbconvert_templates + ['tests/files/*.*', 'exporters/tests/files/*.*'], + 'IPython.nbconvert.filters' : ['marked.js'], 'IPython.nbformat' : ['tests/*.ipynb'] } return package_data