2013-11-01 16:06:08 +00:00
|
|
|
/*\
|
|
|
|
title: $:/plugins/tiddlywiki/markdown/wrapper.js
|
|
|
|
type: application/javascript
|
|
|
|
module-type: parser
|
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
Wraps up the markdown-it parser for use as a Parser in TiddlyWiki
|
2013-11-01 16:06:08 +00:00
|
|
|
|
|
|
|
\*/
|
2023-03-05 10:34:41 +00:00
|
|
|
(function(){
|
2013-11-01 16:06:08 +00:00
|
|
|
|
|
|
|
/*jslint node: true, browser: true */
|
|
|
|
/*global $tw: false */
|
|
|
|
"use strict";
|
|
|
|
|
2023-03-05 10:34:41 +00:00
|
|
|
var MarkdownIt = require("./markdown-it");
|
2013-11-01 16:06:08 +00:00
|
|
|
|
2020-01-20 13:13:36 +00:00
|
|
|
function parseAsBoolean(tiddlerName) {
|
2023-01-14 09:49:04 +00:00
|
|
|
return $tw.wiki.getTiddlerText(tiddlerName,"false").trim().toLowerCase() === "true";
|
2020-01-20 13:13:36 +00:00
|
|
|
}
|
2023-01-14 09:49:04 +00:00
|
|
|
|
2020-01-20 13:13:36 +00:00
|
|
|
var pluginOpts = {
|
|
|
|
renderWikiText: parseAsBoolean("$:/config/markdown/renderWikiText"),
|
|
|
|
renderWikiTextPragma: $tw.wiki.getTiddlerText("$:/config/markdown/renderWikiTextPragma").trim()
|
|
|
|
};
|
2023-01-14 09:49:04 +00:00
|
|
|
|
|
|
|
var markdownOpts = {
|
|
|
|
html: true,
|
|
|
|
xhtmlOut: true,
|
2020-01-20 13:13:36 +00:00
|
|
|
breaks: parseAsBoolean("$:/config/markdown/breaks"),
|
2023-01-14 09:49:04 +00:00
|
|
|
quotes: $tw.wiki.getTiddlerText("$:/config/markdown/quotes").trim(),
|
|
|
|
typographer: parseAsBoolean("$:/config/markdown/typographer"),
|
|
|
|
linkify: parseAsBoolean("$:/config/markdown/linkify")
|
2020-06-11 11:09:10 +00:00
|
|
|
};
|
2020-01-20 13:13:36 +00:00
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
// Retrieve needed TW rule classes and instantiated rules
|
|
|
|
function setupWikiRules(pluginOptions) {
|
|
|
|
var results = {};
|
|
|
|
|
|
|
|
function collectAllRules(classes,type) {
|
|
|
|
var rulesInfo = [], key,
|
|
|
|
self = wikiParser;
|
|
|
|
for(key in classes) {
|
|
|
|
// instantiate the rule
|
|
|
|
var RuleClass = classes[key];
|
|
|
|
var rule = new RuleClass(self);
|
|
|
|
rule.name = key;
|
|
|
|
rule.class = RuleClass;
|
|
|
|
rule.is = {};
|
|
|
|
rule.is[type] = true;
|
|
|
|
rule.init(self);
|
|
|
|
|
|
|
|
rulesInfo.push({
|
|
|
|
rule: rule,
|
|
|
|
matchIndex: -1
|
|
|
|
});
|
|
|
|
};
|
|
|
|
return rulesInfo;
|
|
|
|
}
|
2014-08-27 10:09:59 +00:00
|
|
|
|
2023-03-05 10:34:41 +00:00
|
|
|
var WikiParser = require("$:/core/modules/parsers/wikiparser/wikiparser.js")["text/vnd.tiddlywiki"];
|
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
// first pass: get all rule classes
|
2023-03-05 10:34:41 +00:00
|
|
|
var wikiParser = new WikiParser(null, '', {parseAsInline: true, wiki: $tw.wiki});
|
2023-01-14 09:49:04 +00:00
|
|
|
|
|
|
|
// restore all possible rules from each rule class
|
|
|
|
wikiParser.pragmaRules = collectAllRules(wikiParser.pragmaRuleClasses,'pragma');
|
|
|
|
wikiParser.blockRules = collectAllRules(wikiParser.blockRuleClasses,'block');
|
|
|
|
wikiParser.inlineRules = collectAllRules(wikiParser.inlineRuleClasses,'inline');
|
|
|
|
|
|
|
|
var pragma = pluginOptions.renderWikiText
|
|
|
|
? "\\rules except latex-parser extlink\n" + pluginOptions.renderWikiTextPragma
|
|
|
|
: "\\rules only html entity commentinline commentblock";
|
|
|
|
|
|
|
|
wikiParser.pos = 0;
|
|
|
|
wikiParser.source = pragma;
|
|
|
|
wikiParser.sourceLength = pragma.length;
|
|
|
|
|
|
|
|
// second pass: remove uninterested rules based on \rules pragma
|
|
|
|
wikiParser.parsePragmas();
|
|
|
|
|
|
|
|
results.blockRules = {};
|
|
|
|
results.inlineRules = {}
|
|
|
|
results.blockRuleClasses = {};
|
|
|
|
results.inlineRuleClasses = {};
|
|
|
|
|
|
|
|
// save the rule sets for future markdown parsing
|
|
|
|
wikiParser.blockRules.forEach(function(ruleinfo) {
|
|
|
|
results.blockRules[ruleinfo.rule.name] = ruleinfo;
|
|
|
|
results.blockRuleClasses[ruleinfo.rule.name] = ruleinfo.rule.class;
|
|
|
|
});
|
|
|
|
wikiParser.inlineRules.forEach(function(ruleinfo) {
|
|
|
|
results.inlineRules[ruleinfo.rule.name] = ruleinfo;
|
|
|
|
results.inlineRuleClasses[ruleinfo.rule.name] = ruleinfo.rule.class;
|
|
|
|
});
|
|
|
|
return results;
|
2021-07-06 10:32:32 +00:00
|
|
|
}
|
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
// Creates markdown-it parser
|
|
|
|
function createMarkdownEngine(markdownItOptions, pluginOptions) {
|
|
|
|
var md = new MarkdownIt(markdownItOptions)
|
2023-03-05 10:34:41 +00:00
|
|
|
.use(require("./markdown-it-sub"))
|
|
|
|
.use(require("./markdown-it-sup"))
|
|
|
|
.use(require("./markdown-it-ins"))
|
|
|
|
.use(require("./markdown-it-mark"))
|
|
|
|
.use(require("./markdown-it-footnote"))
|
|
|
|
.use(require("./markdown-it-deflist"));
|
2023-01-14 09:49:04 +00:00
|
|
|
|
|
|
|
var results = setupWikiRules(pluginOptions);
|
|
|
|
|
|
|
|
MarkdownParser.prototype.blockRuleClasses = results.blockRuleClasses;
|
|
|
|
MarkdownParser.prototype.blockRules = results.blockRules;
|
|
|
|
|
|
|
|
MarkdownParser.prototype.inlineRuleClasses = results.inlineRuleClasses;
|
|
|
|
MarkdownParser.prototype.inlineRules = results.inlineRules;
|
|
|
|
|
|
|
|
if(pluginOptions.renderWikiText && $tw.modules.titles["$:/plugins/tiddlywiki/katex/katex.min.js"]) {
|
2023-03-05 10:34:41 +00:00
|
|
|
md.use(require("./markdown-it-katex"));
|
2023-01-14 09:49:04 +00:00
|
|
|
}
|
|
|
|
|
2023-03-05 10:34:41 +00:00
|
|
|
md.use(require("./markdown-it-tiddlywiki"),{
|
2023-01-14 09:49:04 +00:00
|
|
|
renderWikiText: pluginOptions.renderWikiText,
|
|
|
|
blockRules: results.blockRules,
|
|
|
|
inlineRules: results.inlineRules
|
|
|
|
});
|
|
|
|
|
|
|
|
$tw.utils.each(['image','prettylink','prettyextlink'], function(rule) {
|
|
|
|
if(MarkdownParser.prototype.inlineRules[rule]) {
|
|
|
|
// delegate to md; ignore the rule class in WikiParser
|
|
|
|
delete MarkdownParser.prototype.inlineRuleClasses[rule];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return md;
|
2020-01-20 13:13:36 +00:00
|
|
|
}
|
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
/// Parse tree post processing ///
|
|
|
|
|
|
|
|
function deactivateLinks(tree) {
|
|
|
|
$tw.utils.each(tree,function(node) {
|
|
|
|
if(node.type === "link") {
|
|
|
|
node.type = "text";
|
|
|
|
node.text = node.children[0].text;
|
|
|
|
delete node.attributes;
|
|
|
|
delete node.children;
|
|
|
|
delete node.attributes;
|
|
|
|
} else {
|
|
|
|
deactivateLinks(node.children);
|
2020-01-20 13:13:36 +00:00
|
|
|
}
|
2023-01-14 09:49:04 +00:00
|
|
|
});
|
2013-11-01 16:06:08 +00:00
|
|
|
}
|
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
// true if the node contains "_codified_" class attribute
|
|
|
|
function isCodified(node) {
|
|
|
|
return node.attributes
|
|
|
|
&& node.attributes.class
|
|
|
|
&& node.attributes.class.type === "string"
|
|
|
|
&& (node.attributes.class.value.split(" ").indexOf("_codified_") !== -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
function decodeEntities(s) {
|
|
|
|
return s.replace(/(&#?[a-zA-Z0-9]{2,8};)/g,$tw.utils.entityDecode);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add e"..." and e'....' syntax to enable decoding of HTML entities
|
|
|
|
// in string literals.
|
|
|
|
function parseStringLiteralExtended(source,pos) {
|
|
|
|
var node = {
|
|
|
|
type: "string",
|
|
|
|
start: pos
|
|
|
|
};
|
|
|
|
var reString = /(?:"""([\s\S]*?)"""|e?"([^"]*)")|(?:e?'([^']*)')/g;
|
|
|
|
reString.lastIndex = pos;
|
|
|
|
var match = reString.exec(source);
|
|
|
|
if(match && match.index === pos) {
|
|
|
|
node.value = match[1] !== undefined ? match[1] :
|
|
|
|
(match[2] !== undefined ? match[2] : match[3]);
|
|
|
|
node.end = pos + match[0].length;
|
|
|
|
if(match[0].charAt(0) === "e") {
|
|
|
|
node.value = decodeEntities(node.value);
|
2013-11-01 16:06:08 +00:00
|
|
|
}
|
2023-01-14 09:49:04 +00:00
|
|
|
return node;
|
|
|
|
} else {
|
|
|
|
return null;
|
2020-07-31 12:01:21 +00:00
|
|
|
}
|
2023-01-14 09:49:04 +00:00
|
|
|
}
|
2020-01-20 13:13:36 +00:00
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
function processWikiTree(tree,hasWikiLinkRule) {
|
|
|
|
var stack = [].concat(tree);
|
2020-06-11 11:09:10 +00:00
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
var mergeable = function(node) {
|
|
|
|
return node.type === "element" && node.tag === "p" && (!node.attributes || Object.keys(node.attributes).length === 0);
|
|
|
|
};
|
2020-07-31 12:01:21 +00:00
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
while(stack.length) {
|
|
|
|
var node = stack.pop();
|
|
|
|
if(node.type === "element" && node.tag === "p") {
|
|
|
|
// reduce nested <p> nodes
|
|
|
|
while(node.children && node.children.length === 1 && mergeable(node.children[0])) {
|
|
|
|
node.children = node.children[0].children;
|
2020-06-11 11:09:10 +00:00
|
|
|
}
|
2023-01-14 09:49:04 +00:00
|
|
|
} else if(hasWikiLinkRule && isCodified(node)) {
|
|
|
|
deactivateLinks(node.children);
|
|
|
|
continue;
|
2020-06-11 11:09:10 +00:00
|
|
|
}
|
2023-01-14 09:49:04 +00:00
|
|
|
if(node.children && node.children.length > 0) {
|
|
|
|
stack.push.apply(stack,node.children);
|
2015-10-09 13:11:24 +00:00
|
|
|
}
|
2013-11-01 16:06:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
// to extend MarkdownIt outside of this module, do:
|
|
|
|
//
|
|
|
|
// md = $tw.Wiki.parsers["text/markdown"].prototype.md;
|
|
|
|
// md.use(plugin[, options]);
|
|
|
|
MarkdownParser.prototype.md = createMarkdownEngine(markdownOpts,pluginOpts);
|
2013-11-01 16:06:08 +00:00
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
function MarkdownParser(type,text,options) {
|
|
|
|
var env = {}
|
|
|
|
var md = this.md;
|
|
|
|
var mdTree = md.parse(text,env);
|
|
|
|
var textToParse = '<div class="markdown">\n' + md.renderer.render(mdTree,md.options,env) + '</div>';
|
2013-11-01 16:06:08 +00:00
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
//console.log(JSON.stringify(mdTree,null,2));
|
|
|
|
//console.log("\n----------------\n" + textToParse);
|
2013-11-01 16:06:08 +00:00
|
|
|
|
2023-01-14 09:49:04 +00:00
|
|
|
var wikiParser;
|
|
|
|
|
|
|
|
var origParseStringLiteral = $tw.utils.parseStringLiteral;
|
|
|
|
$tw.utils.parseStringLiteral = parseStringLiteralExtended;
|
|
|
|
|
|
|
|
try {
|
|
|
|
wikiParser = new $tw.Wiki.parsers["text/vnd.tiddlywiki"](null,textToParse,{
|
|
|
|
parseAsInline: true,
|
|
|
|
wiki: options.wiki,
|
|
|
|
rules: { pragma: {}, block: this.blockRuleClasses, inline: this.inlineRuleClasses }
|
|
|
|
});
|
|
|
|
}
|
|
|
|
catch (err) {
|
|
|
|
wikiParser = $tw.wiki.parseText("text/vnd.tiddlywiki",
|
|
|
|
"<strong>Error encountered while parsing the tiddler:</strong><p>" + err.message + "</p>",
|
|
|
|
{parseAsInline: false, wiki: options.wiki});
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
$tw.utils.parseStringLiteral = origParseStringLiteral;
|
|
|
|
}
|
|
|
|
if(wikiParser.tree.length > 0) {
|
|
|
|
var hasWikiLinkRule = false;
|
|
|
|
// see if wikilink rule has been invoked
|
|
|
|
$tw.utils.each(wikiParser.inlineRules,function(ruleInfo) {
|
|
|
|
if(ruleInfo.rule.name === "wikilink") {
|
|
|
|
hasWikiLinkRule = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
processWikiTree(wikiParser.tree,hasWikiLinkRule);
|
|
|
|
}
|
|
|
|
this.tree = wikiParser.tree;
|
|
|
|
this.source = text;
|
|
|
|
this.type = type || "text/markdown";
|
|
|
|
this.wiki = options.wiki;
|
|
|
|
}
|
|
|
|
|
|
|
|
exports["text/markdown"] = MarkdownParser;
|
|
|
|
exports["text/x-markdown"] = MarkdownParser;
|
2023-03-05 10:34:41 +00:00
|
|
|
})();
|