1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-28 04:19:56 +00:00
TiddlyWiki5/js/WikiTextParseTree.js

394 lines
9.9 KiB
JavaScript

/*\
title: js/WikiTextParseTree.js
A container for the parse tree generated by parsing wikitext
\*/
(function(){
/*jslint node: true */
"use strict";
var WikiTextRenderer = require("./WikiTextRenderer.js").WikiTextRenderer,
HTML = require("./HTML.js").HTML,
ArgParser = require("./ArgParser.js").ArgParser,
utils = require("./Utils.js");
// Intialise the parse tree object
var WikiTextParseTree = function(tree,dependencies,store) {
this.tree = tree;
this.dependencies = dependencies;
this.store = store;
};
// Compile the parse tree into a JavaScript function that returns the required
// representation of the tree
WikiTextParseTree.prototype.compile = function(type,treenode) {
/*jslint evil: true */
treenode = treenode || this.tree;
var renderer = new WikiTextRenderer(),
renderStep = {},
renderStepIndex = renderer.addRenderStep(renderStep),
output = [];
if(type === "text/html") {
this.compileSubTreeHtml(output,renderer,treenode);
} else if(type === "text/plain") {
this.compileSubTreePlain(output,renderer,treenode);
} else {
return null;
}
// Create the parse tree for the rendering step function definition
var parseTree = this.store.jsParser.createTree(
[
{
type: "Function",
name: null,
params: ["tiddler","renderer","store","utils"], // These are the parameters passed to the tiddler function; must match the invocation in WikiStore.renderTiddler()
elements: [
{
type: "ReturnStatement",
value: {
type: "FunctionCall",
name: {
type: "PropertyAccess",
base: {
type: "ArrayLiteral",
elements: output
},
name: "join"
},
"arguments": [ {
type: "StringLiteral",
value: ""
}
]
}
}
]
}
]);
renderStep.type = "main";
renderStep.step = renderStepIndex;
renderStep.dependencies = {};
renderStep.handler = parseTree.compile("application/javascript").render;
return renderer;
};
var pushString = function(output,s) {
var last = output[output.length-1];
if(output.length > 0 && last.type === "StringLiterals") {
last.value.push(s);
} else if (output.length > 0 && last.type === "StringLiteral") {
last.type = "StringLiterals";
last.value = [last.value,s];
} else {
output.push({type: "StringLiteral", value: s});
}
};
WikiTextParseTree.prototype.compileMacroCall = function(output,renderer,type,node) {
/*jslint evil: true */
var name = node.name,
params = node.params,
macro = this.store.macros[name],
p,
n,
renderStep = {},
renderStepIndex = renderer.addRenderStep(renderStep);
// Check for errors
if(!macro) {
pushString(output,"{{** Unknown macro '" + name + "' **}}");
return;
}
if(macro.types.indexOf(type) === -1) {
pushString(output,"{{** Macro '" + name + "' cannot render to MIME type '" + type + "'**}}");
return;
}
renderStep.type = "macro";
renderStep.macro = name;
renderStep.renderType = type;
renderStep.step = renderStepIndex;
renderStep.dependencies = node.dependencies;
// Slot the parameters into the macro call
var properties = [];
for(p in params) {
if(params[p].type === "string") {
n = {type: "StringLiteral", value: params[p].value};
} else {
n = this.store.jsParser.parse(params[p].value).tree.elements[0];
}
properties.push({
type: "PropertyAssignment",
name: p,
value: n
});
}
renderStep.params = this.store.jsParser.createTree([
{
type: "Function",
name: null,
params: ["tiddler","renderer","store","utils"], // These are the parameters passed to the tiddler function; must match the invocation in WikiStore.renderTiddler()
elements: [ {
type: "ReturnStatement",
value: {
type: "ObjectLiteral",
properties: properties
}
} ]
}
]).compile("application/javascript").render;
// Compile any child nodes
var subOutput = [];
if(node.children) {
this.compileSubTreeHtml(subOutput,renderer,node.children);
}
renderStep.content = this.store.jsParser.createTree([
{
type: "Function",
name: null,
params: ["tiddler","renderer","store","utils"], // These are the parameters passed to the tiddler function; must match the invocation in WikiStore.renderTiddler()
elements: [
{
type: "ReturnStatement",
value: {
type: "FunctionCall",
name: {
type: "PropertyAccess",
base: {
type: "ArrayLiteral",
elements: subOutput
},
name: "join"
},
"arguments": [ {
type: "StringLiteral",
value: ""
}
]
}
}
]
}
]).compile("application/javascript").render;
// Add the wrapper node
var wrapperTag = macro.wrapperTag || "div";
if(type === "text/html" && !this.store.disableHtmlWrapperNodes) {
pushString(output,HTML(HTML.elem(wrapperTag,{
"data-tw-macro": name,
"data-tw-render-step": renderStepIndex
})));
}
// Output the macro call
output.push({
type: "FunctionCall",
name: {
base: {
name: "renderer",
type: "Variable"},
name: "render",
type: "PropertyAccess"},
"arguments": [ {
type: "Variable",
name: "tiddler"
},{
type: "Variable",
name: "store"
},{
type: "NumericLiteral",
value: renderStepIndex
}]
});
if(type === "text/html" && !this.store.disableHtmlWrapperNodes) {
pushString(output,"</" + wrapperTag + ">");
}
};
WikiTextParseTree.prototype.compileElementHtml = function(output,renderer,element,options) {
options = options || {};
pushString(output,HTML(HTML.elem(element.type,element.attributes)));
if(!options.selfClosing) {
if(element.children) {
this.compileSubTreeHtml(output,renderer,element.children);
}
pushString(output,"</" + element.type + ">");
}
};
WikiTextParseTree.prototype.compileSubTreeHtml = function(output,renderer,tree) {
for(var t=0; t<tree.length; t++) {
switch(tree[t].type) {
case "text":
pushString(output,utils.htmlEncode(tree[t].value));
break;
case "entity":
pushString(output,tree[t].value);
break;
case "br":
case "img":
this.compileElementHtml(output,renderer,tree[t],{selfClosing: true}); // Self closing elements
break;
case "macro":
this.compileMacroCall(output,renderer,"text/html",tree[t]);
break;
default:
this.compileElementHtml(output,renderer,tree[t]);
break;
}
}
};
WikiTextParseTree.prototype.compileElementPlain = function(output,renderer,element,options) {
options = options || {};
if(!options.selfClosing) {
if(element.children) {
this.compileSubTreePlain(output,renderer,element.children);
}
}
};
WikiTextParseTree.prototype.compileSubTreePlain = function(output,renderer,tree) {
for(var t=0; t<tree.length; t++) {
switch(tree[t].type) {
case "text":
pushString(output,tree[t].value);
break;
case "entity":
var c = utils.entityDecode(tree[t].value);
if(c) {
pushString(output,c);
} else {
pushString(output,tree[t].value);
}
break;
case "br":
case "img":
this.compileElementPlain(output,renderer,tree[t],{selfClosing: true}); // Self closing elements
break;
case "macro":
this.compileMacroCall(output,renderer,"text/plain",tree[t]);
break;
default:
this.compileElementPlain(output,renderer,tree[t]);
break;
}
}
};
// Render the parse tree to a debugging string of the specified MIME type
WikiTextParseTree.prototype.toString = function(type) {
var renderNode,
renderArray = function(tree) {
var children = [];
for(var t=0; t<tree.length; t++) {
children.push(HTML.elem("li",{
"class": ["nodeWikiText"]
},renderNode(tree[t])));
}
return HTML.elem("ul",{
"class": ["treeWikiText"]
},children);
},
renderTextNode = function(node) {
return [HTML.splitLabel(
node.type,
[HTML.text(node.type)],
[HTML.raw(utils.htmlEncode(node.value).replace(/\n/g,"<br>"))],
["treeNode"]
)];
},
renderDependencies = function(dependencies) {
var output = [];
for(var d in dependencies) {
if(d === "dependentAll") {
output.push(HTML.splitLabel("dependency",[HTML.text(d)],[HTML.text(dependencies[d])]));
} else {
var dependents = [];
for(var t in dependencies[d]) {
dependents.push(t);
}
output.push(HTML.splitLabel("dependency",[HTML.text(d)],[HTML.text(dependents.join(", "))]));
}
}
return HTML.splitLabel(
"dependencies",
[HTML.text("Dependencies")],
output
);
},
renderMacroNode = function(node) {
var params = [],
ret = [];
for(var p in node.params) {
var v = node.params[p].value;
if(node.params[p].type === "eval") {
v = "{{" + v + "}}";
}
params.push(HTML.splitLabel(
"param",
[HTML.text(p)],
[HTML.text(v)]
));
}
ret.push(HTML.splitLabel(
"macro",
[HTML.text(node.name)],
params,
["treeNode"]
));
if(node.dependencies) {
ret.push(renderDependencies(node.dependencies));
}
if(node.children) {
ret.push(renderArray(node.children));
}
return ret;
},
renderHtmlNode = function(node) {
var attributes = [],
ret = [];
for(var a in node.attributes) {
var v = node.attributes[a];
if(typeof v === "string") {
v = v;
} else if(v instanceof Array) {
v = v.join("; ");
} else if(typeof v === "object") {
var o = [];
for(var n in v) {
o.push(n,":",v[n],";");
}
v = o.join("");
}
attributes.push(HTML.splitLabel(
"attribute",
[HTML.text(a)],
[HTML.text(v)]
));
}
ret.push(HTML.splitLabel(
"html",
[HTML.text(node.type)],
attributes,
["treeNode"]
));
if(node.children) {
ret.push(renderArray(node.children));
}
return ret;
};
renderNode = function(node) {
if(node.type === "text") {
return renderTextNode(node);
} else if(node.type === "macro") {
return renderMacroNode(node);
} else {
return renderHtmlNode(node);
}
};
return HTML(renderDependencies(this.dependencies),type) + HTML(renderArray(this.tree),type);
};
exports.WikiTextParseTree = WikiTextParseTree;
})();