[[linkNewWindow|$:/config/markdown/linkNewWindow]]
| ``true``|For external links, should clicking on them open a new window/tab automatically? |
| [[quotes|$:/config/markdown/quotes]]
| ``“”‘’``|Remarkable library config: Double + single quotes replacement pairs, when ``typographer`` enabled |
| [[renderWikiText|$:/config/markdown/renderWikiText]]
| ``true``|After Markdown is parsed, should any text elements be handed off to the ~WikiText parser for further processing? |
-| [[renderWikiTextPragma|$:/config/markdown/renderWikiTextPragma]]
| ``\rules only html image macrocallinline syslink transcludeinline wikilink filteredtranscludeblock macrocallblock transcludeblock``|When handing off to the ~WikiText parser, what pragma rules should it follow? |
+| [[renderWikiTextPragma|$:/config/markdown/renderWikiTextPragma]]
|<$view tiddler="$:/plugins/tiddlywiki/markdown-legacy" subtiddler="$:/config/markdown/renderWikiTextPragma" mode="inline"/>
|When handing off to the ~WikiText parser, what pragma rules should it follow? |
| [[typographer|$:/config/markdown/typographer]]
| ``false``|Remarkable library config: Enable some language-neutral replacement + quotes beautification |
! Creating ~WikiLinks
diff --git a/plugins/tiddlywiki/markdown-legacy/wrapper.js b/plugins/tiddlywiki/markdown-legacy/wrapper.js
new file mode 100755
index 000000000..6fa56232a
--- /dev/null
+++ b/plugins/tiddlywiki/markdown-legacy/wrapper.js
@@ -0,0 +1,341 @@
+/*\
+title: $:/plugins/tiddlywiki/markdown-legacy/wrapper.js
+type: application/javascript
+module-type: parser
+
+Wraps up the remarkable parser for use as a Parser in TiddlyWiki
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var r = require("$:/plugins/tiddlywiki/markdown-legacy/remarkable.js");
+
+var Remarkable = r.Remarkable,
+ linkify = r.linkify,
+ utils = r.utils;
+
+///// Set up configuration options /////
+function parseAsBoolean(tiddlerName) {
+ return $tw.wiki.getTiddlerText(tiddlerName).toLowerCase() === "true";
+}
+var pluginOpts = {
+ linkNewWindow: parseAsBoolean("$:/config/markdown/linkNewWindow"),
+ renderWikiText: parseAsBoolean("$:/config/markdown/renderWikiText"),
+ renderWikiTextPragma: $tw.wiki.getTiddlerText("$:/config/markdown/renderWikiTextPragma").trim()
+};
+var remarkableOpts = {
+ breaks: parseAsBoolean("$:/config/markdown/breaks"),
+ quotes: $tw.wiki.getTiddlerText("$:/config/markdown/quotes"),
+ typographer: parseAsBoolean("$:/config/markdown/typographer")
+};
+var accumulatingTypes = {
+ "text": true,
+ "softbreak": true
+};
+// If rendering WikiText, we treat katex nodes as text.
+if(pluginOpts.renderWikiText) {
+ accumulatingTypes["katex"] = true;
+}
+
+var md = new Remarkable(remarkableOpts);
+
+// If tiddlywiki/katex plugin is present, use remarkable-katex to enable katex support.
+if($tw.modules.titles["$:/plugins/tiddlywiki/katex/katex.min.js"]) {
+ var rk = require("$:/plugins/tiddlywiki/markdown-legacy/remarkable-katex.js");
+ md = md.use(rk);
+}
+
+if(parseAsBoolean("$:/config/markdown/linkify")) {
+ md = md.use(linkify);
+}
+
+function findTagWithType(nodes, startPoint, type, level) {
+ for (var i = startPoint; i < nodes.length; i++) {
+ if(nodes[i].type === type && nodes[i].level === level) {
+ return i;
+ }
+ }
+ return false;
+}
+
+/**
+ * Remarkable creates nodes that look like:
+ * [
+ * { type: 'paragraph_open'},
+ * { type: 'inline', content: 'Hello World', children:[{type: 'text', content: 'Hello World'}]},
+ * { type: 'paragraph_close'}
+ * ]
+ *
+ * But TiddlyWiki wants the Parser (https://tiddlywiki.com/dev/static/Parser.html) to emit nodes like:
+ *
+ * [
+ * { type: 'element', tag: 'p', children: [{type: 'text', text: 'Hello World'}]}
+ * ]
+ */
+function convertNodes(remarkableTree, isStartOfInline) {
+ let out = [];
+ var accumulatedText = '';
+ function withChildren(currentIndex, currentLevel, closingType, nodes, callback) {
+ var j = findTagWithType(nodes, currentIndex + 1, closingType, currentLevel);
+ if(j === false) {
+ console.error("Failed to find a " + closingType + " node after position " + currentIndex);
+ console.log(nodes);
+ return currentIndex + 1;
+ }
+ let children = convertNodes(nodes.slice(currentIndex + 1, j));
+ callback(children);
+ return j;
+ }
+ function wrappedElement(elementTag, currentIndex, currentLevel, closingType, nodes) {
+ return withChildren(currentIndex, currentLevel, closingType, nodes, function(children) {
+ out.push({
+ type: "element",
+ tag: elementTag,
+ children: children
+ });
+ });
+ }
+
+ for (var i = 0; i < remarkableTree.length; i++) {
+ var currentNode = remarkableTree[i];
+ switch (currentNode.type) {
+ case "paragraph_open":
+ // If the paragraph is a "tight" layout paragraph, don't wrap children in a tag.
+ if(currentNode.tight) {
+ i = withChildren(i, currentNode.level, "paragraph_close", remarkableTree, function(children) {
+ Array.prototype.push.apply(out, children);
+ });
+ } else {
+ i = wrappedElement("p", i, currentNode.level, "paragraph_close", remarkableTree);
+ }
+ break;
+
+ case "heading_open":
+ i = wrappedElement("h" + currentNode.hLevel, i, currentNode.level, "heading_close", remarkableTree);
+ break;
+
+ case "bullet_list_open":
+ i = wrappedElement("ul", i, currentNode.level, "bullet_list_close", remarkableTree);
+ break;
+
+ case "ordered_list_open":
+ i = wrappedElement('ol', i, currentNode.level,'ordered_list_close', remarkableTree);
+ break;
+
+ case "list_item_open":
+ i = wrappedElement("li", i, currentNode.level, "list_item_close", remarkableTree);
+ break;
+
+ case "link_open":
+ i = withChildren(i, currentNode.level, "link_close", remarkableTree, function(children) {
+ if(currentNode.href[0] !== "#") {
+ // External link
+ var attributes = {
+ class: { type: "string", value: "tc-tiddlylink-external" },
+ href: { type: "string", value: currentNode.href },
+ rel: { type: "string", value: "noopener noreferrer" }
+ };
+ if(pluginOpts.linkNewWindow) {
+ attributes.target = { type: "string", value: "_blank" };
+ }
+ out.push({
+ type: "element",
+ tag: "a",
+ attributes: attributes,
+ children: children
+ });
+ } else {
+ // Internal link
+ out.push({
+ type: "link",
+ attributes: {
+ to: { type: "string", value: $tw.utils.decodeURISafe(currentNode.href.substr(1)) }
+ },
+ children: children
+ });
+ }
+ });
+ break;
+
+ case "code":
+ out.push({
+ type: "element",
+ tag: currentNode.block ? "pre" : "code",
+ children: [{ type: "text", text: currentNode.content }]
+ });
+ break;
+
+ case "fence":
+ out.push({
+ type: "codeblock",
+ attributes: {
+ language: { type: "string", value: currentNode.params },
+ code: { type: "string", value: currentNode.content }
+ }
+ });
+ break;
+
+ case "image":
+ out.push({
+ type: "image",
+ attributes: {
+ tooltip: { type: "string", value: currentNode.alt },
+ source: { type: "string", value: $tw.utils.decodeURIComponentSafe(currentNode.src) }
+ }
+ });
+ break;
+
+ case "softbreak":
+ if(remarkableOpts.breaks) {
+ out.push({
+ type: "element",
+ tag: "br",
+ });
+ } else {
+ accumulatedText = accumulatedText + '\n';
+ }
+ break;
+
+ case "hardbreak":
+ out.push({
+ type: "element",
+ tag: "br",
+ });
+ break;
+
+ case "th_open":
+ case "td_open":
+ var elementTag = currentNode.type.slice(0, 2);
+ i = withChildren(i, currentNode.level, elementTag + "_close", remarkableTree, function(children) {
+ var attributes = {};
+ if(currentNode.align) {
+ attributes.style = { type: "string", value: "text-align:" + currentNode.align };
+ }
+ out.push({
+ type: "element",
+ tag: elementTag,
+ attributes: attributes,
+ children: children
+ });
+ });
+ break;
+
+ case "hr":
+ out.push({
+ type: 'element',
+ tag: 'hr',
+ });
+ break;
+
+ case "inline":
+ out = out.concat(convertNodes(currentNode.children, true));
+ break;
+
+ case "text":
+ // We need to merge this text block with the upcoming text block and parse it all together.
+ accumulatedText = accumulatedText + currentNode.content;
+ break;
+
+ case "katex":
+ // If rendering WikiText, convert the katex node back to text for parsing by the WikiText LaTeX parser.
+ if(pluginOpts.renderWikiText) {
+ // If this is a block, add a newline to trigger the KaTeX plugins block detection.
+ var displayModeSuffix = currentNode.block ? "\n" : "";
+ accumulatedText = accumulatedText + "$$" + currentNode.content + displayModeSuffix + "$$";
+ } else {
+ out.push({
+ type: "latex",
+ attributes: {
+ text: { type: "text", value: currentNode.content },
+ displayMode: { type: "text", value: currentNode.block ? "true" : "false" }
+ }
+ });
+ }
+ break;
+
+ default:
+ if(currentNode.type.substr(currentNode.type.length - 5) === "_open") {
+ var tagName = currentNode.type.substr(0, currentNode.type.length - 5);
+ i = wrappedElement(tagName, i, currentNode.level, tagName + "_close", remarkableTree);
+ } else {
+ console.error("Unknown node type: " + currentNode.type, currentNode);
+ out.push({
+ type: "text",
+ text: currentNode.content
+ });
+ }
+ break;
+ }
+ // We test to see if we process the block now, or if there's
+ // more to accumulate first.
+ if(accumulatedText
+ && (
+ remarkableOpts.breaks ||
+ (i+1) >= remarkableTree.length ||
+ !accumulatingTypes[remarkableTree[i+1].type]
+ )
+ ) {
+ // The Markdown compiler thinks this is just text.
+ // Hand off to the WikiText parser to see if there's more to render
+ // But only if it's configured to, and we have more than whitespace
+ if(!pluginOpts.renderWikiText || accumulatedText.match(/^\s*$/)) {
+ out.push({
+ type: "text",
+ text: accumulatedText
+ });
+ } else {
+ // If we're inside a block element (div, p, td, h1), and this is the first child in the tree,
+ // handle as a block-level parse. Otherwise not.
+ var parseAsInline = !(isStartOfInline && i === 0);
+ var textToParse = accumulatedText;
+ if(pluginOpts.renderWikiTextPragma !== "") {
+ textToParse = pluginOpts.renderWikiTextPragma + "\n" + textToParse;
+ }
+ var wikiParser = $tw.wiki.parseText("text/vnd.tiddlywiki", textToParse, {
+ parseAsInline: parseAsInline
+ });
+ var rs = wikiParser.tree;
+
+ // If we parsed as a block, but the root element the WikiText parser gave is a paragraph,
+ // we should discard the paragraph, since the way Remarkable nests its nodes, this "inline"
+ // node is always inside something else that's a block-level element
+ if(!parseAsInline
+ && rs.length === 1
+ && rs[0].type === "element"
+ && rs[0].tag === "p"
+ ) {
+ rs = rs[0].children;
+ }
+
+ // If the original text element started with a space, add it back in
+ if(rs.length > 0
+ && rs[0].type === "text"
+ && (accumulatedText[0] === " " || accumulatedText[0] === "\n")
+ ) {
+ rs[0].text = " " + rs[0].text;
+ }
+ out = out.concat(rs);
+ }
+ accumulatedText = '';
+ }
+ }
+ return out;
+}
+
+var MarkdownParser = function(type, text, options) {
+ var tree = md.parse(text, {});
+ //console.debug(tree);
+ tree = convertNodes(tree);
+ //console.debug(tree);
+
+ this.tree = tree;
+};
+
+exports["text/x-markdown"] = MarkdownParser;
+exports["text/markdown"] = MarkdownParser;
+
+})();
diff --git a/plugins/tiddlywiki/markdown/EditorToolbar/bold.tid b/plugins/tiddlywiki/markdown/EditorToolbar/bold.tid
index 74cb4c596..fdfefd531 100755
--- a/plugins/tiddlywiki/markdown/EditorToolbar/bold.tid
+++ b/plugins/tiddlywiki/markdown/EditorToolbar/bold.tid
@@ -4,7 +4,7 @@ tags: $:/tags/EditorToolbar
icon: $:/core/images/bold
caption: {{$:/language/Buttons/Bold/Caption}} (Markdown)
description: {{$:/language/Buttons/Bold/Hint}}
-condition: [
+
+```
+[img[An explanatory tooltip|TiddlerTitle]]
+
+[img width=23 class="tc-image" [https://tiddlywiki.com/fractalveg.jpg]]
+```
+
+
+```
+[[TiddlerTitle]]
+
+[[Displayed Link Title|Tiddler Title]]
+
+[[TW5|https://tiddlywiki.com/]]
+```
+
+
+```
+[ext[Open file|index.html]]
+
+[ext[Open file|../README.md]]
+```
+ =4))break;s=++n}return e.line=s,(o=e.push("code_block","code",0)).content=e.getLines(r,s,4+e.blkIndent,!1)+"\n",o.map=[r,e.line],!0}],["fence",function(e,r,t,n){var s,o,i,a,c,l,u,p=!1,h=e.bMarks[r]+e.tShift[r],f=e.eMarks[r];if(e.sCount[r]-e.blkIndent>=4)return!1;if(h+3>f)return!1;if(126!==(s=e.src.charCodeAt(h))&&96!==s)return!1;if(c=h,(o=(h=e.skipChars(h,s))-c)<3)return!1;if(u=e.src.slice(c,h),i=e.src.slice(h,f),96===s&&i.indexOf(String.fromCharCode(s))>=0)return!1;if(n)return!0;for(a=r;!(++a>=t)&&!((h=c=e.bMarks[a]+e.tShift[a])<(f=e.eMarks[a])&&e.sCount[a] i;n-=d[n]+1)if((o=r[n]).marker===s.marker&&o.open&&o.end<0&&(c=!1,(o.close||s.open)&&(o.length+s.length)%3==0&&(o.length%3==0&&s.length%3==0||(c=!0)),!c)){l=n>0&&!r[n-1].open?d[n-1]+1:0,d[t]=t-n+l,d[n]=l,s.open=!1,o.end=t,o.close=!1,a=-1,f=-2;break}-1!==a&&(u[s.marker][(s.open?3:0)+(s.length||0)%3]=a)}}}var tr=w.isWhiteSpace,nr=w.isPunctChar,sr=w.isMdAsciiPunct;function or(e,r,t,n){this.src=e,this.env=t,this.md=r,this.tokens=n,this.tokens_meta=Array(n.length),this.pos=0,this.posMax=this.src.length,this.level=0,this.pending="",this.pendingLevel=0,this.cache={},this.delimiters=[],this._prev_delimiters=[],this.backticks={},this.backticksScanned=!1,this.linkLevel=0}or.prototype.pushPending=function(){var e=new oe("text","",0);return e.content=this.pending,e.level=this.pendingLevel,this.tokens.push(e),this.pending="",e},or.prototype.push=function(e,r,t){this.pending&&this.pushPending();var n=new oe(e,r,t),s=null;return t<0&&(this.level--,this.delimiters=this._prev_delimiters.pop()),n.level=this.level,t>0&&(this.level++,this._prev_delimiters.push(this.delimiters),this.delimiters=[],s={delimiters:this.delimiters}),this.pendingLevel=this.level,this.tokens.push(n),this.tokens_meta.push(s),n},or.prototype.scanDelims=function(e,r){var t,n,s,o,i,a,c,l,u,p=e,h=!0,f=!0,d=this.posMax,m=this.src.charCodeAt(e);for(t=e>0?this.src.charCodeAt(e-1):32;p \n\n';
+}
+
+function render_paragraph_close(tokens,idx) {
+ return tokens[idx].hidden ? '' : '\n A tab character in Markdown has a size of four spaces. Configure the tab size of your code editor accordingly. For example, if you use <$text text="CodeMirror"/>, it is recommended that you set $:/config/codemirror/indentUnit and $:/config/codemirror/tabSize to `4` to avoid inconsistent indentations. HTML blocks are ultimately parsed by the <$text text=WikiText/> parser: //an opening tag followed by a blank line will activate block-level parsing for its content//. When working with tags designed to contain literal content, such as `
` |
+|[[linkify|$:/config/markdown/linkify]]|`false`|markdown-it library config: Autoconvert URL-like text to links |
+|[[renderWikiText|$:/config/markdown/renderWikiText]]|`true`|After Markdown is parsed, should any text elements be handed off to the ~WikiText parser for further processing? |
+|[[renderWikiTextPragma|$:/config/markdown/renderWikiTextPragma]]|<$view tiddler="$:/plugins/tiddlywiki/markdown" subtiddler="$:/config/markdown/renderWikiTextPragma" mode="inline"/>
|When handing off to the ~WikiText parser, what parser rules should it follow? |
+|[[typographer|$:/config/markdown/typographer]]|`false`|markdown-it library config: Enable some language-neutral replacement + quotes beautification |
+|[[quotes|$:/config/markdown/quotes]]|`“”‘’`|markdown-it library config: Double + single quotes replacement pairs, when `typographer` is enabled |
+
+''IMPORTANT:'' You must reload your wiki for changes to take effect.
+
+~WikiText Pragma
+
+The value of [[renderWikiTextPragma|$:/config/markdown/renderWikiTextPragma]] has been carefully tuned to properly integrate markdown with ~TiddlyWiki. Changing this setting may produce unexpected results, but the inclusion of the following parser rules should be fine:
+
+; image
+: embed images using ~TiddlyWiki's image syntax:
+
+Typographical Replacements
+
+When [[typographer|$:/config/markdown/typographer]] is enabled, markdown-it will provide these typographical replacements:
+
+```
+(c) (C) → ©
+(tm) (TM) → ™
+(r) (R) → ®
++- → ±
+... → …
+?.... → ?..
+!.... → !..
+????? → ???
+!!!!! → !!!
+,, → ,
+-- → –
+--- → —
+```
\ No newline at end of file
diff --git a/plugins/tiddlywiki/markdown/docs_type_markdown.tid b/plugins/tiddlywiki/markdown/docs_type_markdown.tid
index 229397ed7..0e5c1edef 100755
--- a/plugins/tiddlywiki/markdown/docs_type_markdown.tid
+++ b/plugins/tiddlywiki/markdown/docs_type_markdown.tid
@@ -1,4 +1,4 @@
-title: $:/language/Docs/Types/text/x-markdown
+title: $:/language/Docs/Types/text/markdown
description: Markdown
-name: text/x-markdown
+name: text/markdown
group: Text
diff --git a/plugins/tiddlywiki/markdown/editor-operations/make-markdown-link.js b/plugins/tiddlywiki/markdown/editor-operations/make-markdown-link.js
index 6ab90f883..b124325c8 100644
--- a/plugins/tiddlywiki/markdown/editor-operations/make-markdown-link.js
+++ b/plugins/tiddlywiki/markdown/editor-operations/make-markdown-link.js
@@ -13,19 +13,25 @@ Text editor operation to make a markdown link
"use strict";
exports["make-markdown-link"] = function(event,operation) {
+ var rx = /[()\\]/g, rs = '\\$&';
+
if(operation.selection) {
- if(event.paramObject.text.includes("://")) {
- operation.replacement = "[" + operation.selection + "](" + event.paramObject.text + ")";
+ var desc = operation.selection.replace(/[\[\]\\]/g, rs);
+
+ if(event.paramObject.text.indexOf("://") !== -1) {
+ operation.replacement = "[" + desc + "](" + event.paramObject.text.replace(rx, rs) + ")";
} else {
- operation.replacement = "[" + operation.selection + "](#" + event.paramObject.text.replaceAll(" ", "%20") + ")";
+ operation.replacement = "[" + desc + "](#" + encodeURIComponent(event.paramObject.text).replace(rx, rs) + ")";
}
operation.cutStart = operation.selStart;
operation.cutEnd = operation.selEnd;
} else {
- if(event.paramObject.text.includes("://")) {
- operation.replacement = "<" + event.paramObject.text + ">";
+ if(event.paramObject.text.indexOf("://") !== -1) {
+ operation.replacement = "<" + event.paramObject.text.replace(/[<>]/g, function(m, offset, str) {
+ return encodeURI(m);
+ }) + ">";
} else {
- operation.replacement = "[](#" + event.paramObject.text.replaceAll(" ", "%20") + ")";
+ operation.replacement = "[](#" + encodeURIComponent(event.paramObject.text).replace(rx, rs) + ")";
}
operation.cutStart = operation.selStart;
operation.cutEnd = operation.selEnd;
diff --git a/plugins/tiddlywiki/markdown/files/LICENSE b/plugins/tiddlywiki/markdown/files/LICENSE
new file mode 100644
index 000000000..7ffa058cb
--- /dev/null
+++ b/plugins/tiddlywiki/markdown/files/LICENSE
@@ -0,0 +1,22 @@
+Copyright (c) 2014 Vitaly Puzrin, Alex Kocharin.
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/plugins/tiddlywiki/markdown/files/markdown-it-deflist.min.js b/plugins/tiddlywiki/markdown/files/markdown-it-deflist.min.js
new file mode 100644
index 000000000..30ed4e224
--- /dev/null
+++ b/plugins/tiddlywiki/markdown/files/markdown-it-deflist.min.js
@@ -0,0 +1,7 @@
+/*!
+
+markdown-it-deflist
+https://github.com/markdown-it/markdown-it-deflist
+
+*/
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).markdownitDeflist=e()}}((function(){return function e(t,n,r){function i(f,d){if(!n[f]){if(!t[f]){var s="function"==typeof require&&require;if(!d&&s)return s(f,!0);if(o)return o(f,!0);var u=new Error("Cannot find module '"+f+"'");throw u.code="MODULE_NOT_FOUND",u}var a=n[f]={exports:{}};t[f][0].call(a.exports,(function(e){return i(t[f][1][e]||e)}),a,a.exports,e,t,n,r)}return n[f].exports}for(var o="function"==typeof require&&require,f=0;f
\n':'
\n')+'\n'}function l(){return"
\n
\n"},M.fence=function(e,r,t,n,s){var o,i,a,c,l,u=e[r],p=u.info?T(u.info).trim():"",h="",f="";return p&&(h=(a=p.split(/(\s+)/g))[0],f=a.slice(2).join("")),0===(o=t.highlight&&t.highlight(u.content,h,f)||I(u.content)).indexOf(""+I(e[r].content)+"
\n"):""+o+"
\n"},M.image=function(e,r,t,n,s){var o=e[r];return o.attrs[o.attrIndex("alt")][1]=s.renderInlineAsText(o.children,t,n),s.renderToken(e,r,t)},M.hardbreak=function(e,r,t){return t.xhtmlOut?""+o+"
\n":"
\n"},M.softbreak=function(e,r,t){return t.breaks?t.xhtmlOut?"
\n":"
\n":"\n"},M.text=function(e,r){return I(e[r].content)},M.html_block=function(e,r){return e[r].content},M.html_inline=function(e,r){return e[r].content},R.prototype.renderAttrs=function(e){var r,t,n;if(!e.attrs)return"";for(n="",r=0,t=e.attrs.length;r'
+ + escapeTWMarks(md.utils.escapeHtml(tokens[idx].content))
+ + '
';
+}
+
+function render_code_block(tokens,idx) {
+ return '<$codeblock code=e"' + md.utils.escapeHtml(tokens[idx].content) + '" language=""/>\n';
+}
+
+function render_fence(tokens,idx) {
+ var info = tokens[idx].info ? md.utils.unescapeAll(tokens[idx].info).trim() : '';
+ return '<$codeblock code=e"' + md.utils.escapeHtml(tokens[idx].content) + '" language="' + info.split(/(\s+)/g)[0] + '"/>\n';
+}
+
+// add a blank line after opening tag to activate TW block parsing
+function render_paragraph_open(tokens,idx) {
+ return tokens[idx].hidden ? '' : '` and `