From 5b6f5b270471b53e310b70418297cb797523b9a8 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Fri, 15 Sep 2023 22:17:19 +0800 Subject: [PATCH 01/43] feat: parse and show ^id --- .../wikiparser/rules/blockidentifier.js | 39 ++++++++++++++ core/modules/widgets/blockidentifier.js | 52 +++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 core/modules/parsers/wikiparser/rules/blockidentifier.js create mode 100644 core/modules/widgets/blockidentifier.js diff --git a/core/modules/parsers/wikiparser/rules/blockidentifier.js b/core/modules/parsers/wikiparser/rules/blockidentifier.js new file mode 100644 index 000000000..5cc659600 --- /dev/null +++ b/core/modules/parsers/wikiparser/rules/blockidentifier.js @@ -0,0 +1,39 @@ +/*\ +title: $:/core/modules/parsers/wikiparser/rules/blockidentifier.js +type: application/javascript +module-type: wikirule + +Use hash as a tag for paragraph, we call it block identifier. + +1. Hash won't change, it can be written by hand or be generated, and it is a ` \^\S+$` string after line: `text ^cb9d485` or `text ^1`, so it can be human readable (while without space), here are the parse rule for this. +2. When creating widgets for rendering, omit this hash, so it's invisible in view mode. But this widget will create an anchor to jump to. + +\*/ +exports.name = "blockidentifier"; +exports.types = {inline: true}; + +/* +Instantiate parse rule +*/ +exports.init = function(parser) { + this.parser = parser; + // Regexp to match the block identifier located on the end of the line. + this.matchRegExp = /[ ]\^\S+$/mg; +}; + +/* +Parse the most recent match +*/ +exports.parse = function() { + // Move past the match + this.parser.pos = this.matchRegExp.lastIndex; + var id = this.match[0].slice(2); + // Parse tree nodes to return + return [{ + type: "blockidentifier", + attributes: { + id: {type: "string", value: id} + }, + children: [] + }]; +}; diff --git a/core/modules/widgets/blockidentifier.js b/core/modules/widgets/blockidentifier.js new file mode 100644 index 000000000..caad458f2 --- /dev/null +++ b/core/modules/widgets/blockidentifier.js @@ -0,0 +1,52 @@ +/*\ +title: $:/core/modules/widgets/blockidentifier.js +type: application/javascript +module-type: widget + +An invisible element with block id metadata. +\*/ +var Widget = require("$:/core/modules/widgets/widget.js").widget; +var BlockIdentifierWidget = function(parseTreeNode,options) { + this.initialise(parseTreeNode,options); +}; +BlockIdentifierWidget.prototype = new Widget(); + +BlockIdentifierWidget.prototype.render = function(parent,nextSibling) { + // Save the parent dom node + this.parentDomNode = parent; + // Compute our attributes + this.computeAttributes(); + // Execute our logic + this.execute(); + // Create an invisible DOM element with data that can be accessed from JS or CSS + this.spanDomNode = this.document.createElement("span"); + this.spanDomNode.setAttribute("data-id",this.id); + this.spanDomNode.className = "tc-block-id"; + parent.insertBefore(this.spanDomNode,nextSibling); + this.domNodes.push(this.spanDomNode); +}; + +/* +Compute the internal state of the widget +*/ +BlockIdentifierWidget.prototype.execute = function() { + // Get the id from the parse tree node or manually assigned attributes + this.id = this.getAttribute("id"); + // Make the child widgets + this.makeChildWidgets(); +}; + +/* +Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering +*/ +BlockIdentifierWidget.prototype.refresh = function(changedTiddlers) { + var changedAttributes = this.computeAttributes(); + if(($tw.utils.count(changedAttributes) > 0)) { + this.refreshSelf(); + return true; + } else { + return this.refreshChildren(changedTiddlers); + } +}; + +exports.blockidentifier = BlockIdentifierWidget; From 4c407c28e45c1ad4a5a2b8b9b25cf6157d105df2 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Fri, 15 Sep 2023 22:43:23 +0800 Subject: [PATCH 02/43] refactor: use blockid for shorter name --- .../rules/{blockidentifier.js => blockid.js} | 4 ++-- .../widgets/{blockidentifier.js => blockid.js} | 14 +++++++------- 2 files changed, 9 insertions(+), 9 deletions(-) rename core/modules/parsers/wikiparser/rules/{blockidentifier.js => blockid.js} (94%) rename core/modules/widgets/{blockidentifier.js => blockid.js} (75%) diff --git a/core/modules/parsers/wikiparser/rules/blockidentifier.js b/core/modules/parsers/wikiparser/rules/blockid.js similarity index 94% rename from core/modules/parsers/wikiparser/rules/blockidentifier.js rename to core/modules/parsers/wikiparser/rules/blockid.js index 5cc659600..ecc4e2393 100644 --- a/core/modules/parsers/wikiparser/rules/blockidentifier.js +++ b/core/modules/parsers/wikiparser/rules/blockid.js @@ -9,7 +9,7 @@ Use hash as a tag for paragraph, we call it block identifier. 2. When creating widgets for rendering, omit this hash, so it's invisible in view mode. But this widget will create an anchor to jump to. \*/ -exports.name = "blockidentifier"; +exports.name = "blockid"; exports.types = {inline: true}; /* @@ -30,7 +30,7 @@ exports.parse = function() { var id = this.match[0].slice(2); // Parse tree nodes to return return [{ - type: "blockidentifier", + type: "blockid", attributes: { id: {type: "string", value: id} }, diff --git a/core/modules/widgets/blockidentifier.js b/core/modules/widgets/blockid.js similarity index 75% rename from core/modules/widgets/blockidentifier.js rename to core/modules/widgets/blockid.js index caad458f2..0f33c1adb 100644 --- a/core/modules/widgets/blockidentifier.js +++ b/core/modules/widgets/blockid.js @@ -1,17 +1,17 @@ /*\ -title: $:/core/modules/widgets/blockidentifier.js +title: $:/core/modules/widgets/blockid.js type: application/javascript module-type: widget An invisible element with block id metadata. \*/ var Widget = require("$:/core/modules/widgets/widget.js").widget; -var BlockIdentifierWidget = function(parseTreeNode,options) { +var BlockIdWidget = function(parseTreeNode,options) { this.initialise(parseTreeNode,options); }; -BlockIdentifierWidget.prototype = new Widget(); +BlockIdWidget.prototype = new Widget(); -BlockIdentifierWidget.prototype.render = function(parent,nextSibling) { +BlockIdWidget.prototype.render = function(parent,nextSibling) { // Save the parent dom node this.parentDomNode = parent; // Compute our attributes @@ -29,7 +29,7 @@ BlockIdentifierWidget.prototype.render = function(parent,nextSibling) { /* Compute the internal state of the widget */ -BlockIdentifierWidget.prototype.execute = function() { +BlockIdWidget.prototype.execute = function() { // Get the id from the parse tree node or manually assigned attributes this.id = this.getAttribute("id"); // Make the child widgets @@ -39,7 +39,7 @@ BlockIdentifierWidget.prototype.execute = function() { /* Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering */ -BlockIdentifierWidget.prototype.refresh = function(changedTiddlers) { +BlockIdWidget.prototype.refresh = function(changedTiddlers) { var changedAttributes = this.computeAttributes(); if(($tw.utils.count(changedAttributes) > 0)) { this.refreshSelf(); @@ -49,4 +49,4 @@ BlockIdentifierWidget.prototype.refresh = function(changedTiddlers) { } }; -exports.blockidentifier = BlockIdentifierWidget; +exports.blockid = BlockIdWidget; From 18236b547f206d57266f327c76fbc29a404514fd Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 02:38:06 +0800 Subject: [PATCH 03/43] feat: allow add id for code block --- core/modules/parsers/wikiparser/rules/blockid.js | 15 +++++++++++---- core/modules/widgets/blockid.js | 4 ++++ core/ui/EditTemplate/title.tid | 2 +- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/modules/parsers/wikiparser/rules/blockid.js b/core/modules/parsers/wikiparser/rules/blockid.js index ecc4e2393..84a0be41a 100644 --- a/core/modules/parsers/wikiparser/rules/blockid.js +++ b/core/modules/parsers/wikiparser/rules/blockid.js @@ -17,8 +17,10 @@ Instantiate parse rule */ exports.init = function(parser) { this.parser = parser; - // Regexp to match the block identifier located on the end of the line. - this.matchRegExp = /[ ]\^\S+$/mg; + // Regexp to match the block identifier + // 1. located on the end of the line, with a space before it, means it's the id of the current block. + // 2. located at start of the line, no space, means it's the id of the previous block. Because some block can't have id suffix, otherwise id break the block mode parser like codeblock. + this.matchRegExp = /[ ]\^(\S+)$|^\^(\S+)$/mg; }; /* @@ -27,12 +29,17 @@ Parse the most recent match exports.parse = function() { // Move past the match this.parser.pos = this.matchRegExp.lastIndex; - var id = this.match[0].slice(2); + // will be one of following case, another will be undefined + var blockId = this.match[1]; + var blockBeforeId = this.match[2]; // Parse tree nodes to return return [{ type: "blockid", attributes: { - id: {type: "string", value: id} + id: {type: "string", value: blockId || blockBeforeId}, + // `true` means the block is before this node, in parent node's children list. + // `false` means the block is this node's parent node. + before: {type: "boolean", value: Boolean(blockBeforeId)}, }, children: [] }]; diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 0f33c1adb..e5902ee35 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -21,6 +21,9 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { // Create an invisible DOM element with data that can be accessed from JS or CSS this.spanDomNode = this.document.createElement("span"); this.spanDomNode.setAttribute("data-id",this.id); + if(this.before) { + this.spanDomNode.setAttribute("data-before","true"); + } this.spanDomNode.className = "tc-block-id"; parent.insertBefore(this.spanDomNode,nextSibling); this.domNodes.push(this.spanDomNode); @@ -32,6 +35,7 @@ Compute the internal state of the widget BlockIdWidget.prototype.execute = function() { // Get the id from the parse tree node or manually assigned attributes this.id = this.getAttribute("id"); + this.before = this.getAttribute("before"); // Make the child widgets this.makeChildWidgets(); }; diff --git a/core/ui/EditTemplate/title.tid b/core/ui/EditTemplate/title.tid index 5228ad7c0..38e75997a 100644 --- a/core/ui/EditTemplate/title.tid +++ b/core/ui/EditTemplate/title.tid @@ -4,7 +4,7 @@ tags: $:/tags/EditTemplate \whitespace trim <$edit-text field="draft.title" class="tc-titlebar tc-edit-texteditor" focus={{{ [{$:/config/AutoFocus}match[title]then[true]] ~[[false]] }}} tabindex={{$:/config/EditTabIndex}} cancelPopups="yes"/> -<$vars pattern="""[\|\[\]{}]""" bad-chars="""`| [ ] { }`"""> +<$vars pattern="""[\^\|\[\]{}]""" bad-chars="""`| ^ [ ] { }`"""> <$list filter="[all[current]regexp:draft.title]" variable="listItem"> From d5e9d2a71bf6127fa673efcb3c8aab2ee8aa6696 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 02:49:58 +0800 Subject: [PATCH 04/43] feat: allow wiki pretty link to have id --- .../modules/parsers/wikiparser/rules/prettylink.js | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/modules/parsers/wikiparser/rules/prettylink.js b/core/modules/parsers/wikiparser/rules/prettylink.js index 56a2850a3..4d7839979 100644 --- a/core/modules/parsers/wikiparser/rules/prettylink.js +++ b/core/modules/parsers/wikiparser/rules/prettylink.js @@ -23,8 +23,8 @@ exports.types = {inline: true}; exports.init = function(parser) { this.parser = parser; - // Regexp to match - this.matchRegExp = /\[\[(.*?)(?:\|(.*?))?\]\]/mg; + // Regexp to match `[[Title^blockId|Alias]]`, the `^blockId` and `|Alias` are optional. + this.matchRegExp = /\[\[(.*?)(?:\^([^|\s^]+))?(?:\|(.*?))?\]\]/mg; }; exports.parse = function() { @@ -32,8 +32,13 @@ exports.parse = function() { this.parser.pos = this.matchRegExp.lastIndex; // Process the link var text = this.match[1], - link = this.match[2] || text; + blockId = this.match[2] || "", + link = this.match[3] || text; if($tw.utils.isLinkExternal(link)) { + // add back the part after `^` to the ext link, if it happen to has one. + if(blockId) { + link = link + "^" + blockId; + } return [{ type: "element", tag: "a", @@ -51,7 +56,8 @@ exports.parse = function() { return [{ type: "link", attributes: { - to: {type: "string", value: link} + to: {type: "string", value: link}, + id: {type: "string", value: blockId}, }, children: [{ type: "text", text: text From b956e72536389c1a722d26f0668c70600f0ff7d4 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 04:09:21 +0800 Subject: [PATCH 05/43] fix: properly match blockId and pass it to ast --- core/modules/parsers/wikiparser/rules/prettylink.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/modules/parsers/wikiparser/rules/prettylink.js b/core/modules/parsers/wikiparser/rules/prettylink.js index 4d7839979..bfca074f1 100644 --- a/core/modules/parsers/wikiparser/rules/prettylink.js +++ b/core/modules/parsers/wikiparser/rules/prettylink.js @@ -23,8 +23,8 @@ exports.types = {inline: true}; exports.init = function(parser) { this.parser = parser; - // Regexp to match `[[Title^blockId|Alias]]`, the `^blockId` and `|Alias` are optional. - this.matchRegExp = /\[\[(.*?)(?:\^([^|\s^]+))?(?:\|(.*?))?\]\]/mg; + // Regexp to match `[[Alias|Title^blockId]]`, the `Alias|` and `^blockId` are optional. + this.matchRegExp = /\[\[(.*?)(?:\|(.*?)?)?(?:\^([^|\s^]+)?)?\]\]/mg; }; exports.parse = function() { @@ -32,8 +32,8 @@ exports.parse = function() { this.parser.pos = this.matchRegExp.lastIndex; // Process the link var text = this.match[1], - blockId = this.match[2] || "", - link = this.match[3] || text; + link = this.match[2] || text, + blockId = this.match[3] || ""; if($tw.utils.isLinkExternal(link)) { // add back the part after `^` to the ext link, if it happen to has one. if(blockId) { @@ -57,7 +57,7 @@ exports.parse = function() { type: "link", attributes: { to: {type: "string", value: link}, - id: {type: "string", value: blockId}, + toBlockId: {type: "string", value: blockId}, }, children: [{ type: "text", text: text From 3d8ade3ebe6bf29a739f5a478594bec463a50a64 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 04:45:07 +0800 Subject: [PATCH 06/43] feat: redirect tm-focus-selector event to check parent or sibling --- boot/boot.js | 14 +++++++++ .../parsers/wikiparser/rules/blockid.js | 2 +- core/modules/startup/rootwidget.js | 2 ++ core/modules/widgets/blockid.js | 29 +++++++++++++++++-- core/modules/widgets/link.js | 8 +++++ themes/tiddlywiki/vanilla/base.tid | 13 +++++++++ 6 files changed, 65 insertions(+), 3 deletions(-) diff --git a/boot/boot.js b/boot/boot.js index 06d4628c0..2636cb8f4 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -2674,6 +2674,20 @@ $tw.hooks.addHook = function(hookName,definition) { } }; +/* +Delete hooks from the hashmap +*/ +$tw.hooks.removeHook = function(hookName,definition) { + if($tw.utils.hop($tw.hooks.names,hookName)) { + var index = $tw.hooks.names[hookName].findIndex(function(hook) { + return hook === definition; + }); + if(index !== -1) { + $tw.hooks.names[hookName].splice(index, 1); + } + } +}; + /* Invoke the hook by key */ diff --git a/core/modules/parsers/wikiparser/rules/blockid.js b/core/modules/parsers/wikiparser/rules/blockid.js index 84a0be41a..61438981b 100644 --- a/core/modules/parsers/wikiparser/rules/blockid.js +++ b/core/modules/parsers/wikiparser/rules/blockid.js @@ -39,7 +39,7 @@ exports.parse = function() { id: {type: "string", value: blockId || blockBeforeId}, // `true` means the block is before this node, in parent node's children list. // `false` means the block is this node's parent node. - before: {type: "boolean", value: Boolean(blockBeforeId)}, + previousSibling: {type: "boolean", value: Boolean(blockBeforeId)}, }, children: [] }]; diff --git a/core/modules/startup/rootwidget.js b/core/modules/startup/rootwidget.js index 716275cda..580c0e94d 100644 --- a/core/modules/startup/rootwidget.js +++ b/core/modules/startup/rootwidget.js @@ -72,6 +72,8 @@ exports.startup = function() { }); // Install the tm-focus-selector message $tw.rootWidget.addEventListener("tm-focus-selector",function(event) { + event = $tw.hooks.invokeHook("th-focus-selector",event); + if (!event) return; var selector = event.param || "", element, baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index e5902ee35..0d915ab7a 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -18,9 +18,13 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { this.computeAttributes(); // Execute our logic this.execute(); + $tw.hooks.removeHook("th-focus-selector",this.hookFocusElementEvent); + this.hookFocusElementEvent = this.hookFocusElementEvent.bind(this); + $tw.hooks.addHook("th-focus-selector",this.hookFocusElementEvent); // Create an invisible DOM element with data that can be accessed from JS or CSS this.spanDomNode = this.document.createElement("span"); - this.spanDomNode.setAttribute("data-id",this.id); + this.spanDomNode.id = this.id; + this.spanDomNode.setAttribute("data-block-id",this.id); if(this.before) { this.spanDomNode.setAttribute("data-before","true"); } @@ -29,13 +33,34 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { this.domNodes.push(this.spanDomNode); }; +BlockIdWidget.prototype.hookFocusElementEvent = function(event) { + var id = event.param.replace('#',''); + if(id !== this.id) { + return event; + } + var element = this.parentDomNode; + // need to check if the block is before this node + if(this.previousSibling) { + element = element.previousSibling; + } + element.focus({ focusVisible: true }); + // toggle class to trigger highlight animation + $tw.utils.removeClass(element,"tc-focus-highlight"); + $tw.utils.addClass(element,"tc-focus-highlight"); + return false; +}; + +BlockIdWidget.prototype.removeChildDomNodes = function() { + $tw.hooks.removeHook("th-focus-selector",this.hookFocusElementEvent); +}; + /* Compute the internal state of the widget */ BlockIdWidget.prototype.execute = function() { // Get the id from the parse tree node or manually assigned attributes this.id = this.getAttribute("id"); - this.before = this.getAttribute("before"); + this.previousSibling = this.getAttribute("previousSibling"); // Make the child widgets this.makeChildWidgets(); }; diff --git a/core/modules/widgets/link.js b/core/modules/widgets/link.js index 6f199d395..12fc2da23 100755 --- a/core/modules/widgets/link.js +++ b/core/modules/widgets/link.js @@ -167,6 +167,13 @@ LinkWidget.prototype.handleClickEvent = function(event) { shiftKey: event.shiftKey, event: event }); + if(this.toBlockId) { + this.dispatchEvent({ + type: "tm-focus-selector", + param: "#" + this.toBlockId, + event: event, + }); + } if(this.domNodes[0].hasAttribute("href")) { event.preventDefault(); } @@ -180,6 +187,7 @@ Compute the internal state of the widget LinkWidget.prototype.execute = function() { // Pick up our attributes this.to = this.getAttribute("to",this.getVariable("currentTiddler")); + this.toBlockId = this.getAttribute("toBlockId"); this.tooltip = this.getAttribute("tooltip"); this["aria-label"] = this.getAttribute("aria-label"); this.linkClasses = this.getAttribute("class"); diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid index 4603589ae..b7cb1a3fc 100644 --- a/themes/tiddlywiki/vanilla/base.tid +++ b/themes/tiddlywiki/vanilla/base.tid @@ -2413,6 +2413,19 @@ html body.tc-body.tc-single-tiddler-window { color: <>; } +@keyframes fadeHighlight { + 0% { + background-color: <>; + } + 100% { + background-color: transparent; + } +} + +.tc-focus-highlight { + animation: fadeHighlight 2s forwards; +} + @media (min-width: <>) { .tc-static-alert { From 6b6124369cab6249b8be15bb5bb33fec3ad51cd6 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 04:49:50 +0800 Subject: [PATCH 07/43] fix: param maybe null --- core/modules/widgets/blockid.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 0d915ab7a..959b53243 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -34,10 +34,9 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { }; BlockIdWidget.prototype.hookFocusElementEvent = function(event) { + if(!event.param) return event; var id = event.param.replace('#',''); - if(id !== this.id) { - return event; - } + if(id !== this.id) return event; var element = this.parentDomNode; // need to check if the block is before this node if(this.previousSibling) { From d8bdd09eb0f21d0e30aa42164aecd8462e7e9bc9 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 05:00:10 +0800 Subject: [PATCH 08/43] fix: ensure hightlight is visible --- core/modules/widgets/blockid.js | 5 ++++- themes/tiddlywiki/vanilla/base.tid | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 959b53243..eaec7a537 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -45,7 +45,10 @@ BlockIdWidget.prototype.hookFocusElementEvent = function(event) { element.focus({ focusVisible: true }); // toggle class to trigger highlight animation $tw.utils.removeClass(element,"tc-focus-highlight"); - $tw.utils.addClass(element,"tc-focus-highlight"); + // Using setTimeout to ensure the removal takes effect before adding the class again. + setTimeout(function() { + $tw.utils.addClass(element,"tc-focus-highlight"); + }, 50); return false; }; diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid index b7cb1a3fc..e957ea0f9 100644 --- a/themes/tiddlywiki/vanilla/base.tid +++ b/themes/tiddlywiki/vanilla/base.tid @@ -2416,9 +2416,11 @@ html body.tc-body.tc-single-tiddler-window { @keyframes fadeHighlight { 0% { background-color: <>; + border-color: <>; } 100% { background-color: transparent; + border-color: transparent; } } From 0863916f9b0cf654b64b4ba7907ef0372e40862e Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 05:13:40 +0800 Subject: [PATCH 09/43] fix: wait until animation finish and dom show --- core/modules/widgets/blockid.js | 9 +++++++-- core/modules/widgets/link.js | 14 +++++++++----- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index eaec7a537..a7d361bff 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -37,9 +37,14 @@ BlockIdWidget.prototype.hookFocusElementEvent = function(event) { if(!event.param) return event; var id = event.param.replace('#',''); if(id !== this.id) return event; - var element = this.parentDomNode; + var selector = event.param || "", + element, + baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; + element = $tw.utils.querySelectorSafe(selector,baseElement) || this.spanDomNode; + if(!element.parentNode) return; + element = element.parentNode; // need to check if the block is before this node - if(this.previousSibling) { + if(this.previousSibling && element.previousSibling) { element = element.previousSibling; } element.focus({ focusVisible: true }); diff --git a/core/modules/widgets/link.js b/core/modules/widgets/link.js index 12fc2da23..6bb3aa4d2 100755 --- a/core/modules/widgets/link.js +++ b/core/modules/widgets/link.js @@ -168,11 +168,15 @@ LinkWidget.prototype.handleClickEvent = function(event) { event: event }); if(this.toBlockId) { - this.dispatchEvent({ - type: "tm-focus-selector", - param: "#" + this.toBlockId, - event: event, - }); + var duration = $tw.utils.getAnimationDuration(); + var self = this; + setTimeout(function() { + self.dispatchEvent({ + type: "tm-focus-selector", + param: "#" + self.toBlockId, + event: event, + }); + },duration); } if(this.domNodes[0].hasAttribute("href")) { event.preventDefault(); From e6445b79c50afa5f327478cbd4d2ab728ed8ea3d Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 13:40:55 +0800 Subject: [PATCH 10/43] docs: why add hook --- core/modules/widgets/blockid.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index a7d361bff..15db5c095 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -8,6 +8,9 @@ An invisible element with block id metadata. var Widget = require("$:/core/modules/widgets/widget.js").widget; var BlockIdWidget = function(parseTreeNode,options) { this.initialise(parseTreeNode,options); + // only this widget knows target info (if the block is before this node or not), so we need to hook the focus event, and process it here, instead of in the root widget. + this.hookFocusElementEvent = this.hookFocusElementEvent.bind(this); + $tw.hooks.addHook("th-focus-selector",this.hookFocusElementEvent); }; BlockIdWidget.prototype = new Widget(); @@ -18,9 +21,6 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { this.computeAttributes(); // Execute our logic this.execute(); - $tw.hooks.removeHook("th-focus-selector",this.hookFocusElementEvent); - this.hookFocusElementEvent = this.hookFocusElementEvent.bind(this); - $tw.hooks.addHook("th-focus-selector",this.hookFocusElementEvent); // Create an invisible DOM element with data that can be accessed from JS or CSS this.spanDomNode = this.document.createElement("span"); this.spanDomNode.id = this.id; @@ -40,7 +40,7 @@ BlockIdWidget.prototype.hookFocusElementEvent = function(event) { var selector = event.param || "", element, baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; - element = $tw.utils.querySelectorSafe(selector,baseElement) || this.spanDomNode; + element = $tw.utils.querySelectorSafe(selector,baseElement); if(!element.parentNode) return; element = element.parentNode; // need to check if the block is before this node From db83401a69a6a72e69dd73c0f954280745910da6 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 14:26:37 +0800 Subject: [PATCH 11/43] docs: about usage --- .../parsers/wikiparser/rules/blockid.js | 6 +-- core/modules/widgets/blockid.js | 6 +-- .../tiddlers/widgets/BlockIdWidget.tid | 39 ++++++++++++++++ .../Block Level Links in WikiText.tid | 44 +++++++++++++++++++ .../tiddlers/wikitext/Linking in WikiText.tid | 6 +++ 5 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid create mode 100644 editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid diff --git a/core/modules/parsers/wikiparser/rules/blockid.js b/core/modules/parsers/wikiparser/rules/blockid.js index 61438981b..57da17d79 100644 --- a/core/modules/parsers/wikiparser/rules/blockid.js +++ b/core/modules/parsers/wikiparser/rules/blockid.js @@ -37,9 +37,9 @@ exports.parse = function() { type: "blockid", attributes: { id: {type: "string", value: blockId || blockBeforeId}, - // `true` means the block is before this node, in parent node's children list. - // `false` means the block is this node's parent node. - previousSibling: {type: "boolean", value: Boolean(blockBeforeId)}, + // `yes` means the block is before this node, in parent node's children list. + // empty means the block is this node's direct parent node. + previousSibling: {type: "string", value: Boolean(blockBeforeId) ? "yes" : ""}, }, children: [] }]; diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 15db5c095..47ce11722 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -34,14 +34,14 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { }; BlockIdWidget.prototype.hookFocusElementEvent = function(event) { - if(!event.param) return event; + if(!event || !event.param) return event; var id = event.param.replace('#',''); if(id !== this.id) return event; var selector = event.param || "", element, baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; element = $tw.utils.querySelectorSafe(selector,baseElement); - if(!element.parentNode) return; + if(!element || !element.parentNode) return; element = element.parentNode; // need to check if the block is before this node if(this.previousSibling && element.previousSibling) { @@ -67,7 +67,7 @@ Compute the internal state of the widget BlockIdWidget.prototype.execute = function() { // Get the id from the parse tree node or manually assigned attributes this.id = this.getAttribute("id"); - this.previousSibling = this.getAttribute("previousSibling"); + this.previousSibling = this.getAttribute("previousSibling") === "yes"; // Make the child widgets this.makeChildWidgets(); }; diff --git a/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid b/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid new file mode 100644 index 000000000..ab8a5e230 --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid @@ -0,0 +1,39 @@ +caption: block id +created: 20230916061829840 +modified: 20230916062346854 +tags: Widgets +title: BlockIdWidget +type: text/vnd.tiddlywiki + +! Introduction + +The block id widget make an anchor that can be focused and jump to. + +! Content and Attributes + +The content of the `<$blockid>` widget is ignored. + +|!Attribute |!Description | +|id |The unique id for the block | +|previousSibling |`yes` means the block is before this node, in parent node's children list, else it means the block is this node's direct parent node. | + +See [[Block Level Links in WikiText^block091606]] for WikiText syntax of block ID. + +! Example + +< + +[[exampleid1|BlockIdWidget^exampleid1]] +""">> + +<<$blockid id="exampleid2" previousSibling="yes"/>

+ +[[exampleid2|BlockIdWidget^exampleid2]] +""">> + +< + +[[exampleid1|BlockIdWidget^exampleid1]] +""">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid new file mode 100644 index 000000000..f460d1c9b --- /dev/null +++ b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid @@ -0,0 +1,44 @@ +caption: Block Level Links +created: 20230916061138153 +modified: 20230916062623280 +tags: WikiText +title: Block Level Links in WikiText +type: text/vnd.tiddlywiki + +! Adding ID for block ^block091606 + +The basic syntax for block id is: + +<> + +# Don't forget the space between the end of the line and the `^`. +# And there is no space between `^` and the id. +# ID should be a [ext[valid HTML element ID|https://developer.mozilla.org/docs/Web/HTML/Global_attributes/id]], to avoid inadvertent errors, only ASCII letters, digits, '_', and '-' should be used, and the value for an id attribute should start with a letter. + +And this block id widget will be rendered as an invisible element: + +```html + +``` + +!! Adding id to previous block + +Some block, for example, code block, can't be suffixed by `^id`, but we can add the id in the next line, with no space prefix to it. + +<> + +! Link to the block ID + +Adding `^blockID` after the title in the link, will make this link highlight the block with that ID. + +<> + +<> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid index b64d0806a..6d401aad6 100644 --- a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid @@ -123,3 +123,9 @@ See also another example of [[constructing dynamic links|Concatenating text and In TiddlyWiki anchor links can help us link to target points and distinct sections within rendered tiddlers. They can help the reader navigate longer tiddler content. See [[Anchor Links using HTML]] for more information. + +! Linking within tiddlers - Link to block + +You can link to a specific block within a tiddler using `^blockId` syntax. You will also get block level backlinks with this technique. Some examples are in [[BlockIdWidget^exampleid1]]. + +See [[Block Level Links in WikiText]] for more information. From 7200f73cdc0f1bd99e46b9333dc84ec2b4371366 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 15:12:56 +0800 Subject: [PATCH 12/43] refactor: use th-navigated to simplify the code --- core/modules/widgets/blockid.js | 28 ++++++++++--------- core/modules/widgets/link.js | 14 ++-------- core/modules/widgets/navigator.js | 1 + .../dev/tiddlers/new/Hook__th-navigated.tid | 11 ++++++++ .../Block Level Links in WikiText.tid | 4 +-- .../tiddlers/wikitext/Linking in WikiText.tid | 4 +-- 6 files changed, 33 insertions(+), 29 deletions(-) create mode 100644 editions/dev/tiddlers/new/Hook__th-navigated.tid diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 47ce11722..9aef2a3a5 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -9,8 +9,8 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget; var BlockIdWidget = function(parseTreeNode,options) { this.initialise(parseTreeNode,options); // only this widget knows target info (if the block is before this node or not), so we need to hook the focus event, and process it here, instead of in the root widget. - this.hookFocusElementEvent = this.hookFocusElementEvent.bind(this); - $tw.hooks.addHook("th-focus-selector",this.hookFocusElementEvent); + this.hookNavigatedEvent = this.hookNavigatedEvent.bind(this); + $tw.hooks.addHook("th-navigated",this.hookNavigatedEvent); }; BlockIdWidget.prototype = new Widget(); @@ -33,32 +33,34 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { this.domNodes.push(this.spanDomNode); }; -BlockIdWidget.prototype.hookFocusElementEvent = function(event) { - if(!event || !event.param) return event; - var id = event.param.replace('#',''); - if(id !== this.id) return event; - var selector = event.param || "", - element, - baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; - element = $tw.utils.querySelectorSafe(selector,baseElement); +BlockIdWidget.prototype.hookNavigatedEvent = function(event) { + if(!event || !event.toBlockId) return event; + if(event.toBlockId !== this.id) return event; + var selector = "#"+event.toBlockId; + var baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; + // re-query the dom node, because `this.spanDomNode.parentNode` might already be removed from document + var element = $tw.utils.querySelectorSafe(selector,baseElement); if(!element || !element.parentNode) return; + // the actual block is always at the parent level element = element.parentNode; // need to check if the block is before this node if(this.previousSibling && element.previousSibling) { element = element.previousSibling; } - element.focus({ focusVisible: true }); // toggle class to trigger highlight animation $tw.utils.removeClass(element,"tc-focus-highlight"); - // Using setTimeout to ensure the removal takes effect before adding the class again. + // we have enabled `navigateSuppressNavigation` to avoid collision with scroll effect of `tm-navigate` + element.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" }); + element.focus({ focusVisible: true }); setTimeout(function() { + // Using setTimeout to ensure the removal takes effect before adding the class again. $tw.utils.addClass(element,"tc-focus-highlight"); }, 50); return false; }; BlockIdWidget.prototype.removeChildDomNodes = function() { - $tw.hooks.removeHook("th-focus-selector",this.hookFocusElementEvent); + $tw.hooks.removeHook("th-focus-selector",this.hookNavigatedEvent); }; /* diff --git a/core/modules/widgets/link.js b/core/modules/widgets/link.js index 6bb3aa4d2..372a45beb 100755 --- a/core/modules/widgets/link.js +++ b/core/modules/widgets/link.js @@ -150,6 +150,7 @@ LinkWidget.prototype.handleClickEvent = function(event) { this.dispatchEvent({ type: "tm-navigate", navigateTo: this.to, + toBlockId: this.toBlockId, navigateFromTitle: this.getVariable("storyTiddler"), navigateFromNode: this, navigateFromClientRect: { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height @@ -160,24 +161,13 @@ LinkWidget.prototype.handleClickEvent = function(event) { navigateFromClientRight: bounds.right, navigateFromClientBottom: bounds.bottom, navigateFromClientHeight: bounds.height, - navigateSuppressNavigation: event.metaKey || event.ctrlKey || (event.button === 1), + navigateSuppressNavigation: this.toBlockId || event.metaKey || event.ctrlKey || (event.button === 1), metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, shiftKey: event.shiftKey, event: event }); - if(this.toBlockId) { - var duration = $tw.utils.getAnimationDuration(); - var self = this; - setTimeout(function() { - self.dispatchEvent({ - type: "tm-focus-selector", - param: "#" + self.toBlockId, - event: event, - }); - },duration); - } if(this.domNodes[0].hasAttribute("href")) { event.preventDefault(); } diff --git a/core/modules/widgets/navigator.js b/core/modules/widgets/navigator.js index efdbba83f..1ce51d302 100755 --- a/core/modules/widgets/navigator.js +++ b/core/modules/widgets/navigator.js @@ -154,6 +154,7 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) { this.addToHistory(event.navigateTo,event.navigateFromClientRect); } } + $tw.hooks.invokeHook("th-navigated",event); return false; }; diff --git a/editions/dev/tiddlers/new/Hook__th-navigated.tid b/editions/dev/tiddlers/new/Hook__th-navigated.tid new file mode 100644 index 000000000..0315529ba --- /dev/null +++ b/editions/dev/tiddlers/new/Hook__th-navigated.tid @@ -0,0 +1,11 @@ +tags: HookMechanism +title: Hook: th-navigated +type: text/vnd.tiddlywiki + +This hook allows plugins to do things after navigation takes effect. + +Hook function parameters are same as [[Hook: th-navigating]]: + +Return value: + +* possibly modified event object diff --git a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid index f460d1c9b..6b553fc49 100644 --- a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid @@ -1,6 +1,6 @@ caption: Block Level Links created: 20230916061138153 -modified: 20230916062623280 +modified: 20230916070710392 tags: WikiText title: Block Level Links in WikiText type: text/vnd.tiddlywiki @@ -35,7 +35,7 @@ Some block, for example, code block, can't be suffixed by `^id`, but we can add ">> -! Link to the block ID +! Link to the block ID ^linkto091607 Adding `^blockID` after the title in the link, will make this link highlight the block with that ID. diff --git a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid index 6d401aad6..b7aa53fab 100644 --- a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid @@ -1,6 +1,6 @@ caption: Linking created: 20131205155230596 -modified: 20211230145939554 +modified: 20230916070722108 tags: WikiText title: Linking in WikiText type: text/vnd.tiddlywiki @@ -128,4 +128,4 @@ See [[Anchor Links using HTML]] for more information. You can link to a specific block within a tiddler using `^blockId` syntax. You will also get block level backlinks with this technique. Some examples are in [[BlockIdWidget^exampleid1]]. -See [[Block Level Links in WikiText]] for more information. +See [[Block Level Links in WikiText^linkto091607]] for more information. From c0b6b7988ab05246f25cfc5a347e29be36a86cbd Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 15:22:42 +0800 Subject: [PATCH 13/43] fix: element not exist --- core/modules/widgets/blockid.js | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 9aef2a3a5..0118a7e84 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -36,8 +36,27 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { BlockIdWidget.prototype.hookNavigatedEvent = function(event) { if(!event || !event.toBlockId) return event; if(event.toBlockId !== this.id) return event; - var selector = "#"+event.toBlockId; var baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; + var duration = $tw.utils.getAnimationDuration(); + var self = this; + // we have enabled `navigateSuppressNavigation` to avoid collision with scroll effect of `tm-navigate`, but need to wait for tiddler dom stably added to the story view. + setTimeout(function() { + var element = self._getTargetElement(baseElement); + if(!element) return; + // toggle class to trigger highlight animation + $tw.utils.removeClass(element,"tc-focus-highlight"); + element.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" }); + element.focus({ focusVisible: true }); + // Using setTimeout to ensure the removal takes effect before adding the class again. + setTimeout(function() { + $tw.utils.addClass(element,"tc-focus-highlight"); + }, 50); + }, duration); + return false; +}; + +BlockIdWidget.prototype._getTargetElement = function(baseElement) { + var selector = "#"+this.id; // re-query the dom node, because `this.spanDomNode.parentNode` might already be removed from document var element = $tw.utils.querySelectorSafe(selector,baseElement); if(!element || !element.parentNode) return; @@ -47,16 +66,7 @@ BlockIdWidget.prototype.hookNavigatedEvent = function(event) { if(this.previousSibling && element.previousSibling) { element = element.previousSibling; } - // toggle class to trigger highlight animation - $tw.utils.removeClass(element,"tc-focus-highlight"); - // we have enabled `navigateSuppressNavigation` to avoid collision with scroll effect of `tm-navigate` - element.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" }); - element.focus({ focusVisible: true }); - setTimeout(function() { - // Using setTimeout to ensure the removal takes effect before adding the class again. - $tw.utils.addClass(element,"tc-focus-highlight"); - }, 50); - return false; + return element; }; BlockIdWidget.prototype.removeChildDomNodes = function() { From 3bcd822c97da73845ff19450e46ec5da6c956306 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 15:33:03 +0800 Subject: [PATCH 14/43] fix: scroll too slow if tiddler already appear --- core/modules/widgets/blockid.js | 39 +++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 0118a7e84..1138fdd03 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -37,21 +37,19 @@ BlockIdWidget.prototype.hookNavigatedEvent = function(event) { if(!event || !event.toBlockId) return event; if(event.toBlockId !== this.id) return event; var baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; - var duration = $tw.utils.getAnimationDuration(); - var self = this; - // we have enabled `navigateSuppressNavigation` to avoid collision with scroll effect of `tm-navigate`, but need to wait for tiddler dom stably added to the story view. - setTimeout(function() { - var element = self._getTargetElement(baseElement); - if(!element) return; - // toggle class to trigger highlight animation - $tw.utils.removeClass(element,"tc-focus-highlight"); - element.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" }); - element.focus({ focusVisible: true }); - // Using setTimeout to ensure the removal takes effect before adding the class again. + var element = this._getTargetElement(baseElement); + if(element) { + // if tiddler is already in the story view, just move to it. + this._scrollToBlockAndHighlight(element); + } else { + var self = this; + // Here we still need to wait for extra time after `duration`, so tiddler dom is actually added to the story view. + var duration = $tw.utils.getAnimationDuration() + 50; setTimeout(function() { - $tw.utils.addClass(element,"tc-focus-highlight"); - }, 50); - }, duration); + element = self._getTargetElement(baseElement); + self._scrollToBlockAndHighlight(element); + }, duration); + } return false; }; @@ -69,6 +67,19 @@ BlockIdWidget.prototype._getTargetElement = function(baseElement) { return element; }; +BlockIdWidget.prototype._scrollToBlockAndHighlight = function(element) { + if(!element) return; + // toggle class to trigger highlight animation + $tw.utils.removeClass(element,"tc-focus-highlight"); + // We enable the `navigateSuppressNavigation` in LinkWidget when sending `tm-navigate`, otherwise `tm-navigate` will force move to the title + element.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" }); + element.focus({ focusVisible: true }); + // Using setTimeout to ensure the removal takes effect before adding the class again. + setTimeout(function() { + $tw.utils.addClass(element,"tc-focus-highlight"); + }, 50); +}; + BlockIdWidget.prototype.removeChildDomNodes = function() { $tw.hooks.removeHook("th-focus-selector",this.hookNavigatedEvent); }; From 07130c2ad094dcb04c65fb8ce8496a40afb8a2ac Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 16 Sep 2023 15:37:46 +0800 Subject: [PATCH 15/43] fix: code style and types --- core/modules/startup/rootwidget.js | 2 -- core/modules/widgets/blockid.js | 2 +- themes/tiddlywiki/vanilla/base.tid | 4 ++-- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/core/modules/startup/rootwidget.js b/core/modules/startup/rootwidget.js index 580c0e94d..716275cda 100644 --- a/core/modules/startup/rootwidget.js +++ b/core/modules/startup/rootwidget.js @@ -72,8 +72,6 @@ exports.startup = function() { }); // Install the tm-focus-selector message $tw.rootWidget.addEventListener("tm-focus-selector",function(event) { - event = $tw.hooks.invokeHook("th-focus-selector",event); - if (!event) return; var selector = event.param || "", element, baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 1138fdd03..916793ba5 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -81,7 +81,7 @@ BlockIdWidget.prototype._scrollToBlockAndHighlight = function(element) { }; BlockIdWidget.prototype.removeChildDomNodes = function() { - $tw.hooks.removeHook("th-focus-selector",this.hookNavigatedEvent); + $tw.hooks.removeHook("th-navigated",this.hookNavigatedEvent); }; /* diff --git a/themes/tiddlywiki/vanilla/base.tid b/themes/tiddlywiki/vanilla/base.tid index e957ea0f9..f6de39f2a 100644 --- a/themes/tiddlywiki/vanilla/base.tid +++ b/themes/tiddlywiki/vanilla/base.tid @@ -2413,7 +2413,7 @@ html body.tc-body.tc-single-tiddler-window { color: <>; } -@keyframes fadeHighlight { +@keyframes fade-highlight { 0% { background-color: <>; border-color: <>; @@ -2425,7 +2425,7 @@ html body.tc-body.tc-single-tiddler-window { } .tc-focus-highlight { - animation: fadeHighlight 2s forwards; + animation: fade-highlight 2s forwards; } @media (min-width: <>) { From a5c2f8558e3f9b64b7a48181d3b9df7ec83bf373 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sun, 17 Sep 2023 20:03:15 +0800 Subject: [PATCH 16/43] feat: allow different tiddler have same block id in the text, and only jump to the one with a same tiddler title. --- core/modules/widgets/blockid.js | 23 ++++++++++++------- .../tiddlers/widgets/BlockIdWidget.tid | 17 +++++--------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 916793ba5..42f096f4a 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -22,20 +22,22 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { // Execute our logic this.execute(); // Create an invisible DOM element with data that can be accessed from JS or CSS - this.spanDomNode = this.document.createElement("span"); - this.spanDomNode.id = this.id; - this.spanDomNode.setAttribute("data-block-id",this.id); + this.idNode = this.document.createElement("span"); + this.idNode.id = this.id; + this.idNode.setAttribute("data-block-id",this.id); + this.idNode.setAttribute("data-block-title",this.tiddlerTitle); if(this.before) { - this.spanDomNode.setAttribute("data-before","true"); + this.idNode.setAttribute("data-before","true"); } - this.spanDomNode.className = "tc-block-id"; - parent.insertBefore(this.spanDomNode,nextSibling); - this.domNodes.push(this.spanDomNode); + this.idNode.className = "tc-block-id"; + parent.insertBefore(this.idNode,nextSibling); + this.domNodes.push(this.idNode); }; BlockIdWidget.prototype.hookNavigatedEvent = function(event) { if(!event || !event.toBlockId) return event; if(event.toBlockId !== this.id) return event; + if(this.tiddlerTitle && event.navigateTo !== this.tiddlerTitle) return event; var baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; var element = this._getTargetElement(baseElement); if(element) { @@ -55,7 +57,11 @@ BlockIdWidget.prototype.hookNavigatedEvent = function(event) { BlockIdWidget.prototype._getTargetElement = function(baseElement) { var selector = "#"+this.id; - // re-query the dom node, because `this.spanDomNode.parentNode` might already be removed from document + if(this.tiddlerTitle) { + // allow different tiddler have same block id in the text, and only jump to the one with a same tiddler title. + selector += "[data-block-title='"+this.tiddlerTitle+"']"; + } + // re-query the dom node, because `this.idNode.parentNode` might already be removed from document var element = $tw.utils.querySelectorSafe(selector,baseElement); if(!element || !element.parentNode) return; // the actual block is always at the parent level @@ -90,6 +96,7 @@ Compute the internal state of the widget BlockIdWidget.prototype.execute = function() { // Get the id from the parse tree node or manually assigned attributes this.id = this.getAttribute("id"); + this.tiddlerTitle = this.getVariable("currentTiddler"); this.previousSibling = this.getAttribute("previousSibling") === "yes"; // Make the child widgets this.makeChildWidgets(); diff --git a/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid b/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid index ab8a5e230..47d0cb177 100644 --- a/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid @@ -1,6 +1,6 @@ caption: block id created: 20230916061829840 -modified: 20230916062346854 +modified: 20230917120133519 tags: Widgets title: BlockIdWidget type: text/vnd.tiddlywiki @@ -21,19 +21,14 @@ See [[Block Level Links in WikiText^block091606]] for WikiText syntax of block I ! Example -< +< -[[exampleid1|BlockIdWidget^exampleid1]] +[[Link to BlockLevelLinksID1|BlockIdWidget^BlockLevelLinksID1]] """>> -<<$blockid id="exampleid2" previousSibling="yes"/>

+ID is here:<$blockid id="BlockLevelLinksID2" previousSibling="yes"/> -[[exampleid2|BlockIdWidget^exampleid2]] +[[Link to BlockLevelLinksID2|BlockIdWidget^BlockLevelLinksID2]] """>> - -< - -[[exampleid1|BlockIdWidget^exampleid1]] -""">> \ No newline at end of file From cff0240ac8e5adcff8b3321bd270f30f4e29ee91 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sun, 17 Sep 2023 20:11:59 +0800 Subject: [PATCH 17/43] feat: allow using any char in id --- core/modules/widgets/blockid.js | 3 +-- editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid | 4 ++-- .../wikitext/Block Level Links in WikiText.tid | 12 +++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index 42f096f4a..ea27b4d0a 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -23,7 +23,6 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { this.execute(); // Create an invisible DOM element with data that can be accessed from JS or CSS this.idNode = this.document.createElement("span"); - this.idNode.id = this.id; this.idNode.setAttribute("data-block-id",this.id); this.idNode.setAttribute("data-block-title",this.tiddlerTitle); if(this.before) { @@ -56,7 +55,7 @@ BlockIdWidget.prototype.hookNavigatedEvent = function(event) { }; BlockIdWidget.prototype._getTargetElement = function(baseElement) { - var selector = "#"+this.id; + var selector = "span[data-block-id='"+this.id+"']"; if(this.tiddlerTitle) { // allow different tiddler have same block id in the text, and only jump to the one with a same tiddler title. selector += "[data-block-title='"+this.tiddlerTitle+"']"; diff --git a/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid b/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid index 47d0cb177..6f7d6a556 100644 --- a/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid @@ -1,6 +1,6 @@ caption: block id created: 20230916061829840 -modified: 20230917120133519 +modified: 20230917121007649 tags: Widgets title: BlockIdWidget type: text/vnd.tiddlywiki @@ -17,7 +17,7 @@ The content of the `<$blockid>` widget is ignored. |id |The unique id for the block | |previousSibling |`yes` means the block is before this node, in parent node's children list, else it means the block is this node's direct parent node. | -See [[Block Level Links in WikiText^block091606]] for WikiText syntax of block ID. +See [[Block Level Links in WikiText^🤗→AddingIDforblock]] for WikiText syntax of block ID. ! Example diff --git a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid index 6b553fc49..3e0b32964 100644 --- a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid @@ -1,11 +1,11 @@ caption: Block Level Links created: 20230916061138153 -modified: 20230916070710392 +modified: 20230917121127276 tags: WikiText title: Block Level Links in WikiText type: text/vnd.tiddlywiki -! Adding ID for block ^block091606 +! Adding ID for block ^🤗→AddingIDforblock The basic syntax for block id is: @@ -13,12 +13,12 @@ The basic syntax for block id is: # Don't forget the space between the end of the line and the `^`. # And there is no space between `^` and the id. -# ID should be a [ext[valid HTML element ID|https://developer.mozilla.org/docs/Web/HTML/Global_attributes/id]], to avoid inadvertent errors, only ASCII letters, digits, '_', and '-' should be used, and the value for an id attribute should start with a letter. +# ID can contain any char other than `^` and space ` `. And this block id widget will be rendered as an invisible element: ```html - + ``` !! Adding id to previous block @@ -41,4 +41,6 @@ Adding `^blockID` after the title in the link, will make this link highlight the <> -<> \ No newline at end of file +<> + +<> \ No newline at end of file From fef444cb1ce3e6ef62aeddfb53c62349c6343805 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sun, 17 Sep 2023 20:25:40 +0800 Subject: [PATCH 18/43] fix: when id not exist, still navigate to the tiddler --- core/modules/widgets/blockid.js | 30 ++++++++++++++----- core/modules/widgets/link.js | 2 +- core/modules/widgets/navigator.js | 1 + .../Block Level Links in WikiText.tid | 4 +-- .../tiddlers/wikitext/Linking in WikiText.tid | 4 +-- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index ea27b4d0a..aa04deee5 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -9,11 +9,18 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget; var BlockIdWidget = function(parseTreeNode,options) { this.initialise(parseTreeNode,options); // only this widget knows target info (if the block is before this node or not), so we need to hook the focus event, and process it here, instead of in the root widget. + this.hookNavigationAddHistoryEvent = this.hookNavigationAddHistoryEvent.bind(this); this.hookNavigatedEvent = this.hookNavigatedEvent.bind(this); + $tw.hooks.addHook("th-navigating-add-history",this.hookNavigationAddHistoryEvent); $tw.hooks.addHook("th-navigated",this.hookNavigatedEvent); }; BlockIdWidget.prototype = new Widget(); +BlockIdWidget.prototype.removeChildDomNodes = function() { + $tw.hooks.removeHook("th-navigating-add-history",this.hookNavigationAddHistoryEvent); + $tw.hooks.removeHook("th-navigated",this.hookNavigatedEvent); +}; + BlockIdWidget.prototype.render = function(parent,nextSibling) { // Save the parent dom node this.parentDomNode = parent; @@ -33,10 +40,15 @@ BlockIdWidget.prototype.render = function(parent,nextSibling) { this.domNodes.push(this.idNode); }; +BlockIdWidget.prototype._isNavigateToHere = function(event) { + if(!event || !event.toBlockId) return false; + if(event.toBlockId !== this.id) return false; + if(this.tiddlerTitle && event.navigateTo !== this.tiddlerTitle) return false; + return true; +} + BlockIdWidget.prototype.hookNavigatedEvent = function(event) { - if(!event || !event.toBlockId) return event; - if(event.toBlockId !== this.id) return event; - if(this.tiddlerTitle && event.navigateTo !== this.tiddlerTitle) return event; + if(!this._isNavigateToHere(event)) return event; var baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; var element = this._getTargetElement(baseElement); if(element) { @@ -54,6 +66,14 @@ BlockIdWidget.prototype.hookNavigatedEvent = function(event) { return false; }; +BlockIdWidget.prototype.hookNavigationAddHistoryEvent = function(event) { + // DEBUG: console this._isNavigateToHere(event) + console.log(`this._isNavigateToHere(event)`, this._isNavigateToHere(event)); + if(!this._isNavigateToHere(event)) return event; + event.navigateSuppressNavigation = true; + return event; +}; + BlockIdWidget.prototype._getTargetElement = function(baseElement) { var selector = "span[data-block-id='"+this.id+"']"; if(this.tiddlerTitle) { @@ -85,10 +105,6 @@ BlockIdWidget.prototype._scrollToBlockAndHighlight = function(element) { }, 50); }; -BlockIdWidget.prototype.removeChildDomNodes = function() { - $tw.hooks.removeHook("th-navigated",this.hookNavigatedEvent); -}; - /* Compute the internal state of the widget */ diff --git a/core/modules/widgets/link.js b/core/modules/widgets/link.js index 372a45beb..f336f6463 100755 --- a/core/modules/widgets/link.js +++ b/core/modules/widgets/link.js @@ -161,7 +161,7 @@ LinkWidget.prototype.handleClickEvent = function(event) { navigateFromClientRight: bounds.right, navigateFromClientBottom: bounds.bottom, navigateFromClientHeight: bounds.height, - navigateSuppressNavigation: this.toBlockId || event.metaKey || event.ctrlKey || (event.button === 1), + navigateSuppressNavigation: event.metaKey || event.ctrlKey || (event.button === 1), metaKey: event.metaKey, ctrlKey: event.ctrlKey, altKey: event.altKey, diff --git a/core/modules/widgets/navigator.js b/core/modules/widgets/navigator.js index 1ce51d302..84d69dd6b 100755 --- a/core/modules/widgets/navigator.js +++ b/core/modules/widgets/navigator.js @@ -150,6 +150,7 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) { event = $tw.hooks.invokeHook("th-navigating",event); if(event.navigateTo) { this.addToStory(event.navigateTo,event.navigateFromTitle); + event = $tw.hooks.invokeHook("th-navigating-add-history",event); if(!event.navigateSuppressNavigation) { this.addToHistory(event.navigateTo,event.navigateFromClientRect); } diff --git a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid index 3e0b32964..6b0dfaf5b 100644 --- a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid @@ -1,6 +1,6 @@ caption: Block Level Links created: 20230916061138153 -modified: 20230917121127276 +modified: 20230917122221226 tags: WikiText title: Block Level Links in WikiText type: text/vnd.tiddlywiki @@ -35,7 +35,7 @@ Some block, for example, code block, can't be suffixed by `^id`, but we can add ">> -! Link to the block ID ^linkto091607 +! Link to the block ID ^091607 Adding `^blockID` after the title in the link, will make this link highlight the block with that ID. diff --git a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid index b7aa53fab..ae80c7088 100644 --- a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid @@ -1,6 +1,6 @@ caption: Linking created: 20131205155230596 -modified: 20230916070722108 +modified: 20230917121659927 tags: WikiText title: Linking in WikiText type: text/vnd.tiddlywiki @@ -128,4 +128,4 @@ See [[Anchor Links using HTML]] for more information. You can link to a specific block within a tiddler using `^blockId` syntax. You will also get block level backlinks with this technique. Some examples are in [[BlockIdWidget^exampleid1]]. -See [[Block Level Links in WikiText^linkto091607]] for more information. +See [[Block Level Links in WikiText^091607]] for more information. From 0d18b25b26a77f4a7b3bf0ede984792610514d42 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Mon, 18 Sep 2023 11:17:32 +0800 Subject: [PATCH 19/43] Update blockid.js --- core/modules/widgets/blockid.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js index aa04deee5..e2152c88f 100644 --- a/core/modules/widgets/blockid.js +++ b/core/modules/widgets/blockid.js @@ -67,8 +67,6 @@ BlockIdWidget.prototype.hookNavigatedEvent = function(event) { }; BlockIdWidget.prototype.hookNavigationAddHistoryEvent = function(event) { - // DEBUG: console this._isNavigateToHere(event) - console.log(`this._isNavigateToHere(event)`, this._isNavigateToHere(event)); if(!this._isNavigateToHere(event)) return event; event.navigateSuppressNavigation = true; return event; From dfa060024e5b15ab55d73937d117b9821815ff01 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Fri, 22 Sep 2023 23:10:06 +0800 Subject: [PATCH 20/43] docs: about why history change will reflect on storyview --- .../rules/{blockid.js => anchor.js} | 20 +-- .../parsers/wikiparser/rules/prettylink.js | 8 +- core/modules/widgets/anchor.js | 59 ++++++++ core/modules/widgets/blockid.js | 131 ------------------ core/modules/widgets/link.js | 4 +- .../BlockIdWidget.tid => AnchorWidget.tid} | 14 +- .../Block Level Links in WikiText.tid | 8 +- 7 files changed, 87 insertions(+), 157 deletions(-) rename core/modules/parsers/wikiparser/rules/{blockid.js => anchor.js} (69%) create mode 100644 core/modules/widgets/anchor.js delete mode 100644 core/modules/widgets/blockid.js rename editions/tw5.com/tiddlers/{widgets/BlockIdWidget.tid => AnchorWidget.tid} (71%) diff --git a/core/modules/parsers/wikiparser/rules/blockid.js b/core/modules/parsers/wikiparser/rules/anchor.js similarity index 69% rename from core/modules/parsers/wikiparser/rules/blockid.js rename to core/modules/parsers/wikiparser/rules/anchor.js index 57da17d79..cede453cb 100644 --- a/core/modules/parsers/wikiparser/rules/blockid.js +++ b/core/modules/parsers/wikiparser/rules/anchor.js @@ -1,15 +1,15 @@ /*\ -title: $:/core/modules/parsers/wikiparser/rules/blockidentifier.js +title: $:/core/modules/parsers/wikiparser/rules/anchor.js type: application/javascript module-type: wikirule -Use hash as a tag for paragraph, we call it block identifier. +Use hash as a tag for paragraph, we call it anchor. 1. Hash won't change, it can be written by hand or be generated, and it is a ` \^\S+$` string after line: `text ^cb9d485` or `text ^1`, so it can be human readable (while without space), here are the parse rule for this. 2. When creating widgets for rendering, omit this hash, so it's invisible in view mode. But this widget will create an anchor to jump to. \*/ -exports.name = "blockid"; +exports.name = "anchor"; exports.types = {inline: true}; /* @@ -17,7 +17,7 @@ Instantiate parse rule */ exports.init = function(parser) { this.parser = parser; - // Regexp to match the block identifier + // Regexp to match the anchor. // 1. located on the end of the line, with a space before it, means it's the id of the current block. // 2. located at start of the line, no space, means it's the id of the previous block. Because some block can't have id suffix, otherwise id break the block mode parser like codeblock. this.matchRegExp = /[ ]\^(\S+)$|^\^(\S+)$/mg; @@ -30,16 +30,16 @@ exports.parse = function() { // Move past the match this.parser.pos = this.matchRegExp.lastIndex; // will be one of following case, another will be undefined - var blockId = this.match[1]; - var blockBeforeId = this.match[2]; + var anchorId = this.match[1]; + var anchorBeforeId = this.match[2]; // Parse tree nodes to return return [{ - type: "blockid", + type: "anchor", attributes: { - id: {type: "string", value: blockId || blockBeforeId}, - // `yes` means the block is before this node, in parent node's children list. + id: {type: "string", value: anchorId || anchorBeforeId}, + // `yes` means the block that this anchor pointing to, is before this node, both anchor and the block, is in a same parent node's children list. // empty means the block is this node's direct parent node. - previousSibling: {type: "string", value: Boolean(blockBeforeId) ? "yes" : ""}, + previousSibling: {type: "string", value: Boolean(anchorBeforeId) ? "yes" : ""}, }, children: [] }]; diff --git a/core/modules/parsers/wikiparser/rules/prettylink.js b/core/modules/parsers/wikiparser/rules/prettylink.js index bfca074f1..5eb4edde8 100644 --- a/core/modules/parsers/wikiparser/rules/prettylink.js +++ b/core/modules/parsers/wikiparser/rules/prettylink.js @@ -33,11 +33,11 @@ exports.parse = function() { // Process the link var text = this.match[1], link = this.match[2] || text, - blockId = this.match[3] || ""; + anchor = this.match[3] || ""; if($tw.utils.isLinkExternal(link)) { // add back the part after `^` to the ext link, if it happen to has one. - if(blockId) { - link = link + "^" + blockId; + if(anchor) { + link = link + "^" + anchor; } return [{ type: "element", @@ -57,7 +57,7 @@ exports.parse = function() { type: "link", attributes: { to: {type: "string", value: link}, - toBlockId: {type: "string", value: blockId}, + toAnchor: {type: "string", value: anchor}, }, children: [{ type: "text", text: text diff --git a/core/modules/widgets/anchor.js b/core/modules/widgets/anchor.js new file mode 100644 index 000000000..201caa222 --- /dev/null +++ b/core/modules/widgets/anchor.js @@ -0,0 +1,59 @@ +/*\ +title: $:/core/modules/widgets/anchor.js +type: application/javascript +module-type: widget + +An invisible element with anchor id metadata. +\*/ +var Widget = require("$:/core/modules/widgets/widget.js").widget; +var AnchorWidget = function(parseTreeNode,options) { + this.initialise(parseTreeNode,options); + // only this widget knows target info (if the block is before this node or not), so we need to hook the focus event, and process it here, instead of in the root widget. +}; +AnchorWidget.prototype = new Widget(); + +AnchorWidget.prototype.render = function(parent,nextSibling) { + // Save the parent dom node + this.parentDomNode = parent; + // Compute our attributes + this.computeAttributes(); + // Execute our logic + this.execute(); + // Create an invisible DOM element with data that can be accessed from JS or CSS + this.idNode = this.document.createElement("span"); + this.idNode.setAttribute("data-anchor-id",this.id); + this.idNode.setAttribute("data-anchor-title",this.tiddlerTitle); + if(this.before) { + this.idNode.setAttribute("data-before","true"); + } + this.idNode.className = "tc-anchor"; + parent.insertBefore(this.idNode,nextSibling); + this.domNodes.push(this.idNode); +}; + +/* +Compute the internal state of the widget +*/ +AnchorWidget.prototype.execute = function() { + // Get the id from the parse tree node or manually assigned attributes + this.id = this.getAttribute("id"); + this.tiddlerTitle = this.getVariable("currentTiddler"); + this.previousSibling = this.getAttribute("previousSibling") === "yes"; + // Make the child widgets + this.makeChildWidgets(); +}; + +/* +Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering +*/ +AnchorWidget.prototype.refresh = function(changedTiddlers) { + var changedAttributes = this.computeAttributes(); + if(($tw.utils.count(changedAttributes) > 0)) { + this.refreshSelf(); + return true; + } else { + return this.refreshChildren(changedTiddlers); + } +}; + +exports.anchor = AnchorWidget; diff --git a/core/modules/widgets/blockid.js b/core/modules/widgets/blockid.js deleted file mode 100644 index e2152c88f..000000000 --- a/core/modules/widgets/blockid.js +++ /dev/null @@ -1,131 +0,0 @@ -/*\ -title: $:/core/modules/widgets/blockid.js -type: application/javascript -module-type: widget - -An invisible element with block id metadata. -\*/ -var Widget = require("$:/core/modules/widgets/widget.js").widget; -var BlockIdWidget = function(parseTreeNode,options) { - this.initialise(parseTreeNode,options); - // only this widget knows target info (if the block is before this node or not), so we need to hook the focus event, and process it here, instead of in the root widget. - this.hookNavigationAddHistoryEvent = this.hookNavigationAddHistoryEvent.bind(this); - this.hookNavigatedEvent = this.hookNavigatedEvent.bind(this); - $tw.hooks.addHook("th-navigating-add-history",this.hookNavigationAddHistoryEvent); - $tw.hooks.addHook("th-navigated",this.hookNavigatedEvent); -}; -BlockIdWidget.prototype = new Widget(); - -BlockIdWidget.prototype.removeChildDomNodes = function() { - $tw.hooks.removeHook("th-navigating-add-history",this.hookNavigationAddHistoryEvent); - $tw.hooks.removeHook("th-navigated",this.hookNavigatedEvent); -}; - -BlockIdWidget.prototype.render = function(parent,nextSibling) { - // Save the parent dom node - this.parentDomNode = parent; - // Compute our attributes - this.computeAttributes(); - // Execute our logic - this.execute(); - // Create an invisible DOM element with data that can be accessed from JS or CSS - this.idNode = this.document.createElement("span"); - this.idNode.setAttribute("data-block-id",this.id); - this.idNode.setAttribute("data-block-title",this.tiddlerTitle); - if(this.before) { - this.idNode.setAttribute("data-before","true"); - } - this.idNode.className = "tc-block-id"; - parent.insertBefore(this.idNode,nextSibling); - this.domNodes.push(this.idNode); -}; - -BlockIdWidget.prototype._isNavigateToHere = function(event) { - if(!event || !event.toBlockId) return false; - if(event.toBlockId !== this.id) return false; - if(this.tiddlerTitle && event.navigateTo !== this.tiddlerTitle) return false; - return true; -} - -BlockIdWidget.prototype.hookNavigatedEvent = function(event) { - if(!this._isNavigateToHere(event)) return event; - var baseElement = event.event && event.event.target ? event.event.target.ownerDocument : document; - var element = this._getTargetElement(baseElement); - if(element) { - // if tiddler is already in the story view, just move to it. - this._scrollToBlockAndHighlight(element); - } else { - var self = this; - // Here we still need to wait for extra time after `duration`, so tiddler dom is actually added to the story view. - var duration = $tw.utils.getAnimationDuration() + 50; - setTimeout(function() { - element = self._getTargetElement(baseElement); - self._scrollToBlockAndHighlight(element); - }, duration); - } - return false; -}; - -BlockIdWidget.prototype.hookNavigationAddHistoryEvent = function(event) { - if(!this._isNavigateToHere(event)) return event; - event.navigateSuppressNavigation = true; - return event; -}; - -BlockIdWidget.prototype._getTargetElement = function(baseElement) { - var selector = "span[data-block-id='"+this.id+"']"; - if(this.tiddlerTitle) { - // allow different tiddler have same block id in the text, and only jump to the one with a same tiddler title. - selector += "[data-block-title='"+this.tiddlerTitle+"']"; - } - // re-query the dom node, because `this.idNode.parentNode` might already be removed from document - var element = $tw.utils.querySelectorSafe(selector,baseElement); - if(!element || !element.parentNode) return; - // the actual block is always at the parent level - element = element.parentNode; - // need to check if the block is before this node - if(this.previousSibling && element.previousSibling) { - element = element.previousSibling; - } - return element; -}; - -BlockIdWidget.prototype._scrollToBlockAndHighlight = function(element) { - if(!element) return; - // toggle class to trigger highlight animation - $tw.utils.removeClass(element,"tc-focus-highlight"); - // We enable the `navigateSuppressNavigation` in LinkWidget when sending `tm-navigate`, otherwise `tm-navigate` will force move to the title - element.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" }); - element.focus({ focusVisible: true }); - // Using setTimeout to ensure the removal takes effect before adding the class again. - setTimeout(function() { - $tw.utils.addClass(element,"tc-focus-highlight"); - }, 50); -}; - -/* -Compute the internal state of the widget -*/ -BlockIdWidget.prototype.execute = function() { - // Get the id from the parse tree node or manually assigned attributes - this.id = this.getAttribute("id"); - this.tiddlerTitle = this.getVariable("currentTiddler"); - this.previousSibling = this.getAttribute("previousSibling") === "yes"; - // Make the child widgets - this.makeChildWidgets(); -}; - -/* -Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering -*/ -BlockIdWidget.prototype.refresh = function(changedTiddlers) { - var changedAttributes = this.computeAttributes(); - if(($tw.utils.count(changedAttributes) > 0)) { - this.refreshSelf(); - return true; - } else { - return this.refreshChildren(changedTiddlers); - } -}; - -exports.blockid = BlockIdWidget; diff --git a/core/modules/widgets/link.js b/core/modules/widgets/link.js index f336f6463..e588903e4 100755 --- a/core/modules/widgets/link.js +++ b/core/modules/widgets/link.js @@ -150,7 +150,7 @@ LinkWidget.prototype.handleClickEvent = function(event) { this.dispatchEvent({ type: "tm-navigate", navigateTo: this.to, - toBlockId: this.toBlockId, + toAnchor: this.toAnchor, navigateFromTitle: this.getVariable("storyTiddler"), navigateFromNode: this, navigateFromClientRect: { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height @@ -181,7 +181,7 @@ Compute the internal state of the widget LinkWidget.prototype.execute = function() { // Pick up our attributes this.to = this.getAttribute("to",this.getVariable("currentTiddler")); - this.toBlockId = this.getAttribute("toBlockId"); + this.toAnchor = this.getAttribute("toAnchor"); this.tooltip = this.getAttribute("tooltip"); this["aria-label"] = this.getAttribute("aria-label"); this.linkClasses = this.getAttribute("class"); diff --git a/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid b/editions/tw5.com/tiddlers/AnchorWidget.tid similarity index 71% rename from editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid rename to editions/tw5.com/tiddlers/AnchorWidget.tid index 6f7d6a556..d587753d3 100644 --- a/editions/tw5.com/tiddlers/widgets/BlockIdWidget.tid +++ b/editions/tw5.com/tiddlers/AnchorWidget.tid @@ -1,8 +1,8 @@ caption: block id created: 20230916061829840 -modified: 20230917121007649 +modified: 20230922150245402 tags: Widgets -title: BlockIdWidget +title: AnchorWidget type: text/vnd.tiddlywiki ! Introduction @@ -11,7 +11,7 @@ The block id widget make an anchor that can be focused and jump to. ! Content and Attributes -The content of the `<$blockid>` widget is ignored. +The content of the `<$anchor>` widget is ignored. |!Attribute |!Description | |id |The unique id for the block | @@ -21,14 +21,14 @@ See [[Block Level Links in WikiText^🤗→AddingIDforblock]] for WikiText synta ! Example -< +< -[[Link to BlockLevelLinksID1|BlockIdWidget^BlockLevelLinksID1]] +[[Link to BlockLevelLinksID1|AnchorWidget^BlockLevelLinksID1]] """>> < +ID is here:<$anchor id="BlockLevelLinksID2" previousSibling="yes"/> -[[Link to BlockLevelLinksID2|BlockIdWidget^BlockLevelLinksID2]] +[[Link to BlockLevelLinksID2|AnchorWidget^BlockLevelLinksID2]] """>> diff --git a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid index 6b0dfaf5b..16384b18e 100644 --- a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid @@ -1,11 +1,11 @@ caption: Block Level Links created: 20230916061138153 -modified: 20230917122221226 +modified: 20230922150740619 tags: WikiText title: Block Level Links in WikiText type: text/vnd.tiddlywiki -! Adding ID for block ^🤗→AddingIDforblock +<> The basic syntax for block id is: @@ -43,4 +43,6 @@ Adding `^blockID` after the title in the link, will make this link highlight the <> -<> \ No newline at end of file +<> + +<> \ No newline at end of file From 436343ce1b9e84a1359fc27d52770d28e41dc3ab Mon Sep 17 00:00:00 2001 From: linonetwo Date: Fri, 22 Sep 2023 23:14:22 +0800 Subject: [PATCH 21/43] refactor: use history mechanism for block level navigation --- core/modules/story.js | 4 ++-- core/modules/storyviews/classic.js | 15 ++++++++++++++- core/modules/utils/dom/scroller.js | 20 +++++++++++++++++++- core/modules/widgets/anchor.js | 5 +++-- core/modules/widgets/navigator.js | 8 ++++---- 5 files changed, 42 insertions(+), 10 deletions(-) diff --git a/core/modules/story.js b/core/modules/story.js index 16ff5a74a..c7b09ed5d 100644 --- a/core/modules/story.js +++ b/core/modules/story.js @@ -90,12 +90,12 @@ Story.prototype.saveStoryList = function(storyList) { )); }; -Story.prototype.addToHistory = function(navigateTo,navigateFromClientRect) { +Story.prototype.addToHistory = function(navigateTo,fromPageRect,anchor) { var titles = $tw.utils.isArray(navigateTo) ? navigateTo : [navigateTo]; // Add a new record to the top of the history stack var historyList = this.wiki.getTiddlerData(this.historyTitle,[]); $tw.utils.each(titles,function(title) { - historyList.push({title: title, fromPageRect: navigateFromClientRect}); + historyList.push({title: title, fromPageRect: fromPageRect, anchor: anchor}); }); this.wiki.setTiddlerData(this.historyTitle,historyList,{"current-tiddler": titles[titles.length-1]}); }; diff --git a/core/modules/storyviews/classic.js b/core/modules/storyviews/classic.js index c2848c435..0b9d554c0 100644 --- a/core/modules/storyviews/classic.js +++ b/core/modules/storyviews/classic.js @@ -26,13 +26,26 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) { } var listItemWidget = this.listWidget.children[listElementIndex], targetElement = listItemWidget.findFirstDomNode(); + // If anchor is provided, find the element the anchor pointing to + var foundAnchor = false; + if(targetElement && historyInfo.anchor) { + var anchorElement = targetElement.querySelector("[data-anchor-id='" + historyInfo.anchor + "']"); + if(anchorElement) { + targetElement = anchorElement.parentNode; + var isBefore = anchorElement.dataset.anchorPreviousSibling === "true"; + if(isBefore) { + targetElement = targetElement.previousSibling; + } + foundAnchor = true; + } + } // Abandon if the list entry isn't a DOM element (it might be a text node) if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) { return; } if(duration) { // Scroll the node into view - this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement}); + this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundAnchor}); } else { targetElement.scrollIntoView(); } diff --git a/core/modules/utils/dom/scroller.js b/core/modules/utils/dom/scroller.js index 905bb2750..4166507d6 100644 --- a/core/modules/utils/dom/scroller.js +++ b/core/modules/utils/dom/scroller.js @@ -49,7 +49,9 @@ Handle an event */ PageScroller.prototype.handleEvent = function(event) { if(event.type === "tm-scroll") { - var options = {}; + var options = { + highlight: event.highlight, + }; if($tw.utils.hop(event.paramObject,"animationDuration")) { options.animationDuration = event.paramObject.animationDuration; } @@ -65,14 +67,21 @@ PageScroller.prototype.handleEvent = function(event) { /* Handle a scroll event hitting the page document + +options: +- animationDuration: total time of scroll animation +- highlight: highlight the element after scrolling, to make it evident. Usually to focus an anchor in the middle of the tiddler. */ PageScroller.prototype.scrollIntoView = function(element,callback,options) { var self = this, duration = $tw.utils.hop(options,"animationDuration") ? parseInt(options.animationDuration) : $tw.utils.getAnimationDuration(), + highlight = options.highlight || false, srcWindow = element ? element.ownerDocument.defaultView : window; // Now get ready to scroll the body this.cancelScroll(srcWindow); this.startTime = Date.now(); + // toggle class to allow trigger the highlight animation + $tw.utils.removeClass(element,"tc-focus-highlight"); // Get the height of any position:fixed toolbars var toolbar = srcWindow.document.querySelector(".tc-adjust-top-of-scroll"), offset = 0; @@ -121,6 +130,15 @@ PageScroller.prototype.scrollIntoView = function(element,callback,options) { srcWindow.scrollTo(scrollPosition.x + (endX - scrollPosition.x) * t,scrollPosition.y + (endY - scrollPosition.y) * t); if(t < 1) { self.idRequestFrame = self.requestAnimationFrame.call(srcWindow,drawFrame); + } else { + // the animation is end. + if(highlight) { + element.focus({ focusVisible: true }); + // Using setTimeout to ensure the removal takes effect before adding the class again. + setTimeout(function() { + $tw.utils.addClass(element,"tc-focus-highlight"); + }, 50); + } } }; drawFrame(); diff --git a/core/modules/widgets/anchor.js b/core/modules/widgets/anchor.js index 201caa222..c9c6af091 100644 --- a/core/modules/widgets/anchor.js +++ b/core/modules/widgets/anchor.js @@ -23,8 +23,9 @@ AnchorWidget.prototype.render = function(parent,nextSibling) { this.idNode = this.document.createElement("span"); this.idNode.setAttribute("data-anchor-id",this.id); this.idNode.setAttribute("data-anchor-title",this.tiddlerTitle); - if(this.before) { - this.idNode.setAttribute("data-before","true"); + // if the actual block is before this node, we need to add a flag to the node + if(this.previousSibling) { + this.idNode.setAttribute("data-anchor-previous-sibling","true"); } this.idNode.className = "tc-anchor"; parent.insertBefore(this.idNode,nextSibling); diff --git a/core/modules/widgets/navigator.js b/core/modules/widgets/navigator.js index 84d69dd6b..423f65459 100755 --- a/core/modules/widgets/navigator.js +++ b/core/modules/widgets/navigator.js @@ -138,9 +138,10 @@ NavigatorWidget.prototype.addToStory = function(title,fromTitle) { Add a new record to the top of the history stack title: a title string or an array of title strings fromPageRect: page coordinates of the origin of the navigation +anchor:optional anchor id in this tiddler */ -NavigatorWidget.prototype.addToHistory = function(title,fromPageRect) { - this.story.addToHistory(title,fromPageRect,this.historyTitle); +NavigatorWidget.prototype.addToHistory = function(title,fromPageRect,anchor) { + this.story.addToHistory(title,fromPageRect,anchor); }; /* @@ -150,9 +151,8 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) { event = $tw.hooks.invokeHook("th-navigating",event); if(event.navigateTo) { this.addToStory(event.navigateTo,event.navigateFromTitle); - event = $tw.hooks.invokeHook("th-navigating-add-history",event); if(!event.navigateSuppressNavigation) { - this.addToHistory(event.navigateTo,event.navigateFromClientRect); + this.addToHistory(event.navigateTo,event.navigateFromClientRect,event.toAnchor); } } $tw.hooks.invokeHook("th-navigated",event); From ebf84b59e865f5e0a35e992b6b4fb0e1c7abe5d9 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Fri, 22 Sep 2023 23:14:30 +0800 Subject: [PATCH 22/43] docs: about HistoryMechanism in dev doc --- editions/dev/tiddlers/mechanism/HistoryMechanism.tid | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 editions/dev/tiddlers/mechanism/HistoryMechanism.tid diff --git a/editions/dev/tiddlers/mechanism/HistoryMechanism.tid b/editions/dev/tiddlers/mechanism/HistoryMechanism.tid new file mode 100644 index 000000000..81379f4fc --- /dev/null +++ b/editions/dev/tiddlers/mechanism/HistoryMechanism.tid @@ -0,0 +1,8 @@ +created: 20230922141042399 +modified: 20230922141423022 +tags: +title: HistoryMechanism + +The story view is created by [[$:/core/ui/PageTemplate/story]] core page template, which uses list widget to render tiddlers. In this way, page template will reflect to history's change. + +List widget has a `history="$:/HistoryList"` parameter, that will be used in list widget's `handleHistoryChanges` method, and pass to the `this.storyview.navigateTo`, you can read [[storyview module]] for how storyview use the changed history. \ No newline at end of file From 507d004a57b926a8cd7dd8bbdd66dc7357c3be21 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Fri, 22 Sep 2023 23:57:14 +0800 Subject: [PATCH 23/43] feat: adapt for other story views --- core/modules/storyviews/classic.js | 14 ++++++-------- core/modules/storyviews/pop.js | 13 ++++++++++++- core/modules/storyviews/zoomin.js | 15 ++++++++++++++- core/modules/utils/parsetree.js | 15 +++++++++++++++ core/modules/widgets/anchor.js | 20 ++++++++++++++++++-- 5 files changed, 65 insertions(+), 12 deletions(-) diff --git a/core/modules/storyviews/classic.js b/core/modules/storyviews/classic.js index 0b9d554c0..3470f7be2 100644 --- a/core/modules/storyviews/classic.js +++ b/core/modules/storyviews/classic.js @@ -28,14 +28,12 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) { targetElement = listItemWidget.findFirstDomNode(); // If anchor is provided, find the element the anchor pointing to var foundAnchor = false; - if(targetElement && historyInfo.anchor) { - var anchorElement = targetElement.querySelector("[data-anchor-id='" + historyInfo.anchor + "']"); - if(anchorElement) { - targetElement = anchorElement.parentNode; - var isBefore = anchorElement.dataset.anchorPreviousSibling === "true"; - if(isBefore) { - targetElement = targetElement.previousSibling; - } + if(listItemWidget && historyInfo.anchor) { + var anchorWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { + return widget.anchorId === historyInfo.anchor; + }); + if(anchorWidget) { + targetElement = anchorWidget.findAnchorTargetDomNode() foundAnchor = true; } } diff --git a/core/modules/storyviews/pop.js b/core/modules/storyviews/pop.js index e2634e3d5..3b9f9e39c 100644 --- a/core/modules/storyviews/pop.js +++ b/core/modules/storyviews/pop.js @@ -23,12 +23,23 @@ PopStoryView.prototype.navigateTo = function(historyInfo) { } var listItemWidget = this.listWidget.children[listElementIndex], targetElement = listItemWidget.findFirstDomNode(); + // If anchor is provided, find the element the anchor pointing to + var foundAnchor = false; + if(listItemWidget && historyInfo.anchor) { + var anchorWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { + return widget.anchorId === historyInfo.anchor; + }); + if(anchorWidget) { + targetElement = anchorWidget.findAnchorTargetDomNode() + foundAnchor = true; + } + } // Abandon if the list entry isn't a DOM element (it might be a text node) if(!targetElement || targetElement.nodeType === Node.TEXT_NODE) { return; } // Scroll the node into view - this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement}); + this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundAnchor}); }; PopStoryView.prototype.insert = function(widget) { diff --git a/core/modules/storyviews/zoomin.js b/core/modules/storyviews/zoomin.js index d02f705e7..039047372 100644 --- a/core/modules/storyviews/zoomin.js +++ b/core/modules/storyviews/zoomin.js @@ -51,6 +51,16 @@ ZoominListView.prototype.navigateTo = function(historyInfo) { } var listItemWidget = this.listWidget.children[listElementIndex], targetElement = listItemWidget.findFirstDomNode(); + // If anchor is provided, find the element the anchor pointing to + var anchorElement = null; + if(listItemWidget && historyInfo.anchor) { + var anchorWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { + return widget.anchorId === historyInfo.anchor; + }); + if(anchorWidget) { + anchorElement = anchorWidget.findAnchorTargetDomNode() + } + } // Abandon if the list entry isn't a DOM element (it might be a text node) if(!targetElement) { return; @@ -119,7 +129,10 @@ ZoominListView.prototype.navigateTo = function(historyInfo) { },duration); } // Scroll the target into view -// $tw.pageScroller.scrollIntoView(targetElement); + if(anchorElement) { + this.listWidget.dispatchEvent({type: "tm-scroll", target: anchorElement, highlight: true}); + } + // $tw.pageScroller.scrollIntoView(targetElement); }; /* diff --git a/core/modules/utils/parsetree.js b/core/modules/utils/parsetree.js index a74b8f3f8..7ee2de09e 100644 --- a/core/modules/utils/parsetree.js +++ b/core/modules/utils/parsetree.js @@ -103,6 +103,21 @@ exports.findParseTreeNode = function(nodeArray,search) { return undefined; }; +exports.findChildNodeInTree = function(root,searchFn) { + if(searchFn(root)) { + return root; + } + if(root.children && root.children.length > 0) { + for(var i=0; i Date: Fri, 22 Sep 2023 23:59:47 +0800 Subject: [PATCH 24/43] fix: no need for setTimeout --- core/modules/utils/dom/scroller.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/core/modules/utils/dom/scroller.js b/core/modules/utils/dom/scroller.js index 4166507d6..f9cc8c9d9 100644 --- a/core/modules/utils/dom/scroller.js +++ b/core/modules/utils/dom/scroller.js @@ -134,10 +134,7 @@ PageScroller.prototype.scrollIntoView = function(element,callback,options) { // the animation is end. if(highlight) { element.focus({ focusVisible: true }); - // Using setTimeout to ensure the removal takes effect before adding the class again. - setTimeout(function() { - $tw.utils.addClass(element,"tc-focus-highlight"); - }, 50); + $tw.utils.addClass(element,"tc-focus-highlight"); } } }; From 13f6bd84e97f4aa5a1c87a4de5b5ed1b4bf217e8 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 23 Sep 2023 00:05:36 +0800 Subject: [PATCH 25/43] refactor: remove unusned hook --- boot/boot.js | 14 -------------- core/modules/widgets/navigator.js | 1 - editions/dev/tiddlers/new/Hook__th-navigated.tid | 11 ----------- 3 files changed, 26 deletions(-) delete mode 100644 editions/dev/tiddlers/new/Hook__th-navigated.tid diff --git a/boot/boot.js b/boot/boot.js index 2636cb8f4..06d4628c0 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -2674,20 +2674,6 @@ $tw.hooks.addHook = function(hookName,definition) { } }; -/* -Delete hooks from the hashmap -*/ -$tw.hooks.removeHook = function(hookName,definition) { - if($tw.utils.hop($tw.hooks.names,hookName)) { - var index = $tw.hooks.names[hookName].findIndex(function(hook) { - return hook === definition; - }); - if(index !== -1) { - $tw.hooks.names[hookName].splice(index, 1); - } - } -}; - /* Invoke the hook by key */ diff --git a/core/modules/widgets/navigator.js b/core/modules/widgets/navigator.js index 423f65459..ed5d1929e 100755 --- a/core/modules/widgets/navigator.js +++ b/core/modules/widgets/navigator.js @@ -155,7 +155,6 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) { this.addToHistory(event.navigateTo,event.navigateFromClientRect,event.toAnchor); } } - $tw.hooks.invokeHook("th-navigated",event); return false; }; diff --git a/editions/dev/tiddlers/new/Hook__th-navigated.tid b/editions/dev/tiddlers/new/Hook__th-navigated.tid deleted file mode 100644 index 0315529ba..000000000 --- a/editions/dev/tiddlers/new/Hook__th-navigated.tid +++ /dev/null @@ -1,11 +0,0 @@ -tags: HookMechanism -title: Hook: th-navigated -type: text/vnd.tiddlywiki - -This hook allows plugins to do things after navigation takes effect. - -Hook function parameters are same as [[Hook: th-navigating]]: - -Return value: - -* possibly modified event object From 13d0c5cbd53d2fbd709a5d53f4fa02ff6ade6cc2 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Sat, 23 Sep 2023 00:05:50 +0800 Subject: [PATCH 26/43] docs: about toAnchor added in 5.3.2 --- editions/tw5.com/tiddlers/{ => widgets}/AnchorWidget.tid | 0 editions/tw5.com/tiddlers/widgets/LinkWidget.tid | 1 + 2 files changed, 1 insertion(+) rename editions/tw5.com/tiddlers/{ => widgets}/AnchorWidget.tid (100%) diff --git a/editions/tw5.com/tiddlers/AnchorWidget.tid b/editions/tw5.com/tiddlers/widgets/AnchorWidget.tid similarity index 100% rename from editions/tw5.com/tiddlers/AnchorWidget.tid rename to editions/tw5.com/tiddlers/widgets/AnchorWidget.tid diff --git a/editions/tw5.com/tiddlers/widgets/LinkWidget.tid b/editions/tw5.com/tiddlers/widgets/LinkWidget.tid index 26e78c422..938627322 100644 --- a/editions/tw5.com/tiddlers/widgets/LinkWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/LinkWidget.tid @@ -16,6 +16,7 @@ The content of the link widget is rendered within the `` tag representing the |!Attribute |!Description | |to |The title of the target tiddler for the link (defaults to the [[current tiddler|Current Tiddler]]) | +|toAnchor |<<.from-version "5.3.2">> Optional id of the anchor for [[Block Level Links in WikiText]] | |aria-label |Optional accessibility label | |tooltip |Optional tooltip WikiText | |tabindex |Optional numeric [[tabindex|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex]] | From 9c2c6d724ea9cfa7a0ac93371699681404d53222 Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Sun, 9 Jun 2024 23:01:07 +0800 Subject: [PATCH 27/43] fix: link end position should add ^id 's length --- core/modules/parsers/wikiparser/rules/prettylink.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modules/parsers/wikiparser/rules/prettylink.js b/core/modules/parsers/wikiparser/rules/prettylink.js index b1172ac4a..f577dba8c 100644 --- a/core/modules/parsers/wikiparser/rules/prettylink.js +++ b/core/modules/parsers/wikiparser/rules/prettylink.js @@ -40,7 +40,7 @@ exports.parse = function() { textEndPos = this.matchRegExp.lastIndex - 2; } var linkStart = this.match[2] ? (start + this.match[1].length + 1) : start; - var linkEnd = linkStart + link.length; + var linkEnd = anchor ? (linkStart + link.length + 1 + anchor.length) : (linkStart + link.length); if($tw.utils.isLinkExternal(link)) { // add back the part after `^` to the ext link, if it happen to has one. if(anchor) { From 7e9cadf6b0071eeedaef6d0648f37b54881d91b5 Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Sun, 9 Jun 2024 23:05:37 +0800 Subject: [PATCH 28/43] Revert "fix: link end position should add ^id 's length" This reverts commit 9c2c6d724ea9cfa7a0ac93371699681404d53222. --- core/modules/parsers/wikiparser/rules/prettylink.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modules/parsers/wikiparser/rules/prettylink.js b/core/modules/parsers/wikiparser/rules/prettylink.js index f577dba8c..b1172ac4a 100644 --- a/core/modules/parsers/wikiparser/rules/prettylink.js +++ b/core/modules/parsers/wikiparser/rules/prettylink.js @@ -40,7 +40,7 @@ exports.parse = function() { textEndPos = this.matchRegExp.lastIndex - 2; } var linkStart = this.match[2] ? (start + this.match[1].length + 1) : start; - var linkEnd = anchor ? (linkStart + link.length + 1 + anchor.length) : (linkStart + link.length); + var linkEnd = linkStart + link.length; if($tw.utils.isLinkExternal(link)) { // add back the part after `^` to the ext link, if it happen to has one. if(anchor) { From 92ca17a08bbcfbbc15388b7147613f379f4005a8 Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Sun, 9 Jun 2024 23:15:41 +0800 Subject: [PATCH 29/43] fix: correct anchor start end --- core/modules/parsers/wikiparser/rules/anchor.js | 15 +++++++++------ .../parsers/wikiparser/rules/prettylink.js | 6 ++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/core/modules/parsers/wikiparser/rules/anchor.js b/core/modules/parsers/wikiparser/rules/anchor.js index cede453cb..831ec105c 100644 --- a/core/modules/parsers/wikiparser/rules/anchor.js +++ b/core/modules/parsers/wikiparser/rules/anchor.js @@ -18,8 +18,8 @@ Instantiate parse rule exports.init = function(parser) { this.parser = parser; // Regexp to match the anchor. - // 1. located on the end of the line, with a space before it, means it's the id of the current block. - // 2. located at start of the line, no space, means it's the id of the previous block. Because some block can't have id suffix, otherwise id break the block mode parser like codeblock. + // 1. inlineId: located on the end of the line, with a space before it, means it's the id of the current block. + // 2. blockId: located at start of the line, no space, means it's the id of the previous block. Because some block can't have id suffix, otherwise id break the block mode parser like codeblock. this.matchRegExp = /[ ]\^(\S+)$|^\^(\S+)$/mg; }; @@ -30,16 +30,19 @@ exports.parse = function() { // Move past the match this.parser.pos = this.matchRegExp.lastIndex; // will be one of following case, another will be undefined - var anchorId = this.match[1]; - var anchorBeforeId = this.match[2]; + var inlineId = this.match[1]; + var blockId = this.match[2]; + var id = inlineId || blockId || ''; + var anchorStart = this.parser.pos; + var anchorEnd = anchorStart + id.length; // Parse tree nodes to return return [{ type: "anchor", attributes: { - id: {type: "string", value: anchorId || anchorBeforeId}, + id: {type: "string", value: id, start: anchorStart, end: anchorEnd}, // `yes` means the block that this anchor pointing to, is before this node, both anchor and the block, is in a same parent node's children list. // empty means the block is this node's direct parent node. - previousSibling: {type: "string", value: Boolean(anchorBeforeId) ? "yes" : ""}, + previousSibling: {type: "string", value: Boolean(blockId) ? "yes" : ""}, }, children: [] }]; diff --git a/core/modules/parsers/wikiparser/rules/prettylink.js b/core/modules/parsers/wikiparser/rules/prettylink.js index b1172ac4a..bed0365d3 100644 --- a/core/modules/parsers/wikiparser/rules/prettylink.js +++ b/core/modules/parsers/wikiparser/rules/prettylink.js @@ -42,7 +42,7 @@ exports.parse = function() { var linkStart = this.match[2] ? (start + this.match[1].length + 1) : start; var linkEnd = linkStart + link.length; if($tw.utils.isLinkExternal(link)) { - // add back the part after `^` to the ext link, if it happen to has one. + // add back the part after `^` to the ext link, if it happen to has one. Here is is not an anchor, but a part of the external URL. if(anchor) { link = link + "^" + anchor; } @@ -60,11 +60,13 @@ exports.parse = function() { }] }]; } else { + var anchorStart = anchor ? (linkEnd + 1) : linkEnd; + var anchorEnd = anchorStart + anchor.length; return [{ type: "link", attributes: { to: {type: "string", value: link, start: linkStart, end: linkEnd}, - toAnchor: {type: "string", value: anchor}, + toAnchor: {type: "string", value: anchor, start: anchorStart, end: anchorEnd}, }, children: [{ type: "text", text: text, start: start, end: textEndPos From cc12af5ef60b75197c5b00806899a255a80ec3d6 Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Sun, 9 Jun 2024 23:41:00 +0800 Subject: [PATCH 30/43] docs: fix BlockIdWidget --- .../tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid | 2 +- editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid index 16384b18e..71b3dabbd 100644 --- a/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Block Level Links in WikiText.tid @@ -11,7 +11,7 @@ The basic syntax for block id is: <> -# Don't forget the space between the end of the line and the `^`. +# You should add a space between the end of your text and the `^`. # And there is no space between `^` and the id. # ID can contain any char other than `^` and space ` `. diff --git a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid index ae80c7088..e99e76e06 100644 --- a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid @@ -126,6 +126,6 @@ See [[Anchor Links using HTML]] for more information. ! Linking within tiddlers - Link to block -You can link to a specific block within a tiddler using `^blockId` syntax. You will also get block level backlinks with this technique. Some examples are in [[BlockIdWidget^exampleid1]]. +You can link to a specific block within a tiddler using `^blockId` syntax. You will also get block level backlinks with this technique. See [[Block Level Links in WikiText^091607]] for more information. From 98189a998d889fb1f3b31f6a141b4d30f86488b7 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Mon, 30 Sep 2024 18:33:11 +0800 Subject: [PATCH 31/43] test: ast for section mark --- .../tiddlers/tests/test-wikitext-parser.js | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/editions/test/tiddlers/tests/test-wikitext-parser.js b/editions/test/tiddlers/tests/test-wikitext-parser.js index a488ff030..cdf24ff93 100644 --- a/editions/test/tiddlers/tests/test-wikitext-parser.js +++ b/editions/test/tiddlers/tests/test-wikitext-parser.js @@ -418,6 +418,66 @@ describe("WikiText parser tests", function() { expect(parse(wikitext)).toEqual(expectedParseTree); }); + + + it('should parse section marks', function () { + expect( + parse( + "There is a block id that is invisible, but you can find it using developer tool's inspect element feature. ^BlockLevelLinksID1" + ) + ).toEqual([ + { + type: 'element', + tag: 'p', + children: [ + { + type: 'text', + text: "There is a block id that is invisible, but you can find it using developer tool's inspect element feature.", + start: 0, + end: 106, + }, + { + type: 'anchor', + attributes: { + id: { type: 'string', value: 'BlockLevelLinksID1', start: 126, end: 144 }, + previousSibling: { type: 'string', value: '' }, + }, + children: [], + start: 106, + end: 126, + rule: 'anchor', + }, + ], + start: 0, + end: 126, + }, + ]); + }); + + + it('should parse link to section marks', function () { + expect(parse('[[Link to BlockLevelLinksID1|Block Level Links in WikiText^BlockLevelLinksID1]]')).toEqual([ + { + type: 'element', + tag: 'p', + children: [ + { + type: 'link', + attributes: { + to: { type: 'string', value: 'Block Level Links in WikiText', start: 29, end: 58 }, + toAnchor: { type: 'string', value: 'BlockLevelLinksID1', start: 59, end: 77 }, + }, + children: [{ type: 'text', text: 'Link to BlockLevelLinksID1', start: 2, end: 28 }], + start: 0, + end: 79, + rule: 'prettylink', + }, + ], + start: 0, + end: 79, + }, + ]); + }); }); })(); From 8c20319b3a7d56c5c481555473acbea9d4fea346 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Wed, 2 Oct 2024 20:47:24 +0800 Subject: [PATCH 32/43] refactor: rename anchor to block mark --- .../rules/{anchor.js => blockmark.js} | 22 +++++++------- .../parsers/wikiparser/rules/prettylink.js | 14 ++++----- core/modules/story.js | 4 +-- core/modules/storyviews/classic.js | 18 +++++------ core/modules/storyviews/pop.js | 18 +++++------ core/modules/storyviews/zoomin.js | 20 ++++++------- .../widgets/{anchor.js => block-mark.js} | 30 +++++++++---------- core/modules/widgets/link.js | 4 +-- core/modules/widgets/navigator.js | 2 +- .../tiddlers/tests/test-wikitext-parser.js | 6 ++-- .../tw5.com/tiddlers/widgets/AnchorWidget.tid | 14 ++++----- .../tw5.com/tiddlers/widgets/LinkWidget.tid | 2 +- .../tiddlers/wikitext/Linking in WikiText.tid | 2 +- 13 files changed, 77 insertions(+), 79 deletions(-) rename core/modules/parsers/wikiparser/rules/{anchor.js => blockmark.js} (67%) rename core/modules/widgets/{anchor.js => block-mark.js} (68%) diff --git a/core/modules/parsers/wikiparser/rules/anchor.js b/core/modules/parsers/wikiparser/rules/blockmark.js similarity index 67% rename from core/modules/parsers/wikiparser/rules/anchor.js rename to core/modules/parsers/wikiparser/rules/blockmark.js index 831ec105c..2d0e3faa1 100644 --- a/core/modules/parsers/wikiparser/rules/anchor.js +++ b/core/modules/parsers/wikiparser/rules/blockmark.js @@ -1,15 +1,15 @@ /*\ -title: $:/core/modules/parsers/wikiparser/rules/anchor.js +title: $:/core/modules/parsers/wikiparser/rules/blockmark.js type: application/javascript module-type: wikirule -Use hash as a tag for paragraph, we call it anchor. +Use hash as a tag for paragraph, we call it block mark. 1. Hash won't change, it can be written by hand or be generated, and it is a ` \^\S+$` string after line: `text ^cb9d485` or `text ^1`, so it can be human readable (while without space), here are the parse rule for this. -2. When creating widgets for rendering, omit this hash, so it's invisible in view mode. But this widget will create an anchor to jump to. +2. When creating widgets for rendering, omit this hash, so it's invisible in view mode. But this widget will create an mark to jump to. \*/ -exports.name = "anchor"; +exports.name = "blockmark"; exports.types = {inline: true}; /* @@ -17,7 +17,7 @@ Instantiate parse rule */ exports.init = function(parser) { this.parser = parser; - // Regexp to match the anchor. + // Regexp to match the block mark. // 1. inlineId: located on the end of the line, with a space before it, means it's the id of the current block. // 2. blockId: located at start of the line, no space, means it's the id of the previous block. Because some block can't have id suffix, otherwise id break the block mode parser like codeblock. this.matchRegExp = /[ ]\^(\S+)$|^\^(\S+)$/mg; @@ -32,15 +32,15 @@ exports.parse = function() { // will be one of following case, another will be undefined var inlineId = this.match[1]; var blockId = this.match[2]; - var id = inlineId || blockId || ''; - var anchorStart = this.parser.pos; - var anchorEnd = anchorStart + id.length; + var id = inlineId || blockId || ""; + var blockMarkStart = this.parser.pos; + var blockMarkEnd = blockMarkStart + id.length; // Parse tree nodes to return return [{ - type: "anchor", + type: "blockmark", attributes: { - id: {type: "string", value: id, start: anchorStart, end: anchorEnd}, - // `yes` means the block that this anchor pointing to, is before this node, both anchor and the block, is in a same parent node's children list. + id: {type: "string", value: id, start: blockMarkStart, end: blockMarkEnd}, + // `yes` means the block that this block mark pointing to, is before this node, both block mark and the block itself, is in a same parent node's children list. // empty means the block is this node's direct parent node. previousSibling: {type: "string", value: Boolean(blockId) ? "yes" : ""}, }, diff --git a/core/modules/parsers/wikiparser/rules/prettylink.js b/core/modules/parsers/wikiparser/rules/prettylink.js index bed0365d3..f8d83cac5 100644 --- a/core/modules/parsers/wikiparser/rules/prettylink.js +++ b/core/modules/parsers/wikiparser/rules/prettylink.js @@ -34,7 +34,7 @@ exports.parse = function() { // Process the link var text = this.match[1], link = this.match[2] || text, - anchor = this.match[3] || "", + blockMark = this.match[3] || "", textEndPos = this.parser.source.indexOf("|", start); if (textEndPos < 0 || textEndPos > this.matchRegExp.lastIndex) { textEndPos = this.matchRegExp.lastIndex - 2; @@ -42,9 +42,9 @@ exports.parse = function() { var linkStart = this.match[2] ? (start + this.match[1].length + 1) : start; var linkEnd = linkStart + link.length; if($tw.utils.isLinkExternal(link)) { - // add back the part after `^` to the ext link, if it happen to has one. Here is is not an anchor, but a part of the external URL. - if(anchor) { - link = link + "^" + anchor; + // add back the part after `^` to the ext link, if it happen to has one. Here is is not an block mark, but a part of the external URL. + if(blockMark) { + link = link + "^" + blockMark; } return [{ type: "element", @@ -60,13 +60,13 @@ exports.parse = function() { }] }]; } else { - var anchorStart = anchor ? (linkEnd + 1) : linkEnd; - var anchorEnd = anchorStart + anchor.length; + var blockMarkStart = blockMark ? (linkEnd + 1) : linkEnd; + var blockMarkEnd = blockMarkStart + blockMark.length; return [{ type: "link", attributes: { to: {type: "string", value: link, start: linkStart, end: linkEnd}, - toAnchor: {type: "string", value: anchor, start: anchorStart, end: anchorEnd}, + toBlockMark: {type: "string", value: blockMark, start: blockMarkStart, end: blockMarkEnd}, }, children: [{ type: "text", text: text, start: start, end: textEndPos diff --git a/core/modules/story.js b/core/modules/story.js index c7b09ed5d..0994d994d 100644 --- a/core/modules/story.js +++ b/core/modules/story.js @@ -90,12 +90,12 @@ Story.prototype.saveStoryList = function(storyList) { )); }; -Story.prototype.addToHistory = function(navigateTo,fromPageRect,anchor) { +Story.prototype.addToHistory = function(navigateTo,fromPageRect,blockMark) { var titles = $tw.utils.isArray(navigateTo) ? navigateTo : [navigateTo]; // Add a new record to the top of the history stack var historyList = this.wiki.getTiddlerData(this.historyTitle,[]); $tw.utils.each(titles,function(title) { - historyList.push({title: title, fromPageRect: fromPageRect, anchor: anchor}); + historyList.push({title: title, fromPageRect: fromPageRect, blockMark: blockMark}); }); this.wiki.setTiddlerData(this.historyTitle,historyList,{"current-tiddler": titles[titles.length-1]}); }; diff --git a/core/modules/storyviews/classic.js b/core/modules/storyviews/classic.js index 68076b14e..3aecd44ce 100644 --- a/core/modules/storyviews/classic.js +++ b/core/modules/storyviews/classic.js @@ -26,15 +26,15 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) { } var listItemWidget = this.listWidget.children[listElementIndex], targetElement = listItemWidget.findFirstDomNode(); - // If anchor is provided, find the element the anchor pointing to - var foundAnchor = false; - if(listItemWidget && historyInfo.anchor) { - var anchorWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { - return widget.anchorId === historyInfo.anchor; + // If block mark is provided, find the block element that this mark is pointing to + var foundBlockMark = false; + if(listItemWidget && historyInfo.blockMark) { + var blockMarkWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { + return widget.blockMarkId === historyInfo.blockMark; }); - if(anchorWidget) { - targetElement = anchorWidget.findAnchorTargetDomNode() - foundAnchor = true; + if(blockMarkWidget) { + targetElement = blockMarkWidget.findBlockMarkTargetDomNode() + foundBlockMark = true; } } // Abandon if the list entry isn't a DOM element (it might be a text node) @@ -42,7 +42,7 @@ ClassicStoryView.prototype.navigateTo = function(historyInfo) { return; } // Scroll the node into view - this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundAnchor}); + this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundBlockMark}); }; ClassicStoryView.prototype.insert = function(widget) { diff --git a/core/modules/storyviews/pop.js b/core/modules/storyviews/pop.js index 3b9f9e39c..e1d500ceb 100644 --- a/core/modules/storyviews/pop.js +++ b/core/modules/storyviews/pop.js @@ -23,15 +23,15 @@ PopStoryView.prototype.navigateTo = function(historyInfo) { } var listItemWidget = this.listWidget.children[listElementIndex], targetElement = listItemWidget.findFirstDomNode(); - // If anchor is provided, find the element the anchor pointing to - var foundAnchor = false; - if(listItemWidget && historyInfo.anchor) { - var anchorWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { - return widget.anchorId === historyInfo.anchor; + // If block mark is provided, find the block element that this mark is pointing to + var foundBlockMark = false; + if(listItemWidget && historyInfo.blockMark) { + var blockMarkWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { + return widget.blockMarkId === historyInfo.blockMark; }); - if(anchorWidget) { - targetElement = anchorWidget.findAnchorTargetDomNode() - foundAnchor = true; + if(blockMarkWidget) { + targetElement = blockMarkWidget.findBlockMarkTargetDomNode() + foundBlockMark = true; } } // Abandon if the list entry isn't a DOM element (it might be a text node) @@ -39,7 +39,7 @@ PopStoryView.prototype.navigateTo = function(historyInfo) { return; } // Scroll the node into view - this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundAnchor}); + this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: foundBlockMark}); }; PopStoryView.prototype.insert = function(widget) { diff --git a/core/modules/storyviews/zoomin.js b/core/modules/storyviews/zoomin.js index 039047372..63e2897b2 100644 --- a/core/modules/storyviews/zoomin.js +++ b/core/modules/storyviews/zoomin.js @@ -51,14 +51,15 @@ ZoominListView.prototype.navigateTo = function(historyInfo) { } var listItemWidget = this.listWidget.children[listElementIndex], targetElement = listItemWidget.findFirstDomNode(); - // If anchor is provided, find the element the anchor pointing to - var anchorElement = null; - if(listItemWidget && historyInfo.anchor) { - var anchorWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { - return widget.anchorId === historyInfo.anchor; + // If block mark is provided, find the block element that this mark is pointing to + var foundBlockMark = false; + if(listItemWidget && historyInfo.blockMark) { + var blockMarkWidget = $tw.utils.findChildNodeInTree(listItemWidget, function(widget) { + return widget.blockMarkId === historyInfo.blockMark; }); - if(anchorWidget) { - anchorElement = anchorWidget.findAnchorTargetDomNode() + if(blockMarkWidget) { + targetElement = blockMarkWidget.findBlockMarkTargetDomNode() + foundBlockMark = true; } } // Abandon if the list entry isn't a DOM element (it might be a text node) @@ -129,10 +130,7 @@ ZoominListView.prototype.navigateTo = function(historyInfo) { },duration); } // Scroll the target into view - if(anchorElement) { - this.listWidget.dispatchEvent({type: "tm-scroll", target: anchorElement, highlight: true}); - } - // $tw.pageScroller.scrollIntoView(targetElement); + this.listWidget.dispatchEvent({type: "tm-scroll", target: targetElement, highlight: true}); }; /* diff --git a/core/modules/widgets/anchor.js b/core/modules/widgets/block-mark.js similarity index 68% rename from core/modules/widgets/anchor.js rename to core/modules/widgets/block-mark.js index ad9127d5b..027c6b450 100644 --- a/core/modules/widgets/anchor.js +++ b/core/modules/widgets/block-mark.js @@ -1,18 +1,18 @@ /*\ -title: $:/core/modules/widgets/anchor.js +title: $:/core/modules/widgets/block-mark.js type: application/javascript module-type: widget -An invisible element with anchor id metadata. +An invisible element with block-mark id metadata. \*/ var Widget = require("$:/core/modules/widgets/widget.js").widget; -var AnchorWidget = function(parseTreeNode,options) { +var BlockMarkWidget = function(parseTreeNode,options) { this.initialise(parseTreeNode,options); // only this widget knows target info (if the block is before this node or not), so we need to hook the focus event, and process it here, instead of in the root widget. }; -AnchorWidget.prototype = new Widget(); +BlockMarkWidget.prototype = new Widget(); -AnchorWidget.prototype.render = function(parent,nextSibling) { +BlockMarkWidget.prototype.render = function(parent,nextSibling) { // Save the parent dom node this.parentDomNode = parent; // Compute our attributes @@ -21,13 +21,13 @@ AnchorWidget.prototype.render = function(parent,nextSibling) { this.execute(); // Create an invisible DOM element with data that can be accessed from JS or CSS this.idNode = this.document.createElement("span"); - this.idNode.setAttribute("data-anchor-id",this.anchorId); - this.idNode.setAttribute("data-anchor-title",this.tiddlerTitle); + this.idNode.setAttribute("data-block-mark-id",this.blockMarkId); + this.idNode.setAttribute("data-block-mark-title",this.tiddlerTitle); // if the actual block is before this node, we need to add a flag to the node if(this.previousSibling) { - this.idNode.setAttribute("data-anchor-previous-sibling","true"); + this.idNode.setAttribute("data-block-mark-previous-sibling","true"); } - this.idNode.className = "tc-anchor"; + this.idNode.className = "tc-block-mark"; parent.insertBefore(this.idNode,nextSibling); this.domNodes.push(this.idNode); }; @@ -35,9 +35,9 @@ AnchorWidget.prototype.render = function(parent,nextSibling) { /* Compute the internal state of the widget */ -AnchorWidget.prototype.execute = function() { +BlockMarkWidget.prototype.execute = function() { // Get the id from the parse tree node or manually assigned attributes - this.anchorId = this.getAttribute("id"); + this.blockMarkId = this.getAttribute("id"); this.tiddlerTitle = this.getVariable("currentTiddler"); this.previousSibling = this.getAttribute("previousSibling") === "yes"; // Make the child widgets @@ -45,9 +45,9 @@ AnchorWidget.prototype.execute = function() { }; /* -Find the DOM node pointed by this anchor +Find the DOM node pointed by this block mark */ -Widget.prototype.findAnchorTargetDomNode = function() { +Widget.prototype.findBlockMarkTargetDomNode = function() { if(!this.idNode) { return null; } @@ -63,7 +63,7 @@ Widget.prototype.findAnchorTargetDomNode = function() { /* Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering */ -AnchorWidget.prototype.refresh = function(changedTiddlers) { +BlockMarkWidget.prototype.refresh = function(changedTiddlers) { var changedAttributes = this.computeAttributes(); if(($tw.utils.count(changedAttributes) > 0)) { this.refreshSelf(); @@ -73,4 +73,4 @@ AnchorWidget.prototype.refresh = function(changedTiddlers) { } }; -exports.anchor = AnchorWidget; +exports["block-mark"] = BlockMarkWidget; diff --git a/core/modules/widgets/link.js b/core/modules/widgets/link.js index 39dfe9dca..ee1f31d81 100755 --- a/core/modules/widgets/link.js +++ b/core/modules/widgets/link.js @@ -160,7 +160,7 @@ LinkWidget.prototype.handleClickEvent = function(event) { this.dispatchEvent({ type: "tm-navigate", navigateTo: this.to, - toAnchor: this.toAnchor, + toBlockMark: this.toBlockMark, navigateFromTitle: this.getVariable("storyTiddler"), navigateFromNode: this, navigateFromClientRect: { top: bounds.top, left: bounds.left, width: bounds.width, right: bounds.right, bottom: bounds.bottom, height: bounds.height @@ -191,7 +191,7 @@ Compute the internal state of the widget LinkWidget.prototype.execute = function() { // Pick up our attributes this.to = this.getAttribute("to",this.getVariable("currentTiddler")); - this.toAnchor = this.getAttribute("toAnchor"); + this.toBlockMark = this.getAttribute("toBlockMark"); this.tooltip = this.getAttribute("tooltip"); this["aria-label"] = this.getAttribute("aria-label"); this.linkClasses = this.getAttribute("class"); diff --git a/core/modules/widgets/navigator.js b/core/modules/widgets/navigator.js index 8349c429e..4d28b607d 100755 --- a/core/modules/widgets/navigator.js +++ b/core/modules/widgets/navigator.js @@ -152,7 +152,7 @@ NavigatorWidget.prototype.handleNavigateEvent = function(event) { if(event.navigateTo) { this.addToStory(event.navigateTo,event.navigateFromTitle); if(!event.navigateSuppressNavigation) { - this.addToHistory(event.navigateTo,event.navigateFromClientRect,event.toAnchor); + this.addToHistory(event.navigateTo,event.navigateFromClientRect,event.toBlockMark); } } return false; diff --git a/editions/test/tiddlers/tests/test-wikitext-parser.js b/editions/test/tiddlers/tests/test-wikitext-parser.js index cdf24ff93..9171043e7 100644 --- a/editions/test/tiddlers/tests/test-wikitext-parser.js +++ b/editions/test/tiddlers/tests/test-wikitext-parser.js @@ -437,7 +437,7 @@ describe("WikiText parser tests", function() { end: 106, }, { - type: 'anchor', + type: 'blockmark', attributes: { id: { type: 'string', value: 'BlockLevelLinksID1', start: 126, end: 144 }, previousSibling: { type: 'string', value: '' }, @@ -445,7 +445,7 @@ describe("WikiText parser tests", function() { children: [], start: 106, end: 126, - rule: 'anchor', + rule: 'blockmark', }, ], start: 0, @@ -465,7 +465,7 @@ describe("WikiText parser tests", function() { type: 'link', attributes: { to: { type: 'string', value: 'Block Level Links in WikiText', start: 29, end: 58 }, - toAnchor: { type: 'string', value: 'BlockLevelLinksID1', start: 59, end: 77 }, + toBlockMark: { type: 'string', value: 'BlockLevelLinksID1', start: 59, end: 77 }, }, children: [{ type: 'text', text: 'Link to BlockLevelLinksID1', start: 2, end: 28 }], start: 0, diff --git a/editions/tw5.com/tiddlers/widgets/AnchorWidget.tid b/editions/tw5.com/tiddlers/widgets/AnchorWidget.tid index d587753d3..2d6d50f15 100644 --- a/editions/tw5.com/tiddlers/widgets/AnchorWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/AnchorWidget.tid @@ -2,16 +2,16 @@ caption: block id created: 20230916061829840 modified: 20230922150245402 tags: Widgets -title: AnchorWidget +title: BlockMarkWidget type: text/vnd.tiddlywiki ! Introduction -The block id widget make an anchor that can be focused and jump to. +The block id widget make an block mark that can be focused and jump to. ! Content and Attributes -The content of the `<$anchor>` widget is ignored. +The content of the `<$block-mark>` widget is ignored. |!Attribute |!Description | |id |The unique id for the block | @@ -21,14 +21,14 @@ See [[Block Level Links in WikiText^🤗→AddingIDforblock]] for WikiText synta ! Example -< +< -[[Link to BlockLevelLinksID1|AnchorWidget^BlockLevelLinksID1]] +[[Link to BlockLevelLinksID1|BlockMarkWidget^BlockLevelLinksID1]] """>> < +ID is here:<$block-mark id="BlockLevelLinksID2" previousSibling="yes"/> -[[Link to BlockLevelLinksID2|AnchorWidget^BlockLevelLinksID2]] +[[Link to BlockLevelLinksID2|BlockMarkWidget^BlockLevelLinksID2]] """>> diff --git a/editions/tw5.com/tiddlers/widgets/LinkWidget.tid b/editions/tw5.com/tiddlers/widgets/LinkWidget.tid index 2d609836c..39ed91c15 100644 --- a/editions/tw5.com/tiddlers/widgets/LinkWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/LinkWidget.tid @@ -16,7 +16,7 @@ The content of the link widget is rendered within the `` tag representing the |!Attribute |!Description | |to |The title of the target tiddler for the link (defaults to the [[current tiddler|Current Tiddler]]) | -|toAnchor |<<.from-version "5.3.2">> Optional id of the anchor for [[Block Level Links in WikiText]] | +|toBlockMark |<<.from-version "5.3.6">> Optional id of the anchor for [[Block Level Links in WikiText]] | |aria-label |Optional accessibility label | |tooltip |Optional tooltip WikiText | |tabindex |Optional numeric [[tabindex|https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/tabIndex]] | diff --git a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid index e99e76e06..db994b415 100644 --- a/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid +++ b/editions/tw5.com/tiddlers/wikitext/Linking in WikiText.tid @@ -124,7 +124,7 @@ In TiddlyWiki anchor links can help us link to target points and distinct sectio See [[Anchor Links using HTML]] for more information. -! Linking within tiddlers - Link to block +! Linking within tiddlers - Link to block mark You can link to a specific block within a tiddler using `^blockId` syntax. You will also get block level backlinks with this technique. From b34221aadf3f677cedf47db4cc54bd5101342126 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Wed, 2 Oct 2024 20:53:36 +0800 Subject: [PATCH 33/43] fix: blockmark widget name --- core/modules/widgets/{block-mark.js => blockmark.js} | 2 +- .../widgets/{AnchorWidget.tid => BlockMarkWidget.tid} | 6 +++--- .../tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid | 2 ++ 3 files changed, 6 insertions(+), 4 deletions(-) rename core/modules/widgets/{block-mark.js => blockmark.js} (98%) rename editions/tw5.com/tiddlers/widgets/{AnchorWidget.tid => BlockMarkWidget.tid} (85%) diff --git a/core/modules/widgets/block-mark.js b/core/modules/widgets/blockmark.js similarity index 98% rename from core/modules/widgets/block-mark.js rename to core/modules/widgets/blockmark.js index 027c6b450..962e944f8 100644 --- a/core/modules/widgets/block-mark.js +++ b/core/modules/widgets/blockmark.js @@ -73,4 +73,4 @@ BlockMarkWidget.prototype.refresh = function(changedTiddlers) { } }; -exports["block-mark"] = BlockMarkWidget; +exports.blockmark = BlockMarkWidget; diff --git a/editions/tw5.com/tiddlers/widgets/AnchorWidget.tid b/editions/tw5.com/tiddlers/widgets/BlockMarkWidget.tid similarity index 85% rename from editions/tw5.com/tiddlers/widgets/AnchorWidget.tid rename to editions/tw5.com/tiddlers/widgets/BlockMarkWidget.tid index 2d6d50f15..00f584cae 100644 --- a/editions/tw5.com/tiddlers/widgets/AnchorWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/BlockMarkWidget.tid @@ -11,7 +11,7 @@ The block id widget make an block mark that can be focused and jump to. ! Content and Attributes -The content of the `<$block-mark>` widget is ignored. +The content of the `<$blockmark>` widget is ignored. |!Attribute |!Description | |id |The unique id for the block | @@ -21,14 +21,14 @@ See [[Block Level Links in WikiText^🤗→AddingIDforblock]] for WikiText synta ! Example -< +< [[Link to BlockLevelLinksID1|BlockMarkWidget^BlockLevelLinksID1]] """>> < +ID is here:<$blockmark id="BlockLevelLinksID2" previousSibling="yes"/> [[Link to BlockLevelLinksID2|BlockMarkWidget^BlockLevelLinksID2]] """>> diff --git a/editions/tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid b/editions/tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid index 9d69e37c1..3ce971a4b 100644 --- a/editions/tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid +++ b/editions/tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid @@ -63,6 +63,8 @@ The above example can be adapted to work for many situations in ~TiddlyWiki, suc Yes, it's that simple. BUT... in ~TiddlyWiki there are some small differences from standard HTML5 anchor links and some specific ~TiddlyWiki limitations. +To use WikiText style anchor links, see [[Block Level Links in WikiText]]. + >>Back to the top

>>Limitations and things to watch out for

From d2a18b4668a3a2d301c4af8d894e14127b3fcb80 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Wed, 2 Oct 2024 21:10:21 +0800 Subject: [PATCH 34/43] feat: allow static site jumping --- core/modules/widgets/blockmark.js | 5 +++-- core/modules/widgets/link.js | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/modules/widgets/blockmark.js b/core/modules/widgets/blockmark.js index 962e944f8..833e5227c 100644 --- a/core/modules/widgets/blockmark.js +++ b/core/modules/widgets/blockmark.js @@ -3,12 +3,11 @@ title: $:/core/modules/widgets/block-mark.js type: application/javascript module-type: widget -An invisible element with block-mark id metadata. +An invisible element with block mark id metadata. Marking a block level element, so that it can be jumped to. \*/ var Widget = require("$:/core/modules/widgets/widget.js").widget; var BlockMarkWidget = function(parseTreeNode,options) { this.initialise(parseTreeNode,options); - // only this widget knows target info (if the block is before this node or not), so we need to hook the focus event, and process it here, instead of in the root widget. }; BlockMarkWidget.prototype = new Widget(); @@ -23,6 +22,8 @@ BlockMarkWidget.prototype.render = function(parent,nextSibling) { this.idNode = this.document.createElement("span"); this.idNode.setAttribute("data-block-mark-id",this.blockMarkId); this.idNode.setAttribute("data-block-mark-title",this.tiddlerTitle); + // id for anchor jumping in static site + this.idNode.setAttribute("id",this.tiddlerTitle + "-" + this.blockMarkId); // if the actual block is before this node, we need to add a flag to the node if(this.previousSibling) { this.idNode.setAttribute("data-block-mark-previous-sibling","true"); diff --git a/core/modules/widgets/link.js b/core/modules/widgets/link.js index ee1f31d81..79a2811e5 100755 --- a/core/modules/widgets/link.js +++ b/core/modules/widgets/link.js @@ -92,6 +92,7 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) { } // Set an href var wikilinkTransformFilter = this.getVariable("tv-filter-export-link"), + to = this.toBlockMark ? this.to + "-" + this.toBlockMark : this.to, wikiLinkText; if(wikilinkTransformFilter) { // Use the filter to construct the href @@ -102,8 +103,8 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) { // Expand the tv-wikilink-template variable to construct the href var wikiLinkTemplateMacro = this.getVariable("tv-wikilink-template"), wikiLinkTemplate = wikiLinkTemplateMacro ? wikiLinkTemplateMacro.trim() : "#$uri_encoded$"; - wikiLinkText = $tw.utils.replaceString(wikiLinkTemplate,"$uri_encoded$",$tw.utils.encodeURIComponentExtended(this.to)); - wikiLinkText = $tw.utils.replaceString(wikiLinkText,"$uri_doubleencoded$",$tw.utils.encodeURIComponentExtended($tw.utils.encodeURIComponentExtended(this.to))); + wikiLinkText = $tw.utils.replaceString(wikiLinkTemplate,"$uri_encoded$",$tw.utils.encodeURIComponentExtended(to)); + wikiLinkText = $tw.utils.replaceString(wikiLinkText,"$uri_doubleencoded$",$tw.utils.encodeURIComponentExtended($tw.utils.encodeURIComponentExtended(to))); } // Override with the value of tv-get-export-link if defined wikiLinkText = this.getVariable("tv-get-export-link",{params: [{name: "to",value: this.to}],defaultValue: wikiLinkText}); From a19ace31a96c4d3820f3f427f5aa305885092da7 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Wed, 2 Oct 2024 21:13:37 +0800 Subject: [PATCH 35/43] Update Anchor Links using HTML.tid --- .../tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/editions/tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid b/editions/tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid index 3ce971a4b..52a65728b 100644 --- a/editions/tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid +++ b/editions/tw5.com/tiddlers/wikitext/Anchor Links using HTML.tid @@ -1,14 +1,14 @@ created: 20190311094603013 -modified: 20201130064330388 +modified: 20241002211330388 tags: [[HTML in WikiText]] title: Anchor Links using HTML >> +Here we describe a HTML way to link to a section. To use WikiText style anchor links, see [[Block Level Links in WikiText|Block Level Links in WikiText^BlockLevelLinksID1]]. + - - * >>What do they do? * >>How do I make them? * >>Limitations and things to look out for @@ -63,8 +63,6 @@ The above example can be adapted to work for many situations in ~TiddlyWiki, suc Yes, it's that simple. BUT... in ~TiddlyWiki there are some small differences from standard HTML5 anchor links and some specific ~TiddlyWiki limitations. -To use WikiText style anchor links, see [[Block Level Links in WikiText]]. - >>Back to the top

>>Limitations and things to watch out for

From 6557017d4e8b9e51fb86a0017b5fd37310e01655 Mon Sep 17 00:00:00 2001 From: linonetwo Date: Thu, 3 Oct 2024 23:14:11 +0800 Subject: [PATCH 36/43] fix: addClass may cause class pad with space, cause test to fail --- core/modules/utils/dom/dom.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/modules/utils/dom/dom.js b/core/modules/utils/dom/dom.js index 0b71e128c..2037fd37b 100644 --- a/core/modules/utils/dom/dom.js +++ b/core/modules/utils/dom/dom.js @@ -57,7 +57,8 @@ exports.hasClass = function(el,className) { }; exports.addClass = function(el,className) { - var c = (el.getAttribute("class") || "").split(" "); + var classAttr = el.getAttribute("class") || ""; + var c = classAttr.trim() ? classAttr.split(" ") : []; if(c.indexOf(className) === -1) { c.push(className); el.setAttribute("class",c.join(" ")); From 18b492103ccdb2863275d73ab00d49abd81aa12a Mon Sep 17 00:00:00 2001 From: linonetwo Date: Thu, 3 Oct 2024 23:36:44 +0800 Subject: [PATCH 37/43] test: Engaging the search input, The tiddler should be opened and operational, allow clicking link to navigate to block mark --- editions/test/playwright.spec.js | 33 +++++++++++++++++++ .../tiddlers/tests/data/block-mark/Links.tid | 3 ++ .../tiddlers/tests/data/block-mark/Marks.tid | 5 +++ 3 files changed, 41 insertions(+) create mode 100644 editions/test/tiddlers/tests/data/block-mark/Links.tid create mode 100644 editions/test/tiddlers/tests/data/block-mark/Marks.tid diff --git a/editions/test/playwright.spec.js b/editions/test/playwright.spec.js index 1d8c624c7..569a58bd6 100644 --- a/editions/test/playwright.spec.js +++ b/editions/test/playwright.spec.js @@ -22,4 +22,37 @@ test('get started link', async ({ page }) => { // Assert the tests have passed await expect(page.locator('.jasmine-overall-result.jasmine-failed'), "Expected jasmine tests to not have failed").not.toBeVisible(); await expect(page.locator('.jasmine-overall-result.jasmine-passed'), "Expected jasmine tests to have passed").toBeVisible(); + +test('Search tiddler with link and navigate to block mark', async ({ page }) => { + const timeout = 1000 * 30; + test.setTimeout(timeout); + await page.goto(`file:///${crossPlatformIndexPath}`); + + // Engaging the search input + // Search in default search box + await page.fill('input[type="search"]', 'BlockMark/Links'); + // click on link in search results inside the dropdown + await page.click('div.tc-search-drop-down a:has-text("BlockMark/Links")'); + // wait for link to appear and check its href + const searchResultLink = page.locator('a:has-text("Block Level Links in WikiText")'); + await expect(searchResultLink, "Search result link presented").toBeVisible({timeout}); + await expect(searchResultLink, "Search result link have correct href").toHaveAttribute('href', '#BlockMark%2FMarks-BlockLevelLinksID1'); + // click on this link + await page.click('a:has-text("Block Level Links in WikiText")'); + + // The tiddler should be opened and operational, allow clicking link to navigate to block mark + // wait for tiddler to appear and the block focused, and check its properties + const blockMarkedText = page.locator('p:has-text("A block level mark in WikiText.")'); + await expect(blockMarkedText, "Block marked text presented").toBeVisible({timeout}); + await expect(blockMarkedText, "A highlight animation").toHaveClass('tc-focus-highlight'); + // Check the mark span properties + const markSpan = page.locator('span[data-block-mark-id="BlockLevelLinksID1"]') + await expect(markSpan, "Mark span presented but not visible").not.toBeVisible({timeout}), + await Promise.all([ + expect(markSpan).toHaveAttribute('data-block-mark-title', 'BlockMark/Marks'), + expect(markSpan).toHaveClass('tc-block-mark'), + expect(markSpan).toHaveText(''), + expect(markSpan).toHaveClass('tc-block-mark'), + markSpan.evaluate(e => e.id).then(id => expect(id).toBe('BlockMark/Marks-BlockLevelLinksID1')) + ]) }); diff --git a/editions/test/tiddlers/tests/data/block-mark/Links.tid b/editions/test/tiddlers/tests/data/block-mark/Links.tid new file mode 100644 index 000000000..65c272c99 --- /dev/null +++ b/editions/test/tiddlers/tests/data/block-mark/Links.tid @@ -0,0 +1,3 @@ +title: BlockMark/Links + +[[Block Level Links in WikiText|BlockMark/Marks^BlockLevelLinksID1]]. \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/block-mark/Marks.tid b/editions/test/tiddlers/tests/data/block-mark/Marks.tid new file mode 100644 index 000000000..b0ab76a8c --- /dev/null +++ b/editions/test/tiddlers/tests/data/block-mark/Marks.tid @@ -0,0 +1,5 @@ +title: BlockMark/Marks + +First normal paragraph. + +A block level mark in WikiText. ^BlockLevelLinksID1 From b9d861e3a1b5212281a991c4a0a82357f8ceda8d Mon Sep 17 00:00:00 2001 From: linonetwo Date: Thu, 3 Oct 2024 23:37:01 +0800 Subject: [PATCH 38/43] refactor: compact the old test code --- editions/test/playwright.spec.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/editions/test/playwright.spec.js b/editions/test/playwright.spec.js index 569a58bd6..f261a9cf3 100644 --- a/editions/test/playwright.spec.js +++ b/editions/test/playwright.spec.js @@ -9,19 +9,19 @@ test('get started link', async ({ page }) => { // The tests can take a while to run const timeout = 1000 * 30; test.setTimeout(timeout); - // Load the generated test TW html await page.goto(`file:///${crossPlatformIndexPath}`); // Sanity check - await expect(page.locator('.tc-site-title'), "Expected correct page title to verify the test page was loaded").toHaveText('TiddlyWiki5'); - + await expect(page.locator('.tc-site-title'), "Correct page title to verify the test page was loaded").toHaveText('TiddlyWiki5'); // Wait for jasmine results bar to appear - await expect(page.locator('.jasmine-overall-result'), "Expected jasmine test results bar to be present").toBeVisible({timeout}); - + await expect(page.locator('.jasmine-overall-result'), "Jasmine test results bar presented").toBeVisible({timeout}); // Assert the tests have passed - await expect(page.locator('.jasmine-overall-result.jasmine-failed'), "Expected jasmine tests to not have failed").not.toBeVisible(); - await expect(page.locator('.jasmine-overall-result.jasmine-passed'), "Expected jasmine tests to have passed").toBeVisible(); + await Promise.all([ + expect(page.locator('.jasmine-overall-result.jasmine-failed'), "Jasmine tests not failed").not.toBeVisible(), + expect(page.locator('.jasmine-overall-result.jasmine-passed'), "Jasmine tests passed").toBeVisible() + ]); +}); test('Search tiddler with link and navigate to block mark', async ({ page }) => { const timeout = 1000 * 30; From 9b46d7153a92d018278ed164e3f960c27ff76293 Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Fri, 4 Oct 2024 00:27:53 +0800 Subject: [PATCH 39/43] test: should have a backlink to block mark --- editions/test/tiddlers/tests/test-backlinks.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/editions/test/tiddlers/tests/test-backlinks.js b/editions/test/tiddlers/tests/test-backlinks.js index ea7c2b7b4..afad4697e 100644 --- a/editions/test/tiddlers/tests/test-backlinks.js +++ b/editions/test/tiddlers/tests/test-backlinks.js @@ -47,6 +47,17 @@ describe('Backlinks tests', function() { it('should have a backlink', function() { expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing'); + + it("should have a backlink to block mark", function() { + wiki.addTiddler({ + title: "TestIncoming", + text: "With a mark. ^ToThisBlock", + }); + wiki.addTiddler({ + title: "TestOutgoing", + text: "A link to [[TestIncoming^ToThisBlock]]", + }); + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing"); }); }); From f4d7095a33913ea561f0a3b535ad5c70599fde1c Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Fri, 4 Oct 2024 00:28:16 +0800 Subject: [PATCH 40/43] lint: single quote -> double --- .../test/tiddlers/tests/test-backlinks.js | 105 ++++++------ .../tiddlers/tests/test-wikitext-parser.js | 162 +++++++++--------- 2 files changed, 134 insertions(+), 133 deletions(-) diff --git a/editions/test/tiddlers/tests/test-backlinks.js b/editions/test/tiddlers/tests/test-backlinks.js index afad4697e..dbe929615 100644 --- a/editions/test/tiddlers/tests/test-backlinks.js +++ b/editions/test/tiddlers/tests/test-backlinks.js @@ -11,7 +11,7 @@ Tests the backlinks mechanism. /*global $tw: false */ "use strict"; -describe('Backlinks tests', function() { +describe("Backlinks tests", function() { function setupWiki(wikiOptions) { wikiOptions = wikiOptions || {}; // Create a wiki @@ -19,34 +19,35 @@ describe('Backlinks tests', function() { wiki.addIndexersToWiki(); wiki.addTiddler({ - title: 'TestIncoming', - text: '', + title: "TestIncoming", + text: "", }); wiki.addTiddler({ - title: 'TestOutgoing', - text: 'A link to [[TestIncoming]]', + title: "TestOutgoing", + text: "A link to [[TestIncoming]]", }); return wiki; } - describe('a tiddler with no links to it', function() { + describe("a tiddler with no links to it", function() { var wiki = new $tw.Wiki(); wiki.addTiddler({ - title: 'TestIncoming', - text: ''}); + title: "TestIncoming", + text: ""}); - it('should have no backlinks', function() { - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe(''); + it("should have no backlinks", function() { + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe(""); }); }); - describe('A tiddler added to the wiki with a link to it', function() { + describe("A tiddler added to the wiki with a link to it", function() { var wiki = setupWiki(); - it('should have a backlink', function() { - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing'); + it("should have a backlink", function() { + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing"); + }); it("should have a backlink to block mark", function() { wiki.addTiddler({ @@ -61,94 +62,94 @@ describe('Backlinks tests', function() { }); }); - describe('A tiddler that has a link added to it later', function() { - it('should have an additional backlink', function() { + describe("A tiddler that has a link added to it later", function() { + it("should have an additional backlink", function() { var wiki = setupWiki(); wiki.addTiddler({ - title: 'TestOutgoing2', - text: 'Nothing yet!'}); + title: "TestOutgoing2", + text: "Nothing yet!"}); - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing'); + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing"); wiki.addTiddler({ - title: 'TestOutgoing2', - text: 'Updated with link to [[TestIncoming]]'}); + title: "TestOutgoing2", + text: "Updated with link to [[TestIncoming]]"}); - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing,TestOutgoing2'); + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing,TestOutgoing2"); }); }); - describe('A tiddler that has a link remove from it later', function() { + describe("A tiddler that has a link remove from it later", function() { var wiki = setupWiki(); - it('should have one fewer backlink', function() { - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing'); + it("should have one fewer backlink", function() { + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing"); wiki.addTiddler({ - title: 'TestOutgoing', - text: 'No link to ~TestIncoming'}); + title: "TestOutgoing", + text: "No link to ~TestIncoming"}); - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe(''); + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe(""); }); }); - describe('A tiddler linking to another that gets renamed', function() { + describe("A tiddler linking to another that gets renamed", function() { var wiki = setupWiki(); - it('should have its name changed in the backlinks', function() { - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing'); + it("should have its name changed in the backlinks", function() { + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing"); - wiki.renameTiddler('TestOutgoing', 'TestExtroverted'); + wiki.renameTiddler("TestOutgoing", "TestExtroverted"); - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestExtroverted'); + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestExtroverted"); }); }); - describe('A tiddler linking to another that gets deleted', function() { + describe("A tiddler linking to another that gets deleted", function() { var wiki = setupWiki(); - it('should be removed from backlinks', function() { - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe('TestOutgoing'); + it("should be removed from backlinks", function() { + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe("TestOutgoing"); - wiki.deleteTiddler('TestOutgoing'); + wiki.deleteTiddler("TestOutgoing"); - expect(wiki.filterTiddlers('TestIncoming +[backlinks[]]').join(',')).toBe(''); + expect(wiki.filterTiddlers("TestIncoming +[backlinks[]]").join(",")).toBe(""); }); }); - describe('Binary tiddlers should not be parsed', function() { + describe("Binary tiddlers should not be parsed", function() { var wiki = setupWiki(); wiki.addTiddler({ - title: 'TestDoc.doc', - text: 'A link to [[TestOutgoing]]', - type: 'application/msword' + title: "TestDoc.doc", + text: "A link to [[TestOutgoing]]", + type: "application/msword" }); wiki.addTiddler({ - title: 'TestExcel.xls', - text: 'A link to [[TestOutgoing]]', - type: 'application/excel' + title: "TestExcel.xls", + text: "A link to [[TestOutgoing]]", + type: "application/excel" }); wiki.addTiddler({ - title: 'TestOutgoing', - text: 'Some links to [[TestDoc.doc]] and [[TestExcel.xls]].' + title: "TestOutgoing", + text: "Some links to [[TestDoc.doc]] and [[TestExcel.xls]]." }); - it('should ignore office files', function() { - expect(wiki.getIndexer("BackIndexer").subIndexers.link._getTarget(wiki.getTiddler('TestExcel.xls'))).toEqual([]); + it("should ignore office files", function() { + expect(wiki.getIndexer("BackIndexer").subIndexers.link._getTarget(wiki.getTiddler("TestExcel.xls"))).toEqual([]); - expect(wiki.filterTiddlers('[all[]] +[backlinks[]]').join(',')).toBe('TestOutgoing'); + expect(wiki.filterTiddlers("[all[]] +[backlinks[]]").join(",")).toBe("TestOutgoing"); // make it tw5 tiddler wiki.addTiddler({ - title: 'TestExcel.xls', - text: 'A link to [[TestOutgoing]]' + title: "TestExcel.xls", + text: "A link to [[TestOutgoing]]" }); - expect(wiki.filterTiddlers('[all[]] +[backlinks[]]').join(',')).toBe('TestOutgoing,TestExcel.xls'); + expect(wiki.filterTiddlers("[all[]] +[backlinks[]]").join(",")).toBe("TestOutgoing,TestExcel.xls"); }); }); }); diff --git a/editions/test/tiddlers/tests/test-wikitext-parser.js b/editions/test/tiddlers/tests/test-wikitext-parser.js index 9171043e7..9b56fe4ce 100644 --- a/editions/test/tiddlers/tests/test-wikitext-parser.js +++ b/editions/test/tiddlers/tests/test-wikitext-parser.js @@ -26,87 +26,87 @@ describe("WikiText parser tests", function() { it("should parse tags", function() { expect(parse("
")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 4, children : [ { type : 'element', tag : 'br', start : 0, end : 4, openTagStart: 0, openTagEnd: 4, rule: 'html', isBlock : false, attributes : { }, orderedAttributes: [ ] } ] } ] + [ { type : "element", tag : "p", start : 0, end : 4, children : [ { type : "element", tag : "br", start : 0, end : 4, openTagStart: 0, openTagEnd: 4, rule: "html", isBlock : false, attributes : { }, orderedAttributes: [ ] } ] } ] ); expect(parse("
")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 5, children : [ { type : 'text', text : '
', start : 0, end : 5 } ] } ] + [ { type : "element", tag : "p", start : 0, end : 5, children : [ { type : "text", text : "
", start : 0, end : 5 } ] } ] ); expect(parse("
")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 5, children : [ { type : 'element', tag : 'div', start : 0, end : 5, openTagStart: 0, openTagEnd: 5, closeTagStart: 5, closeTagEnd: 5, rule: 'html', isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ ] } ] } ] + [ { type : "element", tag : "p", start : 0, end : 5, children : [ { type : "element", tag : "div", start : 0, end : 5, openTagStart: 0, openTagEnd: 5, closeTagStart: 5, closeTagEnd: 5, rule: "html", isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ ] } ] } ] ); expect(parse("
")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 6, children : [ { type : 'element', tag : 'div', isSelfClosing : true, isBlock : false, attributes : { }, orderedAttributes: [ ], start : 0, end : 6, rule: 'html' } ] } ] + [ { type : "element", tag : "p", start : 0, end : 6, children : [ { type : "element", tag : "div", isSelfClosing : true, isBlock : false, attributes : { }, orderedAttributes: [ ], start : 0, end : 6, rule: "html" } ] } ] ); expect(parse("
")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 11, children : [ { type : 'element', tag : 'div', isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ ], start : 0, end : 11, openTagStart: 0, openTagEnd: 5, closeTagStart: 5, closeTagEnd: 11, rule: 'html' } ] } ] + [ { type : "element", tag : "p", start : 0, end : 11, children : [ { type : "element", tag : "div", isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ ], start : 0, end : 11, openTagStart: 0, openTagEnd: 5, closeTagStart: 5, closeTagEnd: 11, rule: "html" } ] } ] ); expect(parse("
some text
")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 20, children : [ { type : 'element', tag : 'div', openTagStart: 0, openTagEnd: 5, closeTagStart: 14, closeTagEnd: 20, rule: 'html', isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ { type : 'text', text : 'some text', start : 5, end : 14 } ], start : 0, end : 20 } ] } ] + [ { type : "element", tag : "p", start : 0, end : 20, children : [ { type : "element", tag : "div", openTagStart: 0, openTagEnd: 5, closeTagStart: 14, closeTagEnd: 20, rule: "html", isBlock : false, attributes : { }, orderedAttributes: [ ], children : [ { type : "text", text : "some text", start : 5, end : 14 } ], start : 0, end : 20 } ] } ] ); expect(parse("
some text
")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 30, children : [ { type : 'element', tag : 'div', isBlock : false, attributes : { attribute : { type : 'string', value : 'true', start : 4, end : 14, name: 'attribute' } }, orderedAttributes: [ { type : 'string', value : 'true', start : 4, end : 14, name: 'attribute' } ], children : [ { type : 'text', text : 'some text', start : 15, end : 24 } ], start : 0, end : 30, openTagStart: 0, openTagEnd: 15, closeTagStart: 24, closeTagEnd: 30, rule: 'html' } ] } ] + [ { type : "element", tag : "p", start : 0, end : 30, children : [ { type : "element", tag : "div", isBlock : false, attributes : { attribute : { type : "string", value : "true", start : 4, end : 14, name: "attribute" } }, orderedAttributes: [ { type : "string", value : "true", start : 4, end : 14, name: "attribute" } ], children : [ { type : "text", text : "some text", start : 15, end : 24 } ], start : 0, end : 30, openTagStart: 0, openTagEnd: 15, closeTagStart: 24, closeTagEnd: 30, rule: "html" } ] } ] ); expect(parse("
some text
")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 38, children : [ { type : 'element', tag : 'div', openTagStart: 0, openTagEnd: 23, closeTagStart: 32, closeTagEnd: 38, rule: 'html', isBlock : false, attributes : { attribute : { type : 'string', name: 'attribute', value : 'value', start: 4, end: 22 } }, orderedAttributes: [ { type: 'string', name: 'attribute', value : 'value', start: 4, end: 22 } ], children : [ { type : 'text', text : 'some text', start : 23, end : 32 } ], start : 0, end : 38 } ] } ] + [ { type : "element", tag : "p", start : 0, end : 38, children : [ { type : "element", tag : "div", openTagStart: 0, openTagEnd: 23, closeTagStart: 32, closeTagEnd: 38, rule: "html", isBlock : false, attributes : { attribute : { type : "string", name: "attribute", value : "value", start: 4, end: 22 } }, orderedAttributes: [ { type: "string", name: "attribute", value : "value", start: 4, end: 22 } ], children : [ { type : "text", text : "some text", start : 23, end : 32 } ], start : 0, end : 38 } ] } ] ); expect(parse("
some text
")).toEqual( - [ { type : 'element', tag : 'p', start: 0, end: 47, children : [ { type : 'element', tag : 'div', isBlock : false, attributes : { attribute : { type : 'indirect', name: 'attribute', textReference : 'TiddlerTitle', start : 4, end : 31 } }, orderedAttributes: [ { type : 'indirect', name: 'attribute', textReference : 'TiddlerTitle', start : 4, end : 31 } ], children : [ { type : 'text', text : 'some text', start : 32, end : 41 } ], start : 0, end : 47, openTagStart: 0, openTagEnd: 32, closeTagStart: 41, closeTagEnd: 47, rule: 'html' } ] } ] + [ { type : "element", tag : "p", start: 0, end: 47, children : [ { type : "element", tag : "div", isBlock : false, attributes : { attribute : { type : "indirect", name: "attribute", textReference : "TiddlerTitle", start : 4, end : 31 } }, orderedAttributes: [ { type : "indirect", name: "attribute", textReference : "TiddlerTitle", start : 4, end : 31 } ], children : [ { type : "text", text : "some text", start : 32, end : 41 } ], start : 0, end : 47, openTagStart: 0, openTagEnd: 32, closeTagStart: 41, closeTagEnd: 47, rule: "html" } ] } ] ); expect(parse("<$reveal state='$:/temp/search' type='nomatch' text=''>")).toEqual( - [ { type : 'element', tag : 'p', start: 0, end: 55, children : [ { type : 'reveal', tag: '$reveal', rule: 'html', attributes : { state : { start : 8, name : 'state', type : 'string', value : '$:/temp/search', end : 31 }, type : { start : 31, name : 'type', type : 'string', value : 'nomatch', end : 46 }, text : { start : 46, name : 'text', type : 'string', value : '', end : 54 } }, orderedAttributes: [ { start : 8, name : 'state', type : 'string', value : '$:/temp/search', end : 31 }, { start : 31, name : 'type', type : 'string', value : 'nomatch', end : 46 }, { start : 46, name : 'text', type : 'string', value : '', end : 54 } ], start: 0, end : 55, openTagStart: 0, openTagEnd: 55, closeTagStart: 55, closeTagEnd: 55, isBlock : false, children : [ ] } ] } ] + [ { type : "element", tag : "p", start: 0, end: 55, children : [ { type : "reveal", tag: "$reveal", rule: "html", attributes : { state : { start : 8, name : "state", type : "string", value : "$:/temp/search", end : 31 }, type : { start : 31, name : "type", type : "string", value : "nomatch", end : 46 }, text : { start : 46, name : "text", type : "string", value : "", end : 54 } }, orderedAttributes: [ { start : 8, name : "state", type : "string", value : "$:/temp/search", end : 31 }, { start : 31, name : "type", type : "string", value : "nomatch", end : 46 }, { start : 46, name : "text", type : "string", value : "", end : 54 } ], start: 0, end : 55, openTagStart: 0, openTagEnd: 55, closeTagStart: 55, closeTagEnd: 55, isBlock : false, children : [ ] } ] } ] ); expect(parse("
some text
")).toEqual( - [ { type : 'element', tag : 'p', start: 0, end: 54, children : [ { type : 'element', tag : 'div', rule: 'html', isBlock : false, attributes : { attribute : { type : 'indirect', name : 'attribute', textReference : 'TiddlerTitle!!field', start : 4, end : 38 } }, orderedAttributes: [ { type : 'indirect', name : 'attribute', textReference : 'TiddlerTitle!!field', start : 4, end : 38 } ], children : [ { type : 'text', text : 'some text', start : 39, end : 48 } ], start : 0, end : 54, openTagStart: 0, openTagEnd: 39, closeTagStart: 48, closeTagEnd: 54 } ] } ] + [ { type : "element", tag : "p", start: 0, end: 54, children : [ { type : "element", tag : "div", rule: "html", isBlock : false, attributes : { attribute : { type : "indirect", name : "attribute", textReference : "TiddlerTitle!!field", start : 4, end : 38 } }, orderedAttributes: [ { type : "indirect", name : "attribute", textReference : "TiddlerTitle!!field", start : 4, end : 38 } ], children : [ { type : "text", text : "some text", start : 39, end : 48 } ], start : 0, end : 54, openTagStart: 0, openTagEnd: 39, closeTagStart: 48, closeTagEnd: 54 } ] } ] ); expect(parse("
some text
")).toEqual( - [ { type : 'element', tag : 'p', start: 0, end: 55, children : [ { type : 'element', tag : 'div', rule: 'html', isBlock : false, attributes : { attribute : { type : 'indirect', name : 'attribute', textReference : 'Tiddler Title!!field', start : 4, end : 39 } }, orderedAttributes: [ { type : 'indirect', name : 'attribute', textReference : 'Tiddler Title!!field', start : 4, end : 39 } ], children : [ { type : 'text', text : 'some text', start : 40, end : 49 } ], start : 0, end : 55, openTagStart: 0, openTagEnd: 40, closeTagStart: 49, closeTagEnd: 55 } ] } ] + [ { type : "element", tag : "p", start: 0, end: 55, children : [ { type : "element", tag : "div", rule: "html", isBlock : false, attributes : { attribute : { type : "indirect", name : "attribute", textReference : "Tiddler Title!!field", start : 4, end : 39 } }, orderedAttributes: [ { type : "indirect", name : "attribute", textReference : "Tiddler Title!!field", start : 4, end : 39 } ], children : [ { type : "text", text : "some text", start : 40, end : 49 } ], start : 0, end : 55, openTagStart: 0, openTagEnd: 40, closeTagStart: 49, closeTagEnd: 55 } ] } ] ); expect(parse("
\n\nsome text
")).toEqual( - [ { type : 'element', start : 0, attributes : { attribute : { start : 4, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 38 } }, orderedAttributes: [ { start : 4, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 38 } ], tag : 'div', rule: 'html', end : 56, openTagStart: 0, openTagEnd: 39, closeTagStart: 50, closeTagEnd: 56, isBlock : true, children : [ { type : 'element', tag : 'p', start : 41, end : 50, children : [ { type : 'text', text : 'some text', start : 41, end : 50 } ] } ] } ] + [ { type : "element", start : 0, attributes : { attribute : { start : 4, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 38 } }, orderedAttributes: [ { start : 4, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 38 } ], tag : "div", rule: "html", end : 56, openTagStart: 0, openTagEnd: 39, closeTagStart: 50, closeTagEnd: 56, isBlock : true, children : [ { type : "element", tag : "p", start : 41, end : 50, children : [ { type : "text", text : "some text", start : 41, end : 50 } ] } ] } ] ); expect(parse("
\n\nsome text
")).toEqual( - [ { type : 'element', tag : 'p', start: 0, end: 67, children : [ { type : 'element', start : 0, end: 67, openTagStart: 0, openTagEnd: 5, closeTagStart: 61, closeTagEnd: 67, attributes : { }, orderedAttributes: [ ], tag : 'div', rule: 'html', isBlock : false, children : [ { type : 'element', start : 5, attributes : { attribute : { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } }, orderedAttributes: [ { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } ], tag : 'div', end : 61, openTagStart: 5, openTagEnd: 44, closeTagStart: 55, closeTagEnd: 61, rule: 'html', isBlock : true, children : [ { type : 'element', tag : 'p', start : 46, end : 55, children : [ { type : 'text', text : 'some text', start : 46, end : 55 } ] } ] } ] } ] } ] + [ { type : "element", tag : "p", start: 0, end: 67, children : [ { type : "element", start : 0, end: 67, openTagStart: 0, openTagEnd: 5, closeTagStart: 61, closeTagEnd: 67, attributes : { }, orderedAttributes: [ ], tag : "div", rule: "html", isBlock : false, children : [ { type : "element", start : 5, attributes : { attribute : { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } }, orderedAttributes: [ { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } ], tag : "div", end : 61, openTagStart: 5, openTagEnd: 44, closeTagStart: 55, closeTagEnd: 61, rule: "html", isBlock : true, children : [ { type : "element", tag : "p", start : 46, end : 55, children : [ { type : "text", text : "some text", start : 46, end : 55 } ] } ] } ] } ] } ] ); expect(parse("
\n\n!some heading
")).toEqual( - [ { type : 'element', tag : 'p', start: 0, end: 71, children : [ { type : 'element', start : 0, end: 71, openTagStart: 0, openTagEnd: 5, closeTagStart: 71, closeTagEnd: 71, attributes : { }, orderedAttributes: [ ], tag : 'div', rule: 'html', isBlock : false, children : [ { type : 'element', start : 5, attributes : { attribute : { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } }, orderedAttributes: [ { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } ], tag : 'div', end : 71, openTagStart: 5, openTagEnd: 44, closeTagStart: 71, closeTagEnd: 71, rule: 'html', isBlock : true, children : [ { type : 'element', tag : 'h1', start: 46, end: 71, rule: 'heading', attributes : { class : { type : 'string', value : '', start: 47, end: 47 } }, children : [ { type : 'text', text : 'some heading
', start : 47, end : 71 } ] } ] } ] } ] } ] + [ { type : "element", tag : "p", start: 0, end: 71, children : [ { type : "element", start : 0, end: 71, openTagStart: 0, openTagEnd: 5, closeTagStart: 71, closeTagEnd: 71, attributes : { }, orderedAttributes: [ ], tag : "div", rule: "html", isBlock : false, children : [ { type : "element", start : 5, attributes : { attribute : { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } }, orderedAttributes: [ { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } ], tag : "div", end : 71, openTagStart: 5, openTagEnd: 44, closeTagStart: 71, closeTagEnd: 71, rule: "html", isBlock : true, children : [ { type : "element", tag : "h1", start: 46, end: 71, rule: "heading", attributes : { class : { type : "string", value : "", start: 47, end: 47 } }, children : [ { type : "text", text : "some heading", start : 47, end : 71 } ] } ] } ] } ] } ] ); expect(parse("
\n!some heading
")).toEqual( - [ { type : 'element', tag : 'p', start: 0, end: 70, children : [ { type : 'element', start : 0, end: 70, openTagStart: 0, openTagEnd: 5, closeTagStart: 64, closeTagEnd: 70, attributes : { }, orderedAttributes: [ ], tag : 'div', rule: 'html', isBlock : false, children : [ { type : 'element', start : 5, attributes : { attribute : { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } }, orderedAttributes: [ { start : 9, name : 'attribute', type : 'indirect', textReference : 'TiddlerTitle!!field', end : 43 } ], tag : 'div', end : 64, openTagStart: 5, openTagEnd: 44, closeTagStart: 58, closeTagEnd: 64, rule: 'html', isBlock : false, children : [ { type : 'text', text : '\n!some heading', start : 44, end : 58 } ] } ] } ] } ] + [ { type : "element", tag : "p", start: 0, end: 70, children : [ { type : "element", start : 0, end: 70, openTagStart: 0, openTagEnd: 5, closeTagStart: 64, closeTagEnd: 70, attributes : { }, orderedAttributes: [ ], tag : "div", rule: "html", isBlock : false, children : [ { type : "element", start : 5, attributes : { attribute : { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } }, orderedAttributes: [ { start : 9, name : "attribute", type : "indirect", textReference : "TiddlerTitle!!field", end : 43 } ], tag : "div", end : 64, openTagStart: 5, openTagEnd: 44, closeTagStart: 58, closeTagEnd: 64, rule: "html", isBlock : false, children : [ { type : "text", text : "\n!some heading", start : 44, end : 58 } ] } ] } ] } ] ); // Regression test for issue (#3306) expect(parse("
\n\nSome text
")).toEqual( - [ { type : 'element', tag : 'p', start: 0, end: 48, children : [ { type : 'element', start : 0, end: 48, openTagStart: 0, openTagEnd: 5, closeTagStart: 42, closeTagEnd: 48, attributes : { }, orderedAttributes: [ ], tag : 'div', rule: 'html', isBlock : false, children : [ { type : 'element', start : 5, attributes : { }, orderedAttributes: [ ], tag : 'span', end : 42, openTagStart: 5, openTagEnd: 11, closeTagStart: 35, closeTagEnd: 42, rule: 'html', isBlock : false, children : [ { type : 'element', start : 11, attributes : { }, orderedAttributes: [ ], tag : 'span', end : 35, openTagStart: 11, openTagEnd: 17, closeTagStart: 28, closeTagEnd: 35, rule: 'html', isBlock : true, children : [ { type : 'element', tag : 'p', start : 19, end : 28, children : [ { type : 'text', text : 'Some text', start : 19, end : 28 } ] } ] } ] } ] } ] } ] + [ { type : "element", tag : "p", start: 0, end: 48, children : [ { type : "element", start : 0, end: 48, openTagStart: 0, openTagEnd: 5, closeTagStart: 42, closeTagEnd: 48, attributes : { }, orderedAttributes: [ ], tag : "div", rule: "html", isBlock : false, children : [ { type : "element", start : 5, attributes : { }, orderedAttributes: [ ], tag : "span", end : 42, openTagStart: 5, openTagEnd: 11, closeTagStart: 35, closeTagEnd: 42, rule: "html", isBlock : false, children : [ { type : "element", start : 11, attributes : { }, orderedAttributes: [ ], tag : "span", end : 35, openTagStart: 11, openTagEnd: 17, closeTagStart: 28, closeTagEnd: 35, rule: "html", isBlock : true, children : [ { type : "element", tag : "p", start : 19, end : 28, children : [ { type : "text", text : "Some text", start : 19, end : 28 } ] } ] } ] } ] } ] } ] ); }); @@ -193,12 +193,12 @@ describe("WikiText parser tests", function() { it("should block mode filtered transclusions", function() { expect(parse("{{{ filter }}}")).toEqual( - [ { type: 'list', attributes: { filter: { type: 'string', value: ' filter ', start: 3, end: 11 } }, isBlock: true, start: 0, end: 14, rule: "filteredtranscludeblock" } ] + [ { type: "list", attributes: { filter: { type: "string", value: " filter ", start: 3, end: 11 } }, isBlock: true, start: 0, end: 14, rule: "filteredtranscludeblock" } ] ); expect(parse("{{{ fil\nter }}}")).toEqual( - [ { type: 'list', attributes: { filter: { type: 'string', value: ' fil\nter ', start: 3, end: 12 } }, isBlock: true, start: 0, end: 15, rule: "filteredtranscludeblock" } ] + [ { type: "list", attributes: { filter: { type: "string", value: " fil\nter ", start: 3, end: 12 } }, isBlock: true, start: 0, end: 15, rule: "filteredtranscludeblock" } ] ); }); @@ -246,7 +246,7 @@ describe("WikiText parser tests", function() { it("should parse block macro calls", function() { expect(parse("<>\n<>\r\n<>\n<>")).toEqual( - [ { type: 'transclude', start: 0, rule: 'macrocallblock', attributes: { $variable: { name: "$variable", type: "string", value: "john" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "john" }], end: 8, isBlock: true }, { type: 'transclude', start: 9, rule: 'macrocallblock', attributes: { $variable: { name: "$variable", type: "string", value: "paul" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "paul" }], end: 17, isBlock: true }, { type: 'transclude', start: 19, rule: 'macrocallblock', attributes: { $variable: { name: "$variable", type: "string", value: "george" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "george" }], end: 29, isBlock: true }, { type: 'transclude', start: 30, rule: 'macrocallblock', attributes: { $variable: { name: "$variable", type: "string", value: "ringo" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "ringo" }], end: 39, isBlock: true } ] + [ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: { name: "$variable", type: "string", value: "john" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "john" }], end: 8, isBlock: true }, { type: "transclude", start: 9, rule: "macrocallblock", attributes: { $variable: { name: "$variable", type: "string", value: "paul" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "paul" }], end: 17, isBlock: true }, { type: "transclude", start: 19, rule: "macrocallblock", attributes: { $variable: { name: "$variable", type: "string", value: "george" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "george" }], end: 29, isBlock: true }, { type: "transclude", start: 30, rule: "macrocallblock", attributes: { $variable: { name: "$variable", type: "string", value: "ringo" }}, orderedAttributes: [ { name: "$variable", type: "string", value: "ringo" }], end: 39, isBlock: true } ] ); expect(parse("<>")).toEqual( @@ -256,17 +256,17 @@ describe("WikiText parser tests", function() { ); expect(parse("<< carrots\n\n<>")).toEqual( - [ { type: 'element', tag: 'p', start : 0, end : 10, children: [ { type: 'text', text: '<< carrots', start : 0, end : 10 } ] }, { type: 'transclude', start: 12, rule: 'macrocallblock', attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 20, isBlock: true } ] + [ { type: "element", tag: "p", start : 0, end : 10, children: [ { type: "text", text: "<< carrots", start : 0, end : 10 } ] }, { type: "transclude", start: 12, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 20, isBlock: true } ] ); expect(parse("before\n\n<>")).toEqual( - [ { type: 'element', tag: 'p', start : 0, end : 6, children: [ { type: 'text', text: 'before', start : 0, end : 6 } ] }, { type: 'transclude', start: 8, rule: 'macrocallblock', attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 16, isBlock: true } ] + [ { type: "element", tag: "p", start : 0, end : 6, children: [ { type: "text", text: "before", start : 0, end : 6 } ] }, { type: "transclude", start: 8, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 16, isBlock: true } ] ); expect(parse("<>\nafter")).toEqual( - [ { type: 'transclude', start: 0, rule: 'macrocallblock', attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 8, isBlock: true }, { type: 'element', tag: 'p', start: 9, end: 14, children: [ { type: 'text', text: 'after', start: 9, end: 14 } ] } ] + [ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "john"} }, orderedAttributes: [ {name: "$variable", type:"string", value: "john"} ], end: 8, isBlock: true }, { type: "element", tag: "p", start: 9, end: 14, children: [ { type: "text", text: "after", start: 9, end: 14 } ] } ] ); expect(parse("<>")).toEqual( @@ -276,7 +276,7 @@ describe("WikiText parser tests", function() { ); expect(parse("<>' >>")).toEqual( - [ { type: 'transclude', start: 0, rule: 'macrocallblock', attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <>", start: 7, end: 26} ], end: 29, isBlock: true } ] + [ { type: "transclude", start: 0, rule: "macrocallblock", attributes: { $variable: {name: "$variable", type:"string", value: "outie"}, one: {name: "one", type:"string", value: "my <>", start: 7, end: 26} }, orderedAttributes: [ {name: "$variable", type:"string", value: "outie"}, {name: "one", type:"string", value: "my <>", start: 7, end: 26} ], end: 29, isBlock: true } ] ); }); @@ -309,7 +309,7 @@ describe("WikiText parser tests", function() { it("should parse horizontal rules", function() { expect(parse("---Not a rule\n\n----\n\nBetween\n\n---")).toEqual( - [ { type : 'element', tag : 'p', start : 0, end : 13, children : [ { type : 'entity', entity : '—', start: 0, end: 3, rule: 'dash' }, { type : 'text', text : 'Not a rule', start : 3, end : 13 } ] }, { type : 'element', tag : 'hr', start: 15, end: 20, rule: 'horizrule' }, { type : 'element', tag : 'p', start : 21, end : 28, children : [ { type : 'text', text : 'Between', start : 21, end : 28 } ] }, { type : 'element', tag : 'hr', start: 30, end: 33, rule: 'horizrule' } ] + [ { type : "element", tag : "p", start : 0, end : 13, children : [ { type : "entity", entity : "—", start: 0, end: 3, rule: "dash" }, { type : "text", text : "Not a rule", start : 3, end : 13 } ] }, { type : "element", tag : "hr", start: 15, end: 20, rule: "horizrule" }, { type : "element", tag : "p", start : 21, end : 28, children : [ { type : "text", text : "Between", start : 21, end : 28 } ] }, { type : "element", tag : "hr", start: 30, end: 33, rule: "horizrule" } ] ); @@ -318,99 +318,99 @@ describe("WikiText parser tests", function() { it("should parse hard linebreak areas", function() { expect(parse("\"\"\"Something\nin the\nway she moves\n\"\"\"\n\n")).toEqual( - [ { type : 'element', tag : 'p', children : [ { type : 'text', text : 'Something', start : 3, end : 12, rule: 'hardlinebreaks' }, { type : 'element', tag : 'br', rule: 'hardlinebreaks', start: 12, end: 13 }, { type : 'text', text : 'in the', start : 13, end : 19, rule: 'hardlinebreaks' }, { type : 'element', tag : 'br', rule: 'hardlinebreaks', start: 19, end: 20 }, { type : 'text', text : 'way she moves', start : 20, end : 33, rule: 'hardlinebreaks' }, { type : 'element', tag : 'br', rule: 'hardlinebreaks', start: 33, end: 34 } ], start : 0, end : 37 } ] + [ { type : "element", tag : "p", children : [ { type : "text", text : "Something", start : 3, end : 12, rule: "hardlinebreaks" }, { type : "element", tag : "br", rule: "hardlinebreaks", start: 12, end: 13 }, { type : "text", text : "in the", start : 13, end : 19, rule: "hardlinebreaks" }, { type : "element", tag : "br", rule: "hardlinebreaks", start: 19, end: 20 }, { type : "text", text : "way she moves", start : 20, end : 33, rule: "hardlinebreaks" }, { type : "element", tag : "br", rule: "hardlinebreaks", start: 33, end: 34 } ], start : 0, end : 37 } ] ); }); - it("should parse tables", function() { - let wikitext = ` -|!Cell1 |!Cell2 | -|Cell3 |Cell4 |`.trim(); + it("should parse tables", function() { + var wikitext = + "|!Cell1 |!Cell2 |\n" + + "|Cell3 |Cell4 |".trim(); - let expectedParseTree = [{ - type: 'element', - tag: 'table', + var expectedParseTree = [{ + type: "element", + tag: "table", start: 0, end: 33, - rule: 'table', + rule: "table", children: [{ - type: 'element', - tag: 'tbody', + type: "element", + tag: "tbody", start: 0, end: 33, children: [{ - type: 'element', - tag: 'tr', + type: "element", + tag: "tr", attributes: { - 'class': { name: 'class', type: 'string', value: 'evenRow' }, + "class": { name: "class", type: "string", value: "evenRow" }, }, orderedAttributes: [ - { name: 'class', type: 'string', value: 'evenRow' }, + { name: "class", type: "string", value: "evenRow" }, ], start: 0, end: 18, children: [{ - type: 'element', - tag: 'th', + type: "element", + tag: "th", attributes: { - 'align': { name: 'align', type: 'string', value: 'left' }, + "align": { name: "align", type: "string", value: "left" }, }, orderedAttributes: [ - { name: 'align', type: 'string', value: 'left' }, + { name: "align", type: "string", value: "left" }, ], start: 1, end: 8, - children: [{type: 'text', text: 'Cell1', start: 2, end: 7}], + children: [{type: "text", text: "Cell1", start: 2, end: 7}], }, { - type: 'element', - tag: 'th', + type: "element", + tag: "th", attributes: { - 'align': { name: 'align', type: 'string', value: 'left' }, + "align": { name: "align", type: "string", value: "left" }, }, orderedAttributes: [ - { name: 'align', type: 'string', value: 'left' }, + { name: "align", type: "string", value: "left" }, ], start: 9, end: 16, - children: [{type: 'text', text: 'Cell2', start: 10, end: 15}], + children: [{type: "text", text: "Cell2", start: 10, end: 15}], }], }, { - type: 'element', - tag: 'tr', + type: "element", + tag: "tr", attributes: { - 'class': { name: 'class', type: 'string', value: 'oddRow' }, + "class": { name: "class", type: "string", value: "oddRow" }, }, orderedAttributes: [ - { name: 'class', type: 'string', value: 'oddRow' }, + { name: "class", type: "string", value: "oddRow" }, ], start: 18, end: 33, children: [{ - type: 'element', - tag: 'td', + type: "element", + tag: "td", attributes: { - 'align': { name: 'align', type: 'string', value: 'left' }, + "align": { name: "align", type: "string", value: "left" }, }, orderedAttributes: [ - { name: 'align', type: 'string', value: 'left' }, + { name: "align", type: "string", value: "left" }, ], start: 19, end: 25, - children: [{type: 'text', text: 'Cell3', start: 19, end: 24}], + children: [{type: "text", text: "Cell3", start: 19, end: 24}], }, { - type: 'element', - tag: 'td', + type: "element", + tag: "td", attributes: { - 'align': { name: 'align', type: 'string', value: 'left' }, + "align": { name: "align", type: "string", value: "left" }, }, orderedAttributes: [ - { name: 'align', type: 'string', value: 'left' }, + { name: "align", type: "string", value: "left" }, ], start: 26, end: 32, - children: [{type: 'text', text: 'Cell4', start: 26, end: 31}], + children: [{type: "text", text: "Cell4", start: 26, end: 31}], }], }], }], @@ -420,32 +420,32 @@ describe("WikiText parser tests", function() { }); - it('should parse section marks', function () { + it("should parse section marks", function () { expect( parse( "There is a block id that is invisible, but you can find it using developer tool's inspect element feature. ^BlockLevelLinksID1" ) ).toEqual([ { - type: 'element', - tag: 'p', + type: "element", + tag: "p", children: [ { - type: 'text', + type: "text", text: "There is a block id that is invisible, but you can find it using developer tool's inspect element feature.", start: 0, end: 106, }, { - type: 'blockmark', + type: "blockmark", attributes: { - id: { type: 'string', value: 'BlockLevelLinksID1', start: 126, end: 144 }, - previousSibling: { type: 'string', value: '' }, + id: { type: "string", value: "BlockLevelLinksID1", start: 126, end: 144 }, + previousSibling: { type: "string", value: "" }, }, children: [], start: 106, end: 126, - rule: 'blockmark', + rule: "blockmark", }, ], start: 0, @@ -455,22 +455,22 @@ describe("WikiText parser tests", function() { }); - it('should parse link to section marks', function () { - expect(parse('[[Link to BlockLevelLinksID1|Block Level Links in WikiText^BlockLevelLinksID1]]')).toEqual([ + it("should parse link to section marks", function () { + expect(parse("[[Link to BlockLevelLinksID1|Block Level Links in WikiText^BlockLevelLinksID1]]")).toEqual([ { - type: 'element', - tag: 'p', + type: "element", + tag: "p", children: [ { - type: 'link', + type: "link", attributes: { - to: { type: 'string', value: 'Block Level Links in WikiText', start: 29, end: 58 }, - toBlockMark: { type: 'string', value: 'BlockLevelLinksID1', start: 59, end: 77 }, + to: { type: "string", value: "Block Level Links in WikiText", start: 29, end: 58 }, + toBlockMark: { type: "string", value: "BlockLevelLinksID1", start: 59, end: 77 }, }, - children: [{ type: 'text', text: 'Link to BlockLevelLinksID1', start: 2, end: 28 }], + children: [{ type: "text", text: "Link to BlockLevelLinksID1", start: 2, end: 28 }], start: 0, end: 79, - rule: 'prettylink', + rule: "prettylink", }, ], start: 0, From 8fcd08d7cae24969c6b61a9d7c4201056945cdcb Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Fri, 4 Oct 2024 00:49:29 +0800 Subject: [PATCH 41/43] fix: when currentTiddler not set, don't set title on id --- core/modules/widgets/blockmark.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/modules/widgets/blockmark.js b/core/modules/widgets/blockmark.js index 833e5227c..75b08d3a0 100644 --- a/core/modules/widgets/blockmark.js +++ b/core/modules/widgets/blockmark.js @@ -3,7 +3,7 @@ title: $:/core/modules/widgets/block-mark.js type: application/javascript module-type: widget -An invisible element with block mark id metadata. Marking a block level element, so that it can be jumped to. +An invisible element with block mark id metadata. Marking a block level element, so that it can be jumped to or transcluded. \*/ var Widget = require("$:/core/modules/widgets/widget.js").widget; var BlockMarkWidget = function(parseTreeNode,options) { @@ -21,9 +21,13 @@ BlockMarkWidget.prototype.render = function(parent,nextSibling) { // Create an invisible DOM element with data that can be accessed from JS or CSS this.idNode = this.document.createElement("span"); this.idNode.setAttribute("data-block-mark-id",this.blockMarkId); - this.idNode.setAttribute("data-block-mark-title",this.tiddlerTitle); - // id for anchor jumping in static site - this.idNode.setAttribute("id",this.tiddlerTitle + "-" + this.blockMarkId); + if(this.tiddlerTitle) { + this.idNode.setAttribute("data-block-mark-title",this.tiddlerTitle); + // id for anchor jumping in static site + this.idNode.setAttribute("id",this.tiddlerTitle + "-" + this.blockMarkId); + } else { + this.idNode.setAttribute("id",this.blockMarkId); + } // if the actual block is before this node, we need to add a flag to the node if(this.previousSibling) { this.idNode.setAttribute("data-block-mark-previous-sibling","true"); @@ -39,7 +43,7 @@ Compute the internal state of the widget BlockMarkWidget.prototype.execute = function() { // Get the id from the parse tree node or manually assigned attributes this.blockMarkId = this.getAttribute("id"); - this.tiddlerTitle = this.getVariable("currentTiddler"); + this.tiddlerTitle = this.getVariable("currentTiddler", ""); this.previousSibling = this.getAttribute("previousSibling") === "yes"; // Make the child widgets this.makeChildWidgets(); From 64def0bde8b12850fd663770c98d7610d7c494a4 Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Fri, 4 Oct 2024 00:49:55 +0800 Subject: [PATCH 42/43] test: rendered link with block mark --- editions/test/tiddlers/tests/test-wikitext.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/editions/test/tiddlers/tests/test-wikitext.js b/editions/test/tiddlers/tests/test-wikitext.js index eddef73f7..3c5e4b311 100644 --- a/editions/test/tiddlers/tests/test-wikitext.js +++ b/editions/test/tiddlers/tests/test-wikitext.js @@ -21,6 +21,7 @@ describe("WikiText tests", function() { wiki.addTiddler({title: "TiddlerTwo", text: "The rain in Spain\nfalls mainly on the plain"}); wiki.addTiddler({title: "TiddlerThree", text: "The speed of sound\n\nThe light of speed"}); wiki.addTiddler({title: "TiddlerFour", text: "\\define my-macro(adjective:'groovy')\nThis is my ''amazingly'' $adjective$ macro!\n\\end\n\n<$link to=<>>This is a link"}); + wiki.addTiddler({title: "TiddlerFive", text: "Paragraph. ^markID"}); it("should render tiddlers with no special markup as-is", function() { expect(wiki.renderTiddler("text/plain","TiddlerOne")).toBe("The quick brown fox"); @@ -69,6 +70,17 @@ describe("WikiText tests", function() { 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

'); }); + it("handles link wikitext notation", function() { + expect(wiki.renderText("text/html","text/vnd-tiddlywiki","A link to [[TiddlerFive]]")).toBe('

A link to TiddlerFive

' ); + wiki.deleteTiddler("TiddlerFive"); + expect(wiki.renderText("text/html","text/vnd-tiddlywiki","A link to [[TiddlerFive]]")).toBe('

A link to TiddlerFive

'); + }); + it("handles block mark wikitext notation", function() { + expect(wiki.renderText("text/html","text/vnd-tiddlywiki","Link to section [[TiddlerFive^markID]]")).toBe('

Link to section TiddlerFive

' ); + // It has title as namespace in id when `currentTiddler` is set + expect(wiki.renderTiddler("text/html","TiddlerFive", { variables: { currentTiddler: "TiddlerFive" }})).toBe('

Paragraph.

'); + expect(wiki.renderTiddler("text/html","TiddlerFive")).toBe('

Paragraph.

'); + }); }); })(); From ba63ea2a7d1b6e2c7ab2f8c5259420d3447e548b Mon Sep 17 00:00:00 2001 From: lin onetwo Date: Fri, 4 Oct 2024 00:59:54 +0800 Subject: [PATCH 43/43] Update test-wikitext.js --- editions/test/tiddlers/tests/test-wikitext.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/editions/test/tiddlers/tests/test-wikitext.js b/editions/test/tiddlers/tests/test-wikitext.js index 3c5e4b311..3cdc5cc07 100644 --- a/editions/test/tiddlers/tests/test-wikitext.js +++ b/editions/test/tiddlers/tests/test-wikitext.js @@ -72,8 +72,10 @@ describe("WikiText tests", function() { }); it("handles link wikitext notation", function() { expect(wiki.renderText("text/html","text/vnd-tiddlywiki","A link to [[TiddlerFive]]")).toBe('

A link to TiddlerFive

' ); + var tiddler = wiki.getTiddler("TiddlerFive"); wiki.deleteTiddler("TiddlerFive"); expect(wiki.renderText("text/html","text/vnd-tiddlywiki","A link to [[TiddlerFive]]")).toBe('

A link to TiddlerFive

'); + wiki.addTiddler(tiddler); }); it("handles block mark wikitext notation", function() { expect(wiki.renderText("text/html","text/vnd-tiddlywiki","Link to section [[TiddlerFive^markID]]")).toBe('

Link to section TiddlerFive

' );