1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-23 10:07:19 +00:00

Refactored WikiTextCompiler

As promised, to reduce duplication of code
This commit is contained in:
Jeremy Ruston 2012-01-04 19:32:38 +00:00
parent 3ff1d9a76c
commit 733e93de63

View File

@ -19,154 +19,15 @@ var WikiTextCompiler = function(store,title,parser) {
};
WikiTextCompiler.prototype.compile = function(type,treenode) {
this.output = [];
if(type === "text/html") {
return this.compileAsHtml(treenode);
this.compileSubTreeHtml(treenode);
} else if(type === "text/plain") {
return this.compileAsText(treenode);
this.compileSubTreePlain(treenode);
} else {
return null;
}
};
WikiTextCompiler.prototype.compileAsHtml = function(treenode) {
var me = this,
output = [],
compileSubTree;
var pushString = function(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});
}
};
var compileElement = function(element, options) {
options = options || {};
var tagBits = [element.type];
if(element.attributes) {
for(var a in element.attributes) {
var r = element.attributes[a];
if(a === "style") {
var s = [];
for(var t in r) {
s.push(t + ":" + r[t] + ";");
}
r = s.join("");
}
tagBits.push(a + "=\"" + utils.htmlEncode(r) + "\"");
}
}
pushString("<" + tagBits.join(" ") + (options.selfClosing ? " /" : ""));
if(options.insertAfterAttributes) {
pushString(" ");
output.push(options.insertAfterAttributes);
}
pushString(">");
if(!options.selfClosing) {
if(element.children) {
compileSubTree(element.children);
}
pushString("</" + element.type + ">");
}
};
var compileMacroCall = function(name,params) {
var macro = me.store.macros[name];
if(macro) {
var args = new ArgParser(params,{defaultName: "anon"}),
paramsProps = {};
var insertParam = function(name,arg) {
if(arg.evaluated) {
var prog = me.store.jsParser.parse(arg.string);
paramsProps[name] = prog.tree.elements[0];
} else {
paramsProps[name] = {type: "StringLiteral", value: arg.string};
}
};
for(var m in macro.params) {
var param = macro.params[m];
if("byPos" in param && args.byPos[param.byPos]) {
insertParam(m,args.byPos[param.byPos].v);
} else if("byName" in param) {
var arg = args.getValueByName(m);
if(!arg && param.byName === "default") {
arg = args.getValueByName("anon");
}
if(arg) {
insertParam(m,arg);
}
}
}
var macroCall = {
type: "FunctionCall",
name: {
type: "Function",
name: null,
params: ["params"],
elements: []},
"arguments": [ {
type: "ObjectLiteral",
properties: []
}]
};
macroCall.name.elements = macro.code["text/html"].tree.elements;
for(m in paramsProps) {
macroCall["arguments"][0].properties.push({
type: "PropertyAssignment",
name: m,
value: paramsProps[m]
});
}
output.push(macroCall);
} else {
pushString("<span class='error errorUnknownMacro'>Unknown macro '" + name + "'</span>");
}
};
compileSubTree = function(tree) {
for(var t=0; t<tree.length; t++) {
switch(tree[t].type) {
case "text":
pushString(utils.htmlEncode(tree[t].value));
break;
case "entity":
pushString(tree[t].value);
break;
case "br":
case "img":
compileElement(tree[t],{selfClosing: true}); // Self closing elements
break;
case "context":
//compileSubTree(tree[t].children);
break;
case "macro":
compileMacroCall(tree[t].name,tree[t].params);
break;
case "a":
compileElement(tree[t],{
insertAfterAttributes: {
"type": "FunctionCall",
"name": {
"type": "PropertyAccess",
"base": {
"type": "Variable",
"name": "store"},
"name": "classesForLink"},
"arguments":[{
"type": "StringLiteral",
"value": tree[t].attributes.href}]}
});
break;
default:
compileElement(tree[t]);
break;
}
}
};
// Compile the wiki parse tree into a javascript parse tree
compileSubTree(treenode);
// And then render the javascript parse tree back into JavaScript code
// And then wrap the javascript tree and render it back into JavaScript code
var parseTree = this.store.jsParser.createTree(
[
{
@ -182,7 +43,7 @@ WikiTextCompiler.prototype.compileAsHtml = function(treenode) {
type: "PropertyAccess",
base: {
type: "ArrayLiteral",
elements: output
elements: this.output
},
name: "join"
},
@ -199,144 +60,175 @@ WikiTextCompiler.prototype.compileAsHtml = function(treenode) {
return parseTree.render();
};
WikiTextCompiler.prototype.compileAsText = function(treenode) {
WikiTextCompiler.prototype.pushString = function(s) {
var last = this.output[this.output.length-1];
if(this.output.length > 0 && last.type === "StringLiterals") {
last.value.push(s);
} else if (this.output.length > 0 && last.type === "StringLiteral") {
last.type = "StringLiterals";
last.value = [last.value,s];
} else {
this.output.push({type: "StringLiteral", value: s});
}
};
WikiTextCompiler.prototype.compileMacroCall = function(type,name,params) {
var me = this,
output = [],
compileSubTree;
var pushString = function(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});
}
};
var compileElement = function(element, options) {
options = options || {};
if(!options.selfClosing) {
if(element.children) {
compileSubTree(element.children);
macro = this.store.macros[name];
if(macro) {
var args = new ArgParser(params,{defaultName: "anon"}),
paramsProps = {};
var insertParam = function(name,arg) {
if(arg.evaluated) {
paramsProps[name] = me.store.jsParser.parse(arg.string).tree.elements[0];
} else {
paramsProps[name] = {type: "StringLiteral", value: arg.string};
}
}
};
var compileMacroCall = function(name,params) {
var macro = me.store.macros[name];
if(macro) {
var args = new ArgParser(params,{defaultName: "anon"}),
paramsProps = {};
var insertParam = function(name,arg) {
if(arg.evaluated) {
var prog = me.store.jsParser.parse(arg.string);
paramsProps[name] = prog.tree.elements[0];
} else {
paramsProps[name] = {type: "StringLiteral", value: arg.string};
};
for(var m in macro.params) {
var param = macro.params[m];
if("byPos" in param && args.byPos[param.byPos]) {
insertParam(m,args.byPos[param.byPos].v);
} else if("byName" in param) {
var arg = args.getValueByName(m);
if(!arg && param.byName === "default") {
arg = args.getValueByName("anon");
}
};
for(var m in macro.params) {
var param = macro.params[m];
if("byPos" in param && args.byPos[param.byPos]) {
insertParam(m,args.byPos[param.byPos].v);
} else if("byName" in param) {
var arg = args.getValueByName(m);
if(!arg && param.byName === "default") {
arg = args.getValueByName("anon");
}
if(arg) {
insertParam(m,arg);
}
if(arg) {
insertParam(m,arg);
}
}
var macroCall = {
type: "FunctionCall",
name: {
type: "Function",
name: null,
params: ["params"],
elements: []},
"arguments": [ {
type: "ObjectLiteral",
properties: []
}]
};
macroCall.name.elements = macro.code["text/plain"].tree.elements;
for(m in paramsProps) {
macroCall["arguments"][0].properties.push({
type: "PropertyAssignment",
name: m,
value: paramsProps[m]
});
}
output.push(macroCall);
} else {
pushString("{{** Unknown macro '" + name + "' **}}");
}
};
compileSubTree = function(tree) {
for(var t=0; t<tree.length; t++) {
switch(tree[t].type) {
case "text":
pushString(utils.htmlEncode(tree[t].value));
break;
case "entity":
var c = utils.entityDecode(tree[t].value);
if(c) {
pushString(c);
} else {
pushString(tree[t].value);
}
break;
case "br":
case "img":
compileElement(tree[t],{selfClosing: true}); // Self closing elements
break;
case "context":
//compileSubTree(tree[t].children);
break;
case "macro":
compileMacroCall(tree[t].name,tree[t].params);
break;
default:
compileElement(tree[t]);
break;
}
}
};
// Compile the wiki parse tree into a javascript parse tree
compileSubTree(treenode);
// And then render the javascript parse tree back into JavaScript code
var parseTree = this.store.jsParser.createTree(
[
{
var macroCall = {
type: "FunctionCall",
name: {
type: "Function",
name: null,
params: ["tiddler","store","utils"],
elements: [
{
type: "ReturnStatement",
value: {
type: "FunctionCall",
name: {
type: "PropertyAccess",
base: {
type: "ArrayLiteral",
elements: output
},
name: "join"
},
"arguments": [ {
type: "StringLiteral",
value: ""
}
]
}
}
]
params: ["params"],
elements: []},
"arguments": [ {
type: "ObjectLiteral",
properties: []
}]
};
macroCall.name.elements = macro.code[type].tree.elements;
for(m in paramsProps) {
macroCall["arguments"][0].properties.push({
type: "PropertyAssignment",
name: m,
value: paramsProps[m]
});
}
this.output.push(macroCall);
} else {
this.pushString("<span class='error errorUnknownMacro'>Unknown macro '" + name + "'</span>");
}
};
WikiTextCompiler.prototype.compileElementHtml = function(element, options) {
options = options || {};
var tagBits = [element.type];
if(element.attributes) {
for(var a in element.attributes) {
var r = element.attributes[a];
if(a === "style") {
var s = [];
for(var t in r) {
s.push(t + ":" + r[t] + ";");
}
r = s.join("");
}
]);
return parseTree.render();
tagBits.push(a + "=\"" + utils.htmlEncode(r) + "\"");
}
}
this.pushString("<" + tagBits.join(" ") + (options.selfClosing ? " /" : ""));
if(options.insertAfterAttributes) {
this.pushString(" ");
this.output.push(options.insertAfterAttributes);
}
this.pushString(">");
if(!options.selfClosing) {
if(element.children) {
this.compileSubTreeHtml(element.children);
}
this.pushString("</" + element.type + ">");
}
};
WikiTextCompiler.prototype.compileSubTreeHtml = function(tree) {
for(var t=0; t<tree.length; t++) {
switch(tree[t].type) {
case "text":
this.pushString(utils.htmlEncode(tree[t].value));
break;
case "entity":
this.pushString(tree[t].value);
break;
case "br":
case "img":
this.compileElementHtml(tree[t],{selfClosing: true}); // Self closing elements
break;
case "context":
//compileSubTree(tree[t].children);
break;
case "macro":
this.compileMacroCall("text/html",tree[t].name,tree[t].params);
break;
case "a":
this.compileElementHtml(tree[t],{
insertAfterAttributes: {
"type": "FunctionCall",
"name": {
"type": "PropertyAccess",
"base": {
"type": "Variable",
"name": "store"},
"name": "classesForLink"},
"arguments":[{
"type": "StringLiteral",
"value": tree[t].attributes.href}]}
});
break;
default:
this.compileElementHtml(tree[t]);
break;
}
}
};
WikiTextCompiler.prototype.compileElementPlain = function(element, options) {
options = options || {};
if(!options.selfClosing) {
if(element.children) {
this.compileSubTreePlain(element.children);
}
}
};
WikiTextCompiler.prototype.compileSubTreePlain = function(tree) {
for(var t=0; t<tree.length; t++) {
switch(tree[t].type) {
case "text":
this.pushString(utils.htmlEncode(tree[t].value));
break;
case "entity":
this.pushString(tree[t].value);
break;
case "br":
case "img":
this.compileElementPlain(tree[t],{selfClosing: true}); // Self closing elements
break;
case "context":
//compileSubTree(tree[t].children);
break;
case "macro":
this.compileMacroCall("text/plain",tree[t].name,tree[t].params);
break;
default:
this.compileElementPlain(tree[t]);
break;
}
}
};
exports.WikiTextCompiler = WikiTextCompiler;