Split "transclude" widget into a separate "tiddler" and "transclude" widget

Belatedly realised that the design would be clearer without these two
separate concepts being conflated into a single widget.

As a result of this change, any other widget or template that generates
transclude widgets has needed adjustment.
print-window-tiddler
Jeremy Ruston 2013-08-24 16:45:45 +01:00
rodzic 6c5239603c
commit be06257430
19 zmienionych plików z 259 dodań i 151 usunięć

Wyświetl plik

@ -42,34 +42,40 @@ exports.parse = function() {
template = $tw.utils.trim(this.match[3]),
style = this.match[4],
classes = this.match[5];
// Return the transclude widget
var node = {
// Prepare the transclude widget
var transcludeNode = {
type: "element",
tag: "$transclude",
attributes: {
title: {type: "string", value: template || targetTitle}
},
isBlock: true
};
var tiddlerNode = {
type: "element",
tag: "$transclude",
tag: "$tiddler",
attributes: {
target: {type: "string", value: targetTitle}
title: {type: "string", value: targetTitle}
},
isBlock: true
isBlock: true,
children: [transcludeNode]
};
if(targetField) {
node.attributes.field = {type: "string", value: targetField};
transcludeNode.attributes.field = {type: "string", value: targetField};
}
if(targetIndex) {
node.attributes.index = {type: "string", value: targetIndex};
transcludeNode.attributes.index = {type: "string", value: targetIndex};
}
if(tooltip) {
node.attributes.tooltip = {type: "string", value: tooltip};
}
if(template) {
node.attributes.template = {type: "string", value: template};
transcludeNode.attributes.tooltip = {type: "string", value: tooltip};
}
if(style) {
node.attributes.style = {type: "string", value: style};
transcludeNode.attributes.style = {type: "string", value: style};
}
if(classes) {
node.attributes["class"] = {type: "string", value: classes.split(".").join(" ")};
transcludeNode.attributes["class"] = {type: "string", value: classes.split(".").join(" ")};
}
return [node];
return [tiddlerNode];
};
})();

Wyświetl plik

@ -42,33 +42,38 @@ exports.parse = function() {
template = $tw.utils.trim(this.match[3]),
style = this.match[4],
classes = this.match[5];
// Return the transclude widget
var node = {
// Prepare the transclude widget
var transcludeNode = {
type: "element",
tag: "$transclude",
attributes: {
title: {type: "string", value: template || targetTitle}
}
};
var tiddlerNode = {
type: "element",
tag: "$transclude",
tag: "$tiddler",
attributes: {
target: {type: "string", value: targetTitle}
}
title: {type: "string", value: targetTitle}
},
children: [transcludeNode]
};
if(targetField) {
node.attributes.field = {type: "string", value: targetField};
transcludeNode.attributes.field = {type: "string", value: targetField};
}
if(targetIndex) {
node.attributes.index = {type: "string", value: targetIndex};
transcludeNode.attributes.index = {type: "string", value: targetIndex};
}
if(tooltip) {
node.attributes.tooltip = {type: "string", value: tooltip};
}
if(template) {
node.attributes.template = {type: "string", value: template};
transcludeNode.attributes.tooltip = {type: "string", value: tooltip};
}
if(style) {
node.attributes.style = {type: "string", value: style};
transcludeNode.attributes.style = {type: "string", value: style};
}
if(classes) {
node.attributes["class"] = {type: "string", value: classes.split(".").join(" ")};
transcludeNode.attributes["class"] = {type: "string", value: classes.split(".").join(" ")};
}
return [node];
return [tiddlerNode];
};
})();

Wyświetl plik

@ -61,7 +61,7 @@ GridWidget.prototype.generateTable = function() {
type: "element",
tag: "$transclude",
attributes: {
target: {type: "string", value: this.getTableCellTitle(col,row)}
title: {type: "string", value: this.getTableCellTitle(col,row)}
}
}]
};

