1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-06-16 18:39:54 +00:00

Refactored widget renderers to be hosted within HTML element renderers

This arrangement takes better advantage of the similarities between the
now deleted widget renderer and the element renderer. It also obviates
the need for wrapper elements around every widget.
This commit is contained in:
Jeremy Ruston 2013-01-03 16:27:55 +00:00
parent 2124dd1ac1
commit 6d24cedbcc
28 changed files with 429 additions and 564 deletions

View File

@ -37,8 +37,8 @@ exports.parse = function() {
return [{type: "text", text: this.match[0].substr(1)}];
} else {
return [{
type: "widget",
tag: "link",
type: "element",
tag: "$link",
attributes: {
to: {type: "string", value: this.match[0]}
},

View File

@ -40,8 +40,8 @@ exports.parse = function() {
classes = this.match[5];
// Return the list widget
var node = {
type: "widget",
tag: "list",
type: "element",
tag: "$list",
attributes: {
filter: {type: "string", value: filter}
},

View File

@ -40,8 +40,8 @@ exports.parse = function() {
classes = this.match[5];
// Return the list widget
var node = {
type: "widget",
tag: "list",
type: "element",
tag: "$list",
attributes: {
filter: {type: "string", value: filter}
}

View File

@ -32,9 +32,9 @@ exports.init = function(parser) {
this.parser = parser;
// Regexp to match
if(this.is.block) {
this.matchRegExp = /<(\$)?([A-Za-z]+)(\s*[^>]*?)(\/)?>(\r?\n)/mg;
this.matchRegExp = /<([A-Za-z\$]+)(\s*[^>]*?)(\/)?>(\r?\n)/mg;
} else {
this.matchRegExp = /<(\$)?([A-Za-z]+)(\s*[^>]*?)(?:(\/>)|(?:>(\r?\n)?))/mg;
this.matchRegExp = /<([A-Za-z\$]+)(\s*[^>]*?)(?:(\/>)|(?:>(\r?\n)?))/mg;
}
};
@ -43,11 +43,10 @@ Parse the most recent match
*/
exports.parse = function() {
// Get all the details of the match in case this parser is called recursively
var isWidget = !!this.match[1],
tagName = this.match[2],
attributeString = this.match[3],
isSelfClosing = !!this.match[4],
hasLineBreak = !!this.match[5];
var tagName = this.match[1],
attributeString = this.match[2],
isSelfClosing = !!this.match[3],
hasLineBreak = !!this.match[4];
// Move past the tag name and parameters
this.parser.pos = this.matchRegExp.lastIndex;
var reAttr = /\s*([A-Za-z\-_]+)(?:\s*=\s*(?:("[^"]*")|('[^']*')|(\{\{[^\}]*\}\})|([^"'\s]+)))?/mg;
@ -72,8 +71,8 @@ exports.parse = function() {
attrMatch = reAttr.exec(attributeString);
}
// Process the end tag
if(!isSelfClosing && (isWidget || voidElements.indexOf(tagName) === -1)) {
var reEndString = "(</" + (isWidget ? "\\$" : "") + tagName + ">)",
if(!isSelfClosing && voidElements.indexOf(tagName) === -1) {
var reEndString = "(</" + $tw.utils.escapeRegExp(tagName) + ">)",
reEnd = new RegExp(reEndString,"mg"),
content;
if(hasLineBreak) {
@ -90,7 +89,7 @@ exports.parse = function() {
content = [];
}
var element = {
type: isWidget ? "widget" : "element",
type: "element",
tag: tagName,
isBlock: this.is.block || hasLineBreak,
attributes: attributes,

View File

@ -34,8 +34,8 @@ exports.parse = function() {
var text = this.match[1],
link = this.match[2] || text;
return [{
type: "widget",
tag: "link",
type: "element",
tag: "$link",
attributes: {
to: {type: "string", value: link}
},

View File

@ -41,6 +41,9 @@ exports.parse = function() {
var node = {
type: "element",
tag: "span",
attributes: {
"class": {type: "string", value: "tw-inline-style"}
},
children: tree
};
if(classString) {

View File

@ -40,8 +40,8 @@ exports.parse = function() {
classes = this.match[5];
// Return the transclude widget
var node = {
type: "widget",
tag: "transclude",
type: "element",
tag: "$transclude",
attributes: {
target: {type: "string", value: targetTitle}
},

View File

@ -40,8 +40,8 @@ exports.parse = function() {
classes = this.match[5];
// Return the transclude widget
var node = {
type: "widget",
tag: "transclude",
type: "element",
tag: "$transclude",
attributes: {
target: {type: "string", value: targetTitle}
}

View File

@ -64,8 +64,8 @@ exports.parse = function() {
}
}
return [{
type: "widget",
tag: "link",
type: "element",
tag: "$link",
attributes: {
to: {type: "string", value: linkText}
},

View File

@ -12,6 +12,31 @@ Element renderer
/*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.renderContext,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
*/
@ -20,50 +45,87 @@ var ElementRenderer = function(renderTree,renderContext,parseTreeNode) {
this.renderTree = renderTree;
this.renderContext = renderContext;
this.parseTreeNode = parseTreeNode;
// Initialise widget classes
if(!this.widgetClasses) {
ElementRenderer.prototype.widgetClasses = $tw.modules.applyMethods("widget");
}
// 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);
if(tr.title) {
self.dependencies[tr.title] = true;
} else {
self.dependencies[renderContext.tiddlerTitle] = true;
}
self.dependencies[tr.title ? tr.title : renderContext.tiddlerTitle] = true;
}
});
// Compute our attributes
this.attributes = {};
this.computeAttributes();
// Create the renderers for the child nodes
this.children = this.renderTree.createRenderers(this.renderContext,this.parseTreeNode.children);
// 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() {
this.attributes = {};
var changedAttributes = {};
var self = this;
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
if(attribute.type === "indirect") {
self.attributes[name] = self.renderTree.wiki.getTextReference(attribute.textReference,self.renderContext.tiddlerTitle);
var value = self.renderTree.wiki.getTextReference(attribute.textReference,self.renderContext.tiddlerTitle);
if(self.attributes[name] !== value) {
self.attributes[name] = value;
changedAttributes[name] = true;
}
} else { // String attribute
self.attributes[name] = attribute.value;
if(self.attributes[name] !== attribute.value) {
self.attributes[name] = attribute.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.render = function(type) {
var isHtml = type === "text/html",
output = [],attr,a,v;
if(isHtml) {
output.push("<",this.parseTreeNode.tag);
if(this.attributes) {
output.push("<",this.widget.tag);
if(this.widget.attributes) {
attr = [];
for(a in this.attributes) {
for(a in this.widget.attributes) {
attr.push(a);
}
attr.sort();
for(a=0; a<attr.length; a++) {
v = this.attributes[attr[a]];
v = this.widget.attributes[attr[a]];
if(v !== undefined) {
if($tw.utils.isArray(v)) {
v = v.join(" ");
@ -78,19 +140,19 @@ ElementRenderer.prototype.render = function(type) {
}
}
}
if(!this.children || this.children.length === 0) {
if(!this.widget.children || this.widget.children.length === 0) {
output.push("/");
}
output.push(">");
}
if(this.children && this.children.length > 0) {
$tw.utils.each(this.children,function(node) {
if(this.widget.children && this.widget.children.length > 0) {
$tw.utils.each(this.widget.children,function(node) {
if(node.render) {
output.push(node.render(type));
}
});
if(isHtml) {
output.push("</",this.parseTreeNode.tag,">");
output.push("</",this.widget.tag,">");
}
}
return output.join("");
@ -98,25 +160,29 @@ ElementRenderer.prototype.render = function(type) {
ElementRenderer.prototype.renderInDom = function() {
// Create the element
this.domNode = document.createElement(this.parseTreeNode.tag);
this.domNode = document.createElement(this.widget.tag);
// Assign the attributes
this.assignAttributes();
// Render any child nodes
var self = this;
$tw.utils.each(this.children,function(node) {
$tw.utils.each(this.widget.children,function(node) {
if(node.renderInDom) {
self.domNode.appendChild(node.renderInDom());
}
});
// Assign any specified event handlers
$tw.utils.addEventListeners(this.domNode,this.parseTreeNode.events);
$tw.utils.addEventListeners(this.domNode,this.widget.events);
// Call postRenderInDom if the widget provides it
if(this.widget.postRenderInDom) {
this.widget.postRenderInDom();
}
// Return the dom node
return this.domNode;
};
ElementRenderer.prototype.assignAttributes = function() {
var self = this;
$tw.utils.each(this.attributes,function(v,a) {
$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(" ");
@ -131,19 +197,73 @@ ElementRenderer.prototype.assignAttributes = function() {
});
};
ElementRenderer.prototype.refreshInDom = function(changes) {
// Check if any of our dependencies have changed
if($tw.utils.checkDependencies(this.dependencies,changes)) {
// Update our attributes
this.computeAttributes();
this.assignAttributes();
ElementRenderer.prototype.refreshInDom = function(changedTiddlers) {
// Update our attributes if required
var changedAttributes = {};
if($tw.utils.checkDependencies(this.dependencies,changedTiddlers)) {
changedAttributes = this.computeAttributes();
}
// Refresh any child nodes
$tw.utils.each(this.children,function(node) {
if(node.refreshInDom) {
node.refreshInDom(changes);
// 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);
}
});
}
};
ElementRenderer.prototype.getContextTiddlerTitle = function() {
var context = this.renderContext;
while(context) {
if($tw.utils.hop(context,"tiddlerTitle")) {
return context.tiddlerTitle;
}
});
context = context.parentContext;
}
return undefined;
};
/*
Check for render context recursion by returning true if the members of a proposed new render context are already present in the render context chain
*/
ElementRenderer.prototype.checkContextRecursion = function(newRenderContext) {
var context = this.renderContext;
while(context) {
var match = true;
for(var member in newRenderContext) {
if($tw.utils.hop(newRenderContext,member)) {
if(newRenderContext[member] && newRenderContext[member] !== context[member]) {
match = false;
}
}
}
if(match) {
return true;
}
context = context.parentContext;
}
return false;
};
ElementRenderer.prototype.getContextScopeId = function() {
var guidBits = [],
context = this.renderContext;
while(context) {
$tw.utils.each(context,function(field,name) {
if(name !== "parentContext") {
guidBits.push(name + ":" + field + ";");
}
});
guidBits.push("-");
context = context.parentContext;
}
return $tw.utils.toBase64(guidBits.join(""));
};
exports.element = ElementRenderer

View File

@ -1,182 +0,0 @@
/*\
title: $:/core/modules/rendertree/renderers/widget.js
type: application/javascript
module-type: wikirenderer
Widget renderer.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Widget renderer
*/
var WidgetRenderer = function(renderTree,renderContext,parseTreeNode) {
// Store state information
this.renderTree = renderTree;
this.renderContext = renderContext;
this.parseTreeNode = parseTreeNode;
// Widget classes
if(!this.widgetClasses) {
WidgetRenderer.prototype.widgetClasses = $tw.modules.applyMethods("widget");
}
// Compute our attributes
this.attributes = {};
this.computeAttributes();
// Create the widget object
var WidgetClass = this.widgetClasses[this.parseTreeNode.tag];
if(WidgetClass) {
this.widget = new WidgetClass(this);
} else {
WidgetClass = this.widgetClasses.error;
if(WidgetClass) {
this.widget = new WidgetClass(this,"Unknown widget '" + this.parseTreeNode.tag + "'");
}
}
};
WidgetRenderer.prototype.computeAttributes = function() {
var changedAttributes = {};
var self = this;
$tw.utils.each(this.parseTreeNode.attributes,function(attribute,name) {
if(attribute.type === "indirect") {
var value = self.renderTree.wiki.getTextReference(attribute.textReference,self.renderContext.tiddlerTitle);
if(self.attributes[name] !== value) {
self.attributes[name] = value;
changedAttributes[name] = true;
}
} else { // String attribute
if(self.attributes[name] !== attribute.value) {
self.attributes[name] = attribute.value;
changedAttributes[name] = true;
}
}
});
return changedAttributes;
};
WidgetRenderer.prototype.hasAttribute = function(name) {
return $tw.utils.hop(this.attributes,name);
};
WidgetRenderer.prototype.getAttribute = function(name,defaultValue) {
if($tw.utils.hop(this.attributes,name)) {
return this.attributes[name];
} else {
return defaultValue;
}
};
WidgetRenderer.prototype.render = function(type) {
// Render the widget if we've got one
if(this.widget) {
if(this.widget.render) {
return this.widget.render(type);
} else if(this.widget.children) {
var output = [];
$tw.utils.each(this.widget.children,function(node) {
if(node.render) {
output.push(node.render(type));
}
});
return output.join("");
}
}
};
WidgetRenderer.prototype.renderInDom = function() {
var self = this;
// Create the wrapper element
this.domNode = document.createElement(this.parseTreeNode.isBlock ? "div" : "span");
this.domNode.setAttribute("data-widget-type",this.parseTreeNode.tag);
this.domNode.setAttribute("data-widget-attr",JSON.stringify(this.attributes));
// Render the widget if we've got one
if(this.widget) {
if(this.widget.renderInDom) {
this.widget.renderInDom(this.domNode);
} else if(this.widget.children) {
// Render any child nodes
$tw.utils.each(this.widget.children,function(node) {
if(node.renderInDom) {
self.domNode.appendChild(node.renderInDom());
}
});
}
// Attach any event handlers
if(this.widget.getEventListeners) {
$tw.utils.addEventListeners(this.domNode,this.widget.getEventListeners());
}
}
// Call the postRenderInDom hook if the widget has one
if(this.widget && this.widget.postRenderInDom) {
this.widget.postRenderInDom();
}
// Return the dom node
return this.domNode;
};
WidgetRenderer.prototype.refreshInDom = function(changedTiddlers) {
// Update our attributes
var changedAttributes = this.computeAttributes();
// Refresh the widget
if(this.widget && this.widget.refreshInDom) {
this.widget.refreshInDom(changedAttributes,changedTiddlers);
}
};
WidgetRenderer.prototype.getContextTiddlerTitle = function() {
var context = this.renderContext;
while(context) {
if($tw.utils.hop(context,"tiddlerTitle")) {
return context.tiddlerTitle;
}
context = context.parentContext;
}
return undefined;
};
/*
Check for render context recursion by returning true if the members of a proposed new render context are already present in the render context chain
*/
WidgetRenderer.prototype.checkContextRecursion = function(newRenderContext) {
var context = this.renderContext;
while(context) {
var match = true;
for(var member in newRenderContext) {
if($tw.utils.hop(newRenderContext,member)) {
if(newRenderContext[member] && newRenderContext[member] !== context[member]) {
match = false;
}
}
}
if(match) {
return true;
}
context = context.parentContext;
}
return false;
};
WidgetRenderer.prototype.getContextScopeId = function() {
var guidBits = [],
context = this.renderContext;
while(context) {
$tw.utils.each(context,function(field,name) {
if(name !== "parentContext") {
guidBits.push(name + ":" + field + ";");
}
});
guidBits.push("-");
context = context.parentContext;
}
return $tw.utils.toBase64(guidBits.join(""));
};
exports.widget = WidgetRenderer
})();

View File

@ -1,56 +0,0 @@
/*\
title: $:/core/modules/widgetbase.js
type: application/javascript
module-type: global
Base class for widgets
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
This constructor is always overridden with a blank constructor, and so shouldn't be used
*/
var WidgetBase = function() {
};
/*
To be overridden by individual widgets
*/
WidgetBase.prototype.init = function(renderer) {
this.renderer = renderer;
};
/*
Default render() method just renders child nodes
*/
WidgetBase.prototype.render = function(type) {
var output = [];
$tw.utils.each(this.children,function(node) {
if(node.render) {
output.push(node.render(type));
}
});
return output.join("");
};
/*
Default renderInDom() method just renders child nodes
*/
WidgetBase.prototype.renderInDom = function(parentElement) {
this.parentElement = parentElement;
// Render any child nodes
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
parentElement.appendChild(node.renderInDom());
}
});
};
exports.WidgetBase = WidgetBase;
})();

View File

@ -16,10 +16,10 @@ var ButtonWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generateChildNodes();
this.generate();
};
ButtonWidget.prototype.generateChildNodes = function() {
ButtonWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.message = this.renderer.getAttribute("message");
this.param = this.renderer.getAttribute("param");
@ -30,7 +30,7 @@ ButtonWidget.prototype.generateChildNodes = function() {
this.qualifyTiddlerTitles = this.renderer.getAttribute("qualifyTiddlerTitles");
this["class"] = this.renderer.getAttribute("class");
// Compose the button
var classes = ["tw-tiddlybutton"];
var classes = ["tw-button"];
if(this["class"]) {
$tw.utils.pushTop(classes,this["class"]);
}
@ -39,15 +39,11 @@ ButtonWidget.prototype.generateChildNodes = function() {
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
events.push({name: "mouseout", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
}
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[{
type: "element",
tag: "button",
attributes: {
"class": {type: "string", value: classes.join(" ")}
},
children: this.renderer.parseTreeNode.children,
events: events
}]);
// Set the return element
this.tag = "button";
this.attributes ={"class": classes.join(" ")};
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.renderer.parseTreeNode.children);
this.events = events;
};
ButtonWidget.prototype.dispatchMessage = function(event) {
@ -99,16 +95,11 @@ ButtonWidget.prototype.handleMouseOverOrOutEvent = function(event) {
ButtonWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.qualifyTiddlerTitles || changedAttributes["class"] || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup])) {
// Remove old child nodes
$tw.utils.removeChildren(this.parentElement);
// Regenerate and render children
this.generateChildNodes();
var self = this;
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
self.parentElement.appendChild(node.renderInDom());
}
});
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {

View File

@ -17,23 +17,19 @@ var ErrorWidget = function(renderer,errorMessage) {
this.renderer = renderer;
this.errorMessage = errorMessage;
// Generate child nodes
this.generateChildNodes();
this.generate();
};
ErrorWidget.prototype.generateChildNodes = function() {
// Create the wrapper node
var node = {
type: "element",
tag: "span",
children: [{
ErrorWidget.prototype.generate = function() {
// Set the element details
this.tag = "span";
this.attributes = {
"class": "tw-error-widget"
};
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[{
type: "text",
text: this.errorMessage
}]
};
// Set up the attributes for the wrapper element
$tw.utils.addClassToParseTreeNode(node,"tw-error-widget");
// Create the renderers for the wrapper and the children
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[node]);
}]);
};
exports.error = ErrorWidget;

View File

@ -16,10 +16,10 @@ var FieldsWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generateChildNodes();
this.generate();
};
FieldsWidget.prototype.generateChildNodes = function() {
FieldsWidget.prototype.generate = function() {
// Get parameters from our attributes
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.getContextTiddlerTitle());
this.template = this.renderer.getAttribute("template");
@ -46,14 +46,10 @@ FieldsWidget.prototype.generateChildNodes = function() {
}
}
}
// Create the wrapper node
var node = {
type: "element",
tag: this.renderer.parseTreeNode.isBlock ? "div" : "span",
children: [{
type: "text",
text: text.join("")
}]
// Set the element
this.tag = "pre";
this.attributes = {
"class": "tw-fields"
};
// Set up the attributes for the wrapper element
var classes = [];
@ -61,16 +57,19 @@ FieldsWidget.prototype.generateChildNodes = function() {
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
}
if(classes.length > 0) {
$tw.utils.addClassToParseTreeNode(node,classes.join(" "));
this.attributes["class"] = classes.join(" ");
}
if(this.renderer.hasAttribute("style")) {
$tw.utils.addAttributeToParseTreeNode(node,"style",this.renderer.getAttribute("style"));
this.attributes.style = this.renderer.getAttribute("style");
}
if(this.renderer.hasAttribute("tooltip")) {
$tw.utils.addAttributeToParseTreeNode(node,"title",this.renderer.getAttribute("tooltip"));
this.attributes.title = this.renderer.getAttribute("tooltip");
}
// Create the renderers for the wrapper and the children
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[node]);
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[{
type: "text",
text: text.join("")
}]);
};
exports.fields = FieldsWidget;

View File

@ -21,10 +21,10 @@ var LinkWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generateChildNodes();
this.generate();
};
LinkWidget.prototype.generateChildNodes = function() {
LinkWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.to = this.renderer.getAttribute("to");
this.hover = this.renderer.getAttribute("hover");
@ -51,16 +51,14 @@ LinkWidget.prototype.generateChildNodes = function() {
events.push({name: "mouseover", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
events.push({name: "mouseout", handlerObject: this, handlerMethod: "handleMouseOverOrOutEvent"});
}
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[{
type: "element",
tag: "a",
attributes: {
href: {type: "string", value: this.isExternal ? this.to : encodeURIComponent(this.to)},
"class": {type: "string", value: classes.join(" ")}
},
children: this.renderer.parseTreeNode.children,
events: events
}]);
// Set up the element
this.tag = "a";
this.attributes = {
href: this.isExternal ? this.to : encodeURIComponent(this.to),
"class": classes.join(" ")
};
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.renderer.parseTreeNode.children);
this.events = events;
};
LinkWidget.prototype.handleClickEvent = function(event) {
@ -71,7 +69,7 @@ LinkWidget.prototype.handleClickEvent = function(event) {
$tw.utils.dispatchCustomEvent(event.target,"tw-navigate",{
navigateTo: this.to,
navigateFromNode: this,
navigateFromClientRect: this.children[0].domNode.getBoundingClientRect()
navigateFromClientRect: this.renderer.domNode.getBoundingClientRect()
});
event.preventDefault();
return false;
@ -81,10 +79,8 @@ LinkWidget.prototype.handleClickEvent = function(event) {
LinkWidget.prototype.handleMouseOverOrOutEvent = function(event) {
if(this.hover) {
$tw.popup.triggerPopup({
textRef: this.hover,
domNode: this.children[0].domNode,
qualifyTiddlerTitles: this.qualifyHoverTitle,
contextTiddlerTitle: this.renderer.getContextTiddlerTitle(),
title: this.hover,
domNode: this.renderer.domNode,
wiki: this.renderer.renderTree.wiki
});
}
@ -95,20 +91,15 @@ LinkWidget.prototype.handleMouseOverOrOutEvent = function(event) {
LinkWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Set the class for missing tiddlers
if(this.targetTitle && changedTiddlers[this.targetTitle]) {
$tw.utils.toggleClass(this.children[0].domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.targetTitle));
$tw.utils.toggleClass(this.renderer.domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.targetTitle));
}
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.to || changedAttributes.hover || (this.to && changedTiddlers[this.to]) || (this.hover && changedTiddlers[this.hover])) {
// Remove old child nodes
$tw.utils.removeChildren(this.parentElement);
// Regenerate and render children
this.generateChildNodes();
var self = this;
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
self.parentElement.appendChild(node.renderInDom());
}
});
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {

View File

@ -15,8 +15,8 @@ The list widget
var ListWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generateChildNodes();
// Generate widget elements
this.generate();
};
/*
@ -30,7 +30,7 @@ var typeMappings = {
shadows: "[is[shadow]sort[title]]"
};
ListWidget.prototype.generateChildNodes = function() {
ListWidget.prototype.generate = function() {
// Get our attributes
this.itemClass = this.renderer.getAttribute("itemClass");
this.template = this.renderer.getAttribute("template");
@ -50,15 +50,11 @@ ListWidget.prototype.generateChildNodes = function() {
}
}
// Create the list frame element
var classes = ["tw-list-frame"];
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[{
type: "element",
tag: this.renderer.parseTreeNode.isBlock ? "div" : "span",
attributes: {
"class": {type: "string", value: classes.join(" ")}
},
children: listMembers
}]);
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
this.attributes = {
"class": "tw-list-frame"
};
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,listMembers);
};
ListWidget.prototype.getTiddlerList = function() {
@ -138,8 +134,8 @@ ListWidget.prototype.createListElementMacro = function(title) {
} else {
// Use default content
templateTree = [{
type: "widget",
tag: "view",
type: "element",
tag: "$view",
attributes: {
field: {type: "string", value: "title"},
format: {type: "string", value: "link"}
@ -147,10 +143,10 @@ ListWidget.prototype.createListElementMacro = function(title) {
}];
}
}
// Create the tiddler macro
// Create the transclude widget
return {
type: "widget",
tag: "transclude",
type: "element",
tag: "$transclude",
attributes: {
target: {type: "string", value: title},
template: {type: "string", value: template}
@ -164,11 +160,11 @@ Remove a list element from the list, along with the attendant DOM nodes
*/
ListWidget.prototype.removeListElement = function(index) {
// Get the list element
var listElement = this.children[0].children[index];
var listElement = this.children[index];
// Remove the DOM node
listElement.domNode.parentNode.removeChild(listElement.domNode);
// Then delete the actual renderer node
this.children[0].children.splice(index,1);
this.children.splice(index,1);
};
/*
@ -177,8 +173,8 @@ startIndex: index to start search (use zero to search from the top)
title: tiddler title to seach for
*/
ListWidget.prototype.findListElementByTitle = function(startIndex,title) {
while(startIndex < this.children[0].children.length) {
if(this.children[0].children[startIndex].children[0].attributes.target === title) {
while(startIndex < this.children.length) {
if(this.children[startIndex].widget.children[0].attributes.target === title) {
return startIndex;
}
startIndex++;
@ -189,16 +185,11 @@ ListWidget.prototype.findListElementByTitle = function(startIndex,title) {
ListWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Reexecute the widget if any of our attributes have changed
if(changedAttributes.itemClass || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.emptyMessage || changedAttributes.type || changedAttributes.filter || changedAttributes.template) {
// Remove old child nodes
$tw.utils.removeChildren(this.parentElement);
// Regenerate and render children
this.generateChildNodes();
var self = this;
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
self.parentElement.appendChild(node.renderInDom());
}
});
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// Handle any changes to the list, and refresh any nodes we're reusing
this.handleListChanges(changedTiddlers);
@ -207,8 +198,7 @@ ListWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers)
ListWidget.prototype.handleListChanges = function(changedTiddlers) {
var t,
prevListLength = this.list.length,
frame = this.children[0];
prevListLength = this.list.length;
// Get the list of tiddlers, having saved the previous length
this.getTiddlerList();
// Check if the list is empty
@ -228,10 +218,10 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
this.removeListElement(t);
}
// Insert the empty message
frame.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[this.getEmptyMessage()]);
$tw.utils.each(frame.children,function(node) {
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[this.getEmptyMessage()]);
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
frame.domNode.appendChild(node.renderInDom());
this.renderer.domNode.appendChild(node.renderInDom());
}
});
return;
@ -248,19 +238,19 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) {
var index = this.findListElementByTitle(t,this.list[t]);
if(index === undefined) {
// The list element isn't there, so we need to insert it
frame.children.splice(t,0,this.renderer.renderTree.createRenderer(this.renderer.renderContext,this.createListElement(this.list[t])));
frame.domNode.insertBefore(frame.children[t].renderInDom(),frame.domNode.childNodes[t]);
this.children.splice(t,0,this.renderer.renderTree.createRenderer(this.renderer.renderContext,this.createListElement(this.list[t])));
this.renderer.domNode.insertBefore(this.children[t].renderInDom(),this.renderer.domNode.childNodes[t]);
} else {
// Delete any list elements preceding the one we want
for(var n=index-1; n>=t; n--) {
this.removeListElement(n);
}
// Refresh the node we're reusing
frame.children[t].refreshInDom(changedTiddlers);
this.children[t].refreshInDom(changedTiddlers);
}
}
// Remove any left over elements
for(t=frame.children.length-1; t>=this.list.length; t--) {
for(t=this.children.length-1; t>=this.list.length; t--) {
this.removeListElement(t);
}
};

View File

@ -16,19 +16,20 @@ var NavigatorWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generateChildNodes();
this.generate();
};
NavigatorWidget.prototype.generateChildNodes = function() {
NavigatorWidget.prototype.generate = function() {
// Get our parameters
this.storyTitle = this.renderer.getAttribute("story");
this.historyTitle = this.renderer.getAttribute("history");
// Render our children
// Set the element
this.tag = "div";
this.attributes = {
"class": "tw-navigator"
};
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.renderer.parseTreeNode.children);
};
NavigatorWidget.prototype.getEventListeners = function() {
return [
this.events = [
{name: "tw-navigate", handlerObject: this, handlerMethod: "handleNavigateEvent"},
{name: "tw-EditTiddler", handlerObject: this, handlerMethod: "handleEditTiddlerEvent"},
{name: "tw-SaveTiddler", handlerObject: this, handlerMethod: "handleSaveTiddlerEvent"},

View File

@ -16,10 +16,10 @@ var RevealWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generateChildNodes();
this.generate();
};
RevealWidget.prototype.generateChildNodes = function() {
RevealWidget.prototype.generate = function() {
// Get the parameters from the attributes
this.state = this.renderer.getAttribute("state");
this.type = this.renderer.getAttribute("type");
@ -34,26 +34,27 @@ RevealWidget.prototype.generateChildNodes = function() {
this.stateTitle = this.stateTitle + "-" + this.renderer.getContextScopeId();
}
this.readState();
// Compose the node
var node = {
type: "element",
tag: "div",
children: this.isOpen ? this.renderer.parseTreeNode.children : [],
events: [{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}]
};
$tw.utils.addClassToParseTreeNode(node,"tw-reveal");
// Set up the element attributes
var classes = ["tw-reveal"],
styles = [];
if(this["class"]) {
$tw.utils.addClassToParseTreeNode(node,this["class"].join(" "));
$tw.utils.pushTop(classes,this["class"]);
}
switch(this.type) {
case "popup":
$tw.utils.addStyleToParseTreeNode(node,"position","absolute");
$tw.utils.addClassToParseTreeNode(node,"tw-popup");
styles.push("position:absolute;");
classes.push("tw-popup");
break;
}
$tw.utils.addStyleToParseTreeNode(node,"display",this.isOpen ? (this.isBlock ? "block" : "inline") : "none");
// Return the node
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,[node]);
styles.push("display:" + (this.isOpen ? (this.renderer.parseTreeNode.isBlock ? "block" : "inline") : "none") + ";");
// Set the element
this.tag = "div";
this.attributes = {
"class": classes.join(" "),
style: styles.join("")
};
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.isOpen ? this.renderer.parseTreeNode.children : []);
this.events = [{name: "click", handlerObject: this, handlerMethod: "handleClickEvent"}];
};
/*
@ -121,25 +122,20 @@ RevealWidget.prototype.handleClickEvent = function(event) {
RevealWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.state || changedAttributes.type || changedAttributes.text || changedAttributes.position || changedAttributes["default"] || changedAttributes.qualifyTiddlerTitles || changedAttributes["class"]) {
// Remove old child nodes
$tw.utils.removeChildren(this.parentElement);
// Regenerate and render children
this.generateChildNodes();
var self = this;
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
self.parentElement.appendChild(node.renderInDom());
}
});
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
var needChildrenRefresh = true; // Avoid refreshing the children nodes if we don't need to
// Get the open state
this.readState();
// Construct the child nodes if required
if(this.isOpen && this.children[0].children.length === 0) {
this.children[0].children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.renderer.parseTreeNode.children);
var parentNode = this.children[0].domNode;
$tw.utils.each(this.children[0].children,function(child) {
if(this.isOpen && this.children.length === 0) {
this.children = this.renderer.renderTree.createRenderers(this.renderer.renderContext,this.renderer.parseTreeNode.children);
var parentNode = this.renderer.domNode;
$tw.utils.each(this.children,function(child) {
parentNode.appendChild(child.renderInDom());
});
needChildrenRefresh = false;
@ -153,42 +149,44 @@ RevealWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers
});
}
// Set the visibility of the children
this.children[0].domNode.style.display = this.isOpen ? (this.isBlock ? "block" : "inline") : "none";
this.renderer.domNode.style.display = this.isOpen ? (this.renderer.parseTreeNode.isBlock ? "block" : "inline") : "none";
}
// Position the content if required
this.postRenderInDom();
if(this.isOpen) {
this.postRenderInDom();
}
};
RevealWidget.prototype.postRenderInDom = function() {
switch(this.type) {
case "popup":
if(this.isOpen) {
this.children[0].domNode.style.position = "absolute";
this.children[0].domNode.style.zIndex = "1000";
this.renderer.domNode.style.position = "absolute";
this.renderer.domNode.style.zIndex = "1000";
switch(this.position) {
case "left":
this.children[0].domNode.style.left = (this.popup.left - this.children[0].domNode.offsetWidth) + "px";
this.children[0].domNode.style.top = this.popup.top + "px";
this.renderer.domNode.style.left = (this.popup.left - this.renderer.domNode.offsetWidth) + "px";
this.renderer.domNode.style.top = this.popup.top + "px";
break;
case "above":
this.children[0].domNode.style.left = this.popup.left + "px";
this.children[0].domNode.style.top = (this.popup.top - this.children[0].domNode.offsetHeight) + "px";
this.renderer.domNode.style.left = this.popup.left + "px";
this.renderer.domNode.style.top = (this.popup.top - this.renderer.domNode.offsetHeight) + "px";
break;
case "aboveright":
this.children[0].domNode.style.left = (this.popup.left + this.popup.width) + "px";
this.children[0].domNode.style.top = (this.popup.top + this.popup.height - this.children[0].domNode.offsetHeight) + "px";
this.renderer.domNode.style.left = (this.popup.left + this.popup.width) + "px";
this.renderer.domNode.style.top = (this.popup.top + this.popup.height - this.renderer.domNode.offsetHeight) + "px";
break;
case "right":
this.children[0].domNode.style.left = (this.popup.left + this.popup.width) + "px";
this.children[0].domNode.style.top = this.popup.top + "px";
this.renderer.domNode.style.left = (this.popup.left + this.popup.width) + "px";
this.renderer.domNode.style.top = this.popup.top + "px";
break;
case "belowleft":
this.children[0].domNode.style.left = (this.popup.left + this.popup.width - this.children[0].domNode.offsetWidth) + "px";
this.children[0].domNode.style.top = (this.popup.top + this.popup.height) + "px";
this.renderer.domNode.style.left = (this.popup.left + this.popup.width - this.renderer.domNode.offsetWidth) + "px";
this.renderer.domNode.style.top = (this.popup.top + this.popup.height) + "px";
break;
default: // Below
this.children[0].domNode.style.left = this.popup.left + "px";
this.children[0].domNode.style.top = (this.popup.top + this.popup.height) + "px";
this.renderer.domNode.style.left = this.popup.left + "px";
this.renderer.domNode.style.top = (this.popup.top + this.popup.height) + "px";
break;
}
}

View File

@ -12,7 +12,7 @@ Attributes:
The simplest case is to just supply a target tiddler:
{{{
<_transclude target="Foo"/>
<$transclude target="Foo"/>
}}}
This will render the tiddler Foo within the current tiddler. If the tiddler Foo includes
@ -24,7 +24,7 @@ widget are those of the tiddler doing the transcluding, then you can instead spe
as a template:
{{{
<_transclude template="Foo"/>
<$transclude template="Foo"/>
}}}
The effect is the same as the previous example: the text of the tiddler Foo is rendered. The
@ -33,7 +33,7 @@ difference is that the view widget will access the fields of the tiddler doing t
The `target` and `template` attributes may be combined:
{{{
<_transclude template="Bar" target="Foo"/>
<$transclude template="Bar" target="Foo"/>
}}}
Here, the text of the tiddler `Bar` will be transcluded, with the widgets within it accessing the fields
@ -50,13 +50,14 @@ var TranscludeWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generateChildNodes();
this.generate();
};
TranscludeWidget.prototype.generateChildNodes = function() {
TranscludeWidget.prototype.generate = function() {
var tr, templateParseTree, templateTiddler;
// Get the render target details
this.targetTitle = this.renderer.getAttribute("target",this.renderer.getContextTiddlerTitle());
this.targetField = this.renderer.getAttribute("field","text");
// Get the render tree for the template
this.templateTitle = undefined;
if(this.renderer.parseTreeNode.children && this.renderer.parseTreeNode.children.length > 0) {
@ -71,40 +72,47 @@ TranscludeWidget.prototype.generateChildNodes = function() {
})) {
templateParseTree = [{type: "text", text: "Tiddler recursion error in transclude widget"}];
} else {
var parser = this.renderer.renderTree.wiki.parseTiddler(this.templateTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
var parser;
if(this.targetField === "text") {
parser = this.renderer.renderTree.wiki.parseTiddler(this.templateTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock})
} else {
var tiddler = this.renderer.renderTree.wiki.getTiddler(this.targetTitle),
text = tiddler ? tiddler.fields[this.targetField] : "";
if(text === undefined) {
text = ""
}
parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
}
templateParseTree = parser ? parser.tree : [];
}
}
// Create the wrapper node
var node = {
type: "element",
tag: this.renderer.parseTreeNode.isBlock ? "div" : "span",
children: templateParseTree
};
// Set up the attributes for the wrapper element
var classes = [];
var classes = ["tw-transclude"];
if(this.renderer.hasAttribute("class")) {
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
}
if(!this.renderer.renderTree.wiki.tiddlerExists(this.targetTitle)) {
$tw.utils.pushTop(classes,"tw-tiddler-missing");
}
if(classes.length > 0) {
$tw.utils.addClassToParseTreeNode(node,classes.join(" "));
}
if(this.renderer.hasAttribute("style")) {
$tw.utils.addAttributeToParseTreeNode(node,"style",this.renderer.getAttribute("style"));
}
if(this.renderer.hasAttribute("tooltip")) {
$tw.utils.addAttributeToParseTreeNode(node,"title",this.renderer.getAttribute("tooltip"));
}
// Create the renderers for the wrapper and the children
var newRenderContext = {
tiddlerTitle: this.targetTitle,
templateTitle: this.templateTitle,
parentContext: this.renderer.renderContext
};
this.children = this.renderer.renderTree.createRenderers(newRenderContext,[node]);
// Set the element
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
this.attributes = {};
if(classes.length > 0) {
this.attributes["class"] = classes.join(" ");
}
if(this.renderer.hasAttribute("style")) {
this.attributes.style = this.renderer.getAttribute("style");
}
if(this.renderer.hasAttribute("tooltip")) {
this.attributes.title = this.renderer.getAttribute("tooltip");
}
this.children = this.renderer.renderTree.createRenderers(newRenderContext,templateParseTree);
};
TranscludeWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
@ -114,16 +122,11 @@ TranscludeWidget.prototype.refreshInDom = function(changedAttributes,changedTidd
}
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.target || changedAttributes.template || (this.targetTitle && changedTiddlers[this.targetTitle]) || (this.templateTitle && changedTiddlers[this.templateTitle])) {
// Remove old child nodes
$tw.utils.removeChildren(this.parentElement);
// Regenerate and render children
this.generateChildNodes();
var self = this;
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
self.parentElement.appendChild(node.renderInDom());
}
});
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {

View File

@ -31,28 +31,28 @@ TextViewer.prototype.render = function() {
if(this.value !== undefined && this.value !== null) {
value = this.value;
}
return this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
type: "text",
text: value
}]);
};
// We'll cache the available field viewers here
var fieldViewers = undefined;
var ViewWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Initialise the field viewers if they've not been done already
if(!fieldViewers) {
fieldViewers = {text: TextViewer}; // Start with the built-in text viewer
$tw.modules.applyMethods("newfieldviewer",fieldViewers);
if(!this.fieldViewers) {
ViewWidget.prototype.fieldViewers = {text: TextViewer}; // Start with the built-in text viewer
$tw.modules.applyMethods("fieldviewer",this.fieldViewers);
}
// Generate child nodes
this.generateChildNodes();
this.generate();
};
ViewWidget.prototype.generateChildNodes = function() {
ViewWidget.prototype.generate = function() {
// Get parameters from our attributes
this.tiddlerTitle = this.renderer.getAttribute("tiddler",this.renderer.getContextTiddlerTitle());
this.fieldName = this.renderer.getAttribute("field","text");
@ -82,13 +82,13 @@ ViewWidget.prototype.generateChildNodes = function() {
}
}
// Choose the viewer to use
var Viewer = fieldViewers.text;
if($tw.utils.hop(fieldViewers,this.format)) {
Viewer = fieldViewers[this.format];
var Viewer = this.fieldViewers.text;
if($tw.utils.hop(this.fieldViewers,this.format)) {
Viewer = this.fieldViewers[this.format];
}
this.viewer = new Viewer(this,tiddler,this.fieldName,value);
// Ask the viewer to create the children
this.children = this.viewer.render();
// Ask the viewer to create the widget element
this.viewer.render();
};
ViewWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
@ -96,14 +96,11 @@ ViewWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers)
if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.format || (this.tiddlerTitle && changedTiddlers[this.tiddlerTitle])) {
// Remove old child nodes
$tw.utils.removeChildren(this.parentElement);
// Regenerate and render children
this.generateChildNodes();
var self = this;
$tw.utils.each(this.children,function(node) {
if(node.renderInDom) {
self.parentElement.appendChild(node.renderInDom());
}
});
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,
newDomNode = this.renderer.renderInDom();
oldDomNode.parentNode.replaceChild(newDomNode,oldDomNode);
} else {
// We don't need to refresh ourselves, so just refresh any child nodes
$tw.utils.each(this.children,function(node) {

View File

@ -1,7 +1,7 @@
/*\
title: $:/core/modules/widgets/view/viewers/date.js
type: application/javascript
module-type: newfieldviewer
module-type: fieldviewer
A viewer for viewing tiddler fields as a date
@ -25,7 +25,12 @@ DateViewer.prototype.render = function() {
if(this.value !== undefined) {
value = $tw.utils.formatDateString(this.value,template);
}
return this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {
"class": "tw-view-date"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
type: "text",
text: value
}]);

View File

@ -1,7 +1,7 @@
/*\
title: $:/core/modules/widgets/view/viewers/htmlencoded.js
type: application/javascript
module-type: newfieldviewer
module-type: fieldviewer
A viewer for viewing tiddler fields as HTML encoded text
@ -28,7 +28,12 @@ HtmlEncodedViewer.prototype.render = function() {
if(this.value !== undefined && this.value !== null) {
value = this.value;
}
return this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {
"class": "tw-view-htmlencoded"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
type: "text",
text: $tw.utils.htmlEncode(value)
}]);

View File

@ -1,7 +1,7 @@
/*\
title: $:/core/modules/widgets/view/viewers/htmlwikified.js
type: application/javascript
module-type: newfieldviewer
module-type: fieldviewer
A viewer for viewing tiddler fields as a textual HTML representation of the wikified text
@ -22,16 +22,15 @@ var HtmlWikifiedViewer = function(viewWidget,tiddler,field,value) {
HtmlWikifiedViewer.prototype.render = function() {
// Parse the field text
var text = this.viewWidget.renderer.renderTree.wiki.renderText("text/html","text/vnd.tiddlywiki",this.value);
// Create a node containing the HTML representation of the field
var node = {
type: "element",
tag: "pre",
children: [{
// Set the element details
this.viewWidget.tag = "pre";
this.viewWidget.attributes = {
"class": "tw-view-htmlwikified"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
type: "text",
text: text
}]
};
return this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[node]);
}]);
};
exports.htmlwikified = HtmlWikifiedViewer;

View File

@ -1,7 +1,7 @@
/*\
title: $:/core/modules/widgets/view/viewers/jsencoded.js
type: application/javascript
module-type: newfieldviewer
module-type: fieldviewer
A viewer for viewing tiddler fields as JavaScript stringified text
@ -28,10 +28,15 @@ JsEncodedViewer.prototype.render = function() {
if(this.value !== undefined && this.value !== null) {
value = this.value;
}
return this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
type: "text",
text: $tw.utils.stringify(value)
}]);
// Set the element details
this.viewWidget.tag = "pre";
this.viewWidget.attributes = {
"class": "tw-view-jsencoded"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
type: "text",
text: $tw.utils.stringify(value)
}]);
};
exports.jsencoded = JsEncodedViewer;

View File

@ -1,7 +1,7 @@
/*\
title: $:/core/modules/widgets/view/viewers/link.js
type: application/javascript
module-type: newfieldviewer
module-type: fieldviewer
A viewer for viewing tiddler fields as a link
@ -20,23 +20,23 @@ var LinkViewer = function(viewWidget,tiddler,field,value) {
};
LinkViewer.prototype.render = function() {
var parseTree = [];
if(this.value === undefined) {
parseTree.push({type: "text", text: ""});
} else {
parseTree.push({
type: "widget",
tag: "link",
var text = this.value === undefined ? "" : this.value;
// Set the element details
this.viewWidget.tag = "span";
this.viewWidget.attributes = {
"class": "tw-view-link"
};
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[{
type: "element",
tag: "$link",
attributes: {
to: {type: "string", value: this.value}
to: {type: "string", value: text}
},
children: [{
type: "text",
text: this.value
text: text
}]
})
}
return this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,parseTree);
}]);
};
exports.link = LinkViewer;

View File

@ -1,7 +1,7 @@
/*\
title: $:/core/modules/widgets/view/viewers/wikified.js
type: application/javascript
module-type: newfieldviewer
module-type: fieldviewer
A viewer for viewing tiddler fields as wikified text
@ -20,21 +20,22 @@ var WikifiedViewer = function(viewWidget,tiddler,field,value) {
};
WikifiedViewer.prototype.render = function() {
var parseTree;
// If we're viewing the text field of a tiddler then we'll transclude it
if(this.tiddler && this.field === "text") {
parseTree = [{
type: "widget",
tag: "transclude",
// Set the element details
this.viewWidget.tag = this.viewWidget.renderer.parseTreeNode.isBlock ? "div" : "span";
this.viewWidget.attributes = {};
var node = {
type: "element",
tag: "$transclude",
attributes: {
target: {type: "string", value: this.tiddler.fields.title}
"class": "tw-view-wikified",
field: {type: "string", value: this.field}
},
isBlock: this.viewWidget.renderer.parseTreeNode.isBlock
}];
} else {
parseTree = this.viewWidget.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",this.value).tree;
};
if(this.tiddler && this.tiddler.fields.title) {
node.attributes.target = {type: "string", value: this.tiddler.fields.title}
}
return this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,parseTree);
this.viewWidget.children = this.viewWidget.renderer.renderTree.createRenderers(this.viewWidget.renderer.renderContext,[node]);
};
exports.wikified = WikifiedViewer;

View File

@ -8,7 +8,7 @@ title: $:/templates/TagTemplate
* <$view field="title" format="link" />
*.divider
* <div>
<$list filter="[is[current]tagging[]sort[title]]"/>
<$list filter="[is[current]tagging[]sort[title]]"><$view field="title" format="link" /></$list>
</div>
@@