diff --git a/IPython/frontend/html/notebook/handlers.py b/IPython/frontend/html/notebook/handlers.py index b60ad95..2f53693 100644 --- a/IPython/frontend/html/notebook/handlers.py +++ b/IPython/frontend/html/notebook/handlers.py @@ -6,11 +6,17 @@ import json import logging +import os import urllib from tornado import web from tornado import websocket +try: + from docutils.core import publish_string +except ImportError: + publish_string = None + #----------------------------------------------------------------------------- # Top-level handlers @@ -156,3 +162,51 @@ class NotebookHandler(web.RequestHandler): self.set_status(204) self.finish() +#----------------------------------------------------------------------------- +# RST web service handlers +#----------------------------------------------------------------------------- + +_rst_header = """======== +Heading1 +======== + +Heading2 +======== + +Heading3 +-------- + +Heading4 +^^^^^^^^ + +""" + +class RSTHandler(web.RequestHandler): + + def post(self): + if publish_string is None: + raise web.HTTPError(503) + body = self.request.body.strip() + source = _rst_header + body + template_path=os.path.join(os.path.dirname(__file__), u'templates', u'rst_template.html') + print template_path + defaults = {'file_insertion_enabled': 0, + 'raw_enabled': 0, + '_disable_config': 1, + 'stylesheet_path': 0, + 'initial_header_level': 3, + 'template': template_path + } + try: + html = publish_string(source, writer_name='html', + settings_overrides=defaults + ) + except: + raise web.HTTPError(400) + print html +# html = '\n'.join(html.split('\n')[7:-3]) +# print html + self.set_header('Content-Type', 'text/html') + self.finish(html) + + diff --git a/IPython/frontend/html/notebook/notebookapp.py b/IPython/frontend/html/notebook/notebookapp.py index 4b59303..b0a78e5 100644 --- a/IPython/frontend/html/notebook/notebookapp.py +++ b/IPython/frontend/html/notebook/notebookapp.py @@ -32,7 +32,7 @@ from .sessionmanager import SessionManager from .handlers import ( NBBrowserHandler, NewHandler, NamedNotebookHandler, MainKernelHandler, KernelHandler, KernelActionHandler, ZMQStreamHandler, - NotebookRootHandler, NotebookHandler + NotebookRootHandler, NotebookHandler, RSTHandler ) from .notebookmanager import NotebookManager @@ -75,7 +75,8 @@ class NotebookWebApplication(web.Application): (r"/kernels/%s/iopub" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='iopub')), (r"/kernels/%s/shell" % _kernel_id_regex, ZMQStreamHandler, dict(stream_name='shell')), (r"/notebooks", NotebookRootHandler), - (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler) + (r"/notebooks/%s" % _notebook_id_regex, NotebookHandler), + (r"/rstservice/render", RSTHandler) ] settings = dict( template_path=os.path.join(os.path.dirname(__file__), "templates"), diff --git a/IPython/frontend/html/notebook/static/css/notebook.css b/IPython/frontend/html/notebook/static/css/notebook.css index a0d7f5d..1555d00 100644 --- a/IPython/frontend/html/notebook/static/css/notebook.css +++ b/IPython/frontend/html/notebook/static/css/notebook.css @@ -225,25 +225,42 @@ div.html_cell_render { color: black; } -div.html_cell_render em {font-style: italic;} -div.html_cell_render strong {font-weight: bold;} -div.html_cell_render u {text-decoration: underline;} -div.html_cell_render :link { text-decoration: underline } -div.html_cell_render :visited { text-decoration: underline } -div.html_cell_render h1 {font-size: 197%; margin: .67em 0; font-weight: bold;} -div.html_cell_render h2 {font-size: 153.9%; margin: .75em 0; font-weight: bold;} -div.html_cell_render h3 {font-size: 116%; margin: .83em 0; font-weight: bold;} -div.html_cell_render h4 {margin: 1.12em 0; font-weight: bold;} -div.html_cell_render h5 {font-size: 85%.; margin: 1.5em 0; font-weight: bold;} -div.html_cell_render h6 {font-size: 77%; margin: 1.67em 0; font-weight: bold;} -div.html_cell_render ul {list-style:disc; margin-left: 40px;} -div.html_cell_render ul ul {list-style:square; margin-left: 40px;} -div.html_cell_render ul ul ul {list-style:circle; margin-left: 40px;} -div.html_cell_render ol {list-style:upper-roman; margin-left: 40px;} -div.html_cell_render ol ol {list-style:upper-alpha;} -div.html_cell_render ol ol ol {list-style:decimal;} -div.html_cell_render ol ol ol ol {list-style:lower-alpha;} -div.html_cell_render ol ol ol ol ol {list-style:lower-roman;} +div.rst_cell_input { + color: black; +} + +div.rst_cell_render { + font-family: "Helvetica Neue", Arial, Helvetica, Geneva, sans-serif; + /* Slightly bigger than the rest of the notebook */ + font-size: 116%; + outline: none; + resize: none; + width: inherit; + border-style: none; + padding: 5px; + color: black; +} + +.rendered_html {color: black;} +.rendered_html em {font-style: italic;} +.rendered_html strong {font-weight: bold;} +.rendered_html u {text-decoration: underline;} +.rendered_html :link { text-decoration: underline } +.rendered_html :visited { text-decoration: underline } +.rendered_html h1 {font-size: 197%; margin: .67em 0; font-weight: bold;} +.rendered_html h2 {font-size: 153.9%; margin: .75em 0; font-weight: bold;} +.rendered_html h3 {font-size: 116%; margin: .83em 0; font-weight: bold;} +.rendered_html h4 {margin: 1.12em 0; font-weight: bold;} +.rendered_html h5 {font-size: 85%.; margin: 1.5em 0; font-weight: bold;} +.rendered_html h6 {font-size: 77%; margin: 1.67em 0; font-weight: bold;} +.rendered_html ul {list-style:disc; margin-left: 40px;} +.rendered_html ul ul {list-style:square; margin-left: 40px;} +.rendered_html ul ul ul {list-style:circle; margin-left: 40px;} +.rendered_html ol {list-style:upper-roman; margin-left: 40px;} +.rendered_html ol ol {list-style:upper-alpha;} +.rendered_html ol ol ol {list-style:decimal;} +.rendered_html ol ol ol ol {list-style:lower-alpha;} +.rendered_html ol ol ol ol ol {list-style:lower-roman;} .CodeMirror { line-height: 1.231; /* Changed from 1em to our global default */ diff --git a/IPython/frontend/html/notebook/static/js/htmlcell.js b/IPython/frontend/html/notebook/static/js/htmlcell.js index 95590db..e084d43 100644 --- a/IPython/frontend/html/notebook/static/js/htmlcell.js +++ b/IPython/frontend/html/notebook/static/js/htmlcell.js @@ -28,7 +28,8 @@ var IPython = (function (IPython) { value: this.placeholder }); // The tabindex=-1 makes this div focusable. - var render_area = $('
').addClass('html_cell_render').attr('tabindex','-1'); + var render_area = $('').addClass('html_cell_render'). + addClass('rendered_html').attr('tabindex','-1'); cell.append(input_area).append(render_area); this.element = cell; }; @@ -74,7 +75,7 @@ var IPython = (function (IPython) { var output = html_cell.find("div.html_cell_render"); var text = this.get_source(); if (text === "") {text = this.placeholder;}; - this.set_render(text); + this.set_rendered(text); MathJax.Hub.Queue(["Typeset",MathJax.Hub]); html_cell.find('div.html_cell_input').hide(); output.show(); @@ -107,7 +108,7 @@ var IPython = (function (IPython) { }; - HTMLCell.prototype.set_render = function(text) { + HTMLCell.prototype.set_rendered = function(text) { this.element.find('div.html_cell_render').html(text); }; @@ -134,7 +135,7 @@ var IPython = (function (IPython) { if (data.cell_type === 'html') { if (data.source !== undefined) { this.set_source(data.source); - this.set_render(data.source); + this.set_rendered(data.source); }; }; } diff --git a/IPython/frontend/html/notebook/static/js/notebook.js b/IPython/frontend/html/notebook/static/js/notebook.js index 859d00c..452bcb9 100644 --- a/IPython/frontend/html/notebook/static/js/notebook.js +++ b/IPython/frontend/html/notebook/static/js/notebook.js @@ -342,12 +342,34 @@ var IPython = (function (IPython) { } - Notebook.prototype.html_to_code = function (index) { + Notebook.prototype.insert_rst_cell_before = function (index) { + // TODO: Bounds check for i + var i = this.index_or_selected(index); + var cell = new IPython.RSTCell(this); + cell.config_mathjax(); + this.insert_cell_before(cell, i); + this.select(this.find_cell_index(cell)); + return cell; + } + + + Notebook.prototype.insert_rst_cell_after = function (index) { + // TODO: Bounds check for i + var i = this.index_or_selected(index); + var cell = new IPython.RSTCell(this); + cell.config_mathjax(); + this.insert_cell_after(cell, i); + this.select(this.find_cell_index(cell)); + return cell; + } + + + Notebook.prototype.to_code = function (index) { // TODO: Bounds check for i var i = this.index_or_selected(index); var source_element = this.cell_elements().eq(i); var source_cell = source_element.data("cell"); - if (source_cell instanceof IPython.HTMLCell) { + if (source_cell instanceof IPython.HTMLCell || source_cell instanceof IPython.RSTCell) { this.insert_code_cell_after(i); var target_cell = this.cells()[i+1]; target_cell.set_code(source_cell.get_source()); @@ -356,21 +378,51 @@ var IPython = (function (IPython) { }; - Notebook.prototype.code_to_html = function (index) { + Notebook.prototype.to_rst = function (index) { + // TODO: Bounds check for i + var i = this.index_or_selected(index); + var source_element = this.cell_elements().eq(i); + var source_cell = source_element.data("cell"); + var target_cell = null; + if (source_cell instanceof IPython.CodeCell) { + this.insert_rst_cell_after(i); + var target_cell = this.cells()[i+1]; + var text = source_cell.get_code(); + } else if (source_cell instanceof IPython.HTMLCell) { + this.insert_rst_cell_after(i); + var target_cell = this.cells()[i+1]; + var text = source_cell.get_source(); + } + if (target_cell !== null) { + if (text === "") {text = target_cell.placeholder;}; + target_cell.set_source(text); + source_element.remove(); + target_cell.edit(); + } + }; + + + Notebook.prototype.to_html = function (index) { // TODO: Bounds check for i var i = this.index_or_selected(index); var source_element = this.cell_elements().eq(i); var source_cell = source_element.data("cell"); + var target_cell = null; if (source_cell instanceof IPython.CodeCell) { this.insert_html_cell_after(i); var target_cell = this.cells()[i+1]; var text = source_cell.get_code(); + } else if (source_cell instanceof IPython.RSTCell) { + this.insert_html_cell_after(i); + var target_cell = this.cells()[i+1]; + var text = source_cell.get_source(); + } + if (target_cell !== null) { if (text === "") {text = target_cell.placeholder;}; target_cell.set_source(text); - target_cell.set_render(text); source_element.remove(); target_cell.edit(); - }; + } }; diff --git a/IPython/frontend/html/notebook/static/js/notebook_main.js b/IPython/frontend/html/notebook/static/js/notebook_main.js index 0722c70..8a7e386 100644 --- a/IPython/frontend/html/notebook/static/js/notebook_main.js +++ b/IPython/frontend/html/notebook/static/js/notebook_main.js @@ -16,6 +16,7 @@ $(document).ready(function () { styles: {'.MathJax_Display': {"margin": 0}} } }); + IPython.markdown_converter = new Markdown.Converter(); $('div#header').addClass('border-box-sizing'); $('div#main_app').addClass('border-box-sizing ui-widget ui-widget-content'); diff --git a/IPython/frontend/html/notebook/static/js/panelsection.js b/IPython/frontend/html/notebook/static/js/panelsection.js index d1374ae..a197961 100644 --- a/IPython/frontend/html/notebook/static/js/panelsection.js +++ b/IPython/frontend/html/notebook/static/js/panelsection.js @@ -153,10 +153,13 @@ var IPython = (function (IPython) { IPython.notebook.move_cell_down(); }); this.content.find('#to_code').click(function () { - IPython.notebook.html_to_code(); + IPython.notebook.to_code(); }); this.content.find('#to_html').click(function () { - IPython.notebook.code_to_html(); + IPython.notebook.to_html(); + }); + this.content.find('#to_rst').click(function () { + IPython.notebook.to_rst(); }); this.content.find('#run_selected_cell').click(function () { IPython.notebook.execute_selected_cell(); diff --git a/IPython/frontend/html/notebook/static/js/rstcell.js b/IPython/frontend/html/notebook/static/js/rstcell.js new file mode 100644 index 0000000..5e7d612 --- /dev/null +++ b/IPython/frontend/html/notebook/static/js/rstcell.js @@ -0,0 +1,187 @@ + +//============================================================================ +// RSTCell +//============================================================================ + +var IPython = (function (IPython) { + + var RSTCell = function (notebook) { + IPython.Cell.apply(this, arguments); + this.placeholder = "Type ReStructured Text *here*." + this.rendered = false; + }; + + + RSTCell.prototype = new IPython.Cell(); + + + + RSTCell.prototype.create_element = function () { + var cell = $("s around + // "paragraphs" that are wrapped in non-block-level tags, such as anchors, + // phrase emphasis, and spans. The list of tags we're looking for is + // hard-coded: + var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" + var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" + + // First, look for nested blocks, e.g.: + //
tags around block-level tags.
+ text = _HashHTMLBlocks(text);
+ text = _FormParagraphs(text, doNotUnhash);
+
+ return text;
+ }
+
+ function _RunSpanGamut(text) {
+ //
+ // These are all the transformations that occur *within* block-level
+ // tags like paragraphs, headers, and list items.
+ //
+
+ text = _DoCodeSpans(text);
+ text = _EscapeSpecialCharsWithinTagAttributes(text);
+ text = _EncodeBackslashEscapes(text);
+
+ // Process anchor and image tags. Images must come first,
+ // because ![foo][f] looks like an anchor.
+ text = _DoImages(text);
+ text = _DoAnchors(text);
+
+ // Make links out of things like ` Just type tags
+ //
+
+ // Strip leading and trailing lines:
+ text = text.replace(/^\n+/g, "");
+ text = text.replace(/\n+$/g, "");
+
+ var grafs = text.split(/\n{2,}/g);
+ var grafsOut = [];
+
+ //
+ // Wrap tags.
+ //
+ var end = grafs.length;
+ for (var i = 0; i < end; i++) {
+ var str = grafs[i];
+
+ // if this is an HTML marker, copy it
+ if (str.search(/~K(\d+)K/g) >= 0) {
+ grafsOut.push(str);
+ }
+ else if (str.search(/\S/) >= 0) {
+ str = _RunSpanGamut(str);
+ str = str.replace(/^([ \t]*)/g, " ");
+ str += "
\n");
+
+ return text;
+ }
+
+ function _EscapeSpecialCharsWithinTagAttributes(text) {
+ //
+ // Within tags -- meaning between < and > -- encode [\ ` * _] so they
+ // don't conflict with their use in Markdown for code, italics and strong.
+ //
+
+ // Build a regex to find HTML tags and comments. See Friedl's
+ // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
+
+ // SE: changed the comment part of the regex
+
+ var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi;
+
+ text = text.replace(regex, function (wholeMatch) {
+ var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`");
+ tag = escapeCharacters(tag, wholeMatch.charAt(1) == "!" ? "\\`*_/" : "\\`*_"); // also escape slashes in comments to prevent autolinking there -- http://meta.stackoverflow.com/questions/95987
+ return tag;
+ });
+
+ return text;
+ }
+
+ function _DoAnchors(text) {
+ //
+ // Turn Markdown link shortcuts into XHTML tags.
+ //
+ //
+ // First, handle reference-style links: [link text] [id]
+ //
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ \[
+ (
+ (?:
+ \[[^\]]*\] // allow brackets nested one level
+ |
+ [^\[] // or anything else
+ )*
+ )
+ \]
+
+ [ ]? // one optional space
+ (?:\n[ ]*)? // one optional newline followed by spaces
+
+ \[
+ (.*?) // id = $3
+ \]
+ )
+ ()()()() // pad remaining backreferences
+ /g, writeAnchorTag);
+ */
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag);
+
+ //
+ // Next, inline-style links: [link text](url "optional title")
+ //
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ \[
+ (
+ (?:
+ \[[^\]]*\] // allow brackets nested one level
+ |
+ [^\[\]] // or anything else
+ )*
+ )
+ \]
+ \( // literal paren
+ [ \t]*
+ () // no id, so leave $3 empty
+ ( // href = $4
+ (?:
+ \([^)]*\) // allow one level of (correctly nested) parens (think MSDN)
+ |
+ [^()]
+ )*?
+ )>?
+ [ \t]*
+ ( // $5
+ (['"]) // quote char = $6
+ (.*?) // Title = $7
+ \6 // matching quote
+ [ \t]* // ignore any spaces/tabs between closing quote and )
+ )? // title is optional
+ \)
+ )
+ /g, writeAnchorTag);
+ */
+
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()((?:\([^)]*\)|[^()])*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag);
+
+ //
+ // Last, handle reference-style shortcuts: [link text]
+ // These must come last in case you've also got [link test][1]
+ // or [link test](/foo)
+ //
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ \[
+ ([^\[\]]+) // link text = $2; can't contain '[' or ']'
+ \]
+ )
+ ()()()()() // pad rest of backreferences
+ /g, writeAnchorTag);
+ */
+ text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
+
+ return text;
+ }
+
+ function writeAnchorTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
+ if (m7 == undefined) m7 = "";
+ var whole_match = m1;
+ var link_text = m2;
+ var link_id = m3.toLowerCase();
+ var url = m4;
+ var title = m7;
+
+ if (url == "") {
+ if (link_id == "") {
+ // lower-case and turn embedded newlines into spaces
+ link_id = link_text.toLowerCase().replace(/ ?\n/g, " ");
+ }
+ url = "#" + link_id;
+
+ if (g_urls.get(link_id) != undefined) {
+ url = g_urls.get(link_id);
+ if (g_titles.get(link_id) != undefined) {
+ title = g_titles.get(link_id);
+ }
+ }
+ else {
+ if (whole_match.search(/\(\s*\)$/m) > -1) {
+ // Special case for explicit empty url
+ url = "";
+ } else {
+ return whole_match;
+ }
+ }
+ }
+ url = encodeProblemUrlChars(url);
+ url = escapeCharacters(url, "*_");
+ var result = "" + link_text + "";
+
+ return result;
+ }
+
+ function _DoImages(text) {
+ //
+ // Turn Markdown image shortcuts into tags.
+ //
+
+ //
+ // First, handle reference-style labeled images: ![alt text][id]
+ //
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ !\[
+ (.*?) // alt text = $2
+ \]
+
+ [ ]? // one optional space
+ (?:\n[ ]*)? // one optional newline followed by spaces
+
+ \[
+ (.*?) // id = $3
+ \]
+ )
+ ()()()() // pad rest of backreferences
+ /g, writeImageTag);
+ */
+ text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag);
+
+ //
+ // Next, handle inline images: ![alt text](url "optional title")
+ // Don't forget: encode * and _
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ !\[
+ (.*?) // alt text = $2
+ \]
+ \s? // One optional whitespace character
+ \( // literal paren
+ [ \t]*
+ () // no id, so leave $3 empty
+ (\S+?)>? // src url = $4
+ [ \t]*
+ ( // $5
+ (['"]) // quote char = $6
+ (.*?) // title = $7
+ \6 // matching quote
+ [ \t]*
+ )? // title is optional
+ \)
+ )
+ /g, writeImageTag);
+ */
+ text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag);
+
+ return text;
+ }
+
+ function writeImageTag(wholeMatch, m1, m2, m3, m4, m5, m6, m7) {
+ var whole_match = m1;
+ var alt_text = m2;
+ var link_id = m3.toLowerCase();
+ var url = m4;
+ var title = m7;
+
+ if (!title) title = "";
+
+ if (url == "") {
+ if (link_id == "") {
+ // lower-case and turn embedded newlines into spaces
+ link_id = alt_text.toLowerCase().replace(/ ?\n/g, " ");
+ }
+ url = "#" + link_id;
+
+ if (g_urls.get(link_id) != undefined) {
+ url = g_urls.get(link_id);
+ if (g_titles.get(link_id) != undefined) {
+ title = g_titles.get(link_id);
+ }
+ }
+ else {
+ return whole_match;
+ }
+ }
+
+ alt_text = alt_text.replace(/"/g, """);
+ url = escapeCharacters(url, "*_");
+ var result = "";
+
+ return result;
+ }
+
+ function _DoHeaders(text) {
+
+ // Setext-style headers:
+ // Header 1
+ // ========
+ //
+ // Header 2
+ // --------
+ //
+ text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
+ function (wholeMatch, m1) { return "" + _RunSpanGamut(m1) + "
\n\n"; }
+ );
+
+ text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
+ function (matchFound, m1) { return "" + _RunSpanGamut(m1) + "
\n\n"; }
+ );
+
+ // atx-style headers:
+ // # Header 1
+ // ## Header 2
+ // ## Header 2 with closing hashes ##
+ // ...
+ // ###### Header 6
+ //
+
+ /*
+ text = text.replace(/
+ ^(\#{1,6}) // $1 = string of #'s
+ [ \t]*
+ (.+?) // $2 = Header text
+ [ \t]*
+ \#* // optional closing #'s (not counted)
+ \n+
+ /gm, function() {...});
+ */
+
+ text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
+ function (wholeMatch, m1, m2) {
+ var h_level = m1.length;
+ return "` blocks.
+ //
+
+ /*
+ text = text.replace(/
+ (?:\n\n|^)
+ ( // $1 = the code block -- one or more lines, starting with a space/tab
+ (?:
+ (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
+ .*\n+
+ )+
+ )
+ (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
+ /g ,function(){...});
+ */
+
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
+ text += "~0";
+
+ text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
+ function (wholeMatch, m1, m2) {
+ var codeblock = m1;
+ var nextChar = m2;
+
+ codeblock = _EncodeCode(_Outdent(codeblock));
+ codeblock = _Detab(codeblock);
+ codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
+ codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
+
+ codeblock = "
";
+
+ return "\n\n" + codeblock + "\n\n" + nextChar;
+ }
+ );
+
+ // attacklab: strip sentinel
+ text = text.replace(/~0/, "");
+
+ return text;
+ }
+
+ function hashBlock(text) {
+ text = text.replace(/(^\n+|\n+$)/g, "");
+ return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n";
+ }
+
+ function _DoCodeSpans(text) {
+ //
+ // * Backtick quotes are used for " + codeblock + "\n
spans.
+ //
+ // * You can use multiple backticks as the delimiters if you want to
+ // include literal backticks in the code span. So, this input:
+ //
+ // Just type ``foo `bar` baz`` at the prompt.
+ //
+ // Will translate to:
+ //
+ //
foo `bar` baz
at the prompt.`bar`
...
+ //
+
+ /*
+ text = text.replace(/
+ (^|[^\\]) // Character before opening ` can't be a backslash
+ (`+) // $2 = Opening run of `
+ ( // $3 = The code block
+ [^\r]*?
+ [^`] // attacklab: work around lack of lookbehind
+ )
+ \2 // Matching closer
+ (?!`)
+ /gm, function(){...});
+ */
+
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
+ function (wholeMatch, m1, m2, m3, m4) {
+ var c = m3;
+ c = c.replace(/^([ \t]*)/g, ""); // leading whitespace
+ c = c.replace(/[ \t]*$/g, ""); // trailing whitespace
+ c = _EncodeCode(c);
+ return m1 + "" + c + "
";
+ }
+ );
+
+ return text;
+ }
+
+ function _EncodeCode(text) {
+ //
+ // Encode/escape certain characters inside Markdown code runs.
+ // The point is that in code, these characters are literals,
+ // and lose their special Markdown meanings.
+ //
+ // Encode all ampersands; HTML entities are not
+ // entities within a Markdown code span.
+ text = text.replace(/&/g, "&");
+
+ // Do the angle bracket song and dance:
+ text = text.replace(//g, ">");
+
+ // Now, escape characters that are magic in Markdown:
+ text = escapeCharacters(text, "\*_{}[]\\", false);
+
+ // jj the line above breaks this:
+ //---
+
+ //* Item
+
+ // 1. Subitem
+
+ // special char: *
+ //---
+
+ return text;
+ }
+
+ function _DoItalicsAndBold(text) {
+
+ // must go first:
+ text = text.replace(/([\W_]|^)(\*\*|__)(?=\S)([^\r]*?\S[\*_]*)\2([\W_]|$)/g,
+ "$1$3$4");
+
+ text = text.replace(/([\W_]|^)(\*|_)(?=\S)([^\r\*_]*?\S)\2([\W_]|$)/g,
+ "$1$3$4");
+
+ return text;
+ }
+
+ function _DoBlockQuotes(text) {
+
+ /*
+ text = text.replace(/
+ ( // Wrap whole match in $1
+ (
+ ^[ \t]*>[ \t]? // '>' at the start of a line
+ .+\n // rest of the first line
+ (.+\n)* // subsequent consecutive lines
+ \n* // blanks
+ )+
+ )
+ /gm, function(){...});
+ */
+
+ text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
+ function (wholeMatch, m1) {
+ var bq = m1;
+
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ // "----------bug".replace(/^-/g,"") == "bug"
+
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting
+
+ // attacklab: clean up hack
+ bq = bq.replace(/~0/g, "");
+
+ bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines
+ bq = _RunBlockGamut(bq); // recurse
+
+ bq = bq.replace(/(^|\n)/g, "$1 ");
+ // These leading spaces screw with content, so we need to fix that:
+ bq = bq.replace(
+ /(\s*
[^\r]+?<\/pre>)/gm,
+ function (wholeMatch, m1) {
+ var pre = m1;
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ pre = pre.replace(/^ /mg, "~0");
+ pre = pre.replace(/~0/g, "");
+ return pre;
+ });
+
+ return hashBlock("
\n" + bq + "\n
");
+ }
+ );
+ return text;
+ }
+
+ function _FormParagraphs(text, doNotUnhash) {
+ //
+ // Params:
+ // $text - string to process with html