Wyświetl plik

@ -41,7 +41,7 @@ InfoWidget.types.currentfielddescription = function(options) {
tag: "$transclude",
isBlock: false,
attributes: {
target: {type: "string", value: descriptionTitle}
title: {type: "string", value: descriptionTitle}
}
}];
};
@ -71,7 +71,7 @@ InfoWidget.types.modules = function(options) {
tag: "$transclude",
isBlock: false,
attributes: {
target: {type: "string", value: MODULE_TYPE_DESCRIPTION_PREFIX + moduleType}
title: {type: "string", value: MODULE_TYPE_DESCRIPTION_PREFIX + moduleType}
}
});
// List each module

Wyświetl plik

@ -197,23 +197,37 @@ ListWidget.prototype.createListElementTransclusion = function(title) {
}];
}
}
// Create the transclude widget
var widget = {
type: "element",
tag: "$transclude",
isBlock: this.renderer.parseTreeNode.isBlock,
attributes: {},
children: templateTree
};
// Set the target if needed
if(!this.renderer.hasAttribute("hackTemplate")) {
widget.attributes.target = {type: "string", value: title};
widget.attributes.template = {type: "string", value: template};
// Create the element widgets
if(this.renderer.hasAttribute("hackTemplate")) {
return {
type: "element",
tag: "$transclude",
isBlock: this.renderer.parseTreeNode.isBlock,
attributes: {
title: {type: "string", value: title}
}
};
} else {
widget.attributes.template = {type: "string", value: title};
widget.children = undefined;
if(!templateTree) {
templateTree = [{
type: "element",
tag: "$transclude",
attributes: {
title: {type: "string", value: template}
},
children: templateTree
}];
}
return {
type: "element",
tag: "$tiddler",
isBlock: this.renderer.parseTreeNode.isBlock,
attributes: {
title: {type: "string", value: title}
},
children: templateTree
};
}
return widget;
};
/*
@ -245,10 +259,13 @@ ListWidget.prototype.findListElementByTitle = function(startIndex,title) {
var testNode = this.macro ? function(node) {
// We're looking for a macro list element
return node.widget.children[0].parseTreeNode.params[0].value === title;
} : function(node) {
// We're looking for a transclusion list element
return node.widget.children[0].attributes.target === title;
};
} : (this.renderer.hasAttribute("hackTemplate") ? function(node) {
// We're looking for a transclusion list element
return node.widget.children[0].attributes.title === title;
} : function(node) {
// We're looking for a transclusion list element
return node.widget.children[0].attributes.title === title;
});
// Search for the list element
while(startIndex < this.children.length) {
if(testNode(this.children[startIndex])) {

Wyświetl plik

@ -0,0 +1,82 @@
/*\
title: $:/core/modules/widgets/tiddler.js
type: application/javascript
module-type: widget
The tiddler widget sets the current tiddler to a specified title.
Attributes:
title: the title of the current tiddler
class: CSS classes
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
var TiddlerWidget = function(renderer) {
// Save state
this.renderer = renderer;
// Generate child nodes
this.generate();
};
TiddlerWidget.prototype.generate = function() {
var self = this;
this.tiddlerTitle = this.renderer.getAttribute("title","");
// Set up the attributes for the wrapper element
var classes = ["tw-tiddler"];
if(this.renderer.hasAttribute("class")) {
$tw.utils.pushTop(classes,this.renderer.getAttribute("class").split(" "));
}
if(!this.renderer.renderTree.wiki.tiddlerExists(this.tiddlerTitle) && !this.renderer.renderTree.wiki.isShadowTiddler(this.tiddlerTitle)) {
$tw.utils.pushTop(classes,"tw-tiddler-missing");
}
// Save the context for this renderer node
this.renderer.context = {
tiddlerTitle: this.tiddlerTitle
};
// Initialise events
this.events = [];
// Trap and update tag modification events
this.events.push({name: "tw-remove-tag", handlerFunction: function(event) {
event.currentTag = self.tiddlerTitle;
return true;
}});
// Set the element
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
this.attributes = {};
if(classes.length > 0) {
this.attributes["class"] = classes.join(" ");
}
this.children = this.renderer.renderTree.createRenderers(this.renderer,this.renderer.parseTreeNode.children);
};
TiddlerWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Set the class for missing tiddlers
if(this.tiddlerTitle && changedTiddlers[this.tiddlerTitle]) {
$tw.utils.toggleClass(this.renderer.domNode,"tw-tiddler-missing",!this.renderer.renderTree.wiki.tiddlerExists(this.tiddlerTitle));
}
// Check if any of our attributes have changed, or if a tiddler we're interested in has changed
if(changedAttributes.title) {
// 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) {
if(node.refreshInDom) {
node.refreshInDom(changedTiddlers);
}
});
}
};
exports.tiddler = TiddlerWidget;
})();

Wyświetl plik

@ -6,38 +6,7 @@ module-type: widget
The transclude widget includes another tiddler into the tiddler being rendered.
Attributes:
target: the title of the tiddler to transclude
template: the title of the tiddler to use as a template for the transcluded tiddler
The simplest case is to just supply a target tiddler:
{{{
<$transclude target="Foo"/>
}}}
This will render the tiddler Foo within the current tiddler. If the tiddler Foo includes
the view widget (or other widget that reference the fields of the current tiddler), then the
fields of the tiddler Foo will be accessed.
If you want to transclude the tiddler as a template, so that the fields referenced by the view
widget are those of the tiddler doing the transcluding, then you can instead specify the tiddler
as a template:
{{{
<$transclude template="Foo"/>
}}}
The effect is the same as the previous example: the text of the tiddler Foo is rendered. The
difference is that the view widget will access the fields of the tiddler doing the transcluding.
The `target` and `template` attributes may be combined:
{{{
<$transclude template="Bar" target="Foo"/>
}}}
Here, the text of the tiddler `Bar` will be transcluded, with the widgets within it accessing the fields
of the tiddler `Foo`.
title: the title of the tiddler to transclude
\*/
(function(){
@ -55,65 +24,49 @@ var TranscludeWidget = function(renderer) {
TranscludeWidget.prototype.generate = function() {
var self = this,
tr, templateParseTree, templateTiddler;
templateParseTree;
// Get the render target details
this.targetTitle = this.renderer.getAttribute("target",this.renderer.tiddlerTitle);
this.targetField = this.renderer.getAttribute("field");
this.targetIndex = this.renderer.getAttribute("index");
// Get the render tree for the template
this.templateTitle = undefined;
if(this.renderer.parseTreeNode.children && this.renderer.parseTreeNode.children.length > 0) {
// Use the child nodes as the template if we've got them
templateParseTree = this.renderer.parseTreeNode.children;
this.transcludeTitle = this.renderer.getAttribute("title",this.renderer.tiddlerTitle);
this.transcludeField = this.renderer.getAttribute("field");
this.transcludeIndex = this.renderer.getAttribute("index");
// Check for recursion
if(this.renderer.renderTree.checkContextRecursion(this.renderer.parentRenderer,{
transcludeTitle: this.transcludeTitle,
transcludeField: this.transcludeField,
transcludeIndex: this.transcludeIndex
})) {
templateParseTree = [{type: "text", text: "Tiddler recursion error in transclude widget"}];
} else {
this.templateTitle = this.renderer.getAttribute("template",this.targetTitle);
// Check for recursion
if(this.renderer.renderTree.checkContextRecursion(this.renderer.parentRenderer,{
tiddlerTitle: this.targetTitle,
templateTitle: this.templateTitle
})) {
templateParseTree = [{type: "text", text: "Tiddler recursion error in transclude widget"}];
var parser;
if(this.transcludeField === "text" || (!this.transcludeField && !this.transcludeIndex)) {
parser = this.renderer.renderTree.wiki.parseTiddler(this.transcludeTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
} else {
var parser;
if(this.targetField === "text" || (!this.targetField && !this.targetIndex)) {
parser = this.renderer.renderTree.wiki.parseTiddler(this.templateTitle,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
} else {
var tiddler,text;
if(this.targetField) {
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});
} else if(this.targetIndex) {
text = this.renderer.renderTree.wiki.extractTiddlerDataItem(this.targetTitle,this.targetIndex,"");
parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
var tiddler,text;
if(this.transcludeField) {
tiddler = this.renderer.renderTree.wiki.getTiddler(this.transcludeTitle);
text = tiddler ? tiddler.fields[this.transcludeField] : "";
if(text === undefined) {
text = "";
}
parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
} else if(this.transcludeIndex) {
text = this.renderer.renderTree.wiki.extractTiddlerDataItem(this.transcludeTitle,this.transcludeIndex,"");
parser = this.renderer.renderTree.wiki.parseText("text/vnd.tiddlywiki",text,{parseAsInline: !this.renderer.parseTreeNode.isBlock});
}
templateParseTree = parser ? parser.tree : [];
}
templateParseTree = parser ? parser.tree : [];
}
// Set up the attributes for the wrapper element
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) && !this.renderer.renderTree.wiki.isShadowTiddler(this.targetTitle)) {
$tw.utils.pushTop(classes,"tw-tiddler-missing");
}
// Save the context for this renderer node
this.renderer.context = {
tiddlerTitle: this.targetTitle,
templateTitle: this.templateTitle
transcludeTitle: this.transcludeTitle,
transcludeField: this.transcludeField,
transcludeIndex: this.transcludeIndex
};
// Initialise events
this.events = [];
// Trap and update tag modification events
this.events.push({name: "tw-remove-tag", handlerFunction: function(event) {
event.currentTag = self.targetTitle;
return true;
}});
// Set the element
this.tag = this.renderer.parseTreeNode.isBlock ? "div" : "span";
this.attributes = {};
@ -130,12 +83,8 @@ TranscludeWidget.prototype.generate = function() {
};
TranscludeWidget.prototype.refreshInDom = function(changedAttributes,changedTiddlers) {
// Set the class for missing tiddlers
if(this.targetTitle && changedTiddlers[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.target || changedAttributes.template || (this.templateTitle && changedTiddlers[this.templateTitle])) {
if(changedAttributes.transcludeField || changedAttributes.transcludeIndex || (this.transcludeTitle && changedTiddlers[this.transcludeTitle])) {
// Regenerate and rerender the widget and replace the existing DOM node
this.generate();
var oldDomNode = this.renderer.domNode,

Wyświetl plik

@ -9,10 +9,10 @@ modifier: JeremyRuston
<$edit field="draft.title"/>
@@
<$transclude template="$:/core/ui/TagsEditor"/>
<$transclude title="$:/core/ui/TagsEditor"/>
<$reveal state="$:/ShowEditPreview" type="match" text="yes">
<$transclude template="$:/core/ui/EditorHint"/> <$button type="set" set="$:/ShowEditPreview" setTo="no">hide preview</$button>
<$transclude title="$:/core/ui/EditorHint"/> <$button type="set" set="$:/ShowEditPreview" setTo="no">hide preview</$button>
<div class="tw-tiddler-preview">
<div class="tw-tiddler-preview-preview">
<$view field="text" format="wikified"/>
@ -23,8 +23,8 @@ modifier: JeremyRuston
</div>
</$reveal>
<$reveal state="$:/ShowEditPreview" type="nomatch" text="yes">
<$transclude template="$:/core/ui/EditorHint"/> <$button type="set" set="$:/ShowEditPreview" setTo="yes">show preview</$button>
<$transclude title="$:/core/ui/EditorHint"/> <$button type="set" set="$:/ShowEditPreview" setTo="yes">show preview</$button>
<$edit field="text"/>
</$reveal>
<$transclude template="$:/core/ui/FieldEditor"/>
<$transclude title="$:/core/ui/FieldEditor"/>

Wyświetl plik

@ -29,7 +29,7 @@ title: $:/core/ui/MoreSideBar
</$reveal>
<$reveal type="match" state="$:/state/moreSideBarTabSet" text="tagsTab" qualifyTiddlerTitles="yes">
<$list filter="[tags[]sort[title]]" itemClass="tw-menu-list-item">
<$transclude template="$:/core/ui/TagTemplate"/> <small class="tw-menu-list-count"><$count filter="[is[current]tagging[]]"/></small>
<$transclude title="$:/core/ui/TagTemplate"/> <small class="tw-menu-list-count"><$count filter="[is[current]tagging[]]"/></small>
</$list>
</$reveal>
<$reveal type="match" state="$:/state/moreSideBarTabSet" text="missingTab" qualifyTiddlerTitles="yes">

Wyświetl plik

@ -1,6 +1,6 @@
title: $:/core/ui/TagTemplate
<$button popup="$:/state/tagpopup" qualifyTiddlerTitles="yes" class="btn-invisible"><$setstyle name="background-color" value={{!!color}} class="tw-tag-label"><$transclude target={{!!icon}}/> <$view field="title" format="text" /></$setstyle></$button>
<$button popup="$:/state/tagpopup" qualifyTiddlerTitles="yes" class="btn-invisible"><$setstyle name="background-color" value={{!!color}} class="tw-tag-label"><$transclude title={{!!icon}}/> <$view field="title" format="text" /></$setstyle></$button>
<$reveal state="$:/state/tagpopup" type="popup" position="below" qualifyTiddlerTitles="yes" ><div class="tw-drop-down">
<$view field="title" format="link" />
----

Wyświetl plik

@ -18,7 +18,7 @@ title: $:/core/ui/TiddlerInfo
<$list filter="[is[current]listed[]!is[system]]" itemClass="tw-menu-list-item" emptyMessage="This tiddler is not listed by any others"/>
</$reveal>
<$reveal type="match" state="$:/state/tiddlerDropDownTabSet" text="fieldsTab" qualifyTiddlerTitles="yes">
<$transclude template="$:/core/ui/TiddlerFields"/>
<$transclude title="$:/core/ui/TiddlerFields"/>
</$reveal>
</div>
</div>

Wyświetl plik

@ -3,10 +3,10 @@ tags: $:/tags/ViewTemplate
<div class="tw-tiddler-title"><div class="title"><$setstyle name="fill" value={{!!color}}><span class="tw-tiddler-controls"><$button popup="$:/state/tiddlerInfo" qualifyTiddlerTitles="yes" class="btn-invisible">{{$:/core/images/info-button}}</$button><$button message="tw-edit-tiddler" class="btn-invisible">{{$:/core/images/edit-button}}</$button><$button message="tw-close-tiddler" class="btn-invisible">{{$:/core/images/close-button}}</$button>
</span><$transclude target={{!!icon}}/> <$view field="title"/></$setstyle></div>
</span><$transclude title={{!!icon}}/> <$view field="title"/></$setstyle></div>
<$reveal type="nomatch" text="" default="" state="$:/state/tiddlerInfo" qualifyTiddlerTitles="yes" class="tw-tiddler-info" animate="yes">
<$transclude template="$:/core/ui/TiddlerInfo"/>
<$transclude title="$:/core/ui/TiddlerInfo"/>
</$reveal></div>

Wyświetl plik

@ -1,3 +1,3 @@
title: $:/state/moreSideBarTabSet-tiddlerTitle:$:/core/ui/MoreSideBar;templateTitle:$:/core/ui/MoreSideBar;-tiddlerTitle:$:/core/ui/SideBar;templateTitle:$:/core/ui/SideBar;-tiddlerTitle:$:/core/ui/PageTemplate;-
title: $:/state/moreSideBarTabSet--tiddlerTitle:$:/core/ui/MoreSideBar;--tiddlerTitle:$:/core/ui/SideBar;-tiddlerTitle:$:/core/ui/PageTemplate;-
tagsTab

Wyświetl plik

@ -1,3 +1,3 @@
title: $:/state/sideBarTabSet-tiddlerTitle:$:/core/ui/SideBar;templateTitle:$:/core/ui/SideBar;-tiddlerTitle:$:/core/ui/PageTemplate;-
title: $:/state/sideBarTabSet--tiddlerTitle:$:/core/ui/SideBar;-tiddlerTitle:$:/core/ui/PageTemplate;-
openTab

Wyświetl plik

@ -1,6 +1,9 @@
title: Docs
modified: 201308201324
created: 201308241418
creator: JeremyRuston
modified: 201308241531
modifier: JeremyRuston
tags: docs introduction
title: Docs
This is the main documentation hub for TiddlyWiki. See also the DeveloperDocs.
@ -38,19 +41,19 @@ The machinery tying those concepts together includes:
These are the individual rules that make up WikiText:
{{{ [tag[wikitextrule]sort[title]] }}}
<$list filter="[tag[wikitextrule]sort[title]]" type="ul"/>
! Widgets
Widgets give WikiText dynamic functionality:
{{{ [tag[widget]sort[title]] }}}
<$list filter="[tag[widget]sort[title]]" type="ul"/>
! Commands
When run under node.js, TiddlyWiki supports a [[command line interface|TiddlyWiki5 Node Edition]] with the following built-in commands:
{{command}}
{{{ [tag[command]sort[title]] }}}
<$list filter="[tag[command]sort[title]]" type="ul"/>
! Miscellaneous

Wyświetl plik

@ -1,4 +1,8 @@
color: #8bbec7
modified: 201306131057
created: 201308241531
creator: JeremyRuston
modified: 201308241531
modifier: JeremyRuston
title: command
When run under node.js, TiddlyWiki supports a [[command line interface|TiddlyWiki5 Node Edition]] with these built-in commands.

Wyświetl plik

@ -1,6 +1,9 @@
created: 201308241419
creator: JeremyRuston
modified: 201308241419
modifier: JeremyRuston
tags: widget docs
title: ListWidget
modified: 201308201324
tags: widget
\define demoMacro(title)
This is the tiddler called [[Blah-$title$]]

Wyświetl plik

@ -0,0 +1,16 @@
created: 201308241543
creator: JeremyRuston
modified: 201308241543
modifier: JeremyRuston
tags: docs widget
title: TiddlerWidget
! Introduction
The TiddlerWidget sets the ContextTiddler that applies for processing its content.
! Content and Attributes
|!Attribute |!Description |
|title |The title of the tiddler to become the new ContextTiddler |
|class |CSS classes to added to the generated elements |

Wyświetl plik

@ -0,0 +1,23 @@
created: 201308241425
creator: JeremyRuston
modified: 201308241530
modifier: JeremyRuston
tags: widget docs
title: TranscludeWidget
! Introduction
The TranscludeWidget dynamically imports content from another tiddler.
! Attributes
|!Attribute |!Description |
|title |The title of the tiddler to transclude (defaults to the current tiddler) |
|field |The field name of the current tiddler (defaults to "text"; if present takes precedence over the index attribute) |
|index |The index of a property in a [[DataTiddler|DataTiddlers]] |
|class |CSS classes to added to the generated elements |
|style |CSS styles to be added to the generated elements |
|tooltip |Tooltip to be added to the generated elements |
The TranscludeWidget ignores any contained content.