mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-12-27 10:30:28 +00:00
83f85cad7d
It doesn't actually work, though, which is weird, since SVG works just fine using the same mechanism
213 lines
6.5 KiB
JavaScript
213 lines
6.5 KiB
JavaScript
/*\
|
|
title: $:/core/modules/rendertree/renderers/element.js
|
|
type: application/javascript
|
|
module-type: wikirenderer
|
|
|
|
Element renderer
|
|
|
|
\*/
|
|
(function(){
|
|
|
|
/*jslint node: true, browser: true */
|
|
/*global $tw: false */
|
|
"use strict";
|
|
|
|
/*
|
|
Element widget. A degenerate widget that renders ordinary HTML elements
|
|
*/
|
|
var ElementWidget = function(renderer) {
|
|
this.renderer = renderer;
|
|
this.tag = this.renderer.parseTreeNode.tag;
|
|
this.attributes = this.renderer.attributes;
|
|
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
|
|
this.events = this.renderer.parseTreeNode.events;
|
|
};
|
|
|
|
ElementWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
|
|
// Check if any of our attribute dependencies have changed
|
|
if($tw.utils.count(changedAttributes) > 0) {
|
|
// Update our attributes
|
|
this.renderer.assignAttributes();
|
|
}
|
|
// Refresh any child nodes
|
|
$tw.utils.each(this.children,function(node) {
|
|
if(node.refreshInDom) {
|
|
node.refreshInDom(changedTiddlers);
|
|
}
|
|
});
|
|
};
|
|
|
|
/*
|
|
Element renderer
|
|
*/
|
|
var ElementRenderer = function(renderTree,parentRenderer,parseTreeNode) {
|
|
// Store state information
|
|
this.renderTree = renderTree;
|
|
this.parentRenderer = parentRenderer;
|
|
this.parseTreeNode = parseTreeNode;
|
|
// Initialise widget classes
|
|
if(!this.widgetClasses) {
|
|
ElementRenderer.prototype.widgetClasses = $tw.modules.applyMethods("widget");
|
|
}
|
|
// Select the namespace for the tag
|
|
var tagNameSpaces = {
|
|
svg: "http://www.w3.org/2000/svg",
|
|
math: "http://www.w3.org/1998/Math/MathML"
|
|
};
|
|
this.namespace = tagNameSpaces[this.parseTreeNode.tag];
|
|
if(this.namespace) {
|
|
this.context = this.context || {};
|
|
this.context.namespace = this.namespace;
|
|
} else {
|
|
this.namespace = this.renderTree.getContextVariable(this.parentRenderer,"namespace","http://www.w3.org/1999/xhtml");
|
|
}
|
|
// Get the context tiddler title
|
|
this.tiddlerTitle = this.renderTree.getContextVariable(this.parentRenderer,"tiddlerTitle");
|
|
// Compute our dependencies
|
|
this.dependencies = {};
|
|
var self = this;
|
|
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
|
if(attribute.type === "indirect") {
|
|
var tr = $tw.utils.parseTextReference(attribute.textReference);
|
|
self.dependencies[tr.title ? tr.title : self.tiddlerTitle] = true;
|
|
}
|
|
});
|
|
// Compute our attributes
|
|
this.attributes = {};
|
|
this.computeAttributes();
|
|
// Create the parasite widget object if required
|
|
if(this.parseTreeNode.tag.charAt(0) === "$") {
|
|
// Choose the class
|
|
var WidgetClass = this.widgetClasses[this.parseTreeNode.tag.substr(1)];
|
|
// Instantiate the widget
|
|
if(WidgetClass) {
|
|
this.widget = new WidgetClass(this);
|
|
} else {
|
|
WidgetClass = this.widgetClasses.error;
|
|
if(WidgetClass) {
|
|
this.widget = new WidgetClass(this,"Unknown widget '<" + this.parseTreeNode.tag + ">'");
|
|
}
|
|
}
|
|
}
|
|
// If we haven't got a widget, use the generic HTML element widget
|
|
if(!this.widget) {
|
|
this.widget = new ElementWidget(this);
|
|
}
|
|
};
|
|
|
|
ElementRenderer.prototype.computeAttributes = function() {
|
|
var changedAttributes = {},
|
|
self = this,
|
|
value;
|
|
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
|
|
if(attribute.type === "indirect") {
|
|
value = self.renderTree.wiki.getTextReference(attribute.textReference,"",self.tiddlerTitle);
|
|
} else if(attribute.type === "macro") {
|
|
// Get the macro definition
|
|
var macro = self.renderTree.findMacroDefinition(self.parentRenderer,attribute.value.name);
|
|
if(!macro) {
|
|
value = "";
|
|
} else {
|
|
// Substitute the macro parameters
|
|
value = self.renderTree.substituteParameters(macro,attribute.value);
|
|
// Parse the text and render it as text
|
|
value = self.renderTree.wiki.renderText("text/plain","text/vnd.tiddlywiki",value,self.context);
|
|
}
|
|
} else { // String attribute
|
|
value = attribute.value;
|
|
}
|
|
// Check whether the attribute has changed
|
|
if(self.attributes[name] !== value) {
|
|
self.attributes[name] = value;
|
|
changedAttributes[name] = true;
|
|
}
|
|
});
|
|
return changedAttributes;
|
|
};
|
|
|
|
ElementRenderer.prototype.hasAttribute = function(name) {
|
|
return $tw.utils.hop(this.attributes,name);
|
|
};
|
|
|
|
ElementRenderer.prototype.getAttribute = function(name,defaultValue) {
|
|
if($tw.utils.hop(this.attributes,name)) {
|
|
return this.attributes[name];
|
|
} else {
|
|
return defaultValue;
|
|
}
|
|
};
|
|
|
|
ElementRenderer.prototype.renderInDom = function() {
|
|
// Check if our widget is providing an element
|
|
if(this.widget.tag) {
|
|
// Create the element
|
|
this.domNode = this.renderTree.document.createElementNS(this.namespace,this.widget.tag);
|
|
// Assign any specified event handlers
|
|
$tw.utils.addEventListeners(this.domNode,this.widget.events);
|
|
// Assign the attributes
|
|
this.assignAttributes();
|
|
// Render any child nodes
|
|
var self = this;
|
|
$tw.utils.each(this.widget.children,function(node) {
|
|
if(node.renderInDom) {
|
|
self.domNode.appendChild(node.renderInDom());
|
|
}
|
|
});
|
|
// Call postRenderInDom if the widget provides it and we're in the browser
|
|
if($tw.browser && this.widget.postRenderInDom) {
|
|
this.widget.postRenderInDom();
|
|
}
|
|
// Return the dom node
|
|
return this.domNode;
|
|
} else {
|
|
// If we're not generating an element, just render our first child
|
|
return this.widget.children[0].renderInDom();
|
|
}
|
|
};
|
|
|
|
ElementRenderer.prototype.assignAttributes = function() {
|
|
var self = this;
|
|
$tw.utils.each(this.widget.attributes,function(v,a) {
|
|
if(v !== undefined) {
|
|
if($tw.utils.isArray(v)) { // Ahem, could there be arrays other than className?
|
|
self.domNode.className = v.join(" ");
|
|
} else if (typeof v === "object") { // ...or objects other than style?
|
|
for(var p in v) {
|
|
self.domNode.style[$tw.utils.unHyphenateCss(p)] = v[p];
|
|
}
|
|
} else {
|
|
// Setting certain attributes can cause a DOM error (eg xmlns on the svg element)
|
|
try {
|
|
self.domNode.setAttributeNS(null,a,v);
|
|
} catch(e) {
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
ElementRenderer.prototype.refreshInDom = function(changedTiddlers) {
|
|
// Update our attributes if required
|
|
var changedAttributes = {};
|
|
if($tw.utils.checkDependencies(this.dependencies,changedTiddlers)) {
|
|
changedAttributes = this.computeAttributes();
|
|
}
|
|
// Check if the widget has a refreshInDom method
|
|
if(this.widget.refreshInDom) {
|
|
// Let the widget refresh itself
|
|
this.widget.refreshInDom(changedAttributes,changedTiddlers);
|
|
} else {
|
|
// If not, assign the attributes and refresh any child nodes
|
|
this.assignAttributes();
|
|
$tw.utils.each(this.widget.children,function(node) {
|
|
if(node.refreshInDom) {
|
|
node.refreshInDom(changedTiddlers);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
exports.element = ElementRenderer
|
|
|
|
})();
|