Compare commits

...

8 Commits

Author SHA1 Message Date
pmario 0e7caca935 remove commented code 2024-04-25 18:13:09 +02:00
pmario a53c6b5e5c remove empty class="" from button widget to simplify DOM 2024-04-25 18:07:59 +02:00
pmario b4c369adc4 fix tests 2024-04-25 18:07:12 +02:00
pmario 3c888992a8 add options.hasDom to widgets, that create DOM elements 2024-04-25 16:59:05 +02:00
pmario bb9c991ce3 button use tv-limit-nested-buttons variable if set 2024-04-25 16:57:56 +02:00
pmario 05f1ba20ca widget add dom-tree-depth detection 2024-04-25 16:57:23 +02:00
pmario d28271620b some new tests 2024-04-25 16:56:49 +02:00
pmario 1a4c114ecb add parent-ancestorcount-dom macro 2024-04-25 15:18:54 +02:00
36 changed files with 155 additions and 41 deletions

View File

@ -0,0 +1,26 @@
/*\
title: $:/core/modules/macros/parent-ancestorcount-dom.js
type: application/javascript
module-type: macro
Macro to return the parent widget this.ancestors variable
\*/
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Information about this macro
*/
exports.name = "parent-ancestorcount-dom";
exports.params = [];
/*
Run the macro
*/
exports.run = function() {
return (this.parentWidget) ? this.parentWidget.getAncestorCountDom() + "" : "";
};

View File

