diff --git a/js/App.js b/js/App.js
index 066bc2aad..f5b174239 100644
--- a/js/App.js
+++ b/js/App.js
@@ -13,7 +13,7 @@ var WikiStore = require("./WikiStore.js").WikiStore,
Tiddler = require("./Tiddler.js").Tiddler,
tiddlerInput = require("./TiddlerInput.js"),
tiddlerOutput = require("./TiddlerOutput.js"),
- WikiTextProcessor = require("./WikiTextProcessor.js").WikiTextProcessor,
+ WikiTextParser = require("./WikiTextParser.js").WikiTextParser,
JavaScriptParser = require("./JavaScriptParser.js").JavaScriptParser,
Navigators = require("./Navigators.js").Navigators,
StoryNavigator = require("./StoryNavigator.js").StoryNavigator;
@@ -24,8 +24,8 @@ var App = function() {
this.isBrowser = typeof window !== "undefined";
// Create the main store
this.store = new WikiStore();
- // Register the wikitext processor
- this.store.registerTextProcessor("text/x-tiddlywiki",new WikiTextProcessor({
+ // Register the wikitext parser
+ this.store.registerParser("text/x-tiddlywiki",new WikiTextParser({
store: this.store
}));
// Register the standard tiddler serializers and deserializers
diff --git a/js/WikiStore.js b/js/WikiStore.js
index 02bc40b32..a9965fc1b 100755
--- a/js/WikiStore.js
+++ b/js/WikiStore.js
@@ -19,7 +19,7 @@ Available options are:
var WikiStore = function WikiStore(options) {
options = options || {};
this.tiddlers = {};
- this.textProcessors = {};
+ this.parsers = {};
this.tiddlerSerializers = {};
this.tiddlerDeserializers = {};
this.sandbox = options.sandbox;
@@ -28,8 +28,8 @@ var WikiStore = function WikiStore(options) {
});
};
-WikiStore.prototype.registerTextProcessor = function(type,processor) {
- this.textProcessors[type] = processor;
+WikiStore.prototype.registerParser = function(type,parser) {
+ this.parsers[type] = parser;
};
WikiStore.prototype.registerTiddlerSerializer = function(extension,mimeType,serializer) {
@@ -255,12 +255,12 @@ WikiStore.prototype.listTiddlers = function(type,template,emptyMessage) {
*/
WikiStore.prototype.parseText = function(type,text) {
- var processor = this.textProcessors[type];
- if(!processor) {
- processor = this.textProcessors["text/x-tiddlywiki"];
+ var parser = this.parsers[type];
+ if(!parser) {
+ parser = this.parsers["text/x-tiddlywiki"];
}
- if(processor) {
- return processor.parse(text);
+ if(parser) {
+ return parser.parse(text);
} else {
return null;
}
diff --git a/js/WikiTextParseTree.js b/js/WikiTextParseTree.js
new file mode 100644
index 000000000..8287ddc24
--- /dev/null
+++ b/js/WikiTextParseTree.js
@@ -0,0 +1,250 @@
+/*\
+title: js/WikiTextParseTree.js
+
+Compile a wikitext parse tree into a JavaScript function that renders the required
+representation of the tree.
+
+\*/
+(function(){
+
+/*jslint node: true */
+"use strict";
+
+var ArgParser = require("./ArgParser.js").ArgParser,
+ JavaScriptParseTree = require("./JavaScriptParseTree.js").JavaScriptParseTree,
+ utils = require("./Utils.js"),
+ util = require("util");
+
+// Intialise the parse tree object
+var WikiTextParseTree = function(tree,store) {
+ this.tree = tree;
+ this.store = store;
+};
+
+WikiTextParseTree.prototype.render = function(type,treenode,store,title) {
+ /*jslint evil: true */
+ var code = this.compile(type,treenode);
+ var fn = eval(code);
+ var tiddler = store.getTiddler(title);
+ return fn(tiddler,store,utils);
+};
+
+// Compile the parse tree into a JavaScript function that returns the required
+// representation of the tree
+WikiTextParseTree.prototype.compile = function(type,treenode) {
+ treenode = treenode || this.tree;
+ this.output = [];
+ if(type === "text/html") {
+ this.compileSubTreeHtml(treenode);
+ } else if(type === "text/plain") {
+ this.compileSubTreePlain(treenode);
+ } else {
+ return null;
+ }
+ // And then wrap the javascript tree and render it back into JavaScript code
+ var parseTree = this.store.jsParser.createTree(
+ [
+ {
+ type: "Function",
+ name: null,
+ params: ["tiddler","store","utils"],
+ elements: [
+ {
+ type: "ReturnStatement",
+ value: {
+ type: "FunctionCall",
+ name: {
+ type: "PropertyAccess",
+ base: {
+ type: "ArrayLiteral",
+ elements: this.output
+ },
+ name: "join"
+ },
+ "arguments": [ {
+ type: "StringLiteral",
+ value: ""
+ }
+ ]
+ }
+ }
+ ]
+ }
+ ]);
+ return parseTree.render();
+};
+
+WikiTextParseTree.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});
+ }
+};
+
+WikiTextParseTree.prototype.compileMacroCall = function(type,name,params) {
+ var me = this,
+ 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};
+ }
+ };
+ 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[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("Unknown macro '" + name + "'");
+ }
+};
+
+WikiTextParseTree.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("");
+ }
+ 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 + ">");
+ }
+};
+
+WikiTextParseTree.prototype.compileSubTreeHtml = function(tree) {
+ for(var t=0; t this.nextMatch)
@@ -52,18 +78,18 @@ WikiTextParser.prototype.subWikifyUnterm = function(output) {
this.matchStart = ruleMatch.index;
this.matchLength = ruleMatch[0].length;
this.matchText = ruleMatch[0];
- this.nextMatch = this.processor.rulesRegExp.lastIndex;
+ this.nextMatch = this.rulesRegExp.lastIndex;
// Figure out which rule matched and call its handler
var t;
for(t=1; t");
- if(!selfClosing) {
- if(element.children) {
- renderSubTree(element.children);
- }
- output.push("" + element.type + ">");
- }
- };
- renderSubTree = function(tree) {
- for(var t=0; t>",
- filter = args.getValueByName("filter",null),
- tiddlers,
- lastGroup = "",
- ul,
- last = 0,
- t;
- limit = limit ? parseInt(limit,10) : null;
- template = template ? this.store.getTiddler(template) : null;
- if(template) {
- templateType = template.fields.type;
- templateText = template.fields.text;
- }
- groupTemplate = groupTemplate ? this.store.getTiddler(groupTemplate) : null;
- if(groupTemplate) {
- groupTemplateType = groupTemplate.fields.type;
- groupTemplateText = groupTemplate.fields.text;
- }
- if(filter) {
- // Filtering not implemented yet
- tiddlers = this.store.getTitles(field,"excludeLists");
- } else {
- tiddlers = this.store.getTitles(field,"excludeLists");
- }
- if(limit !== null) {
- last = tiddlers.length - Math.min(tiddlers.length,limit);
- }
- for(t=tiddlers.length-1; t>=last; t--) {
- var tiddler = tiddlers[t],
- theGroupParseTree = this.store.parseText(groupTemplateType,groupTemplateText),
- theGroup = theGroupParseTree.render("text/plain",theGroupParseTree.children,this.store,tiddler);
- if(theGroup !== "") {
- if(ul === undefined || theGroup !== lastGroup) {
- ul = {type: "ul", attributes: {"class": "timeline"}, children: []};
- macroNode.output.push(ul);
- ul.children.push({type: "li", attributes: {"class": "listTitle"}, children: [{type: "text", value: theGroup}]});
- lastGroup = theGroup;
- }
- var item = {
- type: "li",
- attributes: {
- "class": "listLink"},
- children: [ {
- type: "context",
- tiddler: tiddler,
- children: []
- }]};
- ul.children.push(item);
- item.children[0].children = this.store.parseText(templateType,templateText).children;
- }
- }
- this.executeMacros(macroNode.output,title);
- }
- },
- list: {
- argOptions: {defaultName:"type"},
- handler: function(macroNode,args,title) {
- var type = args.getValueByName("type","all"),
- template = args.getValueByName("template",null),
- templateType = "text/x-tiddlywiki", templateText = "<>",
- emptyMessage = args.getValueByName("emptyMessage",null);
- // Get the template to use
- template = template ? this.store.getTiddler(template) : null;
- if(template) {
- templateType = template.fields.type;
- templateText = template.fields.text;
- }
- // Get the handler and the tiddlers
- var handler = WikiTextRenderer.macros.list.types[type];
- handler = handler || WikiTextRenderer.macros.list.types.all;
- var tiddlers = handler.call(this);
- // Render them as a list
- var ul = {type: "ul", children: []};
- for(var t=0; t 0) {
- macroNode.output.push(ul);
- this.executeMacros(macroNode.output,title);
- } else if (emptyMessage) {
- macroNode.output.push({type: "text", value: emptyMessage});
- }
- },
- types: {
- all: function() {
- return this.store.getTitles("title","excludeLists");
- },
- missing: function() {
- return this.store.getMissingTitles();
- },
- orphans: function() {
- return this.store.getOrphanTitles();
- },
- shadowed: function() {
- return this.store.getShadowTitles();
- },
- touched: function() {
- // Server syncing isn't implemented yet
- return [];
- },
- filter: function() {
- // Filters aren't implemented yet
- return [];
- }
- }
- },
- slider: {
- handler: function(macroNode,args,title) {
- }
- },
- tabs: {
- handler: function(macroNode,args,title) {
- }
- },
- tag: {
- handler: function(macroNode,args,title) {
- }
- },
- tagging: {
- handler: function(macroNode,args,title) {
- }
- },
- tags: {
- handler: function(macroNode,args,title) {
- }
- },
- tiddler: {
- argOptions: {defaultName:"name",cascadeDefaults:true},
- handler: function(macroNode,args,title) {
- var targetTitle = args.getValueByName("name",null),
- withTokens = args.getValuesByName("with",[]),
- tiddler = this.store.getTiddler(targetTitle),
- text = this.store.getTiddlerText(targetTitle,""),
- t;
- for(t=0; t