1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2026-02-14 22:19:50 +00:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Jeremy Ruston
1018a9eb40 Update version number 2023-11-21 11:21:29 +00:00
Jeremy Ruston
ce30e64a71 Initial Commit 2023-07-31 10:09:44 +01:00
Jeremy Ruston
3e213569e2 Update release note 2023-07-30 22:50:01 +01:00
Jeremy Ruston
4bdac09872 Fix transclude inefficiency (#7647)
* Refactor parse mode out of getTransclusionTarget

* Refactor missing transclusion target

* Add a test to avoid regressions on the handling of macros vs procedures

* Refactor condition logic

* Preparing to split getTransclusionTarget into two separate functions

* Split getTransclusionTarget into getTransclusionTargetIncludingParseTreeNodes

* Resolve another inefficiency

The transclusion target was sometimes being parsed twice when transcluding as text/plain

Associated test results are also made more consistent

* Simplify method naming

* Neatening up
2023-07-30 18:04:05 +01:00
7 changed files with 165 additions and 39 deletions

View File

@@ -171,6 +171,42 @@ ScrollableWidget.prototype.render = function(parent,nextSibling) {
parent.insertBefore(this.outerDomNode,nextSibling); parent.insertBefore(this.outerDomNode,nextSibling);
this.renderChildren(this.innerDomNode,null); this.renderChildren(this.innerDomNode,null);
this.domNodes.push(this.outerDomNode); this.domNodes.push(this.outerDomNode);
// If the scroll position is bound to a tiddler
if(this.scrollableBind) {
// After a delay for rendering, scroll to the bound position
setTimeout(this.updateScrollPositionFromBoundTiddler.bind(this),50);
// Save scroll position on DOM scroll event
this.outerDomNode.addEventListener("scroll",function(event) {
var existingTiddler = self.wiki.getTiddler(self.scrollableBind),
newTiddlerFields = {
title: self.scrollableBind,
"scroll-left": self.outerDomNode.scrollLeft.toString(),
"scroll-top": self.outerDomNode.scrollTop.toString()
};
if(!existingTiddler || (existingTiddler.fields["scroll-left"] !== newTiddlerFields["scroll-left"] || existingTiddler.fields["scroll-top"] !== newTiddlerFields["scroll-top"])) {
self.wiki.addTiddler(new $tw.Tiddler(existingTiddler,newTiddlerFields));
}
});
}
};
ScrollableWidget.prototype.updateScrollPositionFromBoundTiddler = function() {
var tiddler = this.wiki.getTiddler(this.scrollableBind);
if(tiddler) {
var scrollLeftTo = this.outerDomNode.scrollLeft;
if(parseFloat(tiddler.fields["scroll-left"]).toString() === tiddler.fields["scroll-left"]) {
scrollLeftTo = parseFloat(tiddler.fields["scroll-left"]);
}
var scrollTopTo = this.outerDomNode.scrollTop;
if(parseFloat(tiddler.fields["scroll-top"]).toString() === tiddler.fields["scroll-top"]) {
scrollTopTo = parseFloat(tiddler.fields["scroll-top"]);
}
this.outerDomNode.scrollTo({
top: scrollTopTo,
left: scrollLeftTo,
behavior: "instant"
})
}
}; };
/* /*
@@ -178,6 +214,7 @@ Compute the internal state of the widget
*/ */
ScrollableWidget.prototype.execute = function() { ScrollableWidget.prototype.execute = function() {
// Get attributes // Get attributes
this.scrollableBind = this.getAttribute("bind");
this.fallthrough = this.getAttribute("fallthrough","yes"); this.fallthrough = this.getAttribute("fallthrough","yes");
this["class"] = this.getAttribute("class"); this["class"] = this.getAttribute("class");
// Make child widgets // Make child widgets
@@ -193,6 +230,9 @@ ScrollableWidget.prototype.refresh = function(changedTiddlers) {
this.refreshSelf(); this.refreshSelf();
return true; return true;
} }
if(changedAttributes.bind || changedTiddlers[this.getAttribute("bind")]) {
this.updateScrollPositionFromBoundTiddler();
}
return this.refreshChildren(changedTiddlers); return this.refreshChildren(changedTiddlers);
}; };

View File