@ -15,6 +15,7 @@ Browse widget for browsing for files to import
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var BrowseWidget = function(parseTreeNode,options) { var BrowseWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -17,6 +17,7 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget;
var Popup = require("$:/core/modules/utils/dom/popup.js"); var Popup = require("$:/core/modules/utils/dom/popup.js");
var ButtonWidget = function(parseTreeNode,options) { var ButtonWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };
@ -56,14 +57,16 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
this.execute(); this.execute();
// Check "button in button". Return early with an error message // Check "button in button". Return early with an error message
// This check also prevents fatal recursion errors using the transclusion widget // This check also prevents fatal recursion errors using the transclusion widget
if(this.isNestedButton()) { if(this.getVariable("tv-limit-nested-buttons") === "yes") {
var domNode = this.document.createElement("span"); if(this.isNestedButton()) {
var textNode = this.document.createTextNode($tw.language.getString("Error/RecursiveButton")); var domNode = this.document.createElement("span");
domNode.appendChild(textNode); var textNode = this.document.createTextNode($tw.language.getString("Error/RecursiveButton"));
domNode.className = "tc-error"; domNode.appendChild(textNode);
parent.insertBefore(domNode,nextSibling); domNode.className = "tc-error";
this.domNodes.push(domNode); parent.insertBefore(domNode,nextSibling);
return; // an error message this.domNodes.push(domNode);
return; // an error message
}
} }
// Create element // Create element
if(this.buttonTag && $tw.config.htmlUnsafeElements.indexOf(this.buttonTag) === -1) { if(this.buttonTag && $tw.config.htmlUnsafeElements.indexOf(this.buttonTag) === -1) {
@ -72,7 +75,7 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
domNode = this.document.createElement(tag); domNode = this.document.createElement(tag);
this.domNode = domNode; this.domNode = domNode;
// Assign classes // Assign classes
var classes = this["class"].split(" ") || [], var classes = (this["class"]) ? this["class"].split(" ") : [],
isPoppedUp = (this.popup || this.popupTitle) && this.isPoppedUp(); isPoppedUp = (this.popup || this.popupTitle) && this.isPoppedUp();
if(this.selectedClass) { if(this.selectedClass) {
if((this.set || this.setTitle) && this.setTo && this.isSelected()) { if((this.set || this.setTitle) && this.setTo && this.isSelected()) {
@ -86,7 +89,9 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
if(isPoppedUp) { if(isPoppedUp) {
$tw.utils.pushTop(classes,"tc-popup-handle"); $tw.utils.pushTop(classes,"tc-popup-handle");
} }
domNode.className = classes.join(" "); if(classes.length > 0) {
domNode.className = classes.join(" ");
}
// Assign data- attributes // Assign data- attributes
this.assignAttributes(domNode,{ this.assignAttributes(domNode,{
sourcePrefix: "data-", sourcePrefix: "data-",

View File

@ -15,6 +15,7 @@ Checkbox widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var CheckboxWidget = function(parseTreeNode,options) { var CheckboxWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Code block node widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var CodeBlockWidget = function(parseTreeNode,options) { var CodeBlockWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -16,6 +16,7 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget,
dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js"); dmp = require("$:/core/modules/utils/diff-match-patch/diff_match_patch.js");
var DiffTextWidget = function(parseTreeNode,options) { var DiffTextWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Draggable widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var DraggableWidget = function(parseTreeNode,options) { var DraggableWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Droppable widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var DroppableWidget = function(parseTreeNode,options) { var DroppableWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -17,6 +17,7 @@ var IMPORT_TITLE = "$:/Import";
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var DropZoneWidget = function(parseTreeNode,options) { var DropZoneWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -25,6 +25,7 @@ var LINE_WIDTH_TITLE = "$:/config/BitmapEditor/LineWidth",
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var EditBitmapWidget = function(parseTreeNode,options) { var EditBitmapWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Widget to display an editable keyboard shortcut
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var EditShortcutWidget = function(parseTreeNode,options) { var EditShortcutWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Element widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var ElementWidget = function(parseTreeNode,options) { var ElementWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Error widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var ErrorWidget = function(parseTreeNode,options) { var ErrorWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Event handler widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var EventWidget = function(parseTreeNode,options) { var EventWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -31,6 +31,7 @@ The width and height attributes are interpreted as a number of pixels, and do no
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var ImageWidget = function(parseTreeNode,options) { var ImageWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };
@ -65,10 +66,10 @@ ImageWidget.prototype.render = function(parent,nextSibling) {
if(text) { if(text) {
// Render the appropriate element for the image type by looking up the encoding in the content type info // Render the appropriate element for the image type by looking up the encoding in the content type info
var encoding = typeInfo.encoding || "utf8"; var encoding = typeInfo.encoding || "utf8";
if (encoding === "base64") { if(encoding === "base64") {
// .pdf .png .jpg etc. // .pdf .png .jpg etc.
src = "data:" + deserializerType + ";base64," + text; src = "data:" + deserializerType + ";base64," + text;
if (deserializerType === "application/pdf") { if(deserializerType === "application/pdf") {
tag = "embed"; tag = "embed";
} }
} else { } else {

View File

@ -15,6 +15,7 @@ Keyboard shortcut widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var KeyboardWidget = function(parseTreeNode,options) { var KeyboardWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };
@ -53,7 +54,7 @@ KeyboardWidget.prototype.render = function(parent,nextSibling) {
}; };
KeyboardWidget.prototype.handleChangeEvent = function(event) { KeyboardWidget.prototype.handleChangeEvent = function(event) {
if ($tw.keyboardManager.handleKeydownEvent(event, {onlyPriority: true})) { if($tw.keyboardManager.handleKeydownEvent(event, {onlyPriority: true})) {
return true; return true;
} }

View File

@ -15,6 +15,7 @@ Link widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var LinkWidget = function(parseTreeNode,options) { var LinkWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Password widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var PasswordWidget = function(parseTreeNode,options) { var PasswordWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -14,6 +14,7 @@ Set a field or index at a given tiddler via radio buttons
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var RadioWidget = function(parseTreeNode,options) { var RadioWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -15,6 +15,7 @@ Range widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var RangeWidget = function(parseTreeNode,options) { var RangeWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };
@ -114,8 +115,8 @@ RangeWidget.prototype.handleMouseUpEvent = function(event) {
this.invokeActionString(this.actionsMouseUp,this,event,variables); this.invokeActionString(this.actionsMouseUp,this,event,variables);
} }
// TODO remove the following if() once IE is gone! // TODO remove the following if() once IE is gone!
if ($tw.browser.isIE) { if($tw.browser.isIE) {
if (this.startValue !== this.inputDomNode.value) { if(this.startValue !== this.inputDomNode.value) {
this.handleChangeEvent(event); this.handleChangeEvent(event);
this.startValue = this.inputDomNode.value; this.startValue = this.inputDomNode.value;
} }
@ -123,7 +124,7 @@ RangeWidget.prototype.handleMouseUpEvent = function(event) {
} }
RangeWidget.prototype.handleChangeEvent = function(event) { RangeWidget.prototype.handleChangeEvent = function(event) {
if (this.mouseDown) { // TODO refactor this function once IE is gone. if(this.mouseDown) { // TODO refactor this function once IE is gone.
this.handleInputEvent(event); this.handleInputEvent(event);
} }
}; };

View File

@ -15,6 +15,7 @@ Raw widget
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var RawWidget = function(parseTreeNode,options) { var RawWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -17,6 +17,7 @@ var Widget = require("$:/core/modules/widgets/widget.js").widget;
var Popup = require("$:/core/modules/utils/dom/popup.js"); var Popup = require("$:/core/modules/utils/dom/popup.js");
var RevealWidget = function(parseTreeNode,options) { var RevealWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };
@ -96,9 +97,9 @@ RevealWidget.prototype.positionPopup = function(domNode) {
left = Math.max(0,left); left = Math.max(0,left);
top = Math.max(0,top); top = Math.max(0,top);
} }
if (this.popup.absolute) { if(this.popup.absolute) {
// Traverse the offsetParent chain and correct the offset to make it relative to the parent node. // Traverse the offsetParent chain and correct the offset to make it relative to the parent node.
for (var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) { for(var offsetParentDomNode = domNode.offsetParent; offsetParentDomNode; offsetParentDomNode = offsetParentDomNode.offsetParent) {
left -= offsetParentDomNode.offsetLeft; left -= offsetParentDomNode.offsetLeft;
top -= offsetParentDomNode.offsetTop; top -= offsetParentDomNode.offsetTop;
} }

View File

@ -17,6 +17,7 @@ var DEBOUNCE_INTERVAL = 100; // Delay after last scroll event before updating th
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var ScrollableWidget = function(parseTreeNode,options) { var ScrollableWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -25,6 +25,7 @@ Select widget:
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var SelectWidget = function(parseTreeNode,options) { var SelectWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };
@ -118,7 +119,7 @@ SelectWidget.prototype.setSelectValue = function() {
} }
} }
// Assign it to the select element if it's different than the current value // Assign it to the select element if it's different than the current value
if (this.selectMultiple) { if(this.selectMultiple) {
value = value === undefined ? "" : value; value = value === undefined ? "" : value;
var select = this.getSelectDomNode(); var select = this.getSelectDomNode();
var values = Array.isArray(value) ? value : $tw.utils.parseStringArray(value); var values = Array.isArray(value) ? value : $tw.utils.parseStringArray(value);
@ -147,9 +148,9 @@ SelectWidget.prototype.getSelectValues = function() {
select = this.getSelectDomNode(); select = this.getSelectDomNode();
result = []; result = [];
options = select && select.options; options = select && select.options;
for (var i=0; i<options.length; i++) { for(var i=0; i<options.length; i++) {
opt = options[i]; opt = options[i];
if (opt.selected) { if(opt.selected) {
result.push(opt.value || opt.text); result.push(opt.value || opt.text);
} }
} }

View File

@ -14,6 +14,7 @@ Widget base class
/* Maximum permitted depth of the widget tree for recursion detection */ /* Maximum permitted depth of the widget tree for recursion detection */
var MAX_WIDGET_TREE_DEPTH = 1000; var MAX_WIDGET_TREE_DEPTH = 1000;
var MAX_DOM_TREE_DEPTH = 100;
/* /*
Create a widget object for a parse tree node Create a widget object for a parse tree node
@ -65,6 +66,23 @@ Widget.prototype.initialise = function(parseTreeNode,options) {
} }
}); });
} }
// Increment ancestorCountDom if options.hasDom = true otherwise set it to the "old" value
this.getAncestorCountDom(options.hasDom);
};
Widget.prototype.getAncestorCountDom = function(createsDomNode) {
if(this.ancestorCountDom === undefined) {
if(this.parentWidget) {
if(createsDomNode) {
this.ancestorCountDom = this.parentWidget.getAncestorCountDom() + 1;
} else {
this.ancestorCountDom = this.parentWidget.getAncestorCountDom();
}
} else {
this.ancestorCountDom = 0;
}
}
return this.ancestorCountDom;
}; };
/* /*
@ -496,10 +514,18 @@ Widget.prototype.makeChildWidgets = function(parseTreeNodes,options) {
this.children = []; this.children = [];
var self = this; var self = this;
// Check for too much recursion // Check for too much recursion
if(this.getAncestorCount() > this.UNSAFE_max_widget_tree_depth) { // if(this.getAncestorCount() > this.UNSAFE_max_widget_tree_depth) {
this.children.push(this.makeChildWidget({type: "error", attributes: { // if(this.getAncestorCount() > MAX_WIDGET_TREE_DEPTH) {
"$message": {type: "string", value: this.getAncestorCount() + " - " + $tw.language.getString("Error/RecursiveTransclusion")} if((this.getAncestorCountDom() > MAX_DOM_TREE_DEPTH) || (this.getAncestorCount() > this.UNSAFE_max_widget_tree_depth)) {
}})); this.children.push(this.makeChildWidget({
type: "error", attributes: {
"$message": {
type: "string",
value: this.getAncestorCount() + " - " + (this.getAncestorCountDom() + 1 ) + " - " +
$tw.language.getString("Error/RecursiveTransclusion")
}
}
}));
} else { } else {
// Create set variable widgets for each variable // Create set variable widgets for each variable
$tw.utils.each(options.variables,function(value,name) { $tw.utils.each(options.variables,function(value,name) {

View File

@ -15,6 +15,7 @@ Widget to wikify text into a variable
var Widget = require("$:/core/modules/widgets/widget.js").widget; var Widget = require("$:/core/modules/widgets/widget.js").widget;
var WikifyWidget = function(parseTreeNode,options) { var WikifyWidget = function(parseTreeNode,options) {
options.hasDom = true;
this.initialise(parseTreeNode,options); this.initialise(parseTreeNode,options);
}; };

View File

@ -12,4 +12,4 @@ title: Output
+ +
title: ExpectedResult title: ExpectedResult
<p><button class=""><button class=""><button class=""><span class="tc-error">Possible Recursive Error: Button in button is not allowed</span></button></button></button></p> <p><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><button><span class="tc-error">202 - 102 - Recursive transclusion error in transclude widget</span></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></button></p>

View File

@ -12,4 +12,4 @@ title: Output
+ +
title: ExpectedResult title: ExpectedResult
<p><button class="">Test Button<button class="">Second button</button></button></p> <p><button>Test Button<button>Second button</button></button></p>

View File

@ -10,7 +10,9 @@ title: Output
title: Button title: Button
<$button><<parent-ancestorcount>></$button> <$button><<parent-ancestorcount>></$button>
<$button><<parent-ancestorcount-dom>></$button>
+ +
title: ExpectedResult title: ExpectedResult
<p>1000 - <button class="">6</button></p> <p>1000 - <button>6</button>
<button>2</button></p>

View File

@ -10,4 +10,4 @@ title: Output
+ +
title: ExpectedResult title: ExpectedResult
<p><span class="tc-error">1001 - Recursive transclusion error in transclude widget</span></p> <p><span class="tc-error">1001 - 2 - Recursive transclusion error in transclude widget</span></p>

View File

@ -157,7 +157,7 @@ describe("Widget module", function() {
// Render the widget node to the DOM // Render the widget node to the DOM
var wrapper = renderWidgetNode(widgetNode); var wrapper = renderWidgetNode(widgetNode);
// Test the rendering // Test the rendering
expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">1001 - Recursive transclusion error in transclude widget</span>"); expect(wrapper.innerHTML).toBe("<span class=\"tc-error\">1001 - 1 - Recursive transclusion error in transclude widget</span>");
}); });
it("should deal with SVG elements", function() { it("should deal with SVG elements", function() {

View File

@ -1,5 +1,5 @@
created: 20240415102801809 created: 20240415102801809
modified: 20240418072358045 modified: 20240425142244830
tags: tags:
title: test-collatz title: test-collatz
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@ -8,15 +8,12 @@ type: text/vnd.tiddlywiki
<% if [<n>match[1]] %> <% if [<n>match[1]] %>
1 1
<% elseif [<n>remainder[2]match[1]] %> <% elseif [<n>remainder[2]match[1]] %>
<<n>> → <$transclude $variable="collatz" n = {{{ [<n>multiply[3]add[1]] }}}/> <<n>> - ^^@@<<parent-ancestorcount>>@@^^ → <$transclude $variable="collatz" n = {{{ [<n>multiply[3]add[1]] }}}/>
<% else %> <% else %>
<<n>> → <$transclude $variable="collatz" n = {{{ [<n>divide[2]] }}}/> <<n>> - ^^@@<<parent-ancestorcount>>@@^^ → <$transclude $variable="collatz" n = {{{ [<n>divide[2]] }}}/>
<% endif %> <% endif %>
\end \end
\procedure test(x:10) <<set_UNSAFE_max_widget_tree_depth 5000>>
<<set_UNSAFE_max_widget_tree_depth 2000>>
<$transclude $variable=collatz n=<<x>>/>
\end
<<test 77031>> <<collatz 9780657630>>

View File

@ -0,0 +1,16 @@
created: 20240425123038613
modified: 20240425142745826
tags:
title: test-div
type: text/vnd.tiddlywiki
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10
<div><div><div><div><div><div><div><div><div><div>10

View File

@ -1,5 +1,5 @@
created: 20240417070900019 created: 20240417070900019
modified: 20240418072413015 modified: 20240425133540950
tags: tags:
title: test-new-set-get-macros title: test-new-set-get-macros
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
@ -8,6 +8,8 @@ type: text/vnd.tiddlywiki
ancestor count: <<parent-ancestorcount>> ancestor count: <<parent-ancestorcount>>
ancestor dom count: <<parent-ancestorcount-dom>>
UNSAFE_max_widget_tree_depth: <<get_UNSAFE_max_widget_tree_depth>> UNSAFE_max_widget_tree_depth: <<get_UNSAFE_max_widget_tree_depth>>
<<set_UNSAFE_max_widget_tree_depth 2000>> <<set_UNSAFE_max_widget_tree_depth 2000>>

View File

@ -0,0 +1,14 @@
created: 20240425142531569
modified: 20240425142540369
tags:
title: test-recursive button limited
type: text/vnd.tiddlywiki
\whitespace trim
\define tv-limit-nested-buttons() yes
<<parent-ancestorcount-dom>>
<$button>
<$transclude/>
</$button>

View File

@ -1,10 +1,13 @@
created: 20240415124530887 created: 20240415124530887
modified: 20240416111839904 modified: 20240425142547412
tags: tags:
title: test-recursive button title: test-recursive button
type: text/vnd.tiddlywiki type: text/vnd.tiddlywiki
\whitespace trim \whitespace trim
<<parent-ancestorcount-dom>>
<$button> <$button>
<$transclude/> <$transclude/>
</$button> </$button>