-⚠ Attention : Ne faites pas comme ça ! |
+⚠ Attention : Ne faites pas comme ça ! |
$eg$
diff --git a/editions/test/tiddlers/tests/test-wikitext.js b/editions/test/tiddlers/tests/test-wikitext.js
index 3213f6abf..4cab566da 100644
--- a/editions/test/tiddlers/tests/test-wikitext.js
+++ b/editions/test/tiddlers/tests/test-wikitext.js
@@ -63,6 +63,22 @@ describe("WikiText tests", function() {
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","@@color:red;\n \n\nContent \n@@")).toBe("");
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","@@color:red;\n---\n@@")).toBe(" ");
});
+ it("handles inline style wikitext notation", function() {
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@highlighted@@ text")).toBe('some highlighted text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@color:green;.tc-inline-style 1 style and 1 class@@ text")).toBe('some 1 style and 1 class text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@background-color:red;red@@ text")).toBe('some red text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@.myClass class@@ text")).toBe('some class text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@.myClass.secondClass 2 classes@@ text")).toBe('some 2 classes text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@background:red;.myClass style and class@@ text")).toBe('some style and class text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@background:red;color:white;.myClass 2 style and 1 class@@ text")).toBe('some 2 style and 1 class text ');
+ });
});
})();
diff --git a/editions/tw5.com/tiddlers/system/doc-macros.tid b/editions/tw5.com/tiddlers/system/doc-macros.tid
index d4c4b9506..a264198a9 100644
--- a/editions/tw5.com/tiddlers/system/doc-macros.tid
+++ b/editions/tw5.com/tiddlers/system/doc-macros.tid
@@ -117,7 +117,7 @@ This is an example tiddler. See [[Table-of-Contents Macros (Examples)]].
-⚠ Warning: Don't do it this way! |
+⚠ Warning: Don't do it this way! |
$eg$
diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid
index 9c4a342ff..17d3ebb39 100644
--- a/themes/tiddlywiki/vanilla/base.tid
+++ b/themes/tiddlywiki/vanilla/base.tid
@@ -290,6 +290,11 @@ kbd {
color: <>;
}
+.tc-inline-style {
+ background: <>;
+ color: <>;
+}
+
form.tc-form-inline {
display: inline;
}
From 6f98edd6bd2b9d49f9120709dbd81fcc52af887b Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Fri, 7 Oct 2022 18:43:09 +0100
Subject: [PATCH 041/937] Revert "Add default settings for styled inline SPANs
(#6877)" because of failed tests
This reverts commit 23e0eeb556d1933f4fedfa3225ea733c7a0aa260.
---
.../parsers/wikiparser/rules/styleinline.js | 6 +++---
core/palettes/Blanca.tid | 2 --
core/palettes/Blue.tid | 2 --
core/palettes/BrightMute.tid | 2 --
core/palettes/ContrastDark.tid | 2 --
core/palettes/ContrastLight.tid | 2 --
core/palettes/CupertinoDark.tid | 2 --
core/palettes/DarkPhotos.tid | 2 --
core/palettes/DesertSand.tid | 2 --
core/palettes/GruvBoxDark.tid | 2 --
core/palettes/Nord.tid | 2 --
core/palettes/Rocker.tid | 2 --
core/palettes/SolarFlare.tid | 2 --
core/palettes/SolarizedDark.tid | 2 --
core/palettes/SolarizedLight.tid | 2 --
core/palettes/SpartanDay.tid | 2 --
core/palettes/SpartanNight.tid | 2 --
core/palettes/Twilight.tid | 2 --
core/palettes/Vanilla.tid | 2 --
.../tiddlers/$__editions_tw5.com_doc-macros.tid | 2 +-
editions/test/tiddlers/tests/test-wikitext.js | 16 ----------------
editions/tw5.com/tiddlers/system/doc-macros.tid | 2 +-
themes/tiddlywiki/vanilla/base.tid | 5 -----
23 files changed, 5 insertions(+), 62 deletions(-)
diff --git a/core/modules/parsers/wikiparser/rules/styleinline.js b/core/modules/parsers/wikiparser/rules/styleinline.js
index 653e48986..cd42d8f00 100644
--- a/core/modules/parsers/wikiparser/rules/styleinline.js
+++ b/core/modules/parsers/wikiparser/rules/styleinline.js
@@ -41,6 +41,9 @@ exports.parse = function() {
var node = {
type: "element",
tag: "span",
+ attributes: {
+ "class": {type: "string", value: "tc-inline-style"}
+ },
children: tree
};
if(classString) {
@@ -49,9 +52,6 @@ exports.parse = function() {
if(stylesString) {
$tw.utils.addAttributeToParseTreeNode(node,"style",stylesString);
}
- if(!classString && !stylesString) {
- $tw.utils.addClassToParseTreeNode(node,"tc-inline-style");
- }
return [node];
};
diff --git a/core/palettes/Blanca.tid b/core/palettes/Blanca.tid
index 0fd8e2da3..f86705443 100644
--- a/core/palettes/Blanca.tid
+++ b/core/palettes/Blanca.tid
@@ -34,8 +34,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/Blue.tid b/core/palettes/Blue.tid
index cc3846b50..e3aa21952 100644
--- a/core/palettes/Blue.tid
+++ b/core/palettes/Blue.tid
@@ -34,8 +34,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333353
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/BrightMute.tid b/core/palettes/BrightMute.tid
index ddbd6b3cd..64193c1ce 100644
--- a/core/palettes/BrightMute.tid
+++ b/core/palettes/BrightMute.tid
@@ -34,8 +34,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/ContrastDark.tid b/core/palettes/ContrastDark.tid
index 850a5863b..ded11b835 100644
--- a/core/palettes/ContrastDark.tid
+++ b/core/palettes/ContrastDark.tid
@@ -34,8 +34,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #00a
external-link-foreground: #00e
foreground: #000
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: <>
message-border: <>
message-foreground: <>
diff --git a/core/palettes/ContrastLight.tid b/core/palettes/ContrastLight.tid
index ade9f43f8..d3eb2f731 100644
--- a/core/palettes/ContrastLight.tid
+++ b/core/palettes/ContrastLight.tid
@@ -34,8 +34,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #00a
external-link-foreground: #00e
foreground: #fff
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: <>
message-border: <>
message-foreground: <>
diff --git a/core/palettes/CupertinoDark.tid b/core/palettes/CupertinoDark.tid
index 7f08f4100..c956561ed 100644
--- a/core/palettes/CupertinoDark.tid
+++ b/core/palettes/CupertinoDark.tid
@@ -32,8 +32,6 @@ external-link-foreground-hover:
external-link-foreground-visited: #BF5AF2
external-link-foreground: #32D74B
foreground: #FFFFFF
-highlight-background: #ffff78
-highlight-foreground: #000000
menubar-background: #464646
menubar-foreground: #ffffff
message-background: <>
diff --git a/core/palettes/DarkPhotos.tid b/core/palettes/DarkPhotos.tid
index 71fa40f2f..4e1949c04 100644
--- a/core/palettes/DarkPhotos.tid
+++ b/core/palettes/DarkPhotos.tid
@@ -36,8 +36,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/DesertSand.tid b/core/palettes/DesertSand.tid
index ddce80e27..40056962b 100644
--- a/core/palettes/DesertSand.tid
+++ b/core/palettes/DesertSand.tid
@@ -40,8 +40,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #313163
external-link-foreground: #555592
foreground: #2D2A23
-highlight-background: #ffff00
-highlight-foreground: #000000
menubar-background: #CDC2A6
menubar-foreground: #5A5446
message-background: #ECE5CF
diff --git a/core/palettes/GruvBoxDark.tid b/core/palettes/GruvBoxDark.tid
index 3b62eb311..840d8683e 100644
--- a/core/palettes/GruvBoxDark.tid
+++ b/core/palettes/GruvBoxDark.tid
@@ -41,8 +41,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #d3869b
external-link-foreground: #8ec07c
foreground: #fbf1c7
-highlight-background: #ffff79
-highlight-foreground: #000000
menubar-background: #504945
menubar-foreground: <>
message-background: #83a598
diff --git a/core/palettes/Nord.tid b/core/palettes/Nord.tid
index b296ba783..f825f45f2 100644
--- a/core/palettes/Nord.tid
+++ b/core/palettes/Nord.tid
@@ -41,8 +41,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #5E81AC
external-link-foreground: #8FBCBB
foreground: #d8dee9
-highlight-background: #ffff78
-highlight-foreground: #000000
menubar-background: #2E3440
menubar-foreground: #d8dee9
message-background: #2E3440
diff --git a/core/palettes/Rocker.tid b/core/palettes/Rocker.tid
index a91cd1b5f..bc7d2ded6 100644
--- a/core/palettes/Rocker.tid
+++ b/core/palettes/Rocker.tid
@@ -34,8 +34,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/SolarFlare.tid b/core/palettes/SolarFlare.tid
index 4d81bf531..e72815657 100644
--- a/core/palettes/SolarFlare.tid
+++ b/core/palettes/SolarFlare.tid
@@ -131,8 +131,6 @@ external-link-background-hover: inherit
external-link-background-visited: inherit
external-link-background: inherit
external-link-foreground-hover: inherit
-highlight-background: #ffff00
-highlight-foreground: #000000
message-border: #cfd6e6
modal-border: #999999
select-tag-background:
diff --git a/core/palettes/SolarizedDark.tid b/core/palettes/SolarizedDark.tid
index eea273c30..f193bff1c 100644
--- a/core/palettes/SolarizedDark.tid
+++ b/core/palettes/SolarizedDark.tid
@@ -35,8 +35,6 @@ external-link-foreground: #268bd2
external-link-foreground-hover:
external-link-foreground-visited: #268bd2
foreground: #839496
-highlight-background: #ffff78
-highlight-foreground: #000000
message-background: #002b36
message-border: #586e75
message-foreground: #839496
diff --git a/core/palettes/SolarizedLight.tid b/core/palettes/SolarizedLight.tid
index 3cf954bb5..4bcec81d6 100644
--- a/core/palettes/SolarizedLight.tid
+++ b/core/palettes/SolarizedLight.tid
@@ -35,8 +35,6 @@ external-link-foreground: #268bd2
external-link-foreground-hover: inherit
external-link-foreground-visited: #268bd2
foreground: #657b83
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: #fdf6e3
message-border: #93a1a1
message-foreground: #657b83
diff --git a/core/palettes/SpartanDay.tid b/core/palettes/SpartanDay.tid
index cc197144e..3cd8337a7 100644
--- a/core/palettes/SpartanDay.tid
+++ b/core/palettes/SpartanDay.tid
@@ -34,8 +34,6 @@ external-link-foreground-hover:
external-link-foreground-visited:
external-link-foreground:
foreground: rgba(0, 0, 0, 0.87)
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: <>
message-border: <>
message-foreground: rgba(0, 0, 0, 0.54)
diff --git a/core/palettes/SpartanNight.tid b/core/palettes/SpartanNight.tid
index dc47a0774..1962c4782 100644
--- a/core/palettes/SpartanNight.tid
+++ b/core/palettes/SpartanNight.tid
@@ -34,8 +34,6 @@ external-link-foreground-hover:
external-link-foreground-visited: #7c318c
external-link-foreground: #9e3eb3
foreground: rgba(255, 255, 255, 0.7)
-highlight-background: #ffff78
-highlight-foreground: #000000
message-background: <>
message-border: <>
message-foreground: rgba(255, 255, 255, 0.54)
diff --git a/core/palettes/Twilight.tid b/core/palettes/Twilight.tid
index 4c127f822..ff8a0b956 100644
--- a/core/palettes/Twilight.tid
+++ b/core/palettes/Twilight.tid
@@ -43,8 +43,6 @@ external-link-foreground: rgb(179, 179, 255)
external-link-foreground-hover: inherit
external-link-foreground-visited: rgb(153, 153, 255)
foreground: rgb(179, 179, 179)
-highlight-background: #ffff78
-highlight-foreground: #000000
message-background: <>
message-border: #96ccff
message-foreground: <>
diff --git a/core/palettes/Vanilla.tid b/core/palettes/Vanilla.tid
index d84b4ec83..d3c7b1441 100644
--- a/core/palettes/Vanilla.tid
+++ b/core/palettes/Vanilla.tid
@@ -42,8 +42,6 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
-highlight-background: #ffff00
-highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/editions/fr-FR/tiddlers/$__editions_tw5.com_doc-macros.tid b/editions/fr-FR/tiddlers/$__editions_tw5.com_doc-macros.tid
index fcf3ddb80..de6376218 100644
--- a/editions/fr-FR/tiddlers/$__editions_tw5.com_doc-macros.tid
+++ b/editions/fr-FR/tiddlers/$__editions_tw5.com_doc-macros.tid
@@ -117,7 +117,7 @@ C'est un exemple de tiddler. Voir [[Macros Table des matières (Exemples)|Table-
-⚠ Attention : Ne faites pas comme ça ! |
+⚠ Attention : Ne faites pas comme ça ! |
$eg$
diff --git a/editions/test/tiddlers/tests/test-wikitext.js b/editions/test/tiddlers/tests/test-wikitext.js
index 4cab566da..3213f6abf 100644
--- a/editions/test/tiddlers/tests/test-wikitext.js
+++ b/editions/test/tiddlers/tests/test-wikitext.js
@@ -63,22 +63,6 @@ describe("WikiText tests", function() {
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","@@color:red;\n \n\nContent \n@@")).toBe("");
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","@@color:red;\n---\n@@")).toBe(" ");
});
- it("handles inline style wikitext notation", function() {
- expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
- "some @@highlighted@@ text")).toBe('some highlighted text ');
- expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
- "some @@color:green;.tc-inline-style 1 style and 1 class@@ text")).toBe('some 1 style and 1 class text ');
- expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
- "some @@background-color:red;red@@ text")).toBe('some red text ');
- expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
- "some @@.myClass class@@ text")).toBe('some class text ');
- expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
- "some @@.myClass.secondClass 2 classes@@ text")).toBe('some 2 classes text ');
- expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
- "some @@background:red;.myClass style and class@@ text")).toBe('some style and class text ');
- expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
- "some @@background:red;color:white;.myClass 2 style and 1 class@@ text")).toBe('some 2 style and 1 class text ');
- });
});
})();
diff --git a/editions/tw5.com/tiddlers/system/doc-macros.tid b/editions/tw5.com/tiddlers/system/doc-macros.tid
index a264198a9..d4c4b9506 100644
--- a/editions/tw5.com/tiddlers/system/doc-macros.tid
+++ b/editions/tw5.com/tiddlers/system/doc-macros.tid
@@ -117,7 +117,7 @@ This is an example tiddler. See [[Table-of-Contents Macros (Examples)]].
-⚠ Warning: Don't do it this way! |
+⚠ Warning: Don't do it this way! |
$eg$
diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid
index 17d3ebb39..9c4a342ff 100644
--- a/themes/tiddlywiki/vanilla/base.tid
+++ b/themes/tiddlywiki/vanilla/base.tid
@@ -290,11 +290,6 @@ kbd {
color: <>;
}
-.tc-inline-style {
- background: <>;
- color: <>;
-}
-
form.tc-form-inline {
display: inline;
}
From 91327c1af039a4d2dbd88e22d04bef9e36902890 Mon Sep 17 00:00:00 2001
From: Rob Hoelz
Date: Mon, 10 Oct 2022 10:49:20 -0500
Subject: [PATCH 042/937] Use deprecated macro for deprecated commands (#6973)
This just brings the documentation for these commands more inline with
other deprecations, plus offers a link to the recommended alternative
as well as the explanation for deprecation
---
editions/tw5.com/tiddlers/commands/RenderTiddlerCommand.tid | 2 ++
editions/tw5.com/tiddlers/commands/RenderTiddlersCommand.tid | 2 ++
editions/tw5.com/tiddlers/commands/SaveTiddlerCommand.tid | 2 ++
editions/tw5.com/tiddlers/commands/SaveTiddlersCommand.tid | 2 ++
4 files changed, 8 insertions(+)
diff --git a/editions/tw5.com/tiddlers/commands/RenderTiddlerCommand.tid b/editions/tw5.com/tiddlers/commands/RenderTiddlerCommand.tid
index 7e535648d..ede970536 100644
--- a/editions/tw5.com/tiddlers/commands/RenderTiddlerCommand.tid
+++ b/editions/tw5.com/tiddlers/commands/RenderTiddlerCommand.tid
@@ -2,4 +2,6 @@ title: RenderTiddlerCommand
tags: Commands
caption: rendertiddler
+<<.deprecated-since "5.1.15" "RenderCommand">>.
+
{{$:/language/Help/rendertiddler}}
diff --git a/editions/tw5.com/tiddlers/commands/RenderTiddlersCommand.tid b/editions/tw5.com/tiddlers/commands/RenderTiddlersCommand.tid
index d2b62fb99..6d4282fb2 100644
--- a/editions/tw5.com/tiddlers/commands/RenderTiddlersCommand.tid
+++ b/editions/tw5.com/tiddlers/commands/RenderTiddlersCommand.tid
@@ -2,4 +2,6 @@ title: RenderTiddlersCommand
tags: Commands
caption: rendertiddlers
+<<.deprecated-since "5.1.15" "RenderCommand">>.
+
{{$:/language/Help/rendertiddlers}}
diff --git a/editions/tw5.com/tiddlers/commands/SaveTiddlerCommand.tid b/editions/tw5.com/tiddlers/commands/SaveTiddlerCommand.tid
index 3f4626a5b..f51799163 100644
--- a/editions/tw5.com/tiddlers/commands/SaveTiddlerCommand.tid
+++ b/editions/tw5.com/tiddlers/commands/SaveTiddlerCommand.tid
@@ -4,4 +4,6 @@ created: 20131218121606089
modified: 20131218121606089
caption: savetiddler
+<<.deprecated-since "5.1.15" "SaveCommand">>.
+
{{$:/language/Help/savetiddler}}
diff --git a/editions/tw5.com/tiddlers/commands/SaveTiddlersCommand.tid b/editions/tw5.com/tiddlers/commands/SaveTiddlersCommand.tid
index 5ca877bfa..c167cd4a3 100644
--- a/editions/tw5.com/tiddlers/commands/SaveTiddlersCommand.tid
+++ b/editions/tw5.com/tiddlers/commands/SaveTiddlersCommand.tid
@@ -4,4 +4,6 @@ created: 20140609121606089
modified: 20140609121606089
caption: savetiddlers
+<<.deprecated-since "5.1.15" "SaveCommand">>.
+
{{$:/language/Help/savetiddlers}}
From cfd894e6fb94af8176b4687bc93c3faa91e5e351 Mon Sep 17 00:00:00 2001
From: Maurycy Zarzycki
Date: Sat, 15 Oct 2022 13:26:21 +0200
Subject: [PATCH 043/937] allow select widget class to update if it uses a
filter and is output changes (#6987)
* allow select widget class to update if it uses a filter and the filter output changes
* rewrite code to be more idiomatic + updates local property
---
core/modules/widgets/select.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/core/modules/widgets/select.js b/core/modules/widgets/select.js
index b3177d967..8272a2783 100644
--- a/core/modules/widgets/select.js
+++ b/core/modules/widgets/select.js
@@ -175,6 +175,11 @@ SelectWidget.prototype.refresh = function(changedTiddlers) {
return true;
// If the target tiddler value has changed, just update setting and refresh the children
} else {
+ if(changedAttributes.class) {
+ this.selectClass = this.getAttribute("class");
+ this.getSelectDomNode().setAttribute("class",this.selectClass);
+ }
+
var childrenRefreshed = this.refreshChildren(changedTiddlers);
if(changedTiddlers[this.selectTitle] || childrenRefreshed) {
this.setSelectValue();
From 8f079e2d4566418afdfef16e594fc173423decc5 Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Sun, 16 Oct 2022 17:27:46 +0100
Subject: [PATCH 044/937] Fix handling of orderedattributes when adding
classes/styles
Fixes issue referred to in https://github.com/Jermolene/TiddlyWiki5/pull/6877#issuecomment-1277590200
---
core/modules/utils/parsetree.js | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/core/modules/utils/parsetree.js b/core/modules/utils/parsetree.js
index 5bab10706..a74b8f3f8 100644
--- a/core/modules/utils/parsetree.js
+++ b/core/modules/utils/parsetree.js
@@ -65,10 +65,8 @@ exports.addClassToParseTreeNode = function(node,classString) {
// If the class attribute does not exist, we must create it first.
attribute = {name: "class", type: "string", value: ""};
node.attributes["class"] = attribute;
- if(node.orderedAttributes) {
- // If there are orderedAttributes, we've got to add them there too.
- node.orderedAttributes.push(attribute);
- }
+ node.orderedAttributes = node.orderedAttributes || [];
+ node.orderedAttributes.push(attribute);
}
if(attribute.type === "string") {
if(attribute.value !== "") {
@@ -88,10 +86,8 @@ exports.addStyleToParseTreeNode = function(node,name,value) {
if(!attribute) {
attribute = {name: "style", type: "string", value: ""};
node.attributes.style = attribute;
- if(node.orderedAttributes) {
- // If there are orderedAttributes, we've got to add them there too.
- node.orderedAttributes.push(attribute);
- }
+ node.orderedAttributes = node.orderedAttributes || [];
+ node.orderedAttributes.push(attribute);
}
if(attribute.type === "string") {
attribute.value += name + ":" + value + ";";
From b531984f50cd82ebde3f167e157a840465d7a66a Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Sun, 16 Oct 2022 17:28:36 +0100
Subject: [PATCH 045/937] Restore "Add default settings for styled inline SPANs
(#6877)"
This reverts commit 6f98edd6bd2b9d49f9120709dbd81fcc52af887b.
---
.../parsers/wikiparser/rules/styleinline.js | 6 +++---
core/palettes/Blanca.tid | 2 ++
core/palettes/Blue.tid | 2 ++
core/palettes/BrightMute.tid | 2 ++
core/palettes/ContrastDark.tid | 2 ++
core/palettes/ContrastLight.tid | 2 ++
core/palettes/CupertinoDark.tid | 2 ++
core/palettes/DarkPhotos.tid | 2 ++
core/palettes/DesertSand.tid | 2 ++
core/palettes/GruvBoxDark.tid | 2 ++
core/palettes/Nord.tid | 2 ++
core/palettes/Rocker.tid | 2 ++
core/palettes/SolarFlare.tid | 2 ++
core/palettes/SolarizedDark.tid | 2 ++
core/palettes/SolarizedLight.tid | 2 ++
core/palettes/SpartanDay.tid | 2 ++
core/palettes/SpartanNight.tid | 2 ++
core/palettes/Twilight.tid | 2 ++
core/palettes/Vanilla.tid | 2 ++
.../tiddlers/$__editions_tw5.com_doc-macros.tid | 2 +-
editions/test/tiddlers/tests/test-wikitext.js | 16 ++++++++++++++++
editions/tw5.com/tiddlers/system/doc-macros.tid | 2 +-
themes/tiddlywiki/vanilla/base.tid | 5 +++++
23 files changed, 62 insertions(+), 5 deletions(-)
diff --git a/core/modules/parsers/wikiparser/rules/styleinline.js b/core/modules/parsers/wikiparser/rules/styleinline.js
index cd42d8f00..653e48986 100644
--- a/core/modules/parsers/wikiparser/rules/styleinline.js
+++ b/core/modules/parsers/wikiparser/rules/styleinline.js
@@ -41,9 +41,6 @@ exports.parse = function() {
var node = {
type: "element",
tag: "span",
- attributes: {
- "class": {type: "string", value: "tc-inline-style"}
- },
children: tree
};
if(classString) {
@@ -52,6 +49,9 @@ exports.parse = function() {
if(stylesString) {
$tw.utils.addAttributeToParseTreeNode(node,"style",stylesString);
}
+ if(!classString && !stylesString) {
+ $tw.utils.addClassToParseTreeNode(node,"tc-inline-style");
+ }
return [node];
};
diff --git a/core/palettes/Blanca.tid b/core/palettes/Blanca.tid
index f86705443..0fd8e2da3 100644
--- a/core/palettes/Blanca.tid
+++ b/core/palettes/Blanca.tid
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/Blue.tid b/core/palettes/Blue.tid
index e3aa21952..cc3846b50 100644
--- a/core/palettes/Blue.tid
+++ b/core/palettes/Blue.tid
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333353
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/BrightMute.tid b/core/palettes/BrightMute.tid
index 64193c1ce..ddbd6b3cd 100644
--- a/core/palettes/BrightMute.tid
+++ b/core/palettes/BrightMute.tid
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/ContrastDark.tid b/core/palettes/ContrastDark.tid
index ded11b835..850a5863b 100644
--- a/core/palettes/ContrastDark.tid
+++ b/core/palettes/ContrastDark.tid
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #00a
external-link-foreground: #00e
foreground: #000
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: <>
message-border: <>
message-foreground: <>
diff --git a/core/palettes/ContrastLight.tid b/core/palettes/ContrastLight.tid
index d3eb2f731..ade9f43f8 100644
--- a/core/palettes/ContrastLight.tid
+++ b/core/palettes/ContrastLight.tid
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #00a
external-link-foreground: #00e
foreground: #fff
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: <>
message-border: <>
message-foreground: <>
diff --git a/core/palettes/CupertinoDark.tid b/core/palettes/CupertinoDark.tid
index c956561ed..7f08f4100 100644
--- a/core/palettes/CupertinoDark.tid
+++ b/core/palettes/CupertinoDark.tid
@@ -32,6 +32,8 @@ external-link-foreground-hover:
external-link-foreground-visited: #BF5AF2
external-link-foreground: #32D74B
foreground: #FFFFFF
+highlight-background: #ffff78
+highlight-foreground: #000000
menubar-background: #464646
menubar-foreground: #ffffff
message-background: <>
diff --git a/core/palettes/DarkPhotos.tid b/core/palettes/DarkPhotos.tid
index 4e1949c04..71fa40f2f 100644
--- a/core/palettes/DarkPhotos.tid
+++ b/core/palettes/DarkPhotos.tid
@@ -36,6 +36,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/DesertSand.tid b/core/palettes/DesertSand.tid
index 40056962b..ddce80e27 100644
--- a/core/palettes/DesertSand.tid
+++ b/core/palettes/DesertSand.tid
@@ -40,6 +40,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #313163
external-link-foreground: #555592
foreground: #2D2A23
+highlight-background: #ffff00
+highlight-foreground: #000000
menubar-background: #CDC2A6
menubar-foreground: #5A5446
message-background: #ECE5CF
diff --git a/core/palettes/GruvBoxDark.tid b/core/palettes/GruvBoxDark.tid
index 840d8683e..3b62eb311 100644
--- a/core/palettes/GruvBoxDark.tid
+++ b/core/palettes/GruvBoxDark.tid
@@ -41,6 +41,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #d3869b
external-link-foreground: #8ec07c
foreground: #fbf1c7
+highlight-background: #ffff79
+highlight-foreground: #000000
menubar-background: #504945
menubar-foreground: <>
message-background: #83a598
diff --git a/core/palettes/Nord.tid b/core/palettes/Nord.tid
index f825f45f2..b296ba783 100644
--- a/core/palettes/Nord.tid
+++ b/core/palettes/Nord.tid
@@ -41,6 +41,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #5E81AC
external-link-foreground: #8FBCBB
foreground: #d8dee9
+highlight-background: #ffff78
+highlight-foreground: #000000
menubar-background: #2E3440
menubar-foreground: #d8dee9
message-background: #2E3440
diff --git a/core/palettes/Rocker.tid b/core/palettes/Rocker.tid
index bc7d2ded6..a91cd1b5f 100644
--- a/core/palettes/Rocker.tid
+++ b/core/palettes/Rocker.tid
@@ -34,6 +34,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/core/palettes/SolarFlare.tid b/core/palettes/SolarFlare.tid
index e72815657..4d81bf531 100644
--- a/core/palettes/SolarFlare.tid
+++ b/core/palettes/SolarFlare.tid
@@ -131,6 +131,8 @@ external-link-background-hover: inherit
external-link-background-visited: inherit
external-link-background: inherit
external-link-foreground-hover: inherit
+highlight-background: #ffff00
+highlight-foreground: #000000
message-border: #cfd6e6
modal-border: #999999
select-tag-background:
diff --git a/core/palettes/SolarizedDark.tid b/core/palettes/SolarizedDark.tid
index f193bff1c..eea273c30 100644
--- a/core/palettes/SolarizedDark.tid
+++ b/core/palettes/SolarizedDark.tid
@@ -35,6 +35,8 @@ external-link-foreground: #268bd2
external-link-foreground-hover:
external-link-foreground-visited: #268bd2
foreground: #839496
+highlight-background: #ffff78
+highlight-foreground: #000000
message-background: #002b36
message-border: #586e75
message-foreground: #839496
diff --git a/core/palettes/SolarizedLight.tid b/core/palettes/SolarizedLight.tid
index 4bcec81d6..3cf954bb5 100644
--- a/core/palettes/SolarizedLight.tid
+++ b/core/palettes/SolarizedLight.tid
@@ -35,6 +35,8 @@ external-link-foreground: #268bd2
external-link-foreground-hover: inherit
external-link-foreground-visited: #268bd2
foreground: #657b83
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: #fdf6e3
message-border: #93a1a1
message-foreground: #657b83
diff --git a/core/palettes/SpartanDay.tid b/core/palettes/SpartanDay.tid
index 3cd8337a7..cc197144e 100644
--- a/core/palettes/SpartanDay.tid
+++ b/core/palettes/SpartanDay.tid
@@ -34,6 +34,8 @@ external-link-foreground-hover:
external-link-foreground-visited:
external-link-foreground:
foreground: rgba(0, 0, 0, 0.87)
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: <>
message-border: <>
message-foreground: rgba(0, 0, 0, 0.54)
diff --git a/core/palettes/SpartanNight.tid b/core/palettes/SpartanNight.tid
index 1962c4782..dc47a0774 100644
--- a/core/palettes/SpartanNight.tid
+++ b/core/palettes/SpartanNight.tid
@@ -34,6 +34,8 @@ external-link-foreground-hover:
external-link-foreground-visited: #7c318c
external-link-foreground: #9e3eb3
foreground: rgba(255, 255, 255, 0.7)
+highlight-background: #ffff78
+highlight-foreground: #000000
message-background: <>
message-border: <>
message-foreground: rgba(255, 255, 255, 0.54)
diff --git a/core/palettes/Twilight.tid b/core/palettes/Twilight.tid
index ff8a0b956..4c127f822 100644
--- a/core/palettes/Twilight.tid
+++ b/core/palettes/Twilight.tid
@@ -43,6 +43,8 @@ external-link-foreground: rgb(179, 179, 255)
external-link-foreground-hover: inherit
external-link-foreground-visited: rgb(153, 153, 255)
foreground: rgb(179, 179, 179)
+highlight-background: #ffff78
+highlight-foreground: #000000
message-background: <>
message-border: #96ccff
message-foreground: <>
diff --git a/core/palettes/Vanilla.tid b/core/palettes/Vanilla.tid
index d3c7b1441..d84b4ec83 100644
--- a/core/palettes/Vanilla.tid
+++ b/core/palettes/Vanilla.tid
@@ -42,6 +42,8 @@ external-link-foreground-hover: inherit
external-link-foreground-visited: #0000aa
external-link-foreground: #0000ee
foreground: #333333
+highlight-background: #ffff00
+highlight-foreground: #000000
message-background: #ecf2ff
message-border: #cfd6e6
message-foreground: #547599
diff --git a/editions/fr-FR/tiddlers/$__editions_tw5.com_doc-macros.tid b/editions/fr-FR/tiddlers/$__editions_tw5.com_doc-macros.tid
index de6376218..fcf3ddb80 100644
--- a/editions/fr-FR/tiddlers/$__editions_tw5.com_doc-macros.tid
+++ b/editions/fr-FR/tiddlers/$__editions_tw5.com_doc-macros.tid
@@ -117,7 +117,7 @@ C'est un exemple de tiddler. Voir [[Macros Table des matières (Exemples)|Table-
-⚠ Attention : Ne faites pas comme ça ! |
+⚠ Attention : Ne faites pas comme ça ! |
$eg$
diff --git a/editions/test/tiddlers/tests/test-wikitext.js b/editions/test/tiddlers/tests/test-wikitext.js
index 3213f6abf..4cab566da 100644
--- a/editions/test/tiddlers/tests/test-wikitext.js
+++ b/editions/test/tiddlers/tests/test-wikitext.js
@@ -63,6 +63,22 @@ describe("WikiText tests", function() {
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","@@color:red;\n \n\nContent \n@@")).toBe("");
expect(wiki.renderText("text/html","text/vnd-tiddlywiki","@@color:red;\n---\n@@")).toBe(" ");
});
+ it("handles inline style wikitext notation", function() {
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@highlighted@@ text")).toBe('some highlighted text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@color:green;.tc-inline-style 1 style and 1 class@@ text")).toBe('some 1 style and 1 class text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@background-color:red;red@@ text")).toBe('some red text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@.myClass class@@ text")).toBe('some class text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@.myClass.secondClass 2 classes@@ text")).toBe('some 2 classes text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@background:red;.myClass style and class@@ text")).toBe('some style and class text ');
+ expect(wiki.renderText("text/html","text/vnd-tiddlywiki",
+ "some @@background:red;color:white;.myClass 2 style and 1 class@@ text")).toBe('some 2 style and 1 class text ');
+ });
});
})();
diff --git a/editions/tw5.com/tiddlers/system/doc-macros.tid b/editions/tw5.com/tiddlers/system/doc-macros.tid
index d4c4b9506..a264198a9 100644
--- a/editions/tw5.com/tiddlers/system/doc-macros.tid
+++ b/editions/tw5.com/tiddlers/system/doc-macros.tid
@@ -117,7 +117,7 @@ This is an example tiddler. See [[Table-of-Contents Macros (Examples)]].
-⚠ Warning: Don't do it this way! |
+⚠ Warning: Don't do it this way! |
$eg$
diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid
index 9c4a342ff..17d3ebb39 100644
--- a/themes/tiddlywiki/vanilla/base.tid
+++ b/themes/tiddlywiki/vanilla/base.tid
@@ -290,6 +290,11 @@ kbd {
color: <>;
}
+.tc-inline-style {
+ background: <>;
+ color: <>;
+}
+
form.tc-form-inline {
display: inline;
}
From 941c09fae2a9da96fe0f4ffd96e57cd68f4b828d Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Mon, 17 Oct 2022 12:21:34 +0100
Subject: [PATCH 046/937] ScrollableWidget example shouldn't iterate through
all tiddlers
---
editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid b/editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid
index a0664a5da..6fda3a974 100644
--- a/editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/ScrollableWidget.tid
@@ -36,7 +36,7 @@ This example requires the following CSS definitions from [[$:/_tw5.com-styles]]:
This wiki text shows how to display a list within the scrollable widget:
<
-<$list filter='[!is[system]]'>
+<$list filter='[tag[Reference]]'>
<$view field='title'/>: <$list filter='[all[current]links[]sort[title]]' storyview='pop'>
<$link><$view field='title'/>$link>
From 7b408c7adf879f5cd0f1713406bedeec4d9cc937 Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Mon, 17 Oct 2022 16:50:54 +0100
Subject: [PATCH 047/937] Update release note
---
editions/prerelease/tiddlers/Release 5.2.4.tid | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/editions/prerelease/tiddlers/Release 5.2.4.tid b/editions/prerelease/tiddlers/Release 5.2.4.tid
index 595d9cdf4..9c5ae019b 100644
--- a/editions/prerelease/tiddlers/Release 5.2.4.tid
+++ b/editions/prerelease/tiddlers/Release 5.2.4.tid
@@ -1,6 +1,6 @@
caption: 5.2.4
-created: 20220924141149286
-modified: 20220924141149286
+created: 20221017165036377
+modified: 20221017165036377
tags: ReleaseNotes
title: Release 5.2.4
type: text/vnd.tiddlywiki
@@ -16,6 +16,8 @@ type: text/vnd.tiddlywiki
Improvements to the following translations:
* Chinese
+* Polish
+* Spanish
* Japanese
Improvements to the translation features of TiddlyWiki:
@@ -32,6 +34,9 @@ Improvements to the translation features of TiddlyWiki:
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/d62a16ee464fb9984b766b48504829a1a3eb143b">> problem with long presses on tiddler links triggering a preview on iOS/iPadOS
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6910">> consistency of button and input elements across browsers
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/d825f1c875f5e46158c9c41c8c66471138c162d1">> edit preview to use the [[View Template Body Cascade]]
+* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6970">> detection of infinite recursion errors in widgets and filters
+* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/36896c3db8c9678c0385a561996248a6f00a45ff">> opening a tiddler in a new window to use the [[View Template Body Cascade]]
+* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/6877">> default styles for [[styled runs|Styles and Classes in WikiText]]
! Widget Improvements
@@ -47,12 +52,14 @@ Improvements to the translation features of TiddlyWiki:
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6936">> new operators for reading and formatting JSON data: [[jsonget Operator]], [[jsonindexes Operator]], [[jsontype Operator]] and [[format Operator]]
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/commit/c5d3d4c26e8fe27f272dda004aec27d6b66c4f60">> safe mode to disable wiki store indexers
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/166a1565843878083fb1eba47c73b8e67b78400d">> safe mode to prevent globally disabling parser rules
-
+* <<.link-badge-removed "https://github.com/Jermolene/TiddlyWiki5/commit/1df4c29d73073788ba3859668112e8bb46171a6c">> restriction of the LetWidget being unable to create variables whose names begin with a dollar sign
+* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/6735">> keyboard shortcut handling to allow to global shortcuts to override all other shortcuts
! Bug Fixes
-*
+* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/fb34df84ed41882c1c2a6ff54f0e908b43ef95a3">> "new image" keyboard shortcut not to assign journal tags
+* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/6987">> SelectWidget class to update if it uses a filter
! Developer Improvements
From 24dbf691801f51289d69cd7c1801daf10c30155c Mon Sep 17 00:00:00 2001
From: Rob Hoelz
Date: Tue, 18 Oct 2022 11:08:04 -0500
Subject: [PATCH 048/937] Fix [is[variable]] operator doesn't work for "fake"
variables #6303 (#6996)
* Add tests for [is[variable]] and "faked" variables
See GH #6303
* Make is[variable] and variables[] operators resilient to fake widgets
Co-authored-by: jeremy@jermolene.com
---
core/modules/filters/is/variable.js | 4 +--
core/modules/filters/variables.js | 12 ++++++---
.../tests/data/filters/fake-variables.tid | 27 +++++++++++++++++++
3 files changed, 38 insertions(+), 5 deletions(-)
create mode 100644 editions/test/tiddlers/tests/data/filters/fake-variables.tid
diff --git a/core/modules/filters/is/variable.js b/core/modules/filters/is/variable.js
index 1f2e5d1b3..110d9a7c8 100644
--- a/core/modules/filters/is/variable.js
+++ b/core/modules/filters/is/variable.js
@@ -19,13 +19,13 @@ exports.variable = function(source,prefix,options) {
var results = [];
if(prefix === "!") {
source(function(tiddler,title) {
- if(!(title in options.widget.variables)) {
+ if(options.widget.getVariable(title) === undefined) {
results.push(title);
}
});
} else {
source(function(tiddler,title) {
- if(title in options.widget.variables) {
+ if(options.widget.getVariable(title) !== undefined) {
results.push(title);
}
});
diff --git a/core/modules/filters/variables.js b/core/modules/filters/variables.js
index fda40a404..c92b780d2 100644
--- a/core/modules/filters/variables.js
+++ b/core/modules/filters/variables.js
@@ -16,9 +16,15 @@ Filter operator for returning the names of the active variables
Export our filter function
*/
exports.variables = function(source,operator,options) {
- var names = [];
- for(var variable in options.widget.variables) {
- names.push(variable);
+ var names = [],
+ widget = options.widget;
+ while(widget && !widget.hasOwnProperty("variables")) {
+ widget = widget.parentWidget;
+ }
+ if(widget && widget.variables) {
+ for(var variable in widget.variables) {
+ names.push(variable);
+ }
}
return names.sort();
};
diff --git a/editions/test/tiddlers/tests/data/filters/fake-variables.tid b/editions/test/tiddlers/tests/data/filters/fake-variables.tid
new file mode 100644
index 000000000..0dad85871
--- /dev/null
+++ b/editions/test/tiddlers/tests/data/filters/fake-variables.tid
@@ -0,0 +1,27 @@
+title: Filters/FakeVariables
+description: Test for https://github.com/Jermolene/TiddlyWiki5/issues/6303
+type: text/vnd.tiddlywiki-multiple
+tags: [[$:/tags/wiki-test-spec]]
+
+title: Output
+
+\whitespace trim
+<$list variable="var" filter="[[existing variable should have output]] :filter[[..currentTiddler]is[variable]]">
+ <>
+$list>
+
+<$list variable="var" filter="[[non-existing variable should not have output]] :filter[[nonExistingVariable]is[variable]]">
+<>
+$list>
+
+<$list variable="var" filter="[[existing variable negated should not have output]] :filter[[..currentTiddler]!is[variable]]">
+<>
+$list>
+
+<$list variable="var" filter="[[non-existing variable negated should have output]] :filter[[nonExistingVariable]!is[variable]]">
+<>
+$list>
++
+title: ExpectedResult
+
+existing variable should have output non-existing variable negated should have output
\ No newline at end of file
From 5b85786f737c94ee102490c68c22a563e418dd07 Mon Sep 17 00:00:00 2001
From: FlashSystems
Date: Sat, 22 Oct 2022 14:13:39 +0200
Subject: [PATCH 049/937] Fix popup position if popup is triggered from within
an offsetParent element (#6887)
* Fix popup location for tables
This commit introduces the `popupAbsCoords` option to the $button widget
and implements an absolut coordinate format.
Coordinates for popups are stored in the format `(x,y,w,h)`. These
coordinates are relative to the offset parent of the element that
defines the popup.
This commits adds a second format `@(x,y,w,h)`. Coordinates specified in
this format a relative to the pages root element.
The `popupAbsCoords` option of the $button widget enables the use of
this coordinates.
* Unify the declaration of the RegEx for parsing the popup-position
The regular expression was declared in three locations with the same
content. This commit supplies a new function `parseCoordinates` in
`popup.js`. This function returns the parsed coordinates and understands
the classic/absolute coordinates.
This function is used in `reveal.js` and `action-popup.js` to parse the
coordinates.
* Add documentation for coordinate systems
* Consolidate creating coordinate strings
The Popup object now contains a `buildCoordinates` method that can be
used to build coordinate strings. It takes an "enum" for the coordinate-
system to use. This makes everything easily extensible and prevents the
use of magic values.
* Add tests for `parseCoordinates` and `buildCoordinates`
* Add `tv-popup-abs-coords` to `collectDOMVariables`
This will make the absolute coordinates available for the
`DraggableWidget` and the `EventCatcherWidget`.
* Add documentation for the `tv-popup-abs-coords`
... to the `DraggableWidget` and the `EventCatcherWidget`.
---
core/modules/utils/dom/dom.js | 17 ++++-
core/modules/utils/dom/popup.js | 69 ++++++++++++++++--
core/modules/widgets/action-popup.js | 16 ++---
core/modules/widgets/button.js | 5 +-
core/modules/widgets/reveal.js | 19 +++--
editions/test/tiddlers/tests/test-popup.js | 71 +++++++++++++++++++
.../tiddlers/concepts/CoordinateSystems.tid | 42 +++++++++++
.../tiddlers/mechanisms/PopupMechanism.tid | 1 +
.../tiddlers/widgets/ActionPopupWidget.tid | 7 +-
.../tw5.com/tiddlers/widgets/ButtonWidget.tid | 3 +-
.../tiddlers/widgets/DraggableWidget.tid | 3 +-
.../tiddlers/widgets/EventCatcherWidget.tid | 7 +-
.../dynannotate/modules/dynannotate.js | 2 +-
13 files changed, 228 insertions(+), 34 deletions(-)
create mode 100644 editions/test/tiddlers/tests/test-popup.js
create mode 100644 editions/tw5.com/tiddlers/concepts/CoordinateSystems.tid
diff --git a/core/modules/utils/dom/dom.js b/core/modules/utils/dom/dom.js
index 330d184cc..84bbb1068 100644
--- a/core/modules/utils/dom/dom.js
+++ b/core/modules/utils/dom/dom.js
@@ -294,8 +294,21 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) {
});
if(selectedNode.offsetLeft) {
- // Add a variable with a popup coordinate string for the selected node
- variables["tv-popup-coords"] = "(" + selectedNode.offsetLeft + "," + selectedNode.offsetTop +"," + selectedNode.offsetWidth + "," + selectedNode.offsetHeight + ")";
+ // Add variables with a (relative and absolute) popup coordinate string for the selected node
+ var nodeRect = {
+ left: selectedNode.offsetLeft,
+ top: selectedNode.offsetTop,
+ width: selectedNode.offsetWidth,
+ height: selectedNode.offsetHeight
+ };
+ variables["tv-popup-coords"] = $tw.popup.buildCoordinates($tw.popup.coordinatePrefix.csOffsetParent,nodeRect);
+
+ var absRect = $tw.utils.extend({}, nodeRect);
+ for (var currentNode = selectedNode.offsetParent; currentNode; currentNode = currentNode.offsetParent) {
+ absRect.left += currentNode.offsetLeft;
+ absRect.top += currentNode.offsetTop;
+ }
+ variables["tv-popup-abs-coords"] = $tw.popup.buildCoordinates($tw.popup.coordinatePrefix.csAbsolute,absRect);
// Add variables for offset of selected node
variables["tv-selectednode-posx"] = selectedNode.offsetLeft.toString();
diff --git a/core/modules/utils/dom/popup.js b/core/modules/utils/dom/popup.js
index 5eed80c88..0a898156e 100644
--- a/core/modules/utils/dom/popup.js
+++ b/core/modules/utils/dom/popup.js
@@ -22,6 +22,19 @@ var Popup = function(options) {
this.popups = []; // Array of {title:,wiki:,domNode:} objects
};
+/*
+Global regular expression for parsing the location of a popup.
+This is also used by the Reveal widget.
+*/
+Popup.popupLocationRegExp = /^(@?)\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/
+
+/*
+Objekt containing the available prefixes for coordinates build with the `buildCoordinates` function:
+ - csOffsetParent: Uses a coordinate system based on the offset parent (no prefix).
+ - csAbsolute: Use an absolute coordinate system (prefix "@").
+*/
+Popup.prototype.coordinatePrefix = { csOffsetParent: "", csAbsolute: "@" }
+
/*
Trigger a popup open or closed. Parameters are in a hashmap:
title: title of the tiddler where the popup details are stored
@@ -136,8 +149,17 @@ Popup.prototype.show = function(options) {
height: options.domNode.offsetHeight
};
}
- var popupRect = "(" + rect.left + "," + rect.top + "," +
- rect.width + "," + rect.height + ")";
+ if(options.absolute && options.domNode) {
+ // Walk the offsetParent chain and add the position of the offsetParents to make
+ // the position absolute to the root node of the page.
+ var currentNode = options.domNode.offsetParent;
+ while(currentNode) {
+ rect.left += currentNode.offsetLeft;
+ rect.top += currentNode.offsetTop;
+ currentNode = currentNode.offsetParent;
+ }
+ }
+ var popupRect = $tw.popup.buildCoordinates(options.absolute?$tw.popup.coordinatePrefix.csAbsolute:$tw.popup.coordinatePrefix.csOffsetParent,rect);
if(options.noStateReference) {
options.wiki.setText(options.title,"text",undefined,popupRect);
} else {
@@ -175,10 +197,49 @@ Popup.prototype.cancel = function(level) {
Returns true if the specified title and text identifies an active popup
*/
Popup.prototype.readPopupState = function(text) {
- var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/;
- return popupLocationRegExp.test(text);
+ return Popup.popupLocationRegExp.test(text);
};
+/*
+Parses a coordinate string in the format `(x,y,w,h)` or `@(x,y,z,h)` and returns
+an object containing the position, width and height. The absolute-Mark is boolean
+value that indicates the coordinate system of the coordinates. If they start with
+an `@`, `absolute` is set and the coordinates are relative to the root element. If
+the initial `@` is missing, they are relative to the offset parent element and
+`absoute` is false.
+*/
+Popup.prototype.parseCoordinates = function(coordinates) {
+ var match = Popup.popupLocationRegExp.exec(coordinates);
+ if(match) {
+ return {
+ absolute: (match[1] === "@"),
+ left: parseFloat(match[2]),
+ top: parseFloat(match[3]),
+ width: parseFloat(match[4]),
+ height: parseFloat(match[5])
+ };
+ } else {
+ return false;
+ }
+}
+
+/*
+Builds a coordinate string from a coordinate system identifier and an object
+containing the left, top, width and height values.
+Use constants defined in the coordinatePrefix property to specify a coordinate
+system.
+If one of the parameters is invalid for building a coordinate string `(0,0,0,0)`
+will be returned.
+*/
+Popup.prototype.buildCoordinates = function(prefix,position) {
+ var coord = prefix + "(" + position.left + "," + position.top + "," + position.width + "," + position.height + ")";
+ if (Popup.popupLocationRegExp.test(coord)) {
+ return coord;
+ } else {
+ return "(0,0,0,0)";
+ }
+}
+
exports.Popup = Popup;
})();
diff --git a/core/modules/widgets/action-popup.js b/core/modules/widgets/action-popup.js
index 2903532b6..2d47540d6 100644
--- a/core/modules/widgets/action-popup.js
+++ b/core/modules/widgets/action-popup.js
@@ -57,20 +57,20 @@ Invoke the action associated with this widget
*/
ActionPopupWidget.prototype.invokeAction = function(triggeringWidget,event) {
// Trigger the popup
- var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
- match = popupLocationRegExp.exec(this.actionCoords || "");
- if(match) {
+ var coordinates = $tw.popup.parseCoordinates(this.actionCoords || "");
+ if(coordinates) {
$tw.popup.triggerPopup({
domNode: null,
domNodeRect: {
- left: parseFloat(match[1]),
- top: parseFloat(match[2]),
- width: parseFloat(match[3]),
- height: parseFloat(match[4])
+ left: coordinates.left,
+ top: coordinates.top,
+ width: coordinates.width,
+ height: coordinates.height
},
title: this.actionState,
wiki: this.wiki,
- floating: this.floating
+ floating: this.floating,
+ absolute: coordinates.absolute
});
} else {
$tw.popup.cancel(0);
diff --git a/core/modules/widgets/button.js b/core/modules/widgets/button.js
index a32820e8b..f266d47bf 100644
--- a/core/modules/widgets/button.js
+++ b/core/modules/widgets/button.js
@@ -173,6 +173,7 @@ ButtonWidget.prototype.triggerPopup = function(event) {
if(this.popupTitle) {
$tw.popup.triggerPopup({
domNode: this.domNodes[0],
+ absolute: (this.popupAbsCoords === "yes"),
title: this.popupTitle,
wiki: this.wiki,
noStateReference: true
@@ -180,6 +181,7 @@ ButtonWidget.prototype.triggerPopup = function(event) {
} else {
$tw.popup.triggerPopup({
domNode: this.domNodes[0],
+ absolute: (this.popupAbsCoords === "yes"),
title: this.popup,
wiki: this.wiki
});
@@ -223,6 +225,7 @@ ButtonWidget.prototype.execute = function() {
this.setField = this.getAttribute("setField");
this.setIndex = this.getAttribute("setIndex");
this.popupTitle = this.getAttribute("popupTitle");
+ this.popupAbsCoords = this.getAttribute("popupAbsCoords", "no");
this.tabIndex = this.getAttribute("tabindex");
this.isDisabled = this.getAttribute("disabled","no");
// Make child widgets
@@ -252,7 +255,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
ButtonWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
- if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
+ if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
this.refreshSelf();
return true;
} else if(changedAttributes["class"]) {
diff --git a/core/modules/widgets/reveal.js b/core/modules/widgets/reveal.js
index 46e55e99e..0c82e8598 100755
--- a/core/modules/widgets/reveal.js
+++ b/core/modules/widgets/reveal.js
@@ -94,6 +94,13 @@ RevealWidget.prototype.positionPopup = function(domNode) {
left = Math.max(0,left);
top = Math.max(0,top);
}
+ if (this.popup.absolute) {
+ // Traverse the offsetParent chain and correct the offset to make it relative to the parent node.
+ for (var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) {
+ left -= offsetParentDomNode.offsetLeft;
+ top -= offsetParentDomNode.offsetTop;
+ }
+ }
domNode.style.left = left + "px";
domNode.style.top = top + "px";
};
@@ -183,19 +190,11 @@ RevealWidget.prototype.compareStateText = function(state) {
};
RevealWidget.prototype.readPopupState = function(state) {
- var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
- match = popupLocationRegExp.exec(state);
+ this.popup = $tw.popup.parseCoordinates(state);
// Check if the state matches the location regexp
- if(match) {
+ if(this.popup) {
// If so, we're open
this.isOpen = true;
- // Get the location
- this.popup = {
- left: parseFloat(match[1]),
- top: parseFloat(match[2]),
- width: parseFloat(match[3]),
- height: parseFloat(match[4])
- };
} else {
// If not, we're closed
this.isOpen = false;
diff --git a/editions/test/tiddlers/tests/test-popup.js b/editions/test/tiddlers/tests/test-popup.js
new file mode 100644
index 000000000..360e7f17f
--- /dev/null
+++ b/editions/test/tiddlers/tests/test-popup.js
@@ -0,0 +1,71 @@
+/*\
+title: test-popup.js
+type: application/javascript
+tags: [[$:/tags/test-spec]]
+
+Tests some utility function of the Popup prototype.
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+describe("Popup tests", function() {
+
+ it("parseCoordinates should parse valid coordinates", function() {
+ var popup = new $tw.utils.Popup({
+ rootElement: $tw.fakeDocument.createElement("div")
+ });
+
+ expect(popup.parseCoordinates("(1,2,3,4)")).toEqual({absolute: false, left: 1, top: 2, width: 3, height: 4});
+ expect(popup.parseCoordinates("(1.5,2.6,3.7,4.8)")).toEqual({absolute: false, left: 1.5, top: 2.6, width: 3.7, height: 4.8});
+ expect(popup.parseCoordinates("@(1,2,3,4)")).toEqual({absolute: true, left: 1, top: 2, width: 3, height: 4});
+ expect(popup.parseCoordinates("@(1.5,2.6,3.7,4.8)")).toEqual({absolute: true, left: 1.5, top: 2.6, width: 3.7, height: 4.8});
+ });
+
+ it("parseCoordinates should not parse invalid coordinates", function() {
+ var popup = new $tw.utils.Popup({
+ rootElement: $tw.fakeDocument.createElement("div")
+ });
+
+ expect(popup.parseCoordinates("#(1,2,3,4)")).toEqual(false);
+ expect(popup.parseCoordinates("(1,2,3,4")).toEqual(false);
+ expect(popup.parseCoordinates("(1,2,3)")).toEqual(false);
+ });
+
+ it("buildCoordinates should create valid coordinates", function() {
+ var popup = new $tw.utils.Popup({
+ rootElement: $tw.fakeDocument.createElement("div")
+ });
+
+ var coordinates = {
+ left: 1.5,
+ top: 2.6,
+ width: 3.7,
+ height: 4.8
+ };
+
+ expect(popup.buildCoordinates(popup.coordinatePrefix.csOffsetParent, coordinates)).toEqual("(1.5,2.6,3.7,4.8)");
+ expect(popup.buildCoordinates(popup.coordinatePrefix.csAbsolute, coordinates)).toEqual("@(1.5,2.6,3.7,4.8)");
+ });
+
+ it("buildCoordinates should detect invalid input", function() {
+ var popup = new $tw.utils.Popup({
+ rootElement: $tw.fakeDocument.createElement("div")
+ });
+
+ var coordinates = {
+ left: "invalid",
+ top: 2.6,
+ width: 3.7,
+ height: 4.8
+ };
+
+ expect(popup.buildCoordinates(popup.coordinatePrefix.csOffsetParent, coordinates)).toEqual("(0,0,0,0)");
+ expect(popup.buildCoordinates("dummy", coordinates)).toEqual("(0,0,0,0)");
+ });
+});
+
+})();
diff --git a/editions/tw5.com/tiddlers/concepts/CoordinateSystems.tid b/editions/tw5.com/tiddlers/concepts/CoordinateSystems.tid
new file mode 100644
index 000000000..9b66f941b
--- /dev/null
+++ b/editions/tw5.com/tiddlers/concepts/CoordinateSystems.tid
@@ -0,0 +1,42 @@
+created: 20220810201659784
+modified: 20220810201659784
+tags: Concepts
+title: Coordinate Systems
+type: text/vnd.tiddlywiki
+
+TiddlyWiki (primarily the RevealWidget) supports two coordinate systems for positioning popups (see PopupMechanism to learn more about popups).
+
+<<.from-version "5.2.4">> We introduced absolute coordinates that may not work with all extensions and plugins. For maximum backwards compatibility, use absolute coordinates only where necessary.
+
+!! Relative coordinate system
+
+The default coordinate system is relative to the nearest positioned ancestor element. This is either:
+
+* an element with a non-static position, or
+* a ''td'', ''th'', ''table'' in case the element itself is static positioned.
+
+For tiddlers the nearest positioned ancestor element mostly is the body of the tiddler. Read the next chapter to learn about the exceptions.
+
+Relative coordinates are expressed in the form ''(x,y,w,h)''. Where ''x'' and ''y'' represent the position and ''w'' and ''h'' the width and height of the element.
+
+!! Absolute coordinate system
+
+The relative coordinate system works flawless most of the time. Problems occure if the target element (for example, a popup) and the source element (the triggering button) do not share the same positioned ancherstor element. This is often the case if the popup is declared outside a table and the triggering button is declared within a table cell. In this case the coordiante systems have different origins and the popup will be displayed in the wrong location.
+
+Absolute coordinates can fix this problem by using the root element of the page (the upper-left corner of the page) as the origin of the coordinate system. Absolute coordinates are expressed in the form ''@(x,y,w,h)''. Where ''x'' and ''y'' represent the position and ''w'' and ''h'' the width and height of the element. The leading ''@''-symbol marks these coordinates as absolute.
+
+The ButtonWidget has an option (''popupAbsCoords'') to put absolute coordinates into the state tiddler. The DraggableWidget and the EventCatcherWidget provide the absolute coordinate of an event within the attribute `tv-popup-abs-coords`.
+
+
+!! Example
+
+The following example shows a popup that is triggerd from within a table cell. The table cell is the nearest positioned ancestor element. The popup was defined outside the table cell. The button using relative coordinates will open the popup in the wrong location because the button and the popup do not agree on the same coordinate system. Using absolute coordinates fixes this problem.
+
+<
+
+Popup
+
+$reveal>
+
+| Table Row 1 |<$button popup="$:/state/CoordinateSampleReveal">Relative coordinates$button>|
+| Table Row 2 |<$button popup="$:/state/CoordinateSampleReveal" popupAbsCoords="yes">Absolute coordinates$button>|'>>
diff --git a/editions/tw5.com/tiddlers/mechanisms/PopupMechanism.tid b/editions/tw5.com/tiddlers/mechanisms/PopupMechanism.tid
index 60566a329..79f3de4c5 100644
--- a/editions/tw5.com/tiddlers/mechanisms/PopupMechanism.tid
+++ b/editions/tw5.com/tiddlers/mechanisms/PopupMechanism.tid
@@ -8,6 +8,7 @@ The popup mechanism allows blocks of content to be selectively displayed and pos
* [[StateTiddlers|StateMechanism]] to record whether a popup is currently displayed or not
* The RevealWidget to selectively display the popup content
+** <<.from-version "5.2.4">> For positioning the popups relative or absolute coordinates can be used. See [[Coordinate Systems]] for more information about usage and format.
** For "sticky" popups — those that don't close when clicking inside one — set the ''class'' attribute to `tc-popup-keep`
* The ButtonWidget to trigger the display of the popup by setting the state tiddler appropriately
diff --git a/editions/tw5.com/tiddlers/widgets/ActionPopupWidget.tid b/editions/tw5.com/tiddlers/widgets/ActionPopupWidget.tid
index e423b6576..590e25a92 100644
--- a/editions/tw5.com/tiddlers/widgets/ActionPopupWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/ActionPopupWidget.tid
@@ -1,6 +1,6 @@
caption: action-popup
created: 20200303114556528
-modified: 20210501203451387
+modified: 20220815205132124
tags: Widgets ActionWidgets
title: ActionPopupWidget
type: text/vnd.tiddlywiki
@@ -15,10 +15,11 @@ The ''action-popup'' widget is invisible. Any content within it is ignored.
|!Attribute |!Description |
|$state |The title of the state tiddler for the popup |
-|$coords |Optional coordinates for the handle to which popup is positioned (in the format `(x,y,w,h)`) |
+|$coords |Optional coordinates for the handle to which popup is positioned (see [[Coordinate Systems]] for the supported formats) |
|$floating |<<.from-version "5.2.0">> Optional. Defaults to `no`. Set to `yes` to create a popup that must be closed explicitly. |
-<<.from-version "5.1.23">> If the ''$coords'' attribute is missing or empty then all popups are cancelled.
+<<.from-version "5.1.23">> If the ''$coords'' attribute is missing or empty then all popups are cancelled.
+<<.from-version "5.2.4">> The ''$coords'' attribute supports absolute and relative coordinates. See [[Coordinate Systems]] for more information.
<<.tip "Delete the state tiddler for a floating popup to close it.">>
diff --git a/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid b/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid
index e94504659..da61838af 100644
--- a/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid
@@ -1,6 +1,6 @@
caption: button
created: 20131024141900000
-modified: 20211009121239795
+modified: 20220810192251345
tags: Widgets TriggeringWidgets
title: ButtonWidget
type: text/vnd.tiddlywiki
@@ -37,6 +37,7 @@ The content of the `<$button>` widget is displayed within the button.
|default |Default value if <<.attr set>> tiddler is missing for testing against <<.attr setTo>> to determine <<.attr selectedClass>> |
|popup |Title of a state tiddler for a popup that is toggled when the button is clicked. See PopupMechanism for details |
|popupTitle |Title of a state tiddler for a popup that is toggled when the button is clicked. In difference to the <<.attr popup>> attribute, ''no'' TextReference is used. See PopupMechanism for details |
+|popupAbsCoords |<<.from-version "5.2.4">> If set to ''yes'' writes absolute coordinates to the tiddler referenced by the <<.attr popup>>. If set to ''no'' (the default) uses relative coordinates. See [[Coordinate Systems]] for details |
|aria-label |Optional [[Accessibility]] label |
|tooltip |Optional tooltip |
|class |An optional CSS class name to be assigned to the HTML element|
diff --git a/editions/tw5.com/tiddlers/widgets/DraggableWidget.tid b/editions/tw5.com/tiddlers/widgets/DraggableWidget.tid
index fc65c4e74..c25b15791 100644
--- a/editions/tw5.com/tiddlers/widgets/DraggableWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/DraggableWidget.tid
@@ -41,7 +41,8 @@ The LinkWidget incorporates the functionality of the DraggableWidget via the ''d
|!Variables |!Description |
|`modifier` |The [[modifier Variable]] contains the Modifier Key held while dragging |
|`dom-*` |All DOM attributes of the node being dragged are made available as variables, with the prefix `dom-` |
-|`tv-popup-coords` |A co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node that's being dragged where the event originated |
+|`tv-popup-coords` |A relative co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated (see [[Coordinate Systems]] for more information) |
+|`tv-popup-abs-coords` |<<.from-version "5.2.4">> An absolute co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated (see [[Coordinate Systems]] for more information) |
|`tv-selectednode-posx` |`x` offset position of the dragged DOM node |
|`tv-selectednode-posy` |`y` offset position of the dragged DOM node |
|`tv-selectednode-width` |`offsetWidth` of the dragged DOM node |
diff --git a/editions/tw5.com/tiddlers/widgets/EventCatcherWidget.tid b/editions/tw5.com/tiddlers/widgets/EventCatcherWidget.tid
index f0c8c257c..5c2568a19 100644
--- a/editions/tw5.com/tiddlers/widgets/EventCatcherWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/EventCatcherWidget.tid
@@ -1,5 +1,5 @@
created: 20201123113532200
-modified: 20220507184043398
+modified: 20221012194222875
tags: Widgets TriggeringWidgets
title: EventCatcherWidget
type: text/vnd.tiddlywiki
@@ -10,7 +10,7 @@ type: text/vnd.tiddlywiki
//This is an advanced widget intended for use by those familiar with HTML, CSS and JavaScript handling of DOM events.//
-The event catcher widget traps DOM-initiated Javascript events dispatched within its child content, and allows invoking a series of ActionWidgets in response to those events.
+The event catcher widget traps DOM-initiated Javascript events dispatched within its child content, and allows invoking a series of ActionWidgets in response to those events.
In order for the events to be trapped:
@@ -47,7 +47,8 @@ The following variables are made available to the actions:
|`event-mousebutton` |The mouse button (if any) used to trigger the event (can be "left", "right" or "middle"). Note that not all event types support the mousebutton property |
|`event-type` |The type property of the JavaScript event |
|`event-detail-*` |Any properties in the detail attribute of the event are made available with the prefix `event-detail-` |
-|`tv-popup-coords` |A co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated |
+|`tv-popup-coords` |A relative co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated (see [[Coordinate Systems]] for more information) |
+|`tv-popup-abs-coords` |<<.from-version "5.2.4">> An absolute co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated (see [[Coordinate Systems]] for more information) |
|`tv-widgetnode-width` |<<.from-version "5.2.3">> `offsetWidth` of the DOM node created by the eventcatcher widget |
|`tv-widgetnode-height` |<<.from-version "5.2.3">> `offsetHeight` of the DOM node created by the eventcatcher widget |
|`tv-selectednode-posx` |`x` offset position of the selected DOM node |
diff --git a/plugins/tiddlywiki/dynannotate/modules/dynannotate.js b/plugins/tiddlywiki/dynannotate/modules/dynannotate.js
index 9a913384d..bd898ebba 100644
--- a/plugins/tiddlywiki/dynannotate/modules/dynannotate.js
+++ b/plugins/tiddlywiki/dynannotate/modules/dynannotate.js
@@ -191,7 +191,7 @@ DynannotateWidget.prototype.applyAnnotations = function() {
"tv-selection-posy": (bounds.top).toString(),
"tv-selection-width": (bounds.width).toString(),
"tv-selection-height": (bounds.height).toString(),
- "tv-selection-coords": "(" + bounds.left + "," + bounds.top + "," + bounds.width + "," + bounds.height + ")"
+ "tv-selection-coords": $tw.popup.buildCoordinates($tw.popup.coordinatePrefix.csOffsetParent,bounds)
});
if(self.hasAttribute("popup")) {
$tw.popup.triggerPopup({
From b9d27e9fd5f5420dac375762670182275f33e0aa Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Sat, 22 Oct 2022 13:22:15 +0100
Subject: [PATCH 050/937] Revert "Fix popup position if popup is triggered from
within an offsetParent element (#6887)"
This reverts commit 5b85786f737c94ee102490c68c22a563e418dd07.
---
core/modules/utils/dom/dom.js | 17 +----
core/modules/utils/dom/popup.js | 69 ++----------------
core/modules/widgets/action-popup.js | 16 ++---
core/modules/widgets/button.js | 5 +-
core/modules/widgets/reveal.js | 19 ++---
editions/test/tiddlers/tests/test-popup.js | 71 -------------------
.../tiddlers/concepts/CoordinateSystems.tid | 42 -----------
.../tiddlers/mechanisms/PopupMechanism.tid | 1 -
.../tiddlers/widgets/ActionPopupWidget.tid | 7 +-
.../tw5.com/tiddlers/widgets/ButtonWidget.tid | 3 +-
.../tiddlers/widgets/DraggableWidget.tid | 3 +-
.../tiddlers/widgets/EventCatcherWidget.tid | 7 +-
.../dynannotate/modules/dynannotate.js | 2 +-
13 files changed, 34 insertions(+), 228 deletions(-)
delete mode 100644 editions/test/tiddlers/tests/test-popup.js
delete mode 100644 editions/tw5.com/tiddlers/concepts/CoordinateSystems.tid
diff --git a/core/modules/utils/dom/dom.js b/core/modules/utils/dom/dom.js
index 84bbb1068..330d184cc 100644
--- a/core/modules/utils/dom/dom.js
+++ b/core/modules/utils/dom/dom.js
@@ -294,21 +294,8 @@ exports.collectDOMVariables = function(selectedNode,domNode,event) {
});
if(selectedNode.offsetLeft) {
- // Add variables with a (relative and absolute) popup coordinate string for the selected node
- var nodeRect = {
- left: selectedNode.offsetLeft,
- top: selectedNode.offsetTop,
- width: selectedNode.offsetWidth,
- height: selectedNode.offsetHeight
- };
- variables["tv-popup-coords"] = $tw.popup.buildCoordinates($tw.popup.coordinatePrefix.csOffsetParent,nodeRect);
-
- var absRect = $tw.utils.extend({}, nodeRect);
- for (var currentNode = selectedNode.offsetParent; currentNode; currentNode = currentNode.offsetParent) {
- absRect.left += currentNode.offsetLeft;
- absRect.top += currentNode.offsetTop;
- }
- variables["tv-popup-abs-coords"] = $tw.popup.buildCoordinates($tw.popup.coordinatePrefix.csAbsolute,absRect);
+ // Add a variable with a popup coordinate string for the selected node
+ variables["tv-popup-coords"] = "(" + selectedNode.offsetLeft + "," + selectedNode.offsetTop +"," + selectedNode.offsetWidth + "," + selectedNode.offsetHeight + ")";
// Add variables for offset of selected node
variables["tv-selectednode-posx"] = selectedNode.offsetLeft.toString();
diff --git a/core/modules/utils/dom/popup.js b/core/modules/utils/dom/popup.js
index 0a898156e..5eed80c88 100644
--- a/core/modules/utils/dom/popup.js
+++ b/core/modules/utils/dom/popup.js
@@ -22,19 +22,6 @@ var Popup = function(options) {
this.popups = []; // Array of {title:,wiki:,domNode:} objects
};
-/*
-Global regular expression for parsing the location of a popup.
-This is also used by the Reveal widget.
-*/
-Popup.popupLocationRegExp = /^(@?)\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/
-
-/*
-Objekt containing the available prefixes for coordinates build with the `buildCoordinates` function:
- - csOffsetParent: Uses a coordinate system based on the offset parent (no prefix).
- - csAbsolute: Use an absolute coordinate system (prefix "@").
-*/
-Popup.prototype.coordinatePrefix = { csOffsetParent: "", csAbsolute: "@" }
-
/*
Trigger a popup open or closed. Parameters are in a hashmap:
title: title of the tiddler where the popup details are stored
@@ -149,17 +136,8 @@ Popup.prototype.show = function(options) {
height: options.domNode.offsetHeight
};
}
- if(options.absolute && options.domNode) {
- // Walk the offsetParent chain and add the position of the offsetParents to make
- // the position absolute to the root node of the page.
- var currentNode = options.domNode.offsetParent;
- while(currentNode) {
- rect.left += currentNode.offsetLeft;
- rect.top += currentNode.offsetTop;
- currentNode = currentNode.offsetParent;
- }
- }
- var popupRect = $tw.popup.buildCoordinates(options.absolute?$tw.popup.coordinatePrefix.csAbsolute:$tw.popup.coordinatePrefix.csOffsetParent,rect);
+ var popupRect = "(" + rect.left + "," + rect.top + "," +
+ rect.width + "," + rect.height + ")";
if(options.noStateReference) {
options.wiki.setText(options.title,"text",undefined,popupRect);
} else {
@@ -197,49 +175,10 @@ Popup.prototype.cancel = function(level) {
Returns true if the specified title and text identifies an active popup
*/
Popup.prototype.readPopupState = function(text) {
- return Popup.popupLocationRegExp.test(text);
+ var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/;
+ return popupLocationRegExp.test(text);
};
-/*
-Parses a coordinate string in the format `(x,y,w,h)` or `@(x,y,z,h)` and returns
-an object containing the position, width and height. The absolute-Mark is boolean
-value that indicates the coordinate system of the coordinates. If they start with
-an `@`, `absolute` is set and the coordinates are relative to the root element. If
-the initial `@` is missing, they are relative to the offset parent element and
-`absoute` is false.
-*/
-Popup.prototype.parseCoordinates = function(coordinates) {
- var match = Popup.popupLocationRegExp.exec(coordinates);
- if(match) {
- return {
- absolute: (match[1] === "@"),
- left: parseFloat(match[2]),
- top: parseFloat(match[3]),
- width: parseFloat(match[4]),
- height: parseFloat(match[5])
- };
- } else {
- return false;
- }
-}
-
-/*
-Builds a coordinate string from a coordinate system identifier and an object
-containing the left, top, width and height values.
-Use constants defined in the coordinatePrefix property to specify a coordinate
-system.
-If one of the parameters is invalid for building a coordinate string `(0,0,0,0)`
-will be returned.
-*/
-Popup.prototype.buildCoordinates = function(prefix,position) {
- var coord = prefix + "(" + position.left + "," + position.top + "," + position.width + "," + position.height + ")";
- if (Popup.popupLocationRegExp.test(coord)) {
- return coord;
- } else {
- return "(0,0,0,0)";
- }
-}
-
exports.Popup = Popup;
})();
diff --git a/core/modules/widgets/action-popup.js b/core/modules/widgets/action-popup.js
index 2d47540d6..2903532b6 100644
--- a/core/modules/widgets/action-popup.js
+++ b/core/modules/widgets/action-popup.js
@@ -57,20 +57,20 @@ Invoke the action associated with this widget
*/
ActionPopupWidget.prototype.invokeAction = function(triggeringWidget,event) {
// Trigger the popup
- var coordinates = $tw.popup.parseCoordinates(this.actionCoords || "");
- if(coordinates) {
+ var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
+ match = popupLocationRegExp.exec(this.actionCoords || "");
+ if(match) {
$tw.popup.triggerPopup({
domNode: null,
domNodeRect: {
- left: coordinates.left,
- top: coordinates.top,
- width: coordinates.width,
- height: coordinates.height
+ left: parseFloat(match[1]),
+ top: parseFloat(match[2]),
+ width: parseFloat(match[3]),
+ height: parseFloat(match[4])
},
title: this.actionState,
wiki: this.wiki,
- floating: this.floating,
- absolute: coordinates.absolute
+ floating: this.floating
});
} else {
$tw.popup.cancel(0);
diff --git a/core/modules/widgets/button.js b/core/modules/widgets/button.js
index f266d47bf..a32820e8b 100644
--- a/core/modules/widgets/button.js
+++ b/core/modules/widgets/button.js
@@ -173,7 +173,6 @@ ButtonWidget.prototype.triggerPopup = function(event) {
if(this.popupTitle) {
$tw.popup.triggerPopup({
domNode: this.domNodes[0],
- absolute: (this.popupAbsCoords === "yes"),
title: this.popupTitle,
wiki: this.wiki,
noStateReference: true
@@ -181,7 +180,6 @@ ButtonWidget.prototype.triggerPopup = function(event) {
} else {
$tw.popup.triggerPopup({
domNode: this.domNodes[0],
- absolute: (this.popupAbsCoords === "yes"),
title: this.popup,
wiki: this.wiki
});
@@ -225,7 +223,6 @@ ButtonWidget.prototype.execute = function() {
this.setField = this.getAttribute("setField");
this.setIndex = this.getAttribute("setIndex");
this.popupTitle = this.getAttribute("popupTitle");
- this.popupAbsCoords = this.getAttribute("popupAbsCoords", "no");
this.tabIndex = this.getAttribute("tabindex");
this.isDisabled = this.getAttribute("disabled","no");
// Make child widgets
@@ -255,7 +252,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of
*/
ButtonWidget.prototype.refresh = function(changedTiddlers) {
var changedAttributes = this.computeAttributes();
- if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
+ if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) {
this.refreshSelf();
return true;
} else if(changedAttributes["class"]) {
diff --git a/core/modules/widgets/reveal.js b/core/modules/widgets/reveal.js
index 0c82e8598..46e55e99e 100755
--- a/core/modules/widgets/reveal.js
+++ b/core/modules/widgets/reveal.js
@@ -94,13 +94,6 @@ RevealWidget.prototype.positionPopup = function(domNode) {
left = Math.max(0,left);
top = Math.max(0,top);
}
- if (this.popup.absolute) {
- // Traverse the offsetParent chain and correct the offset to make it relative to the parent node.
- for (var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) {
- left -= offsetParentDomNode.offsetLeft;
- top -= offsetParentDomNode.offsetTop;
- }
- }
domNode.style.left = left + "px";
domNode.style.top = top + "px";
};
@@ -190,11 +183,19 @@ RevealWidget.prototype.compareStateText = function(state) {
};
RevealWidget.prototype.readPopupState = function(state) {
- this.popup = $tw.popup.parseCoordinates(state);
+ var popupLocationRegExp = /^\((-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+),(-?[0-9\.E]+)\)$/,
+ match = popupLocationRegExp.exec(state);
// Check if the state matches the location regexp
- if(this.popup) {
+ if(match) {
// If so, we're open
this.isOpen = true;
+ // Get the location
+ this.popup = {
+ left: parseFloat(match[1]),
+ top: parseFloat(match[2]),
+ width: parseFloat(match[3]),
+ height: parseFloat(match[4])
+ };
} else {
// If not, we're closed
this.isOpen = false;
diff --git a/editions/test/tiddlers/tests/test-popup.js b/editions/test/tiddlers/tests/test-popup.js
deleted file mode 100644
index 360e7f17f..000000000
--- a/editions/test/tiddlers/tests/test-popup.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*\
-title: test-popup.js
-type: application/javascript
-tags: [[$:/tags/test-spec]]
-
-Tests some utility function of the Popup prototype.
-
-\*/
-(function(){
-
-/*jslint node: true, browser: true */
-/*global $tw: false */
-"use strict";
-
-describe("Popup tests", function() {
-
- it("parseCoordinates should parse valid coordinates", function() {
- var popup = new $tw.utils.Popup({
- rootElement: $tw.fakeDocument.createElement("div")
- });
-
- expect(popup.parseCoordinates("(1,2,3,4)")).toEqual({absolute: false, left: 1, top: 2, width: 3, height: 4});
- expect(popup.parseCoordinates("(1.5,2.6,3.7,4.8)")).toEqual({absolute: false, left: 1.5, top: 2.6, width: 3.7, height: 4.8});
- expect(popup.parseCoordinates("@(1,2,3,4)")).toEqual({absolute: true, left: 1, top: 2, width: 3, height: 4});
- expect(popup.parseCoordinates("@(1.5,2.6,3.7,4.8)")).toEqual({absolute: true, left: 1.5, top: 2.6, width: 3.7, height: 4.8});
- });
-
- it("parseCoordinates should not parse invalid coordinates", function() {
- var popup = new $tw.utils.Popup({
- rootElement: $tw.fakeDocument.createElement("div")
- });
-
- expect(popup.parseCoordinates("#(1,2,3,4)")).toEqual(false);
- expect(popup.parseCoordinates("(1,2,3,4")).toEqual(false);
- expect(popup.parseCoordinates("(1,2,3)")).toEqual(false);
- });
-
- it("buildCoordinates should create valid coordinates", function() {
- var popup = new $tw.utils.Popup({
- rootElement: $tw.fakeDocument.createElement("div")
- });
-
- var coordinates = {
- left: 1.5,
- top: 2.6,
- width: 3.7,
- height: 4.8
- };
-
- expect(popup.buildCoordinates(popup.coordinatePrefix.csOffsetParent, coordinates)).toEqual("(1.5,2.6,3.7,4.8)");
- expect(popup.buildCoordinates(popup.coordinatePrefix.csAbsolute, coordinates)).toEqual("@(1.5,2.6,3.7,4.8)");
- });
-
- it("buildCoordinates should detect invalid input", function() {
- var popup = new $tw.utils.Popup({
- rootElement: $tw.fakeDocument.createElement("div")
- });
-
- var coordinates = {
- left: "invalid",
- top: 2.6,
- width: 3.7,
- height: 4.8
- };
-
- expect(popup.buildCoordinates(popup.coordinatePrefix.csOffsetParent, coordinates)).toEqual("(0,0,0,0)");
- expect(popup.buildCoordinates("dummy", coordinates)).toEqual("(0,0,0,0)");
- });
-});
-
-})();
diff --git a/editions/tw5.com/tiddlers/concepts/CoordinateSystems.tid b/editions/tw5.com/tiddlers/concepts/CoordinateSystems.tid
deleted file mode 100644
index 9b66f941b..000000000
--- a/editions/tw5.com/tiddlers/concepts/CoordinateSystems.tid
+++ /dev/null
@@ -1,42 +0,0 @@
-created: 20220810201659784
-modified: 20220810201659784
-tags: Concepts
-title: Coordinate Systems
-type: text/vnd.tiddlywiki
-
-TiddlyWiki (primarily the RevealWidget) supports two coordinate systems for positioning popups (see PopupMechanism to learn more about popups).
-
-<<.from-version "5.2.4">> We introduced absolute coordinates that may not work with all extensions and plugins. For maximum backwards compatibility, use absolute coordinates only where necessary.
-
-!! Relative coordinate system
-
-The default coordinate system is relative to the nearest positioned ancestor element. This is either:
-
-* an element with a non-static position, or
-* a ''td'', ''th'', ''table'' in case the element itself is static positioned.
-
-For tiddlers the nearest positioned ancestor element mostly is the body of the tiddler. Read the next chapter to learn about the exceptions.
-
-Relative coordinates are expressed in the form ''(x,y,w,h)''. Where ''x'' and ''y'' represent the position and ''w'' and ''h'' the width and height of the element.
-
-!! Absolute coordinate system
-
-The relative coordinate system works flawless most of the time. Problems occure if the target element (for example, a popup) and the source element (the triggering button) do not share the same positioned ancherstor element. This is often the case if the popup is declared outside a table and the triggering button is declared within a table cell. In this case the coordiante systems have different origins and the popup will be displayed in the wrong location.
-
-Absolute coordinates can fix this problem by using the root element of the page (the upper-left corner of the page) as the origin of the coordinate system. Absolute coordinates are expressed in the form ''@(x,y,w,h)''. Where ''x'' and ''y'' represent the position and ''w'' and ''h'' the width and height of the element. The leading ''@''-symbol marks these coordinates as absolute.
-
-The ButtonWidget has an option (''popupAbsCoords'') to put absolute coordinates into the state tiddler. The DraggableWidget and the EventCatcherWidget provide the absolute coordinate of an event within the attribute `tv-popup-abs-coords`.
-
-
-!! Example
-
-The following example shows a popup that is triggerd from within a table cell. The table cell is the nearest positioned ancestor element. The popup was defined outside the table cell. The button using relative coordinates will open the popup in the wrong location because the button and the popup do not agree on the same coordinate system. Using absolute coordinates fixes this problem.
-
-<
-
-Popup
-
-$reveal>
-
-| Table Row 1 |<$button popup="$:/state/CoordinateSampleReveal">Relative coordinates$button>|
-| Table Row 2 |<$button popup="$:/state/CoordinateSampleReveal" popupAbsCoords="yes">Absolute coordinates$button>|'>>
diff --git a/editions/tw5.com/tiddlers/mechanisms/PopupMechanism.tid b/editions/tw5.com/tiddlers/mechanisms/PopupMechanism.tid
index 79f3de4c5..60566a329 100644
--- a/editions/tw5.com/tiddlers/mechanisms/PopupMechanism.tid
+++ b/editions/tw5.com/tiddlers/mechanisms/PopupMechanism.tid
@@ -8,7 +8,6 @@ The popup mechanism allows blocks of content to be selectively displayed and pos
* [[StateTiddlers|StateMechanism]] to record whether a popup is currently displayed or not
* The RevealWidget to selectively display the popup content
-** <<.from-version "5.2.4">> For positioning the popups relative or absolute coordinates can be used. See [[Coordinate Systems]] for more information about usage and format.
** For "sticky" popups — those that don't close when clicking inside one — set the ''class'' attribute to `tc-popup-keep`
* The ButtonWidget to trigger the display of the popup by setting the state tiddler appropriately
diff --git a/editions/tw5.com/tiddlers/widgets/ActionPopupWidget.tid b/editions/tw5.com/tiddlers/widgets/ActionPopupWidget.tid
index 590e25a92..e423b6576 100644
--- a/editions/tw5.com/tiddlers/widgets/ActionPopupWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/ActionPopupWidget.tid
@@ -1,6 +1,6 @@
caption: action-popup
created: 20200303114556528
-modified: 20220815205132124
+modified: 20210501203451387
tags: Widgets ActionWidgets
title: ActionPopupWidget
type: text/vnd.tiddlywiki
@@ -15,11 +15,10 @@ The ''action-popup'' widget is invisible. Any content within it is ignored.
|!Attribute |!Description |
|$state |The title of the state tiddler for the popup |
-|$coords |Optional coordinates for the handle to which popup is positioned (see [[Coordinate Systems]] for the supported formats) |
+|$coords |Optional coordinates for the handle to which popup is positioned (in the format `(x,y,w,h)`) |
|$floating |<<.from-version "5.2.0">> Optional. Defaults to `no`. Set to `yes` to create a popup that must be closed explicitly. |
-<<.from-version "5.1.23">> If the ''$coords'' attribute is missing or empty then all popups are cancelled.
-<<.from-version "5.2.4">> The ''$coords'' attribute supports absolute and relative coordinates. See [[Coordinate Systems]] for more information.
+<<.from-version "5.1.23">> If the ''$coords'' attribute is missing or empty then all popups are cancelled.
<<.tip "Delete the state tiddler for a floating popup to close it.">>
diff --git a/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid b/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid
index da61838af..e94504659 100644
--- a/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid
@@ -1,6 +1,6 @@
caption: button
created: 20131024141900000
-modified: 20220810192251345
+modified: 20211009121239795
tags: Widgets TriggeringWidgets
title: ButtonWidget
type: text/vnd.tiddlywiki
@@ -37,7 +37,6 @@ The content of the `<$button>` widget is displayed within the button.
|default |Default value if <<.attr set>> tiddler is missing for testing against <<.attr setTo>> to determine <<.attr selectedClass>> |
|popup |Title of a state tiddler for a popup that is toggled when the button is clicked. See PopupMechanism for details |
|popupTitle |Title of a state tiddler for a popup that is toggled when the button is clicked. In difference to the <<.attr popup>> attribute, ''no'' TextReference is used. See PopupMechanism for details |
-|popupAbsCoords |<<.from-version "5.2.4">> If set to ''yes'' writes absolute coordinates to the tiddler referenced by the <<.attr popup>>. If set to ''no'' (the default) uses relative coordinates. See [[Coordinate Systems]] for details |
|aria-label |Optional [[Accessibility]] label |
|tooltip |Optional tooltip |
|class |An optional CSS class name to be assigned to the HTML element|
diff --git a/editions/tw5.com/tiddlers/widgets/DraggableWidget.tid b/editions/tw5.com/tiddlers/widgets/DraggableWidget.tid
index c25b15791..fc65c4e74 100644
--- a/editions/tw5.com/tiddlers/widgets/DraggableWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/DraggableWidget.tid
@@ -41,8 +41,7 @@ The LinkWidget incorporates the functionality of the DraggableWidget via the ''d
|!Variables |!Description |
|`modifier` |The [[modifier Variable]] contains the Modifier Key held while dragging |
|`dom-*` |All DOM attributes of the node being dragged are made available as variables, with the prefix `dom-` |
-|`tv-popup-coords` |A relative co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated (see [[Coordinate Systems]] for more information) |
-|`tv-popup-abs-coords` |<<.from-version "5.2.4">> An absolute co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated (see [[Coordinate Systems]] for more information) |
+|`tv-popup-coords` |A co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node that's being dragged where the event originated |
|`tv-selectednode-posx` |`x` offset position of the dragged DOM node |
|`tv-selectednode-posy` |`y` offset position of the dragged DOM node |
|`tv-selectednode-width` |`offsetWidth` of the dragged DOM node |
diff --git a/editions/tw5.com/tiddlers/widgets/EventCatcherWidget.tid b/editions/tw5.com/tiddlers/widgets/EventCatcherWidget.tid
index 5c2568a19..f0c8c257c 100644
--- a/editions/tw5.com/tiddlers/widgets/EventCatcherWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/EventCatcherWidget.tid
@@ -1,5 +1,5 @@
created: 20201123113532200
-modified: 20221012194222875
+modified: 20220507184043398
tags: Widgets TriggeringWidgets
title: EventCatcherWidget
type: text/vnd.tiddlywiki
@@ -10,7 +10,7 @@ type: text/vnd.tiddlywiki
//This is an advanced widget intended for use by those familiar with HTML, CSS and JavaScript handling of DOM events.//
-The event catcher widget traps DOM-initiated Javascript events dispatched within its child content, and allows invoking a series of ActionWidgets in response to those events.
+The event catcher widget traps DOM-initiated Javascript events dispatched within its child content, and allows invoking a series of ActionWidgets in response to those events.
In order for the events to be trapped:
@@ -47,8 +47,7 @@ The following variables are made available to the actions:
|`event-mousebutton` |The mouse button (if any) used to trigger the event (can be "left", "right" or "middle"). Note that not all event types support the mousebutton property |
|`event-type` |The type property of the JavaScript event |
|`event-detail-*` |Any properties in the detail attribute of the event are made available with the prefix `event-detail-` |
-|`tv-popup-coords` |A relative co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated (see [[Coordinate Systems]] for more information) |
-|`tv-popup-abs-coords` |<<.from-version "5.2.4">> An absolute co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated (see [[Coordinate Systems]] for more information) |
+|`tv-popup-coords` |A co-ordinate string that can be used with the ActionPopupWidget to trigger a popup at the DOM node matching the selector where the event originated |
|`tv-widgetnode-width` |<<.from-version "5.2.3">> `offsetWidth` of the DOM node created by the eventcatcher widget |
|`tv-widgetnode-height` |<<.from-version "5.2.3">> `offsetHeight` of the DOM node created by the eventcatcher widget |
|`tv-selectednode-posx` |`x` offset position of the selected DOM node |
diff --git a/plugins/tiddlywiki/dynannotate/modules/dynannotate.js b/plugins/tiddlywiki/dynannotate/modules/dynannotate.js
index bd898ebba..9a913384d 100644
--- a/plugins/tiddlywiki/dynannotate/modules/dynannotate.js
+++ b/plugins/tiddlywiki/dynannotate/modules/dynannotate.js
@@ -191,7 +191,7 @@ DynannotateWidget.prototype.applyAnnotations = function() {
"tv-selection-posy": (bounds.top).toString(),
"tv-selection-width": (bounds.width).toString(),
"tv-selection-height": (bounds.height).toString(),
- "tv-selection-coords": $tw.popup.buildCoordinates($tw.popup.coordinatePrefix.csOffsetParent,bounds)
+ "tv-selection-coords": "(" + bounds.left + "," + bounds.top + "," + bounds.width + "," + bounds.height + ")"
});
if(self.hasAttribute("popup")) {
$tw.popup.triggerPopup({
From 3f55f827a612742da481fb4597313aad9faf9591 Mon Sep 17 00:00:00 2001
From: Saq Imtiaz
Date: Fri, 28 Oct 2022 13:58:58 +0200
Subject: [PATCH 051/937] Extend page template with filter assigned classes
(#6976)
* Extend page template with filter assigned classes
* feat: added dynamic class support for tiddler templates and documentation
---
core/ui/EditTemplate.tid | 2 +-
core/ui/PageTemplate.tid | 5 +----
core/ui/ViewTemplate.tid | 2 +-
.../SystemTag_ $__tags_ClassFilters_PageTemplate.tid | 9 +++++++++
.../SystemTag_ $__tags_ClassFilters_TiddlerTemplate.tid | 9 +++++++++
5 files changed, 21 insertions(+), 6 deletions(-)
create mode 100644 editions/tw5.com/tiddlers/systemtags/SystemTag_ $__tags_ClassFilters_PageTemplate.tid
create mode 100644 editions/tw5.com/tiddlers/systemtags/SystemTag_ $__tags_ClassFilters_TiddlerTemplate.tid
diff --git a/core/ui/EditTemplate.tid b/core/ui/EditTemplate.tid
index 57151fe38..b63a02ee2 100644
--- a/core/ui/EditTemplate.tid
+++ b/core/ui/EditTemplate.tid
@@ -23,7 +23,7 @@ title: $:/core/ui/EditTemplate
>
data-tags={{!!tags}}
- class={{{ tc-tiddler-frame tc-tiddler-edit-frame [ is[tiddler]then[tc-tiddler-exists]] [is[missing]!is[shadow]then[tc-tiddler-missing]] [is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [is[system]then[tc-tiddler-system]] [{!!class}] [tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}}
+ class={{{ [all[shadows+tiddlers]tag[$:/tags/ClassFilters/TiddlerTemplate]!is[draft]] :map:flat[subfilter{!!text}] tc-tiddler-frame tc-tiddler-edit-frame [is[tiddler]then[tc-tiddler-exists]] [is[missing]!is[shadow]then[tc-tiddler-missing]] [is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [is[system]then[tc-tiddler-system]] [{!!class}] [tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}}
role="region"
aria-label={{$:/language/EditTemplate/Caption}}>
<$fieldmangler>
diff --git a/core/ui/PageTemplate.tid b/core/ui/PageTemplate.tid
index a432cc76a..c9e929551 100644
--- a/core/ui/PageTemplate.tid
+++ b/core/ui/PageTemplate.tid
@@ -3,9 +3,6 @@ name: {{$:/language/PageTemplate/Name}}
description: {{$:/language/PageTemplate/Description}}
\whitespace trim
-\define containerClasses()
-tc-page-container tc-page-view-$(storyviewTitle)$ tc-language-$(languageTitle)$
-\end
\import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]
<$vars
@@ -17,7 +14,7 @@ tc-page-container tc-page-view-$(storyviewTitle)$ tc-language-$(languageTitle)$
storyviewTitle={{$:/view}}
languageTitle={{{ [{$:/language}get[name]] }}}>
->>
+ ] [[tc-language-]addsuffix ] :and[unique[]join[ ]] }}} >
<$navigator story="$:/StoryList" history="$:/HistoryList" openLinkFromInsideRiver={{$:/config/Navigation/openLinkFromInsideRiver}} openLinkFromOutsideRiver={{$:/config/Navigation/openLinkFromOutsideRiver}} relinkOnRename={{$:/config/RelinkOnRename}}>
diff --git a/core/ui/ViewTemplate.tid b/core/ui/ViewTemplate.tid
index 7cb8bec3c..f0aba9c97 100644
--- a/core/ui/ViewTemplate.tid
+++ b/core/ui/ViewTemplate.tid
@@ -7,7 +7,7 @@ $:/state/folded/$(currentTiddler)$
\define cancel-delete-tiddler-actions(message) <$action-sendmessage $message="tm-$message$-tiddler"/>
\import [all[shadows+tiddlers]tag[$:/tags/Macro/View]!has[draft.of]]
<$vars storyTiddler=<> tiddlerInfoState=<>>
-> data-tags={{!!tags}} class={{{ tc-tiddler-frame tc-tiddler-view-frame [ is[tiddler]then[tc-tiddler-exists]] [is[missing]!is[shadow]then[tc-tiddler-missing]] [is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [is[shadow]is[tiddler]then[tc-tiddler-overridden-shadow]] [is[system]then[tc-tiddler-system]] [{!!class}] [tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}} role="article">
+> data-tags={{!!tags}} class={{{ [all[shadows+tiddlers]tag[$:/tags/ClassFilters/TiddlerTemplate]!is[draft]] :map:flat[subfilter{!!text}] tc-tiddler-frame tc-tiddler-view-frame [ is[tiddler]then[tc-tiddler-exists]] [is[missing]!is[shadow]then[tc-tiddler-missing]] [is[shadow]then[tc-tiddler-exists tc-tiddler-shadow]] [is[shadow]is[tiddler]then[tc-tiddler-overridden-shadow]] [is[system]then[tc-tiddler-system]] [{!!class}] [tags[]encodeuricomponent[]addprefix[tc-tagged-]] +[join[ ]] }}} role="article">
<$list filter="[all[shadows+tiddlers]tag[$:/tags/ViewTemplate]!has[draft.of]]" variable="listItem">
<$transclude tiddler=<>/>
$list>
diff --git a/editions/tw5.com/tiddlers/systemtags/SystemTag_ $__tags_ClassFilters_PageTemplate.tid b/editions/tw5.com/tiddlers/systemtags/SystemTag_ $__tags_ClassFilters_PageTemplate.tid
new file mode 100644
index 000000000..dd6a7c699
--- /dev/null
+++ b/editions/tw5.com/tiddlers/systemtags/SystemTag_ $__tags_ClassFilters_PageTemplate.tid
@@ -0,0 +1,9 @@
+caption: $:/tags/ClassFilters/PageTemplate
+created: 20221020035315795
+description: marks filters evaluated to dynamically add classes to the page template.
+modified: 20221020035945262
+tags: SystemTags
+title: SystemTag: $:/tags/ClassFilters/PageTemplate
+type: text/vnd.tiddlywiki
+
+The [[system tag|SystemTags]] `$:/tags/ClassFilters/PageTemplate` marks filters marks filters evaluated to dynamically add their output as CSS classes to the page template.
\ No newline at end of file
diff --git a/editions/tw5.com/tiddlers/systemtags/SystemTag_ $__tags_ClassFilters_TiddlerTemplate.tid b/editions/tw5.com/tiddlers/systemtags/SystemTag_ $__tags_ClassFilters_TiddlerTemplate.tid
new file mode 100644
index 000000000..20e552905
--- /dev/null
+++ b/editions/tw5.com/tiddlers/systemtags/SystemTag_ $__tags_ClassFilters_TiddlerTemplate.tid
@@ -0,0 +1,9 @@
+caption: $:/tags/ClassFilters/TiddlerTemplate
+created: 20221020035738692
+description: marks filters evaluated to dynamically add classes to the page template.
+modified: 20221020035933363
+tags: SystemTags
+title: SystemTag: $:/tags/ClassFilters/TiddlerTemplate
+type: text/vnd.tiddlywiki
+
+The [[system tag|SystemTags]] `$:/tags/ClassFilters/TiddlerTemplate` marks filters marks filters evaluated to dynamically add their output as CSS classes to the tiddler template.
\ No newline at end of file
From 6af3eb539b6d77e4902ca470e62a5b7f7a44ad23 Mon Sep 17 00:00:00 2001
From: btheado
Date: Sun, 30 Oct 2022 12:10:12 -0400
Subject: [PATCH 052/937] Adds a javascript widget tutorial to the dev
tiddlywiki edition (#7016)
* Initial widget tutorials extracted from https://btheado.github.io/tw-widget-tutorial/
* Fixes for refresh behavior change
---
editions/dev/tiddlers/HelloThere.tid | 3 +-
.../Child widgets tutorial.tid | 140 ++++++++++++++++++
.../Do nothing widget demo.tid | 22 +++
.../Do nothing widget tutorial.tid | 25 ++++
.../Hello World demo.tid | 23 +++
.../Hello World widget tutorial.tid | 18 +++
.../Javascript Widget Tutorial.tid | 37 +++++
.../Undefined widget demo.tid | 18 +++
.../Undefined widget tutorial.tid | 11 ++
.../Widget attributes demo I.tid | 48 ++++++
.../Widget attributes demo II.tid | 48 ++++++
.../Widget attributes tutorial part I.tid | 50 +++++++
.../Widget attributes tutorial part II.tid | 33 +++++
.../Widget refresh demo I.tid | 44 ++++++
.../Widget refresh demo II.tid | 40 +++++
.../Widget refresh demo III.tid | 50 +++++++
.../Widget refresh tutorial part I.tid | 47 ++++++
.../Widget refresh tutorial part II.tid | 37 +++++
.../Widget refresh tutorial part III.tid | 26 ++++
.../javascript-widget-tutorial/domwidget.js | 47 ++++++
.../domwidget.js.meta | 6 +
.../javascript-widget-tutorial/donothing.js | 16 ++
.../donothing.js.meta | 6 +
.../hello-attribute-optimized.js | 51 +++++++
.../hello-attribute-optimized.js.meta | 6 +
.../hello-attribute.js | 47 ++++++
.../hello-attribute.js.meta | 6 +
.../javascript-widget-tutorial/hello.js | 35 +++++
.../javascript-widget-tutorial/hello.js.meta | 6 +
.../refreshcount.js | 43 ++++++
.../refreshcount.js.meta | 6 +
.../tiddlerfield-norefresh.js | 36 +++++
.../tiddlerfield-norefresh.js.meta | 6 +
.../tiddlerfield.js | 46 ++++++
.../tiddlerfield.js.meta | 6 +
editions/dev/tiddlywiki.info | 3 +-
36 files changed, 1090 insertions(+), 2 deletions(-)
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Child widgets tutorial.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Do nothing widget demo.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Do nothing widget tutorial.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Hello World demo.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Hello World widget tutorial.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Javascript Widget Tutorial.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Undefined widget demo.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Undefined widget tutorial.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes demo I.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes demo II.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes tutorial part I.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes tutorial part II.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo I.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo II.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo III.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part I.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part II.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part III.tid
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/domwidget.js
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/domwidget.js.meta
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/donothing.js
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/donothing.js.meta
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute-optimized.js
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute-optimized.js.meta
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute.js
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute.js.meta
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/hello.js
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/hello.js.meta
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/refreshcount.js
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/refreshcount.js.meta
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield-norefresh.js
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield-norefresh.js.meta
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield.js
create mode 100644 editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield.js.meta
diff --git a/editions/dev/tiddlers/HelloThere.tid b/editions/dev/tiddlers/HelloThere.tid
index caa8ed435..e3b8979a8 100644
--- a/editions/dev/tiddlers/HelloThere.tid
+++ b/editions/dev/tiddlers/HelloThere.tid
@@ -1,5 +1,5 @@
created: 20190115173333457
-modified: 20190115173723915
+modified: 20221029175754753
tags: TableOfContents
title: HelloThere
type: text/vnd.tiddlywiki
@@ -15,6 +15,7 @@ Welcome to the developer documentation for TiddlyWiki (https://tiddlywiki.com/).
** [[Using ES2016 for Writing Plugins]]
** [[Adding Babel Polyfill to TiddlyWiki]]
** [[TiddlyWiki Drag and Drop Interoperability]]
+** [[Javascript Widget Tutorial]]
* The original developer documentation from https://tiddlywiki.com:
** [[TiddlyWiki for Developers]]
** [[TiddlyWiki Coding Style Guidelines]]
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Child widgets tutorial.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Child widgets tutorial.tid
new file mode 100644
index 000000000..f6b1a63d1
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Child widgets tutorial.tid
@@ -0,0 +1,140 @@
+created: 20190202160512541
+modified: 20190222231130254
+title: Child widgets tutorial
+type: text/vnd.tiddlywiki
+
+\define showTrees(wikitext)
+
+
+wiki text | html | renders as |
+
+
+
+`$wikitext$` |
+
+<$wikify name=html text="""$wikitext$""" output=html mode=inline>
+<$text text=<>/>
+$wikify>
+ |
+$wikitext$ |
+
+
+
+
+
+
+parse tree | widget tree |
+
+
+
+
+
+<$wikify name=parsetree text="""$wikitext$""" output=parsetree mode=inline>
+<>
+$wikify>
+
+ |
+
+
+<$wikify name=widgettree text="""$wikitext$""" output=widgettree mode=inline>
+<>
+$wikify>
+
+ |
+
+
+
+\end
+
+! Introduction
+
+Until now the examples have covered only simple, leaf widgets, but widgets can be arranged into a hierarchy. Compared to a leaf-only widget, widget classes which support having children must contain code to instantiate the children.
+
+Not all widgets need to support having children. Widgets whose only purpose is to integrate third party javascript libraries, for example may not need it.
+
+! wiki text ⇨ parse tree ⇨ widget tree ⇨ DOM tree
+
+# [[wiki text|https://tiddlywiki.com/#WikiText]] - Users write content in tiddlers using wiki text.
+# [[parse tree|https://tiddlywiki.com/dev/#ParsingMechanism]] - A parse tree is a JSON data structure describing the wiki text. Wiki text can be converted into a parse tree using `this.wiki.parseText`.
+# ''widget tree'' - Most items in the parse tree correspond to one or more items in the widget tree. The `this.makeChildWidgets` method is used to convert the parse tree into a widget tree.
+# [[DOM tree|https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction]] - A DOM tree is a standard javascript datastructure used by browsers to display a web page. The `this.renderChildren` method is used to instantiate the widget class and then render each widget in the widget tree. If a given widget will be visible in the browser, then one or more DOM nodes will be created.
+
+!Examples
+
+!! Simple trees without nesting
+
+[[Widgets in wiki text|https://tiddlywiki.com/#Widgets%20in%20WikiText]] have a syntax similar to html (i.e. `<$list/>`). But all [[wiki markup|https://tiddlywiki.com/#WikiText]] is seen by the tiddlywiki code as widgets.
+
+Even a simple string with no special syntax will be treated as a widget. In this case a widget of type `text`:
+
+<>
+
+The above bare string of text is mostly just a synonym for this widget syntax:
+
+<""">>
+
+Some of the details of the parseTree and widgetTree are different in the two examples, but the general structure is the same and the rendered output is the same.
+
+In these two simple examples, there is no nesting and so no child widgets are created.
+
+!! Trees with one level of nesting
+
+This next example shows the structure for bold wiki syntax. It is still simple, but does introduce one level of nesting: `element`→`text`
+
+The wiki syntax for bold converts to the standard html element `strong`. Any standard html element is represented in tiddlywiki as a widget of type `element`:
+
+<>
+
+Another example showing one level of nesting (`link`→`text`):
+
+<link$link>""">>
+
+!!Widgets with no DOM contribution
+Not all widgets contribute items to the DOM. The purpose of some widgets is to affect the display of descendant widgets by changing some state. The `$set` widget for example sets a variable which will be accessible to the descendant widgets:
+
+<""">>
+
+Nothing is rendered and there is no html, but the parse tree and widget tree contain information about the variable.
+
+!! Dynamic widget children using this.wiki.parseText
+
+In all the examples so far, there has been a close mapping between the nesting structure of the parse tree and the widget tree. If the item is in the parse tree, then it is in the widget tree. If the parse tree item has children, then the widget tree has the same number of children.
+
+However, there are several examples of widgets in which more levels of nesting are created dynamically during the widget rendering process
+
+The `$macrocall` widget is one example:
+
+<""" >>
+
+The parse tree has just a single type=$macrocall element with no children. The widget tree has that same type=$macrocall element, but it also has a child widget which wasn't in the parse tree.
+
+In all cases, the `$macrocall` widget calls the given macro and then calls `this.wiki.parseText` on the output. This results in a new parse tree which is used to make the child widgets.
+
+In this particular case, the `now` macro is called. It returns a simple string of text, so when it is parsed the result causes the creation of a single child text widget containing the current date and time.
+
+!! Widgets which do not support nesting
+
+Just as some widgets can cause widget trees to have more nesting than the parse tree, some widgets can cause widget trees to have less nesting.
+
+This happens when there is no use for the widget to have any children. The `$text` widget, for example, has no use for displaying any descendants.
+
+This behavior is accomplished by not calling the `makeChildWidgets` method. Without that method call, the child widgets are not created from the child parse tree items. For example:
+
+<$macrocall $name=showTrees wikitext="""<$text text="hi">ignored child text$text>"""/>
+
+Since the `$text` widget does not have a call to `makeChildWidgets`, 'ignored child text' above is present in the parse tree, but not in the widget tree.
+
+!Reference
+
+|!method|!when to call|!what it does|
+|`makeChildWidgets`|directly or indirectly from `render` method|converts child parse tree items into widget tree items|
+|`renderChildren`|directly or indirectly from `render` method, after the `makeChildWidgets` call|calls the `render` method of the child widget|
+|`refreshChildren`|directly or indirectly from `refresh` method, only needed if `refreshSelf` is not called|calls the `refresh` method of of the child widgets so they can check if refresh is needed|
+|`this.wiki.parseText`|pass the output parse tree to `makeChildWidgets`|converts the given text to a parse tree...only needed for dynamically constructed parsetree|
+
+|!example call|!purpose|!widgets using this approach|
+|`this.makeChildWidgets()`|construct child widgets from the static parse tree|[[$link|$:/core/modules/widgets/link.js]], [[$button|$:/core/modules/widgets/button.js]], etc.|
+|`this.makeChildWidgets(parseTreeNodes)`|construct child widgets from a dynamic parse tree|[[$list|$:/core/modules/widgets/list.js]], [[$transclude|$:/core/modules/widgets/transclude.js]], [[$macrocall|$:/core/modules/widgets/macrocall.js]], etc|
+|`this.renderChildren(parent, nextSibling)`|when the widget adds nothing to the dom, just pass the `render` arguments through to the children|[[$set|$:/core/modules/widgets/set.js]], [[$importvariables|$:/core/modules/widgets/importvariables.js]], [[$tiddler|$:/core/modules/widgets/tiddler.js]], [[$wikify|$:/core/modules/widgets/wikify.js]], etc.|
+|`this.renderChildren(domNode, null)`|passes the dom node generated by this widget into the children|[[$link|$:/core/modules/widgets/link.js]], [[$button|$:/core/modules/widgets/button.js]], etc|
+
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Do nothing widget demo.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Do nothing widget demo.tid
new file mode 100644
index 000000000..0d40db9c5
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Do nothing widget demo.tid
@@ -0,0 +1,22 @@
+created: 20190201120100249
+modified: 20190201222600576
+tags:
+title: Do nothing widget demo
+type: text/vnd.tiddlywiki
+
+
+<$list name=refresh filter=[[donothing.js]get[text]]>
+<$innerwiki width="600" height="400" style="width:100%;">
+ <$data title="$:/DefaultTiddlers" text="[[do nothing widget]]"/>
+ <$data $tiddler=donothing.js/>
+ <$data title="do nothing widget" text="""
+```
+<$donothing/>
+```
+
+Renders as:
+
+<$donothing/>
+"""/>
+$innerwiki>
+$list>
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Do nothing widget tutorial.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Do nothing widget tutorial.tid
new file mode 100644
index 000000000..2155ab528
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Do nothing widget tutorial.tid
@@ -0,0 +1,25 @@
+created: 20190201232102417
+modified: 20190202145547621
+tags:
+title: Do nothing widget tutorial
+type: text/vnd.tiddlywiki
+
+In order to define a widget in a tiddler, the tiddler must satisfy these requirements:
+
+* type field is application/javascript
+* module-type field is widget
+* the text field contains the javascript code
+
+The [[donothing.js]] tiddler fulfills the requirements and its code looks like this:
+{{donothing.js}}
+That code does 2 key things:
+
+* Imports the core Widget class ([[$:/core/modules/widgets/widget.js]])
+* exports the class in an attribute with the name we want our widget to have (`donothing`)
+
+Here's what it looks like:
+{{Do nothing widget demo}}
+And it worked. No error message this time.
+
+''Exercise'': Modify [[donothing.js]] and [[Do nothing widget demo]] so the widget is named `noop` instead of `donothing`
+
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Hello World demo.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Hello World demo.tid
new file mode 100644
index 000000000..43c8cbfd7
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Hello World demo.tid
@@ -0,0 +1,23 @@
+created: 20190201114718313
+modified: 20190201231556294
+tags:
+title: Hello World demo
+type: text/vnd.tiddlywiki
+
+
+<$list name=refresh filter=[[hello.js]get[text]]>
+<$innerwiki width="600" height="400" style="width:100%;">
+ <$data title="$:/DefaultTiddlers" text="[[hello world widget]]"/>
+ <$data $tiddler=hello.js/>
+ <$data title="hello world widget" text="""
+```
+<$hello/>
+```
+
+Renders as:
+
+<$hello/>
+"""/>
+
+$innerwiki>
+$list>
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Hello World widget tutorial.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Hello World widget tutorial.tid
new file mode 100644
index 000000000..e663255f1
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Hello World widget tutorial.tid
@@ -0,0 +1,18 @@
+created: 20190201232200698
+modified: 20190216175629825
+tags:
+title: Hello World widget tutorial
+type: text/vnd.tiddlywiki
+
+Now let's create a widget which actually has output.
+
+When tiddlywiki encounters a widget definition like `<$hello>` it will create an object which is an instance of the class which is exported by the widget code.
+
+In addition to creating an instance of the class, tiddlywiki will call the render method of the resulting object. The render method is expected to create a dom node which will be display in place of the widget.
+
+In the `donothing` example the core widget was exported. The core widget class doesn't have a render method which creates a dom node and that's why there is no output. Getting output requires writing a widget class which inherits from the core `Widget` class. Code in the render method of this class will display the hello world message.
+
+The [[hello.js]] tiddler has code which accomplishes that:
+{{hello.js}}
+The code for importing the core widget class remains, but now we also have code to create our own class which inherits from it. In addition an implementation of the `render` method is included. Here is the result:
+{{Hello World demo}}
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Javascript Widget Tutorial.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Javascript Widget Tutorial.tid
new file mode 100644
index 000000000..f6ee01182
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Javascript Widget Tutorial.tid
@@ -0,0 +1,37 @@
+created: 20190202035524804
+modified: 20221029161501848
+tags:
+title: Javascript Widget Tutorial
+type: text/vnd.tiddlywiki
+
+! Introduction
+This tutorial provides step-by-step, interactive examples of how to write code for tiddlywiki widgets. The demo javascript code can be modified without having to reload the entire wiki.
+
+Intended audience:
+
+# Those who know tiddlywiki well and know programming and javascript and want to write their own widget. I don't make any effort to explain javascript here. For that you will need other resources.
+# Those who know tiddlywiki well and don't know javascript, but want to understand more about how tiddlywiki works. You should be able to skim through and interact with the demos and learn something.
+
+!The tutorial
+*[[Undefined widget tutorial]]
+*[[Do nothing widget tutorial]]
+*[[Hello World widget tutorial]]
+*[[Widget refresh tutorial part I]]
+*[[Widget refresh tutorial part II]]
+*[[Widget refresh tutorial part III]]
+*[[Widget attributes tutorial part I]]
+*[[Widget attributes tutorial part II]]
+*[[Child widgets tutorial]]
+
+! Notes
+
+tiddlywiki doesn't support dynamically reloading javascript. If you change a javascript tiddler, then you need to save and reload the wiki before the changes will take affect.
+
+To avoid the need for such reloads, the excellent [[innerwiki plugin|https://tiddlywiki.com/prerelease/plugins/tiddlywiki/innerwiki/]] is used. This allows an inner wiki to be created from a subset of tiddlers in the outer wiki. Each time the inner wiki is refreshed, its entire wiki is reloaded and the javascript changes in the inner wiki will take affect.
+
+Without the need for reloads, a tiddlywiki instance with the [[innerwiki plugin|https://tiddlywiki.com/prerelease/plugins/tiddlywiki/innerwiki/]] installed works great as a playground for interacting with tiddlywiki javascript.
+
+! Other documentation on writing TW widgets
+
+*WidgetModules
+*[[Widgets]]
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Undefined widget demo.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Undefined widget demo.tid
new file mode 100644
index 000000000..cadcf13bf
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Undefined widget demo.tid
@@ -0,0 +1,18 @@
+created: 20190201212238781
+modified: 20190201213112748
+tags:
+title: Undefined widget demo
+type: text/vnd.tiddlywiki
+
+<$innerwiki width="600" height="400" style="width:100%;">
+ <$data title="$:/DefaultTiddlers" text="[[Undefined widget]]"/>
+ <$data title="Undefined widget" text="""
+```
+<$donothing/>
+```
+
+Renders as:
+
+<$donothing/>
+"""/>
+$innerwiki>
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Undefined widget tutorial.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Undefined widget tutorial.tid
new file mode 100644
index 000000000..1d049b649
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Undefined widget tutorial.tid
@@ -0,0 +1,11 @@
+created: 20190201232001970
+modified: 20190202145655413
+tags:
+title: Undefined widget tutorial
+type: text/vnd.tiddlywiki
+
+Let's start be defining a minimal widget which does nothing. It doesn't support any attributes, no child elements, and it doesn't output anything. The name of the widget will be `donothing`. If it does nothing, then how can we verify after writing the code that we accomplished anything? Well, let's see what happens when an undefined widget is referenced.
+
+{{Undefined widget demo}}
+
+Since we haven't written the code, the attempt to render the widget gives an undefined widget error. So we will know the donothing code accomplishes something if we don't get the undefined widget error when rendering.
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes demo I.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes demo I.tid
new file mode 100644
index 000000000..78fb19967
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes demo I.tid
@@ -0,0 +1,48 @@
+created: 20190204020507195
+modified: 20190204031520013
+tags:
+title: Widget attributes demo I
+type: text/vnd.tiddlywiki
+
+
+<$list name=refresh filter=[[hello-attribute.js]get[text]]>
+<$innerwiki width="600" height="400" style="width:100%;">
+ <$data title="$:/DefaultTiddlers" text="[[hello world widget]]"/>
+ <$data $tiddler=hello-attribute.js/>
+ <$data title="hello world widget" text="""
+```
+<$hello/>
+```
+
+Renders as:
+
+<$hello/>
+
+---
+
+```
+<$hello message="pale blue dot"/>
+```
+
+Renders as:
+
+<$hello message="pale blue dot"/>
+
+---
+
+<$edit-text focus=yes tiddler=test tag=input/>
+
+```
+<$hello message={{test!!text}}/>
+```
+
+Renders as:
+
+<$hello message={{test!!text}}/>
+
+"""/>
+
+ <$data title="test" text="Alice"/>
+
+$innerwiki>
+$list>
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes demo II.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes demo II.tid
new file mode 100644
index 000000000..334387e0b
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes demo II.tid
@@ -0,0 +1,48 @@
+created: 20190205024953535
+modified: 20190205025028737
+tags:
+title: Widget attributes demo II
+type: text/vnd.tiddlywiki
+
+
+<$list name=refresh filter=[[hello-attribute-optimized.js]get[text]]>
+<$innerwiki width="600" height="400" style="width:100%;">
+ <$data title="$:/DefaultTiddlers" text="[[hello world widget]]"/>
+ <$data $tiddler=hello-attribute-optimized.js/>
+ <$data title="hello world widget" text="""
+```
+<$hello/>
+```
+
+Renders as:
+
+<$hello/>
+
+---
+
+```
+<$hello message="pale blue dot"/>
+```
+
+Renders as:
+
+<$hello message="pale blue dot"/>
+
+---
+
+<$edit-text focus=yes tiddler=test tag=input/>
+
+```
+<$hello message={{test!!text}}/>
+```
+
+Renders as:
+
+<$hello message={{test!!text}}/>
+
+"""/>
+
+ <$data title="test" text="Alice"/>
+
+$innerwiki>
+$list>
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes tutorial part I.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes tutorial part I.tid
new file mode 100644
index 000000000..9f9dfaad9
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes tutorial part I.tid
@@ -0,0 +1,50 @@
+created: 20190202035425715
+modified: 20190205023518575
+tags:
+title: Widget attributes tutorial part I
+type: text/vnd.tiddlywiki
+
+So far none of the widgets we've implemented have had any code for handling widget attributes A vast majority of useful widgets will need to support attributes in order to make them useful.
+
+As an example, let's change the Hello World widget so it can greet not just the world, but whoever/whatever the user wants. To do that we can add support for an attribute named `what` and call it like `<$hello what="pale blue dot"/>`.
+
+The tiddlywiki widget class provides methods `computeAttributes` and `getAttribute` which can together be used by the widget code to discover what values are passed into the widget.
+
+The `computeAttributes` and `getAttribute` methods can be called like this (the second parameter to `getAttribute` will be used as the default value if the user doesn't provide that attribute in the widget call):
+
+```javascript
+this.computeAttributes();
+var message = this.getAttribute("message", "World");
+```
+
+Then the `message` variable can be used to construct the Hello XXX string:
+
+```javascript
+var textNode = this.document.createTextNode("Hello, " + message + "!");
+```
+
+The original [[hello.js]] code only implements a `render` method. The `refresh` method is not needed because the output from the widget can never be different...it will always be "Hello, World!".
+
+Even with a `message` attribute, you might think the `render` by itself is enough. After all, the value of the input parameter `message` can only be changed by modifying the wiki text which means the tiddler will be redisplayed from scratch.
+
+However, tiddlywiki has a syntax which allows parameter values to vary without modifying the wiki text. See https://tiddlywiki.com/#Widgets%20in%20WikiText for details. As one example, if the widget were called like `<$hello message={{MyTiddler!!field}}/>`, then every time the `field` field of `MyTiddler` were modified, only the `refresh` method would be called. Therefore, in order to get the widget display to update, the refresh method needs to be implemented.
+
+If the `computeAttributes` and `getAttribute` calls are placed in the `render` method then we can implement a performance unoptimized version of refresh as was done in [[Widget refresh tutorial part II]]:
+
+```javascript
+/*
+A widget with optimized performance will selectively refresh, but here we refresh always
+*/
+MyWidget.prototype.refresh = function(changedTiddlers) {
+ // Regenerate and rerender the widget and
+ // replace the existing DOM node
+ this.refreshSelf();
+ return true;
+};
+```
+
+The full code can be seen at [[hello-attribute.js]] and here is the result ([[Widget attributes demo I]]):
+
+{{Widget attributes demo I}}
+
+The third example above is the only one which requires the refresh method in order to behave properly.
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes tutorial part II.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes tutorial part II.tid
new file mode 100644
index 000000000..bc0c25fc2
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget attributes tutorial part II.tid
@@ -0,0 +1,33 @@
+created: 20190205023543910
+modified: 20190217012121130
+tags:
+title: Widget attributes tutorial part II
+type: text/vnd.tiddlywiki
+
+This example will build on the previous one. The only modification will be to add a check to the `refresh` method. The `refreshSelf` will only be called if a change to the attributes is detected.
+
+The `computeAttributes` method returns a Javascript object containing properties for each attribute which has changed. So a check like `if (changedAttributes.attr1 || changedAttributes.attr2 || changedAttributes.attr3)` etc. can be used to detect the change. See the refresh method in [[$:/core/modules/widgets/view.js]] for an example showing the check for multiple attributes.
+
+For this example, `message` is the only attribute implemented.
+
+```javascript
+/*
+Refresh if the attribute value changed since render
+*/
+MyWidget.prototype.refresh = function(changedTiddlers) {
+ // Find which attributes have changed
+ var changedAttributes = this.computeAttributes();
+ if (changedAttributes.message) {
+ this.refreshSelf();
+ return true;
+ } else {
+ return false;
+ }
+};
+```
+
+The full code can be seen at [[hello-attribute-optimized.js]] and here is the result ([[Widget attributes demo II]]):
+
+{{Widget attributes demo II}}
+
+The visible behavior here should be identical to the unoptimized behavior of the previous tutorial.
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo I.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo I.tid
new file mode 100644
index 000000000..0040093e3
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo I.tid
@@ -0,0 +1,44 @@
+created: 20190201233806976
+modified: 20221029194854112
+tags:
+title: Widget refresh demo I
+type: text/vnd.tiddlywiki
+
+
+<$list name=refresh filter=[[tiddlerfield-norefresh.js]get[text]]>
+<$innerwiki width="600" height="400" style="width:100%;">
+ <$data title="$:/DefaultTiddlers" text="[[tiddler field widget]]"/>
+ <$data title="test" text="type new text here"/>
+ <$data $tiddler=tiddlerfield-norefresh.js/>
+ <$data title="tiddler field widget" text="""
+<$edit-text focus=yes tiddler=test tag=input/>
+<$button set="!!refresh" setTo={{test}}>Force refresh$button>
+<$list filter="[{!!refresh}]">
+
+
+
+
+```
+<$tiddlerfield/>
+```
+
+Renders as:
+
+<$tiddlerfield/>
+
+
+
+```
+<$view tiddler="test"/>
+```
+
+Renders as:
+
+<$view tiddler="test"/>
+
+
+$list>
+"""/>
+
+$innerwiki>
+$list>
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo II.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo II.tid
new file mode 100644
index 000000000..60997a9ca
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo II.tid
@@ -0,0 +1,40 @@
+created: 20190202032354223
+modified: 20190217005540498
+tags:
+title: Widget refresh demo II
+type: text/vnd.tiddlywiki
+
+
+<$list name=refresh filter=[[tiddlerfield.js]get[text]]>
+<$innerwiki width="600" height="400" style="width:100%;">
+ <$data title="$:/DefaultTiddlers" text="[[tiddler field widget]]"/>
+ <$data title="test" text="type new text here"/>
+ <$data $tiddler=tiddlerfield.js/>
+ <$data title="tiddler field widget" text="""
+<$edit-text focus=yes tiddler=test tag=input/>
+
+
+
+
+```
+<$tiddlerfield/>
+```
+
+Renders as:
+
+<$tiddlerfield/>
+
+
+
+```
+<$view tiddler="test"/>
+```
+
+Renders as:
+
+<$view tiddler="test"/>
+
+"""/>
+
+$innerwiki>
+$list>
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo III.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo III.tid
new file mode 100644
index 000000000..0c6fec3dd
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh demo III.tid
@@ -0,0 +1,50 @@
+created: 20190202122928187
+modified: 20190216191939585
+tags:
+title: Widget refresh demo III
+type: text/vnd.tiddlywiki
+
+
+<$list name=refresh filter=[[refreshcount.js]get[text]]>
+<$innerwiki width="600" height="400" style="width:100%;">
+ <$data title="$:/DefaultTiddlers" text="[[refresh count widget]]"/>
+ <$data title="test" text="Text field of tiddler='test'"/>
+ <$data $tiddler=refreshcount.js/>
+ <$data title="refresh count widget" text="""
+
+*<$button set="test!!test" setTo="hello">Modify a different tiddler$button>
+*<$button set="!!test" setTo="hello">Modify this tiddler$button>
+*<$edit-text focus=yes tiddler=test tag=input/>
+
+
+
+
+```
+<$refreshcount/>
+```
+
+Renders as:
+
+<$refreshcount/>
+
+
+
+
+```
+<$list filter="[[test]get[text]]">
+<>
+<$refreshcount/>
+$list>
+```
+
+Renders as:
+
+<$list filter="[[test]get[text]]">
+<>
+<$refreshcount/>
+$list>
+"""/>
+
+
+$innerwiki>
+$list>
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part I.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part I.tid
new file mode 100644
index 000000000..4665e6535
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part I.tid
@@ -0,0 +1,47 @@
+created: 20190201232847949
+modified: 20221029203553291
+tags:
+title: Widget refresh tutorial part I
+type: text/vnd.tiddlywiki
+
+But what if we want to display dynamic content? How can we display information and keep it up to date as it changes? Let's display the content of a tiddler field.
+
+The [[tiddlerfield-norefresh.js]] which defines the `tiddlerfield` widget is almost the same as [[hello.js]] except for this part:
+
+```javascript
+MyWidget.prototype.render = function(parent,nextSibling) {
+ this.parentDomNode = parent;
+ var text = this.wiki.getTiddlerText("test", " ")
+ var textNode = this.document.createTextNode(text);
+ parent.insertBefore(textNode,nextSibling);
+ this.domNodes.push(textNode);
+};
+```
+
+Instead of creating the text dom node from a static string, the text field of the `test` tiddler is used. This is similar to using the view widget like this: `<$view tiddler="test"/>`
+
+Here's how it looks (see [[Widget refresh demo I]] to look at the code):
+
+{{Widget refresh demo I}}
+
+Notice if you change the text in the input box, the output from the `tiddlerfield` widget doesn't change, but the output of the `view` widget does. Only after the ''Force refresh'' button is clicked does the output of `tiddlerfield` update to match the input box contents.
+
+What's going on here? The render method of the widget code is only called by tiddlywiki core when the widget is first created. After that, it isn't called again unless the widget is completely destroyed and then created again.
+
+The tiddlywiki ~ViewWidget has a properly written `refresh` method so typing in the input box will cause its content to update. However, the `tiddlerfield` widget does not have a `refresh` method at all. It has no way of being notified that the `test` tiddler content has changed. Its output will not change until the ''Force refresh'' button is clicked.
+
+See the next example for an implementation of the `refresh` method for the `tiddlerfield` widget.
+
+The code for the refresh button looks like this:
+
+```
+<$button set="!!refresh" setTo={{test}}>Force refresh$button>
+```
+
+and the widgets are enclosed in a list widget like this:
+
+```
+<$list filter="[{!!refresh}]">...$list>
+```
+
+When the button is clicked the field `refresh` in the containing tiddler is modified and it causes the children of the list widget to be created from scratch. The render method is called and the output of the `tiddlerfield` widget updates.
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part II.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part II.tid
new file mode 100644
index 000000000..dfb86a640
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part II.tid
@@ -0,0 +1,37 @@
+created: 20190201232910076
+modified: 20190217014335419
+tags:
+title: Widget refresh tutorial part II
+type: text/vnd.tiddlywiki
+
+This example is like [[Widget refresh tutorial part I]] except the widget output will be automatically refreshed when the tiddler field changes.
+
+[[tiddlerfield.js]] is the same as [[tiddlerfield-norefresh.js]], except this `refresh` method is added:
+
+```javascript
+/*
+A widget with optimized performance will selectively refresh, but here we refresh always
+*/
+MyWidget.prototype.refresh = function(changedTiddlers) {
+ // Regenerate and rerender the widget and
+ // replace the existing DOM node
+ this.refreshSelf();
+ return true;
+};
+```
+
+The `refreshSelf` method called above is implemented by the core widget class and it takes care of cleaning the old dom node and calling the render function.
+
+Here is the result ([[Widget refresh demo II]]):
+
+{{Widget refresh demo II}}
+
+And now any typing into the input box will cause both the `tiddlerfield` and the `view` widget output to refresh immediately.
+
+Note this is a naive version of `refresh`. It unconditionally refreshes itself. This is far from optimal since the `refresh` method for all visible widgets is called every time the tiddler store changes. But the way we've defined our widget, the output ONLY depends on the tiddler titled `text`.
+
+In tiddlywiki the tiddler store is used for everything and almost any interaction will result in an update to the store. This means almost any interaction will cause the refresh method to be called. If you type into the search box, for example, the `tiddlerfield` widget will be refreshed with every keystroke.
+
+Adding and removing dom elements is a relatively expensive operation, so if someone has used the list widget to create a few hundred instances of this widget, then such tiddlywiki interactions might gain a noticable lag. Therefore, it usually makes sense to avoid modifying the dom when possible by writing a smarter `refresh` method.
+
+''Exercise'' - change the refresh method above to only call `refreshSelf` when the `changedTiddlers` input array contains `test` as one of its entries (Hint: see the refresh method in $:/core/modules/widgets/view.js for an example).
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part III.tid b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part III.tid
new file mode 100644
index 000000000..f8729b784
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/Widget refresh tutorial part III.tid
@@ -0,0 +1,26 @@
+created: 20190202034841184
+modified: 20221029201023638
+tags:
+title: Widget refresh tutorial part III
+type: text/vnd.tiddlywiki
+
+This tutorial is intended to demonstrate a few examples of when calls to the `refresh` method happen vs. when a widget is recreated from scratch.
+
+This is accomplished with a [[refreshcount.js]] widget which sets a counter to zero when the widget is created and increments the counter every time the `refresh` method is called. Here is the full code:
+
+{{refreshcount.js}}
+
+These are the key parts of the code from above:
+
+```javascript
+this.refreshCount = 0;
+this.document.createTextNode(this.refreshCount + " refreshes");
+this.refreshCount++;
+```
+
+In the following example (see [[Widget refresh demo III]] for the code), two instances of the `refreshcount` widget are created. One at the top level and the other inside a list widget whose filter results depend on the value in the `text` field of the `test` widget. The tiddler store can be modified in a few ways via two buttons and an input box:
+{{Widget refresh demo III}}
+
+* ''Modify a different tiddler'' - every time this button is clicked, both counters increment, indicating the `refresh` method is being called
+* ''Modify this tiddler'' - clicking this button modifies the tiddler itself. In earlier ~TiddlyWiki versions that caused both widgets to be recreated from scratch and the counters are thereby reset to zero. Since [[version 5.2.0|https://tiddlywiki.com/#Release%205.2.0]] modifying another field in this tiddler does not cause the widgets to be recreated and the counters are not reset.
+* ''Text field of tiddler='test''' - typing text into the input box causes the counter in the standalone `refreshcount` widget to be incremented. But the other instance of the widget is embedded inside a list widget whose filter output depends on the value of the tiddler field which is being modified. This causes it to recreate its children from scratch and the counter is reset every time. So with every keystroke, the counter on the left is incremented, but the counter on the right goes to/stays at zero.
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/domwidget.js b/editions/dev/tiddlers/javascript-widget-tutorial/domwidget.js
new file mode 100644
index 000000000..243839764
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/domwidget.js
@@ -0,0 +1,47 @@
+/*\
+
+Library function for creating widget using a dom creating function
+
+\*/
+(function() {
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+function createDomWidget(domCreatorFunction) {
+
+ var MyWidget = function(parseTreeNode, options) {
+ this.initialise(parseTreeNode, options);
+ };
+
+ /*
+ Inherit from the base widget class
+ */
+ MyWidget.prototype = new Widget();
+
+ /*
+ Render this widget into the DOM
+ */
+ MyWidget.prototype.render = function(parent, nextSibling) {
+ this.parentDomNode = parent;
+ var domNode = domCreatorFunction(this.document);
+ parent.insertBefore(domNode, nextSibling);
+ this.domNodes.push(domNode);
+ };
+
+ /*
+ A widget with optimized performance will selectively refresh, but here we refresh always
+ */
+ MyWidget.prototype.refresh = function(changedTiddlers) {
+ // Regenerate and rerender the widget and replace the existing DOM node
+ this.refreshSelf();
+ return true;
+ };
+
+ return MyWidget;
+}
+module.exports = createDomWidget;
+})();
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/domwidget.js.meta b/editions/dev/tiddlers/javascript-widget-tutorial/domwidget.js.meta
new file mode 100644
index 000000000..55235998f
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/domwidget.js.meta
@@ -0,0 +1,6 @@
+created: 20190201025244440
+modified: 20190201030708723
+module-type: library
+tags:
+title: domwidget.js
+type: application/javascript
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/donothing.js b/editions/dev/tiddlers/javascript-widget-tutorial/donothing.js
new file mode 100644
index 000000000..b1899b645
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/donothing.js
@@ -0,0 +1,16 @@
+/*\
+
+Do nothing widget
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+exports.donothing = Widget;
+
+})();
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/donothing.js.meta b/editions/dev/tiddlers/javascript-widget-tutorial/donothing.js.meta
new file mode 100644
index 000000000..a8e70b03c
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/donothing.js.meta
@@ -0,0 +1,6 @@
+created: 20190201115945945
+modified: 20190201222441271
+module-type: widget
+tags:
+title: donothing.js
+type: application/javascript
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute-optimized.js b/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute-optimized.js
new file mode 100644
index 000000000..71cec1762
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute-optimized.js
@@ -0,0 +1,51 @@
+/*\
+
+Hello, World widget
+
+\*/
+(function() {
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+var MyWidget = function(parseTreeNode, options) {
+ this.initialise(parseTreeNode, options);
+};
+
+/*
+Inherit from the base widget class
+*/
+MyWidget.prototype = new Widget();
+
+/*
+Render this widget into the DOM
+*/
+MyWidget.prototype.render = function(parent, nextSibling) {
+ this.parentDomNode = parent;
+ this.computeAttributes();
+ var message = this.getAttribute("message", "World");
+ var textNode = this.document.createTextNode("Hello, " + message + "!");
+ parent.insertBefore(textNode, nextSibling);
+ this.domNodes.push(textNode);
+};
+
+/*
+Refresh if the attribute value changed since render
+*/
+MyWidget.prototype.refresh = function(changedTiddlers) {
+ // Find which attributes have changed
+ var changedAttributes = this.computeAttributes();
+ if (changedAttributes.message) {
+ this.refreshSelf();
+ return true;
+ } else {
+ return false;
+ }
+};
+
+exports.hello = MyWidget;
+
+})();
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute-optimized.js.meta b/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute-optimized.js.meta
new file mode 100644
index 000000000..e195f4bfa
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute-optimized.js.meta
@@ -0,0 +1,6 @@
+created: 20190205024846183
+modified: 20190205025110882
+module-type: widget
+tags:
+title: hello-attribute-optimized.js
+type: application/javascript
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute.js b/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute.js
new file mode 100644
index 000000000..3e91aa122
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute.js
@@ -0,0 +1,47 @@
+/*\
+
+Hello, World widget
+
+\*/
+(function() {
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+var MyWidget = function(parseTreeNode, options) {
+ this.initialise(parseTreeNode, options);
+};
+
+/*
+Inherit from the base widget class
+*/
+MyWidget.prototype = new Widget();
+
+/*
+Render this widget into the DOM
+*/
+MyWidget.prototype.render = function(parent, nextSibling) {
+ this.parentDomNode = parent;
+ this.computeAttributes();
+ var message = this.getAttribute("message", "World");
+ var textNode = this.document.createTextNode("Hello, " + message + "!");
+ parent.insertBefore(textNode, nextSibling);
+ this.domNodes.push(textNode);
+};
+
+/*
+A widget with optimized performance will selectively refresh, but here we refresh always
+*/
+MyWidget.prototype.refresh = function(changedTiddlers) {
+ // Regenerate and rerender the widget and
+ // replace the existing DOM node
+ this.refreshSelf();
+ return true;
+};
+
+exports.hello = MyWidget;
+
+})();
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute.js.meta b/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute.js.meta
new file mode 100644
index 000000000..008086c2a
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/hello-attribute.js.meta
@@ -0,0 +1,6 @@
+created: 20190204020011193
+modified: 20190204030332147
+module-type: widget
+tags:
+title: hello-attribute.js
+type: application/javascript
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/hello.js b/editions/dev/tiddlers/javascript-widget-tutorial/hello.js
new file mode 100644
index 000000000..91afbd8f4
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/hello.js
@@ -0,0 +1,35 @@
+/*\
+
+Hello, World widget
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+var MyWidget = function(parseTreeNode,options) {
+ this.initialise(parseTreeNode,options);
+};
+
+/*
+Inherit from the base widget class
+*/
+MyWidget.prototype = new Widget();
+
+/*
+Render this widget into the DOM
+*/
+MyWidget.prototype.render = function(parent,nextSibling) {
+ this.parentDomNode = parent;
+ var textNode = this.document.createTextNode("Hello, World!");
+ parent.insertBefore(textNode,nextSibling);
+ this.domNodes.push(textNode);
+};
+
+exports.hello = MyWidget;
+
+})();
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/hello.js.meta b/editions/dev/tiddlers/javascript-widget-tutorial/hello.js.meta
new file mode 100644
index 000000000..2518f08ec
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/hello.js.meta
@@ -0,0 +1,6 @@
+created: 20190201114558816
+modified: 20190201224846870
+module-type: widget
+tags:
+title: hello.js
+type: application/javascript
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/refreshcount.js b/editions/dev/tiddlers/javascript-widget-tutorial/refreshcount.js
new file mode 100644
index 000000000..b8ee53d89
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/refreshcount.js
@@ -0,0 +1,43 @@
+/*\
+
+widget to count the number of times this widget refreshes
+
+\*/
+(function() {
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+var MyWidget = function(parseTreeNode, options) {
+ this.refreshCount = 0;
+ this.initialise(parseTreeNode, options);
+};
+
+/*
+Inherit from the base widget class
+*/
+MyWidget.prototype = new Widget();
+
+/*
+Render this widget into the DOM
+*/
+MyWidget.prototype.render = function(parent, nextSibling) {
+ this.parentDomNode = parent;
+ var textNode = this.document.createTextNode(this.refreshCount + " refreshes");
+ parent.insertBefore(textNode, nextSibling);
+ this.domNodes.push(textNode);
+};
+
+MyWidget.prototype.refresh = function(changedTiddlers) {
+ // Regenerate and rerender the widget and replace the existing DOM node
+ this.refreshCount++;
+ this.refreshSelf();
+ return true;
+};
+
+exports.refreshcount = MyWidget;
+
+})();
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/refreshcount.js.meta b/editions/dev/tiddlers/javascript-widget-tutorial/refreshcount.js.meta
new file mode 100644
index 000000000..86831cb85
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/refreshcount.js.meta
@@ -0,0 +1,6 @@
+created: 20190201005026324
+modified: 20190202143451303
+module-type: widget
+tags:
+title: refreshcount.js
+type: application/javascript
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield-norefresh.js b/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield-norefresh.js
new file mode 100644
index 000000000..59f179b5d
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield-norefresh.js
@@ -0,0 +1,36 @@
+/*\
+
+Hello, World widget
+
+\*/
+(function() {
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+var MyWidget = function(parseTreeNode, options) {
+ this.initialise(parseTreeNode, options);
+};
+
+/*
+Inherit from the base widget class
+*/
+MyWidget.prototype = new Widget();
+
+/*
+Render this widget into the DOM
+*/
+MyWidget.prototype.render = function(parent, nextSibling) {
+ this.parentDomNode = parent;
+ var text = this.wiki.getTiddlerText("test", "")
+ var textNode = this.document.createTextNode(text);
+ parent.insertBefore(textNode, nextSibling);
+ this.domNodes.push(textNode);
+};
+
+exports.tiddlerfield = MyWidget;
+
+})();
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield-norefresh.js.meta b/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield-norefresh.js.meta
new file mode 100644
index 000000000..bab1ed0cb
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield-norefresh.js.meta
@@ -0,0 +1,6 @@
+created: 20190201233714872
+modified: 20190202030615781
+module-type: widget
+tags:
+title: tiddlerfield-norefresh.js
+type: application/javascript
\ No newline at end of file
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield.js b/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield.js
new file mode 100644
index 000000000..c19e84f60
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield.js
@@ -0,0 +1,46 @@
+/*\
+
+Hello, World widget
+
+\*/
+(function() {
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var Widget = require("$:/core/modules/widgets/widget.js").widget;
+
+var MyWidget = function(parseTreeNode, options) {
+ this.initialise(parseTreeNode, options);
+};
+
+/*
+Inherit from the base widget class
+*/
+MyWidget.prototype = new Widget();
+
+/*
+Render this widget into the DOM
+*/
+MyWidget.prototype.render = function(parent, nextSibling) {
+ this.parentDomNode = parent;
+ var text = this.wiki.getTiddlerText("test", "")
+ var textNode = this.document.createTextNode(text);
+ parent.insertBefore(textNode, nextSibling);
+ this.domNodes.push(textNode);
+};
+
+/*
+A widget with optimized performance will selectively refresh, but here we refresh always
+*/
+MyWidget.prototype.refresh = function(changedTiddlers) {
+ // Regenerate and rerender the widget and
+ // replace the existing DOM node
+ this.refreshSelf();
+ return true;
+};
+
+exports.tiddlerfield = MyWidget;
+
+})();
diff --git a/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield.js.meta b/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield.js.meta
new file mode 100644
index 000000000..b5160e75a
--- /dev/null
+++ b/editions/dev/tiddlers/javascript-widget-tutorial/tiddlerfield.js.meta
@@ -0,0 +1,6 @@
+created: 20190202032530728
+modified: 20190202032700995
+module-type: widget
+tags:
+title: tiddlerfield.js
+type: application/javascript
\ No newline at end of file
diff --git a/editions/dev/tiddlywiki.info b/editions/dev/tiddlywiki.info
index 186ff45ee..1a8207f1e 100644
--- a/editions/dev/tiddlywiki.info
+++ b/editions/dev/tiddlywiki.info
@@ -5,7 +5,8 @@
"tiddlywiki/nodewebkitsaver",
"tiddlywiki/github-fork-ribbon",
"tiddlywiki/menubar",
- "tiddlywiki/internals"
+ "tiddlywiki/internals",
+ "tiddlywiki/innerwiki"
],
"themes": [
"tiddlywiki/vanilla",
From f54ecc23f3562a7931334576a7a02ab4f7cedf3c Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Mon, 31 Oct 2022 12:03:47 +0000
Subject: [PATCH 053/937] Fix wikification of tiddler titles in advanced search
filter dropdown
Closes #7017
Thanks @ericshulman
---
core/ui/AdvancedSearch/FilterButtons/dropdown.tid | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/ui/AdvancedSearch/FilterButtons/dropdown.tid b/core/ui/AdvancedSearch/FilterButtons/dropdown.tid
index b5c3c2ecc..b1d400aeb 100644
--- a/core/ui/AdvancedSearch/FilterButtons/dropdown.tid
+++ b/core/ui/AdvancedSearch/FilterButtons/dropdown.tid
@@ -15,7 +15,7 @@ tags: $:/tags/AdvancedSearch/FilterButton
<$list filter="[all[shadows+tiddlers]tag[$:/tags/Filter]]">
-<$link to={{!!filter}}><$transclude field="description"/>$link>
+<$link to={{!!filter}}><$let tv-wikilinks="no"><$transclude field="description"/>$let>$link>
$list>
From 30af537b9102369ed70213dad42ec5361c4c985e Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Tue, 1 Nov 2022 09:45:08 +0000
Subject: [PATCH 054/937] Update release note
---
editions/prerelease/tiddlers/Release 5.2.4.tid | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/editions/prerelease/tiddlers/Release 5.2.4.tid b/editions/prerelease/tiddlers/Release 5.2.4.tid
index 9c5ae019b..f6da952af 100644
--- a/editions/prerelease/tiddlers/Release 5.2.4.tid
+++ b/editions/prerelease/tiddlers/Release 5.2.4.tid
@@ -1,6 +1,6 @@
caption: 5.2.4
-created: 20221017165036377
-modified: 20221017165036377
+created: 20221101094408196
+modified: 20221101094408196
tags: ReleaseNotes
title: Release 5.2.4
type: text/vnd.tiddlywiki
@@ -49,6 +49,7 @@ Improvements to the translation features of TiddlyWiki:
! Hackability Improvements
+* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6976">> support for [[SystemTag: $:/tags/ClassFilters/TiddlerTemplate]] and [[SystemTag: $:/tags/ClassFilters/PageTemplate]] to assign dynamic CSS classes to both tiddler frames and the page template
* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/6936">> new operators for reading and formatting JSON data: [[jsonget Operator]], [[jsonindexes Operator]], [[jsontype Operator]] and [[format Operator]]
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/commit/c5d3d4c26e8fe27f272dda004aec27d6b66c4f60">> safe mode to disable wiki store indexers
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/166a1565843878083fb1eba47c73b8e67b78400d">> safe mode to prevent globally disabling parser rules
@@ -60,6 +61,8 @@ Improvements to the translation features of TiddlyWiki:
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/fb34df84ed41882c1c2a6ff54f0e908b43ef95a3">> "new image" keyboard shortcut not to assign journal tags
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/6987">> SelectWidget class to update if it uses a filter
+* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/6303">> issue with availability of variables within filter runs
+* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7017">> issue with wikification within the advanced search filter dropdown
! Developer Improvements
From 8fe2f6086dc1829152ffa2fe866cf7f032eb4b18 Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Tue, 1 Nov 2022 09:48:08 +0000
Subject: [PATCH 055/937] Update release notes credits
---
editions/prerelease/tiddlers/Release 5.2.4.tid | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/editions/prerelease/tiddlers/Release 5.2.4.tid b/editions/prerelease/tiddlers/Release 5.2.4.tid
index f6da952af..b80ecd52a 100644
--- a/editions/prerelease/tiddlers/Release 5.2.4.tid
+++ b/editions/prerelease/tiddlers/Release 5.2.4.tid
@@ -84,14 +84,20 @@ Improvements to the translation features of TiddlyWiki:
<<.contributors """
bestony
+btheado
BramChen
+EvidentlyCube
+FlashSystems
flibbles
fu-sen
+joebordes
+hoelzro
Marxsal
oflg
pmario
rmunn
roma0104
+saqimtiaz
tw-FRed
twMat
xcazin
From 62f26d6630e4668af2c33b481230ae421ae6779e Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Tue, 1 Nov 2022 10:07:54 +0000
Subject: [PATCH 056/937] Improve genesis widget examples
---
editions/prerelease/tiddlers/Release 5.2.4.tid | 2 +-
.../tw5.com/tiddlers/widgets/GenesisWidget.tid | 16 ++++++++++++++--
2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/editions/prerelease/tiddlers/Release 5.2.4.tid b/editions/prerelease/tiddlers/Release 5.2.4.tid
index b80ecd52a..3f38343a8 100644
--- a/editions/prerelease/tiddlers/Release 5.2.4.tid
+++ b/editions/prerelease/tiddlers/Release 5.2.4.tid
@@ -34,8 +34,8 @@ Improvements to the translation features of TiddlyWiki:
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/d62a16ee464fb9984b766b48504829a1a3eb143b">> problem with long presses on tiddler links triggering a preview on iOS/iPadOS
* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6910">> consistency of button and input elements across browsers
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/d825f1c875f5e46158c9c41c8c66471138c162d1">> edit preview to use the [[View Template Body Cascade]]
-* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6970">> detection of infinite recursion errors in widgets and filters
* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/36896c3db8c9678c0385a561996248a6f00a45ff">> opening a tiddler in a new window to use the [[View Template Body Cascade]]
+* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/6970">> detection of infinite recursion errors in widgets and filters
* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/6877">> default styles for [[styled runs|Styles and Classes in WikiText]]
! Widget Improvements
diff --git a/editions/tw5.com/tiddlers/widgets/GenesisWidget.tid b/editions/tw5.com/tiddlers/widgets/GenesisWidget.tid
index 9b324e8fa..a9d2cf713 100644
--- a/editions/tw5.com/tiddlers/widgets/GenesisWidget.tid
+++ b/editions/tw5.com/tiddlers/widgets/GenesisWidget.tid
@@ -1,6 +1,6 @@
caption: genesis
-created: 20220924140702430
-modified: 20220924140702430
+created: 20221101100729587
+modified: 20221101100729587
tags: Widgets
title: GenesisWidget
type: text/vnd.tiddlywiki
@@ -27,3 +27,15 @@ Note that attributes explicitly specified take precedence over attributes with t
<$macrocall $name='wikitext-example-without-html'
src='<$genesis $type="div" class="tc-thing" label="Squeak">Mouse$genesis>'/>
+
+<$macrocall $name='wikitext-example-without-html'
+src="""\define my-banner(mode:"inline",caption)
+<$genesis $type={{{ [<__mode__>match[inline]then[span]else[div]] }}} class="tc-mybanner">
+<<__caption__>>
+$genesis>
+\end
+
+<>
+
+<>
+"""/>
From 3be9b1381472c8e096cb23d175094e85af36507b Mon Sep 17 00:00:00 2001
From: Cameron Fischer
Date: Wed, 2 Nov 2022 13:26:08 -0400
Subject: [PATCH 057/937] Fix for #4767: lazy-loading deletes tiddler bodies
(#7014)
---
core/modules/server/routes/put-tiddler.js | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/core/modules/server/routes/put-tiddler.js b/core/modules/server/routes/put-tiddler.js
index ed3b48fcc..ff1bd2737 100644
--- a/core/modules/server/routes/put-tiddler.js
+++ b/core/modules/server/routes/put-tiddler.js
@@ -30,6 +30,16 @@ exports.handler = function(request,response,state) {
if(fields.revision) {
delete fields.revision;
}
+ // If this is a skinny tiddler, it means the client never got the full
+ // version of the tiddler to edit. So we must preserve whatever text
+ // already exists on the server, or else we'll inadvertently delete it.
+ if(fields._is_skinny !== undefined) {
+ var tiddler = state.wiki.getTiddler(title);
+ if(tiddler) {
+ fields.text = tiddler.fields.text;
+ }
+ delete fields._is_skinny;
+ }
state.wiki.addTiddler(new $tw.Tiddler(fields,{title: title}));
var changeCount = state.wiki.getChangeCount(title).toString();
response.writeHead(204, "OK",{
From 965bd090a905f5756e79124b698c894f7f72ad5b Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Sat, 5 Nov 2022 09:10:31 +0000
Subject: [PATCH 058/937] list-links macro: add "field" parameter
See https://talk.tiddlywiki.org/t/choosing-what-field-to-show-with-list-links/5039?u=jeremyruston
---
core/wiki/macros/list.tid | 12 ++++++------
editions/tw5.com/tiddlers/macros/ListMacro.tid | 4 +++-
2 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/core/wiki/macros/list.tid b/core/wiki/macros/list.tid
index 2856f3c32..6b6a32b10 100644
--- a/core/wiki/macros/list.tid
+++ b/core/wiki/macros/list.tid
@@ -1,21 +1,21 @@
title: $:/core/macros/list
tags: $:/tags/Macro
-\define list-links(filter,type:"ul",subtype:"li",class:"",emptyMessage)
+\define list-links(filter,type:"ul",subtype:"li",class:"",emptyMessage,field:"caption")
\whitespace trim
-<$type$ class="$class$">
+<$genesis $type=<<__type__>> class=<<__class__>>>
<$list filter="$filter$" emptyMessage=<<__emptyMessage__>>>
-<$subtype$>
+<$genesis $type=<<__subtype__>>>
<$link to={{!!title}}>
<$let tv-wikilinks="no">
-<$transclude field="caption">
+<$transclude field=<<__field__>>>
<$view field="title"/>
$transclude>
$let>
$link>
-$subtype$>
+$genesis>
$list>
-$type$>
+$genesis>
\end
\define list-links-draggable-drop-actions()
diff --git a/editions/tw5.com/tiddlers/macros/ListMacro.tid b/editions/tw5.com/tiddlers/macros/ListMacro.tid
index 7ee39ee12..21eb169dc 100644
--- a/editions/tw5.com/tiddlers/macros/ListMacro.tid
+++ b/editions/tw5.com/tiddlers/macros/ListMacro.tid
@@ -1,6 +1,6 @@
caption: list-links
created: 20140917083515996
-modified: 20190206000000000
+modified: 20221105090835041
tags: Macros [[Core Macros]]
title: list-links Macro
type: text/vnd.tiddlywiki
@@ -15,6 +15,8 @@ Note: Each first [[step|Filter Step]] of a [[filter run|Filter Run]] not given a
;filter
: A [[filter|Filters]] selecting which tiddlers to include
+;caption
+: The name of the field to transclude for each list item, defaultingt to `caption`
;type
: An HTML element to use for the overall list element, defaulting to `ul`
;subtype
From e8148ff97808546d8d3fbe05e7d67e44f0d88316 Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Sat, 5 Nov 2022 09:41:43 +0000
Subject: [PATCH 059/937] Missed off 965bd090a905f5756e79124b698c894f7f72ad5b
Thanks @pmario @saqimtiaz
---
core/wiki/macros/list.tid | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/core/wiki/macros/list.tid b/core/wiki/macros/list.tid
index 6b6a32b10..2d82b601a 100644
--- a/core/wiki/macros/list.tid
+++ b/core/wiki/macros/list.tid
@@ -4,7 +4,7 @@ tags: $:/tags/Macro
\define list-links(filter,type:"ul",subtype:"li",class:"",emptyMessage,field:"caption")
\whitespace trim
<$genesis $type=<<__type__>> class=<<__class__>>>
-<$list filter="$filter$" emptyMessage=<<__emptyMessage__>>>
+<$list filter=<<__filter__>> emptyMessage=<<__emptyMessage__>>>
<$genesis $type=<<__subtype__>>>
<$link to={{!!title}}>
<$let tv-wikilinks="no">
From ae12e8fb69a494b650490587da815281ff28e6a7 Mon Sep 17 00:00:00 2001
From: tw-FRed <95534072+tw-FRed@users.noreply.github.com>
Date: Sat, 5 Nov 2022 18:44:41 +0100
Subject: [PATCH 060/937] Help Plugin: Update links to community resources
(#7023)
* Promote Talk TiddlyWiki
* Change link to GG to https
---
plugins/tiddlywiki/help/tabs/Support.tid | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)
diff --git a/plugins/tiddlywiki/help/tabs/Support.tid b/plugins/tiddlywiki/help/tabs/Support.tid
index 3a5e955d7..0e3bc25f3 100644
--- a/plugins/tiddlywiki/help/tabs/Support.tid
+++ b/plugins/tiddlywiki/help/tabs/Support.tid
@@ -4,9 +4,13 @@ caption: Support
~TiddlyWiki is an open source project with a vibrant community of users and developers. We're always happy to help new users get the most from ~TiddlyWiki.
-Join the ~TiddlyWiki mailing list:
+Join the ~TiddlyWiki forum:
-http://groups.google.com/group/TiddlyWiki
+https://talk.tiddlywiki.org/
+
+For the convenience of existing users, we also continue to operate the original TiddlyWiki group (hosted on Google Groups since 2005):
+
+https://groups.google.com/group/TiddlyWiki
Post bug reports to the ~TiddlyWiki ~GitHub repository:
From 72f06581b6b178ad8c085ad8179f127a5235cf17 Mon Sep 17 00:00:00 2001
From: Xavier Cazin
Date: Tue, 8 Nov 2022 19:09:42 +0100
Subject: [PATCH 061/937] More consistency in fr-FR captions for cascade
examples in ControlPanel (#7025)
---
languages/fr-FR/ControlPanel.multids | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/languages/fr-FR/ControlPanel.multids b/languages/fr-FR/ControlPanel.multids
index 0f93509bd..0a8248773 100644
--- a/languages/fr-FR/ControlPanel.multids
+++ b/languages/fr-FR/ControlPanel.multids
@@ -36,7 +36,7 @@ EditorTypes/Hint: Ces tiddlers déterminent l'éditeur à utiliser pour éditer
EditorTypes/Type/Caption: Type
EditTemplateBody/Caption: Édition du corps
EditTemplateBody/Hint: Cette cascade de règles est utilisée par le template d'édition par défaut pour choisir dynamiquement le template à appliquer pour éditer le corps d'un tiddler.
-FieldEditor/Caption: Éditeur de champ
+FieldEditor/Caption: Édition des champs
FieldEditor/Hint: Cette cascade de règles sert à choisir dynamiquement le template de rendu d'un champ de tiddler en fonction de son nom. Il est utilisé dans le template d'édition.
Info/Caption: Info
Info/Hint: Information sur ce TiddlyWiki
@@ -198,7 +198,7 @@ Settings/TitleLinks/Yes/Description: Afficher les titres des tiddlers comme des
Settings/MissingLinks/Caption: Liens wiki
Settings/MissingLinks/Hint: Peut-on pointer vers des tiddlers qui n'existent pas encore ?
Settings/MissingLinks/Description: Active les liens vers les tiddlers inexistants
-StoryTiddler/Caption: Tiddler dans le déroulé
+StoryTiddler/Caption: Tiddlers du déroulé
StoryTiddler/Hint: Cette cascade de règles sert à choisir dynamiquement le template d'affichage d'un tiddler dans le déroulé.
StoryView/Caption: Vue sur le déroulé
StoryView/Prompt: Vue courante :
@@ -227,5 +227,5 @@ Toolbars/ViewToolbar/Hint: Choix des boutons à afficher pour les tiddlers en mo
Tools/Download/Full/Caption: Télécharger le wiki complet
ViewTemplateBody/Caption: Visualisation du corps
ViewTemplateBody/Hint: Cette cascade de règles est utilisée par le template de visualisation par défaut pour choisir dynamiquement le template d'affichage du corps d'un tiddler.
-ViewTemplateTitle/Caption: Template de visualisation du titre
+ViewTemplateTitle/Caption: Visualisation du titre
ViewTemplateTitle/Hint: Cette cascade de règles est utilisée par le template de visualisation par défaut pour choisir dynamiquement le template d'affichage du titre d'un tiddler.
From 344110e2890caf711ab8f3c4f4deaa7d86771231 Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Wed, 9 Nov 2022 13:56:28 +0000
Subject: [PATCH 062/937] Rearrange ordering of MP4 extension so that it
defaults to video not audio
Previously using the --load command to import an MP4 file would cause it to be recognised as autio/mp4 instead of video/mp4
---
boot/boot.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/boot/boot.js b/boot/boot.js
index 6bfa6495a..c64b881f4 100644
--- a/boot/boot.js
+++ b/boot/boot.js
@@ -2403,11 +2403,11 @@ $tw.boot.initStartup = function(options) {
$tw.utils.registerFileType("application/x-font-ttf","base64",".woff");
$tw.utils.registerFileType("application/font-woff2","base64",".woff2");
$tw.utils.registerFileType("audio/ogg","base64",".ogg");
+ $tw.utils.registerFileType("audio/mp4","base64",[".mp4",".m4a"]);
$tw.utils.registerFileType("video/ogg","base64",[".ogm",".ogv",".ogg"]);
$tw.utils.registerFileType("video/webm","base64",".webm");
$tw.utils.registerFileType("video/mp4","base64",".mp4");
$tw.utils.registerFileType("audio/mp3","base64",".mp3");
- $tw.utils.registerFileType("audio/mp4","base64",[".mp4",".m4a"]);
$tw.utils.registerFileType("text/markdown","utf8",[".md",".markdown"],{deserializerType:"text/x-markdown"});
$tw.utils.registerFileType("text/x-markdown","utf8",[".md",".markdown"]);
$tw.utils.registerFileType("application/enex+xml","utf8",".enex");
From fedc23d73cce7cb79047fe6769e1f6999acfb40d Mon Sep 17 00:00:00 2001
From: Jeremy Ruston
Date: Sat, 12 Nov 2022 17:09:31 +0000
Subject: [PATCH 063/937] Introduce Twitter Archivist Plugin
---
bin/build-site.sh | 9 +
.../tiddlers/DefaultTiddlers.tid | 3 +
.../twitter-archivist/tiddlers/HelloThere.tid | 3 +
.../tiddlers/SiteSubtitle.tid | 3 +
.../twitter-archivist/tiddlers/SiteTitle.tid | 3 +
editions/twitter-archivist/tiddlywiki.info | 16 ++
.../tiddlywiki/twitter-archivist/archivist.js | 264 ++++++++++++++++++
.../configTiddlerInfoMode.tid | 2 +
.../twitter-archivist/loadtwitterarchive.js | 53 ++++
.../tiddlywiki/twitter-archivist/macros.tid | 204 ++++++++++++++
.../tiddlywiki/twitter-archivist/plugin.info | 6 +
.../tiddlywiki/twitter-archivist/readme.tid | 55 ++++
.../tiddlywiki/twitter-archivist/startup.js | 38 +++
.../tiddlywiki/twitter-archivist/styles.tid | 47 ++++
.../twitter-archivist/template-archive.tid | 3 +
.../twitter-archivist/template-tweet.tid | 3 +
.../twitter-archivist/template-tweeter.tid | 3 +
.../view-template-body-cascade.tid | 7 +
18 files changed, 722 insertions(+)
create mode 100644 editions/twitter-archivist/tiddlers/DefaultTiddlers.tid
create mode 100644 editions/twitter-archivist/tiddlers/HelloThere.tid
create mode 100644 editions/twitter-archivist/tiddlers/SiteSubtitle.tid
create mode 100644 editions/twitter-archivist/tiddlers/SiteTitle.tid
create mode 100644 editions/twitter-archivist/tiddlywiki.info
create mode 100644 plugins/tiddlywiki/twitter-archivist/archivist.js
create mode 100644 plugins/tiddlywiki/twitter-archivist/configTiddlerInfoMode.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/loadtwitterarchive.js
create mode 100644 plugins/tiddlywiki/twitter-archivist/macros.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/plugin.info
create mode 100644 plugins/tiddlywiki/twitter-archivist/readme.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/startup.js
create mode 100644 plugins/tiddlywiki/twitter-archivist/styles.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/template-archive.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/template-tweet.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/template-tweeter.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/view-template-body-cascade.tid
diff --git a/bin/build-site.sh b/bin/build-site.sh
index eca15dd63..0dff6d0b1 100755
--- a/bin/build-site.sh
+++ b/bin/build-site.sh
@@ -233,6 +233,15 @@ node $TW5_BUILD_TIDDLYWIKI \
--build index \
|| exit 1
+# /editions/twitter-archivist/index.html Twitter Archivist edition
+node $TW5_BUILD_TIDDLYWIKI \
+ ./editions/twitter-archivist \
+ --verbose \
+ --load $TW5_BUILD_OUTPUT/build.tid \
+ --output $TW5_BUILD_OUTPUT/editions/twitter-archivist/ \
+ --build index \
+ || exit 1
+
######################################################
#
# Plugin demos
diff --git a/editions/twitter-archivist/tiddlers/DefaultTiddlers.tid b/editions/twitter-archivist/tiddlers/DefaultTiddlers.tid
new file mode 100644
index 000000000..a3c362aff
--- /dev/null
+++ b/editions/twitter-archivist/tiddlers/DefaultTiddlers.tid
@@ -0,0 +1,3 @@
+title: $:/DefaultTiddlers
+
+HelloThere
\ No newline at end of file
diff --git a/editions/twitter-archivist/tiddlers/HelloThere.tid b/editions/twitter-archivist/tiddlers/HelloThere.tid
new file mode 100644
index 000000000..d1bb60ca7
--- /dev/null
+++ b/editions/twitter-archivist/tiddlers/HelloThere.tid
@@ -0,0 +1,3 @@
+title: HelloThere
+
+{{$:/plugins/tiddlywiki/twitter-archivist/readme}}
diff --git a/editions/twitter-archivist/tiddlers/SiteSubtitle.tid b/editions/twitter-archivist/tiddlers/SiteSubtitle.tid
new file mode 100644
index 000000000..d6b1cd1d4
--- /dev/null
+++ b/editions/twitter-archivist/tiddlers/SiteSubtitle.tid
@@ -0,0 +1,3 @@
+title: $:/SiteTitle
+
+Get Your Tweets Into ~TiddlyWiki
\ No newline at end of file
diff --git a/editions/twitter-archivist/tiddlers/SiteTitle.tid b/editions/twitter-archivist/tiddlers/SiteTitle.tid
new file mode 100644
index 000000000..910c8747d
--- /dev/null
+++ b/editions/twitter-archivist/tiddlers/SiteTitle.tid
@@ -0,0 +1,3 @@
+title: $:/SiteTitle
+
+Twitter Archivist
\ No newline at end of file
diff --git a/editions/twitter-archivist/tiddlywiki.info b/editions/twitter-archivist/tiddlywiki.info
new file mode 100644
index 000000000..681467706
--- /dev/null
+++ b/editions/twitter-archivist/tiddlywiki.info
@@ -0,0 +1,16 @@
+{
+ "description": "Twitter Archivist Edition",
+ "plugins": [
+ "tiddlywiki/twitter-archivist"
+ ],
+ "languages": [
+ ],
+ "themes": [
+ "tiddlywiki/vanilla",
+ "tiddlywiki/snowwhite"
+ ],
+ "build": {
+ "index": [
+ "--rendertiddler","$:/core/save/all","index.html","text/plain"]
+ }
+}
\ No newline at end of file
diff --git a/plugins/tiddlywiki/twitter-archivist/archivist.js b/plugins/tiddlywiki/twitter-archivist/archivist.js
new file mode 100644
index 000000000..2162ee556
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/archivist.js
@@ -0,0 +1,264 @@
+/*\
+title: $:/plugins/tiddlywiki/twitter-archivist/archivist.js
+type: application/javascript
+module-type: utils
+
+Utility class for manipulating Twitter archives
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+function TwitterArchivist(options) {
+ options = options || {};
+ this.source = options.source;
+}
+
+TwitterArchivist.prototype.loadArchive = async function(options) {
+ options = options || {};
+ const wiki = options.wiki;
+ await this.source.init();
+ // Process the manifest and profile
+ const manifestData = await this.loadTwitterJsData("data/manifest.js","window.__THAR_CONFIG = ",""),
+ profileData = await this.loadTwitterJsData("data/profile.js","window.YTD.profile.part0 = ",""),
+ accountData = await this.loadTwitterJsData("data/account.js","window.YTD.account.part0 = ",""),
+ username = manifestData.userInfo.userName,
+ user_id = manifestData.userInfo.accountId;
+ wiki.addTiddler({
+ title: "Twitter Archive for @" + username,
+ tags: "$:/tags/TwitterArchive",
+ user_id: user_id,
+ username: username,
+ displayname: manifestData.userInfo.displayName,
+ generation_date: $tw.utils.stringifyDate(new Date(manifestData.archiveInfo.generationDate)),
+ account_created_date: $tw.utils.stringifyDate(new Date(accountData[0].account.createdAt)),
+ bio: profileData[0].profile.description.bio,
+ website: profileData[0].profile.description.website,
+ location: profileData[0].profile.description.location
+ });
+ // Process the media
+ await this.source.processFiles("data/tweets_media","base64",function(mediaItem) {
+ var ext = mediaItem.filename.split(".").slice(-1)[0];
+ if("jpg png".split(" ").indexOf(ext) !== -1) {
+ var extensionInfo = $tw.utils.getFileExtensionInfo("." + ext),
+ type = extensionInfo ? extensionInfo.type : null;
+ wiki.addTiddler({
+ title: "Tweet Media - " + mediaItem.filename,
+ tags: "$:/tags/TweetMedia",
+ status_id: mediaItem.filename.split("-")[0],
+ text: mediaItem.contents,
+ type: type
+ });
+ }
+ });
+ // Process the favourites
+ const likeData = await this.loadTwitterJsData("data/like.js","window.YTD.like.part0 = ","");
+ $tw.utils.each(likeData,function(like) {
+ // Create the tweet tiddler
+ var tiddler = {
+ title: "Tweet - " + like.like.tweetId,
+ text: "\\rules only html entity extlink\n" + (like.like.fullText || "").replace("\n"," "),
+ status_id: like.like.tweetId,
+ liked_by: user_id,
+ tags: "$:/tags/Tweet"
+ };
+ wiki.addTiddler(tiddler);
+ });
+ // Process the tweets
+ const tweetData = await this.loadTwitterJsData("data/tweets.js","window.YTD.tweets.part0 = ","");
+ $tw.utils.each(tweetData,function(tweet) {
+ // Compile the tags for the tweet
+ var tags = ["$:/tags/Tweet"];
+ // Create tiddlers for each mentioned tweeter, and hyperlink the mention in the test
+ var rawText = tweet.tweet.full_text,
+ posText = 0,
+ text = "";
+ var mentions = [];
+ $tw.utils.each(tweet.tweet.entities.user_mentions,function(mention) {
+ var title = "Tweeter - " + mention.id_str;
+ tags.push(title);
+ wiki.addTiddler({
+ title: title,
+ screenname: "@" + mention.screen_name,
+ tags: "$:/tags/Tweeter",
+ user_id: mention.id_str,
+ name: mention.name
+ });
+ text = text +
+ $tw.utils.htmlEncode(rawText.substring(posText,mention.indices[0])) +
+ "<$link to=\"" + title + "\">" +
+ $tw.utils.htmlEncode(rawText.substring(mention.indices[0],mention.indices[1])) +
+ "$link>";
+ posText = mention.indices[1];
+ mentions.push(mention.id_str);
+ });
+ if(posText < rawText.length) {
+ text = text + $tw.utils.htmlEncode(rawText.substring(posText));
+ }
+ text = text.replace("\n"," ");
+ // Create the tweet tiddler
+ var tiddler = {
+ title: "Tweet - " + tweet.tweet.id_str,
+ text: "\\rules only html entity extlink\n" + text,
+ status_id: tweet.tweet.id_str,
+ user_id: user_id,
+ favorite_count: tweet.tweet.favorite_count,
+ retweet_count: tweet.tweet.retweet_count,
+ tags: tags,
+ created: $tw.utils.stringifyDate(new Date(tweet.tweet.created_at)),
+ modified: $tw.utils.stringifyDate(new Date(tweet.tweet.created_at))
+ };
+ if(tweet.tweet.in_reply_to_status_id_str) {
+ tiddler.in_reply_to_status_id = tweet.tweet.in_reply_to_status_id_str;
+ }
+ if(mentions.length > 0) {
+ tiddler.mention_user_ids = $tw.utils.stringifyList(mentions);
+ }
+ wiki.addTiddler(tiddler);
+ });
+};
+
+TwitterArchivist.prototype.loadTwitterJsData = async function(filePath,prefix,suffix) {
+ var tweetFileData = await this.source.loadTwitterJsData(filePath);
+ if(prefix) {
+ if(tweetFileData.slice(0,prefix.length) !== prefix) {
+ throw "Reading Twitter JS file " + filePath + " missing prefix '" + prefix + "'";
+ }
+ tweetFileData = tweetFileData.slice(prefix.length);
+ }
+ if(suffix) {
+ if(tweetFileData.slice(-suffix.length) !== suffix) {
+ throw "Reading Twitter JS file " + filePath + " missing suffix '" + suffix + "'";
+ }
+ tweetFileData = tweetFileData.slice(0,tweetFileData.length - suffix.length);
+ }
+ return JSON.parse(tweetFileData);
+};
+
+function TwitterArchivistSourceNodeJs(options) {
+ options = options || {};
+ this.archivePath = options.archivePath;
+}
+
+TwitterArchivistSourceNodeJs.prototype.init = async function() {
+};
+
+TwitterArchivistSourceNodeJs.prototype.processFiles = async function(dirPath,encoding,callback) {
+ var fs = require("fs"),
+ path = require("path"),
+ dirPath = path.resolve(this.archivePath,dirPath),
+ filenames = fs.readdirSync(dirPath);
+ $tw.utils.each(filenames,function(filename) {
+ callback({
+ filename: filename,
+ contents: fs.readFileSync(path.resolve(dirPath,filename),encoding)
+ });
+ });
+};
+
+TwitterArchivistSourceNodeJs.prototype.loadTwitterJsData = async function(filePath) {
+ var fs = require("fs"),
+ path = require("path");
+ return fs.readFileSync(path.resolve(this.archivePath,filePath),"utf8");
+};
+
+function TwitterArchivistSourceBrowser(options) {
+ options = options || {};
+}
+
+TwitterArchivistSourceBrowser.prototype.init = async function() {
+ // Open directory
+ this.rootDirHandle = await window.showDirectoryPicker();
+};
+
+TwitterArchivistSourceBrowser.prototype.processFiles = async function(dirPath,encoding,callback) {
+ const dirHandle = await this.walkDirectory(dirPath.split("/"));
+ for await (const [filename, fileHandle] of dirHandle.entries()) {
+ const contents = await fileHandle.getFile();
+ callback({
+ filename: filename,
+ contents: arrayBufferToBase64(await contents.arrayBuffer())
+ });
+ }
+};
+
+TwitterArchivistSourceBrowser.prototype.loadTwitterJsData = async function(filePath) {
+ const filePathParts = filePath.split("/");
+ const dirHandle = await this.walkDirectory(filePathParts.slice(0,-1));
+ const fileHandle = await dirHandle.getFileHandle(filePathParts.slice(-1)[0]);
+ const contents = await fileHandle.getFile();
+ return await contents.text();
+};
+
+TwitterArchivistSourceBrowser.prototype.walkDirectory = async function(arrayDirectoryEntries) {
+ var entries = arrayDirectoryEntries.slice(0),
+ dirHandle = this.rootDirHandle;
+ while(entries.length > 0) {
+ dirHandle = await dirHandle.getDirectoryHandle(entries[0]);
+ entries.shift();
+ }
+ return dirHandle;
+};
+
+// Thanks to MatheusFelipeMarinho
+// https://github.com/MatheusFelipeMarinho/venom/blob/43ead0bfffa57a536a5cff67dd909e55da9f0915/src/lib/wapi/helper/array-buffer-to-base64.js#L55
+function arrayBufferToBase64(arrayBuffer) {
+ var base64 = '';
+ var encodings =
+ 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
+
+ var bytes = new Uint8Array(arrayBuffer);
+ var byteLength = bytes.byteLength;
+ var byteRemainder = byteLength % 3;
+ var mainLength = byteLength - byteRemainder;
+
+ var a, b, c, d;
+ var chunk;
+
+ // Main loop deals with bytes in chunks of 3
+ for (var i = 0; i < mainLength; i = i + 3) {
+ // Combine the three bytes into a single integer
+ chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
+
+ // Use bitmasks to extract 6-bit segments from the triplet
+ a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
+ b = (chunk & 258048) >> 12; // 258048 = (2^6 - 1) << 12
+ c = (chunk & 4032) >> 6; // 4032 = (2^6 - 1) << 6
+ d = chunk & 63; // 63 = 2^6 - 1
+
+ // Convert the raw binary segments to the appropriate ASCII encoding
+ base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d];
+ }
+
+ // Deal with the remaining bytes and padding
+ if (byteRemainder == 1) {
+ chunk = bytes[mainLength];
+
+ a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
+
+ // Set the 4 least significant bits to zero
+ b = (chunk & 3) << 4; // 3 = 2^2 - 1
+
+ base64 += encodings[a] + encodings[b] + '==';
+ } else if (byteRemainder == 2) {
+ chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
+
+ a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
+ b = (chunk & 1008) >> 4; // 1008 = (2^6 - 1) << 4
+
+ // Set the 2 least significant bits to zero
+ c = (chunk & 15) << 2; // 15 = 2^4 - 1
+
+ base64 += encodings[a] + encodings[b] + encodings[c] + '=';
+ }
+ return base64;
+}
+
+exports.TwitterArchivist = TwitterArchivist;
+exports.TwitterArchivistSourceNodeJs = TwitterArchivistSourceNodeJs;
+exports.TwitterArchivistSourceBrowser = TwitterArchivistSourceBrowser;
+
+})();
diff --git a/plugins/tiddlywiki/twitter-archivist/configTiddlerInfoMode.tid b/plugins/tiddlywiki/twitter-archivist/configTiddlerInfoMode.tid
new file mode 100644
index 000000000..3b3716299
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/configTiddlerInfoMode.tid
@@ -0,0 +1,2 @@
+title: $:/config/TiddlerInfo/Mode
+text: sticky
diff --git a/plugins/tiddlywiki/twitter-archivist/loadtwitterarchive.js b/plugins/tiddlywiki/twitter-archivist/loadtwitterarchive.js
new file mode 100644
index 000000000..497b82bf5
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/loadtwitterarchive.js
@@ -0,0 +1,53 @@
+/*\
+title: $:/plugins/tiddlywiki/twitter-archivist/loadtwitterarchive.js
+type: application/javascript
+module-type: command
+
+Read tiddlers from an unzipped Twitter archive
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+var widget = require("$:/core/modules/widgets/widget.js");
+
+exports.info = {
+ name: "loadtwitterarchive",
+ synchronous: false
+};
+
+var Command = function(params,commander,callback) {
+ this.params = params;
+ this.commander = commander;
+ this.callback = callback;
+};
+
+Command.prototype.execute = function() {
+ var self = this;
+ if(this.params.length < 1) {
+ return "Missing path to Twitter archive";
+ }
+ var archivePath = this.params[0];
+ // Load tweets
+ var archiveSource = new $tw.utils.TwitterArchivistSourceNodeJs({
+ archivePath: archivePath
+ }),
+ archivist = new $tw.utils.TwitterArchivist({
+ source: archiveSource
+ });
+ archivist.loadArchive({
+ wiki: this.commander.wiki
+ }).then(function() {
+ self.callback(null);
+ }).catch(function(err) {
+ self.callback(err);
+ });
+ return null;
+};
+
+exports.Command = Command;
+
+})();
diff --git a/plugins/tiddlywiki/twitter-archivist/macros.tid b/plugins/tiddlywiki/twitter-archivist/macros.tid
new file mode 100644
index 000000000..c0003274d
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/macros.tid
@@ -0,0 +1,204 @@
+title: $:/plugins/tiddlywiki/twitter-archivist/macros
+tags: $:/tags/Macro
+
+\define skinny-tabs(tabNames,tabCaptions,defaultTab,state)
+<$let
+ currTab={{{ [<__state__>get[text]else<__defaultTab__>] }}}
+>
+
+
+ <$list filter="[enlist<__tabNames__>]" variable="tab" counter="tabCounter">
+ <$let
+ caption={{{ [enlist<__tabCaptions__>nth] }}}
+ >
+ <$list filter="[match]" variable="ignore">
+ <$button aria-checked="true" class="tc-tab-selected" role="switch">
+ <$action-setfield $tiddler=<<__state__>> $value=<>/>
+ <$text text=<>/>
+ $button>
+ $list>
+ <$list filter="[!match]" variable="ignore">
+ <$button role="switch">
+ <$action-setfield $tiddler=<<__state__>> $value=<>/>
+ <$text text=<>/>
+ $button>
+ $list>
+ $let>
+ $list>
+
+
+
+ <$list filter="[enlist<__tabNames__>]" variable="tab" counter="tabCounter">
+ <$list filter="[ match]" variable="ignore">
+
+ <$macrocall $name=<>/>
+
+ $list>
+ <$list filter="[!match]" variable="ignore">
+
+ $list>
+ $list>
+
+
+$let>
+\end
+
+\define list-archives()
+\whitespace trim
+
+<$list filter="[tag[$:/tags/TwitterArchive]sort[displayname]]">
+ -
+ <$link><$text text=<>/>$link>
+
+$list>
+
+\end
+
+\define show-archive()
+<$let
+ user_id={{!!user_id}}
+>
+
+$let>
+\end
+
+\define show-archive-tweets()
+<$let user_id={{!!user_id}}>
+ <$list filter="[tag[$:/tags/Tweet]field:user_id!sort[created]limit[50]]">
+ <>
+ $list>
+$let>
+\end
+
+\define show-favorited-tweets()
+<$let user_id={{!!user_id}}>
+ <$list filter="[tag[$:/tags/Tweet]field:liked_bylimit[50]]">
+ <>
+ $list>
+$let>
+\end
+
+\define show-archive-attribute(caption,field,prefix,format:"text",template)
+
+
+ <$text text=<<__caption__>>/>
+ |
+
+ <$text text={{{ [<__prefix__>] }}}/>
+ <$view field=<<__field__>> format=<<__format__>> template=<<__template__>>/>
+ |
+
+\end
+
+\define show-archive-calculated-attribute(caption,filter)
+
+
+ <$text text=<<__caption__>>/>
+ |
+
+ <$text text={{{ [subfilter<__filter__>] }}}/>
+ |
+
+\end
+
+\define show-tweet()
+
+\end
+
+\define show-tweet-thread(archive)
+
+\end
+
+\define show-tweeter()
+
+
+ Username | <$text text={{!!screenname}}/> |
+ Display Name | <$text text={{!!name}}/> |
+ User ID | <$text text={{!!user_id}}/> |
+
+
+View on Twitter
+<$macrocall $name="skinny-tabs" tabNames="show-tweeter-mentions" tabCaptions="Mentions" defaultTab="show-tweeter-mentions" state=<>/>
+\end
+
+\define show-tweeter-mentions()
+<$list filter="[tag[$:/tags/Tweet]tag]">
+ <$macrocall $name="show-tweet" archive=<> title=<>/>
+$list>
+\end
diff --git a/plugins/tiddlywiki/twitter-archivist/plugin.info b/plugins/tiddlywiki/twitter-archivist/plugin.info
new file mode 100644
index 000000000..016e746e7
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/plugin.info
@@ -0,0 +1,6 @@
+{
+ "title": "$:/plugins/tiddlywiki/twitter-archivist",
+ "name": "Twitter Archivist",
+ "description": "Twitter archiving tools",
+ "list": "readme"
+}
diff --git a/plugins/tiddlywiki/twitter-archivist/readme.tid b/plugins/tiddlywiki/twitter-archivist/readme.tid
new file mode 100644
index 000000000..f5dad5e6f
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/readme.tid
@@ -0,0 +1,55 @@
+title: $:/plugins/tiddlywiki/twitter-archivist/readme
+
+! Introduction
+
+The Twitter Archivist imports the tweets and associated media from a [[Twitter Archive|https://help.twitter.com/en/managing-your-account/how-to-download-your-twitter-archive]] as individual tiddlers.
+
+The Twitter Archivist plugin is available from the official plugin library for installation in your own wikis.
+
+! Limitations
+
+This initial version of the Twitter Archivist has several shortcomings that may be addressed in the future:
+
+* Does not handle editable tweets
+* Does not handle direct messages
+
+! Limitations of Twitter Archives
+
+The Twitter Archive format itself has many shortcomings which affect this tool:
+
+* Retweets come through as old-school RTs, which means that they are often truncated
+* Likes only have minimal information, lacking date, author and mentions
+* External links go to the t.co shortener
+* Twitter archives can be delivered in multiple parts, but this tool has only been tested with single archives. It is hoped that cumulatively importing each of the archives in turn should work
+
+A future version of this tool may use the Twitter API to get around these restrictions.
+
+! Getting Started
+
+First, request your Tweet archive from Twitter. Once it is available, download and unzip it.
+
+The Twitter Archivist can operate in the browser or under Node.js.
+
+!! In the Browser
+
+To import a Twitter archive in the browser, click the button below and navigate to the root of the archive:
+
+<$button>
+<$action-sendmessage $message="tm-load-twitter-archive"/>
+Open Twitter archive
+$button>
+
+!! Under Node.js
+
+To import a Twitter archive under Node.js, use the `--loadtwitterarchive` command:
+
+```
+tiddlywiki editions/twitter-archivist/ --loadtwitterarchive '/path/to/archive' --build index
+```
+
+! Imported Archives
+
+Any imported archives will show here:
+
+<>
+
diff --git a/plugins/tiddlywiki/twitter-archivist/startup.js b/plugins/tiddlywiki/twitter-archivist/startup.js
new file mode 100644
index 000000000..b87453dda
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/startup.js
@@ -0,0 +1,38 @@
+/*\
+title: $:/plugins/tiddlywiki/twitter-archivist/startup.js
+type: application/javascript
+module-type: startup
+
+Twitter initialisation
+
+\*/
+(function(){
+
+/*jslint node: true, browser: true */
+/*global $tw: false */
+"use strict";
+
+// Export name and synchronous status
+exports.name = "twitter-archivist";
+exports.after = ["startup"];
+exports.synchronous = true;
+
+exports.startup = function() {
+ $tw.rootWidget.addEventListener("tm-load-twitter-archive",function(event) {
+ // Load tweets
+ var archiveSource = new $tw.utils.TwitterArchivistSourceBrowser({
+ }),
+ archivist = new $tw.utils.TwitterArchivist({
+ source: archiveSource
+ });
+ archivist.loadArchive({
+ wiki: $tw.wiki
+ }).then(function() {
+ alert("Archived tweets imported");
+ }).catch(function(err) {
+ alert("Error importing archived tweets: " + err);
+ });
+ });
+};
+
+})();
diff --git a/plugins/tiddlywiki/twitter-archivist/styles.tid b/plugins/tiddlywiki/twitter-archivist/styles.tid
new file mode 100644
index 000000000..7ed95a0c1
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/styles.tid
@@ -0,0 +1,47 @@
+title: $:/plugins/tiddlywiki/twitter-archivist/styles
+tags: [[$:/tags/Stylesheet]]
+code-body: yes
+
+\rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock
+
+.tc-twitter-tweet {
+ border: 1px solid <>;
+ border-radius: 8px;
+ margin: 1em 0;
+ padding: 1em;
+}
+
+.tc-twitter-tweet-reply {
+ font-size: 0.7em;
+}
+
+.tc-twitter-tweet-reply .tc-twitter-tweet {
+ margin: 0.5em 0 0.5em 1em;
+ padding: 0.5em;
+}
+
+.tc-twitter-tweet-header-displayname {
+ font-weight: bold;
+}
+
+.tc-twitter-tweet-header-username,
+.tc-twitter-tweet-header-date {
+ color: #536471;
+}
+
+.tc-twitter-tweet-reply-to {
+ font-size: 0.7em;
+}
+
+.tc-twitter-tweet-body {
+ margin: 0.25em 0;
+ line-height: 1.3;
+}
+
+.tc-twitter-tweet-reply .tc-twitter-tweet-body {
+ margin: 0.5em 0;
+}
+
+.tc-twitter-tweet-footer {
+ font-size: 0.8em;
+}
\ No newline at end of file
diff --git a/plugins/tiddlywiki/twitter-archivist/template-archive.tid b/plugins/tiddlywiki/twitter-archivist/template-archive.tid
new file mode 100644
index 000000000..40e2c7100
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/template-archive.tid
@@ -0,0 +1,3 @@
+title: $:/plugins/tiddlywiki/twitter-archivist/template/archive
+
+<>
diff --git a/plugins/tiddlywiki/twitter-archivist/template-tweet.tid b/plugins/tiddlywiki/twitter-archivist/template-tweet.tid
new file mode 100644
index 000000000..48d0d023b
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/template-tweet.tid
@@ -0,0 +1,3 @@
+title: $:/plugins/tiddlywiki/twitter-archivist/template/tweet
+
+<>
diff --git a/plugins/tiddlywiki/twitter-archivist/template-tweeter.tid b/plugins/tiddlywiki/twitter-archivist/template-tweeter.tid
new file mode 100644
index 000000000..bff644922
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/template-tweeter.tid
@@ -0,0 +1,3 @@
+title: $:/plugins/tiddlywiki/twitter-archivist/template/tweeter
+
+<>
diff --git a/plugins/tiddlywiki/twitter-archivist/view-template-body-cascade.tid b/plugins/tiddlywiki/twitter-archivist/view-template-body-cascade.tid
new file mode 100644
index 000000000..ffc18c3d4
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/view-template-body-cascade.tid
@@ -0,0 +1,7 @@
+title: $:/plugins/tiddlywiki/twitter-archivist/view-template-body-cascade
+tags: $:/tags/ViewTemplateBodyFilter
+list-before:
+
+[tag[$:/tags/Tweet]then[$:/plugins/tiddlywiki/twitter-archivist/template/tweet]]
+[tag[$:/tags/TwitterArchive]then[$:/plugins/tiddlywiki/twitter-archivist/template/archive]]
+[tag[$:/tags/Tweeter]then[$:/plugins/tiddlywiki/twitter-archivist/template/tweeter]]
From 34353f40654946048dfec59d63fc7e9c0a86e700 Mon Sep 17 00:00:00 2001
From: "jeremy@jermolene.com"
Date: Sun, 13 Nov 2022 11:18:47 +0000
Subject: [PATCH 064/937] Twitter Archivist: Fix display of tweet author
Also:
* Cleaner user interface
* Added data model spec
* Added todo list
* Icons for archives
---
.../tiddlers/DefaultTiddlers.tid | 3 +-
.../twitter-archivist/tiddlers/HelloThere.tid | 8 ++-
.../twitter-archivist/Twitter Archives.tid | 5 ++
.../tiddlywiki/twitter-archivist/archivist.js | 2 +
.../tiddlywiki/twitter-archivist/macros.tid | 43 +++++++------
.../tiddlywiki/twitter-archivist/plugin.info | 2 +-
.../tiddlywiki/twitter-archivist/readme.tid | 50 +++++----------
plugins/tiddlywiki/twitter-archivist/spec.tid | 61 +++++++++++++++++++
plugins/tiddlywiki/twitter-archivist/todo.tid | 14 +++++
.../tiddlywiki/twitter-archivist/usage.tid | 18 ++++++
10 files changed, 149 insertions(+), 57 deletions(-)
create mode 100644 plugins/tiddlywiki/twitter-archivist/Twitter Archives.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/spec.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/todo.tid
create mode 100644 plugins/tiddlywiki/twitter-archivist/usage.tid
diff --git a/editions/twitter-archivist/tiddlers/DefaultTiddlers.tid b/editions/twitter-archivist/tiddlers/DefaultTiddlers.tid
index a3c362aff..b88bde5f1 100644
--- a/editions/twitter-archivist/tiddlers/DefaultTiddlers.tid
+++ b/editions/twitter-archivist/tiddlers/DefaultTiddlers.tid
@@ -1,3 +1,4 @@
title: $:/DefaultTiddlers
-HelloThere
\ No newline at end of file
+[[Twitter Archives]]
+HelloThere
diff --git a/editions/twitter-archivist/tiddlers/HelloThere.tid b/editions/twitter-archivist/tiddlers/HelloThere.tid
index d1bb60ca7..3aceef65c 100644
--- a/editions/twitter-archivist/tiddlers/HelloThere.tid
+++ b/editions/twitter-archivist/tiddlers/HelloThere.tid
@@ -1,3 +1,9 @@
title: HelloThere
-{{$:/plugins/tiddlywiki/twitter-archivist/readme}}
+!!! Welcome to the Twitter Archivist for TiddlyWiki
+
+<$tiddler tiddler="$:/plugins/tiddlywiki/twitter-archivist">
+ <$set name="tabsList" filter="[list]">
+ <$macrocall $name="tabs" state=<> tabsList=<> default={{{ [enlist] }}} template="$:/core/ui/PluginInfo"/>
+ $set>
+$tiddler>
diff --git a/plugins/tiddlywiki/twitter-archivist/Twitter Archives.tid b/plugins/tiddlywiki/twitter-archivist/Twitter Archives.tid
new file mode 100644
index 000000000..2b7aca018
--- /dev/null
+++ b/plugins/tiddlywiki/twitter-archivist/Twitter Archives.tid
@@ -0,0 +1,5 @@
+title: Twitter Archives
+color: #1D9CF0
+icon: $:/core/images/twitter
+
+<>
diff --git a/plugins/tiddlywiki/twitter-archivist/archivist.js b/plugins/tiddlywiki/twitter-archivist/archivist.js
index 2162ee556..02384f102 100644
--- a/plugins/tiddlywiki/twitter-archivist/archivist.js
+++ b/plugins/tiddlywiki/twitter-archivist/archivist.js
@@ -29,6 +29,8 @@ TwitterArchivist.prototype.loadArchive = async function(options) {
user_id = manifestData.userInfo.accountId;
wiki.addTiddler({
title: "Twitter Archive for @" + username,
+ icon: "$:/core/images/twitter",
+ color: "#1D9CF0",
tags: "$:/tags/TwitterArchive",
user_id: user_id,
username: username,
diff --git a/plugins/tiddlywiki/twitter-archivist/macros.tid b/plugins/tiddlywiki/twitter-archivist/macros.tid
index c0003274d..a45592c10 100644
--- a/plugins/tiddlywiki/twitter-archivist/macros.tid
+++ b/plugins/tiddlywiki/twitter-archivist/macros.tid
@@ -45,13 +45,22 @@ tags: $:/tags/Macro
\define list-archives()
\whitespace trim
-
-<$list filter="[tag[$:/tags/TwitterArchive]sort[displayname]]">
- -
- <$link><$text text=<>/>$link>
-
+<$list filter="[tag[$:/tags/TwitterArchive]limit[1]]" emptyMessage=<> variable="ignore">
+
+ <$list filter="[tag[$:/tags/TwitterArchive]sort[displayname]]">
+ -
+ <$link><$text text=<>/>$link>
+
+ $list>
+
$list>
-
+\end
+
+\define list-archives-empty()
+
+No Twitter Archives are currently loaded
+
+<$transclude tiddler="$:/plugins/tiddlywiki/twitter-archivist/usage" mode="block"/>
\end
\define show-archive()
@@ -122,24 +131,24 @@ tags: $:/tags/Macro
\define show-tweet()
| | | | | |