2012-12-13 21:34:31 +00:00
|
|
|
/*\
|
|
|
|
title: $:/core/modules/rendertree/wikirendertree.js
|
|
|
|
type: application/javascript
|
|
|
|
module-type: global
|
|
|
|
|
|
|
|
Wiki text render tree
|
|
|
|
|
|
|
|
\*/
|
|
|
|
(function(){
|
|
|
|
|
|
|
|
/*jslint node: true, browser: true */
|
|
|
|
/*global $tw: false */
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/*
|
|
|
|
Create a render tree object for a parse tree
|
2013-05-15 16:32:17 +00:00
|
|
|
parser: reference to the parse tree to be rendered
|
|
|
|
options: see below
|
|
|
|
Options include:
|
|
|
|
wiki: mandatory reference to wiki associated with this render tree
|
|
|
|
context: optional hashmap of context variables (see below)
|
2013-05-15 21:15:57 +00:00
|
|
|
parentRenderer: optional reference to a parent renderer node for the context chain
|
2013-05-17 09:12:25 +00:00
|
|
|
document: optional document object to use instead of global document
|
2013-05-15 16:32:17 +00:00
|
|
|
Context variables include:
|
|
|
|
tiddlerTitle: title of the tiddler providing the context
|
|
|
|
templateTitle: title of the tiddler providing the current template
|
|
|
|
macroDefinitions: hashmap of macro definitions
|
2012-12-13 21:34:31 +00:00
|
|
|
*/
|
|
|
|
var WikiRenderTree = function(parser,options) {
|
|
|
|
this.parser = parser;
|
|
|
|
this.wiki = options.wiki;
|
2013-05-15 16:32:17 +00:00
|
|
|
this.context = options.context || {};
|
2013-05-15 21:15:57 +00:00
|
|
|
this.parentRenderer = options.parentRenderer;
|
2013-05-17 16:29:43 +00:00
|
|
|
this.document = options.document;
|
2012-12-26 22:02:59 +00:00
|
|
|
// Hashmap of the renderer classes
|
|
|
|
if(!this.rendererClasses) {
|
|
|
|
WikiRenderTree.prototype.rendererClasses = $tw.modules.applyMethods("wikirenderer");
|
|
|
|
}
|
2012-12-13 21:34:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Generate the full render tree for this parse tree
|
|
|
|
*/
|
2013-05-15 16:32:17 +00:00
|
|
|
WikiRenderTree.prototype.execute = function() {
|
|
|
|
this.rendererTree = this.createRenderers(this,this.parser.tree);
|
2012-12-13 21:34:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Create an array of renderers for an array of parse tree nodes
|
|
|
|
*/
|
2013-05-15 16:32:17 +00:00
|
|
|
WikiRenderTree.prototype.createRenderers = function(parentRenderer,parseTreeNodes) {
|
2012-12-13 21:34:31 +00:00
|
|
|
var rendererNodes = [];
|
2012-12-15 17:34:48 +00:00
|
|
|
if(parseTreeNodes) {
|
|
|
|
for(var t=0; t<parseTreeNodes.length; t++) {
|
2013-05-15 16:32:17 +00:00
|
|
|
rendererNodes.push(this.createRenderer(parentRenderer,parseTreeNodes[t]));
|
2012-12-15 17:34:48 +00:00
|
|
|
}
|
2012-12-13 21:34:31 +00:00
|
|
|
}
|
|
|
|
return rendererNodes;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Create a renderer node for a parse tree node
|
|
|
|
*/
|
2013-05-15 16:32:17 +00:00
|
|
|
WikiRenderTree.prototype.createRenderer = function(parentRenderer,parseTreeNode) {
|
2012-12-26 22:02:59 +00:00
|
|
|
var RenderNodeClass = this.rendererClasses[parseTreeNode.type];
|
2013-05-15 16:32:17 +00:00
|
|
|
return new RenderNodeClass(this,parentRenderer,parseTreeNode);
|
2012-12-13 21:34:31 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Render to the DOM
|
|
|
|
*/
|
|
|
|
WikiRenderTree.prototype.renderInDom = function(container) {
|
|
|
|
this.container = container;
|
|
|
|
$tw.utils.each(this.rendererTree,function(node) {
|
|
|
|
if(node.renderInDom) {
|
|
|
|
container.appendChild(node.renderInDom());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Update the DOM rendering in the light of a set of changes
|
|
|
|
*/
|
|
|
|
WikiRenderTree.prototype.refreshInDom = function(changes) {
|
|
|
|
$tw.utils.each(this.rendererTree,function(node) {
|
|
|
|
if(node.refreshInDom) {
|
|
|
|
node.refreshInDom(changes);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2013-05-15 16:32:17 +00:00
|
|
|
/*
|
|
|
|
Find the value of a given context variable for a particular renderer node
|
|
|
|
*/
|
2013-05-15 17:43:19 +00:00
|
|
|
WikiRenderTree.prototype.getContextVariable = function(renderer,name,defaultValue) {
|
2013-05-15 16:32:17 +00:00
|
|
|
while(renderer) {
|
|
|
|
if($tw.utils.hop(renderer.context,name)) {
|
|
|
|
return renderer.context[name];
|
|
|
|
}
|
|
|
|
renderer = renderer.parentRenderer;
|
|
|
|
};
|
2013-05-15 17:43:19 +00:00
|
|
|
return defaultValue;
|
2013-05-15 16:32:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Check for render context recursion from a particular renderer node by returning true if the members of a proposed new render context are already present in the render context chain
|
|
|
|
*/
|
|
|
|
WikiRenderTree.prototype.checkContextRecursion = function(renderer,newContext) {
|
|
|
|
while(renderer) {
|
|
|
|
var context = renderer.context;
|
|
|
|
if(context) {
|
|
|
|
var match = true;
|
|
|
|
for(var member in newContext) {
|
|
|
|
if($tw.utils.hop(context,member)) {
|
|
|
|
if(newContext[member] !== context[member]) {
|
|
|
|
match = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
match = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(match) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
renderer = renderer.parentRenderer;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
WikiRenderTree.prototype.getContextScopeId = function(renderer) {
|
2013-07-03 15:00:10 +00:00
|
|
|
var guidBits = [],
|
2013-07-05 21:37:55 +00:00
|
|
|
scopeComponents = ["tiddlerTitle","templateTitle"],
|
|
|
|
processContext = function(field,name) {
|
2013-07-03 15:00:10 +00:00
|
|
|
if(scopeComponents.indexOf(name) !== -1) {
|
|
|
|
guidBits.push(name + ":" + field + ";");
|
|
|
|
}
|
2013-07-05 21:37:55 +00:00
|
|
|
};
|
|
|
|
while(renderer) {
|
|
|
|
if(renderer.context) {
|
|
|
|
$tw.utils.each(renderer.context,processContext);
|
2013-05-15 16:32:17 +00:00
|
|
|
guidBits.push("-");
|
|
|
|
}
|
|
|
|
renderer = renderer.parentRenderer;
|
|
|
|
}
|
|
|
|
return guidBits.join("");
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
Find a named macro definition
|
|
|
|
*/
|
|
|
|
WikiRenderTree.prototype.findMacroDefinition = function(renderer,name) {
|
|
|
|
while(renderer) {
|
|
|
|
if(renderer.context && renderer.context.macroDefinitions && renderer.context.macroDefinitions[name]) {
|
|
|
|
return renderer.context.macroDefinitions[name];
|
|
|
|
}
|
|
|
|
renderer = renderer.parentRenderer;
|
|
|
|
}
|
|
|
|
return undefined;
|
|
|
|
};
|
|
|
|
|
2013-06-18 14:37:19 +00:00
|
|
|
/*
|
|
|
|
Expand the parameters of a macro
|
|
|
|
*/
|
|
|
|
WikiRenderTree.prototype.substituteParameters = function(macroDefinition,macroCallParseTreeNode) {
|
|
|
|
var text = macroDefinition.text,
|
|
|
|
nextAnonParameter = 0; // Next candidate anonymous parameter in macro call
|
|
|
|
// Step through each of the parameters in the macro definition
|
|
|
|
for(var p=0; p<macroDefinition.params.length; p++) {
|
|
|
|
// Check if we've got a macro call parameter with the same name
|
|
|
|
var paramInfo = macroDefinition.params[p],
|
|
|
|
paramValue = undefined;
|
|
|
|
for(var m=0; m<macroCallParseTreeNode.params.length; m++) {
|
|
|
|
if(macroCallParseTreeNode.params[m].name === paramInfo.name) {
|
|
|
|
paramValue = macroCallParseTreeNode.params[m].value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If not, use the next available anonymous macro call parameter
|
|
|
|
if(!paramValue && macroCallParseTreeNode.params.length > 0) {
|
|
|
|
while(macroCallParseTreeNode.params[nextAnonParameter].name && nextAnonParameter < macroCallParseTreeNode.params.length-1) {
|
|
|
|
nextAnonParameter++;
|
|
|
|
}
|
|
|
|
if(!macroCallParseTreeNode.params[nextAnonParameter].name) {
|
|
|
|
paramValue = macroCallParseTreeNode.params[nextAnonParameter].value;
|
|
|
|
nextAnonParameter++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If we've still not got a value, use the default, if any
|
|
|
|
paramValue = paramValue || paramInfo["default"] || "";
|
|
|
|
// Replace any instances of this parameter
|
|
|
|
text = text.replace(new RegExp("\\$" + $tw.utils.escapeRegExp(paramInfo.name) + "\\$","mg"),paramValue);
|
|
|
|
}
|
|
|
|
return text;
|
|
|
|
};
|
|
|
|
|
2012-12-13 21:34:31 +00:00
|
|
|
exports.WikiRenderTree = WikiRenderTree;
|
|
|
|
|
|
|
|
})();
|