@@ -41,30 +41,43 @@ TranscludeWidget.prototype.execute = function() {
this.collectAttributes(); this.collectAttributes();
this.collectStringParameters(); this.collectStringParameters();
this.collectSlotFillParameters(); this.collectSlotFillParameters();
// Get the target text and parse tree nodes that we are transcluding // Determine whether we're being used in inline or block mode
var target = this.getTransclusionTarget(), var parseAsInline = !this.parseTreeNode.isBlock;
parseTreeNodes; if(this.transcludeMode === "inline") {
this.sourceText = target.text; parseAsInline = true;
this.parserType = target.type; } else if(this.transcludeMode === "block") {
this.parseAsInline = target.parseAsInline; parseAsInline = false;
}
// Set 'thisTiddler' // Set 'thisTiddler'
this.setVariable("thisTiddler",this.transcludeTitle); this.setVariable("thisTiddler",this.transcludeTitle);
var parseTreeNodes, target;
// Process the transclusion according to the output type // Process the transclusion according to the output type
switch(this.transcludeOutput || "text/html") { switch(this.transcludeOutput || "text/html") {
case "text/html": case "text/html":
// Return the parse tree nodes // Return the parse tree nodes of the target
target = this.parseTransclusionTarget(parseAsInline);
this.parseAsInline = target.parseAsInline;
parseTreeNodes = target.parseTreeNodes; parseTreeNodes = target.parseTreeNodes;
break; break;
case "text/raw": case "text/raw":
// Just return the raw text // Just return the raw text
parseTreeNodes = [{type: "text", text: this.sourceText}]; target = this.getTransclusionTarget();
parseTreeNodes = [{type: "text", text: target.text}];
break; break;
default: default:
// text/plain // "text/plain" is the plain text result of wikifying the text
var plainText = this.wiki.renderText("text/plain",this.parserType,this.sourceText,{parentWidget: this}); target = this.parseTransclusionTarget(parseAsInline);
parseTreeNodes = [{type: "text", text: plainText}]; var widgetNode = this.wiki.makeWidget(target.parser,{
parentWidget: this,
document: $tw.fakeDocument
});
var container = $tw.fakeDocument.createElement("div");
widgetNode.render(container,null);
parseTreeNodes = [{type: "text", text: container.textContent}];
break; break;
} }
this.sourceText = target.text;
this.parserType = target.type;
// Set the legacy transclusion context variables only if we're not transcluding a variable // Set the legacy transclusion context variables only if we're not transcluding a variable
if(!this.transcludeVariable) { if(!this.transcludeVariable) {
var recursionMarker = this.makeRecursionMarker(); var recursionMarker = this.makeRecursionMarker();
@@ -161,17 +174,44 @@ TranscludeWidget.prototype.collectSlotFillParameters = function() {
}; };
/* /*
Get transcluded parse tree nodes as an object {text:,type:,parseTreeNodes:,parseAsInline:} Get transcluded details as an object {text:,type:}
*/ */
TranscludeWidget.prototype.getTransclusionTarget = function() { TranscludeWidget.prototype.getTransclusionTarget = function() {
var self = this; var self = this;
// Determine whether we're being used in inline or block mode var text;
var parseAsInline = !this.parseTreeNode.isBlock; // Return the text and type of the target
if(this.transcludeMode === "inline") { if(this.hasAttribute("$variable")) {
parseAsInline = true; if(this.transcludeVariable) {
} else if(this.transcludeMode === "block") { // Transcluding a variable
parseAsInline = false; var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()});
text = variableInfo.text;
return {
text: variableInfo.text,
type: this.transcludeType
};
} }
} else {
// Transcluding a text reference
var parserInfo = this.wiki.getTextReferenceParserInfo(
this.transcludeTitle,
this.transcludeField,
this.transcludeIndex,
{
subTiddler: this.transcludeSubTiddler,
defaultType: this.transcludeType
});
return {
text: parserInfo.text,
type: parserInfo.type
};
}
};
/*
Get transcluded parse tree nodes as an object {text:,type:,parseTreeNodes:,parseAsInline:}
*/
TranscludeWidget.prototype.parseTransclusionTarget = function(parseAsInline) {
var self = this;
var parser; var parser;
// Get the parse tree // Get the parse tree
if(this.hasAttribute("$variable")) { if(this.hasAttribute("$variable")) {
@@ -237,7 +277,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() {
} }
$tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"]) $tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"])
}); });
} else if(srcVariable && (srcVariable.isMacroDefinition || !srcVariable.isFunctionDefinition)) { } else if(srcVariable && !srcVariable.isFunctionDefinition) {
// For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__" // For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__"
parser = { parser = {
tree: [ tree: [
@@ -269,22 +309,13 @@ TranscludeWidget.prototype.getTransclusionTarget = function() {
}); });
} }
// Return the parse tree // Return the parse tree
if(parser) {
return { return {
parseTreeNodes: parser.tree, parser: parser,
parseTreeNodes: parser ? parser.tree : (this.slotFillParseTrees["ts-missing"] || []),
parseAsInline: parseAsInline, parseAsInline: parseAsInline,
text: parser.source, text: parser && parser.source,
type: parser.type type: parser && parser.type
}; };
} else {
// If there's no parse tree then return the missing slot value
return {
parseTreeNodes: (this.slotFillParseTrees["ts-missing"] || []),
parseAsInline: parseAsInline,
text: null,
type: null
};
}
}; };
/* /*

View File

@@ -9,9 +9,10 @@ type: text/vnd.tiddlywiki
! Overview of v5.3.1 ! Overview of v5.3.1
! Reversions of v5.3.0 Changes ! Bug Fixes and Reversions of v5.3.0 Changes
* Reverted adding the `widget.destroy()` method because of performance concerns (see https://github.com/Jermolene/TiddlyWiki5/pull/7468) * Reverted adding the `widget.destroy()` method because of performance concerns (see https://github.com/Jermolene/TiddlyWiki5/pull/7468)
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7647">> inefficiency when transcluding with the ''$output'' attribute set to `text/plain`
! Plugin Improvements ! Plugin Improvements

View File

@@ -0,0 +1,3 @@
title: $:/my-scroll-position
scroll-left: 0
scroll-top: 100

View File

@@ -33,4 +33,4 @@ $param$ with a ''buffalo''
+ +
title: ExpectedResult title: ExpectedResult
<p>Going to lunch with a ''buffalo''</p><p>Going to breakfastwith a<strong>buffalo</strong></p><p>Going to dinner with a <strong>buffalo</strong></p>Going to lunch with a buffalo with a buffaloGoing to dinner with a buffalo <p>Going to lunch with a ''buffalo''</p><p>Going to breakfastwith a<strong>buffalo</strong></p><p>Going to dinner with a <strong>buffalo</strong></p>Going to lunch with a ''buffalo''Going to breakfastwith abuffaloGoing to dinner with a buffalo

View File

@@ -0,0 +1,26 @@
title: Procedures/Double/Underscore
description: Checking that procedures don't expose parameters as variables wrapped in double underscores
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Output
\whitespace trim
\procedure mamacro(one:"red",two:"green")
It is $one$ and $two$<<__one__>><<__two__>>.
\end
<$macrocall $name="mamacro"/>
<$transclude $variable="mamacro"/>
<$transclude $variable="mamacro" one="orange"/>
<$transclude $variable="mamacro" 0="pink"/>
<$transclude $variable="mamacro" one="purple" 1="pink"/>
+
title: ExpectedResult
<p>It is $one$ and $two$.</p><p>It is $one$ and $two$.</p><p>It is $one$ and $two$.</p><p>It is $one$ and $two$.</p><p>It is $one$ and $two$.</p>

View File

@@ -1,6 +1,6 @@
caption: scrollable caption: scrollable
created: 20140324223413403 created: 20140324223413403
modified: 20220620115347910 modified: 20230731100903977
tags: Widgets tags: Widgets
title: ScrollableWidget title: ScrollableWidget
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@@ -16,12 +16,15 @@ The content of the `<$scrollable>` widget is displayed within a pair of wrapper
|!Attribute |!Description | |!Attribute |!Description |
|class |The CSS class(es) to be applied to the outer DIV | |class |The CSS class(es) to be applied to the outer DIV |
|fallthrough |See below | |fallthrough |See below |
|bind |<<.from-version "5.3.2">> Optional title of tiddler to which the scroll position should be bound |
Binding the scroll position to a tiddler automatically copies the scroll coordinates into the `scroll-left` and `scroll-top` fields as scrolling occurs. Conversely, setting those field values will automatically cause the scrollable to scroll if it can.
<$macrocall $name=".note" _="""If a scrollable widget can't handle the `tm-scroll` message because the inner DIV fits within the outer DIV, then by default the message falls through to the parent widget. Setting the ''fallthrough'' attribute to `no` prevents this behaviour."""/> <$macrocall $name=".note" _="""If a scrollable widget can't handle the `tm-scroll` message because the inner DIV fits within the outer DIV, then by default the message falls through to the parent widget. Setting the ''fallthrough'' attribute to `no` prevents this behaviour."""/>
! Examples ! Examples
This example requires the following CSS definitions from [[$:/_tw5.com-styles]]: These examples require the following CSS definitions from [[$:/_tw5.com-styles]]:
``` ```
.tc-scrollable-demo { .tc-scrollable-demo {
@@ -33,6 +36,8 @@ This example requires the following CSS definitions from [[$:/_tw5.com-styles]]:
} }
``` ```
!! Simple Usage
This wiki text shows how to display a list within the scrollable widget: This wiki text shows how to display a list within the scrollable widget:
<<wikitext-example-without-html "<$scrollable class='tc-scrollable-demo'> <<wikitext-example-without-html "<$scrollable class='tc-scrollable-demo'>
@@ -46,3 +51,23 @@ This wiki text shows how to display a list within the scrollable widget:
</$scrollable> </$scrollable>
">> ">>
!! Binding scroll position to a tiddler
[[Current scroll position|$:/my-scroll-position]]: {{$:/my-scroll-position!!scroll-left}}, {{$:/my-scroll-position!!scroll-top}}
<$button>
<$action-setfield $tiddler="$:/my-scroll-position" scroll-left="100" scroll-top="100"/>
Set current scroll position to 100,100
</$button>
<<wikitext-example-without-html "<$scrollable class='tc-scrollable-demo' bind='$:/my-scroll-position'>
<$list filter='[tag[Reference]]'>
<$view field='title'/>: <$list filter='[all[current]links[]sort[title]]' storyview='pop'>
<$link><$view field='title'/></$link>
</$list>
</$list>
</$scrollable>
">>