diff --git a/core/language/en-GB/Misc.multids b/core/language/en-GB/Misc.multids
index b5e6e2374..a806774c1 100644
--- a/core/language/en-GB/Misc.multids
+++ b/core/language/en-GB/Misc.multids
@@ -37,6 +37,7 @@ Error/NetworkErrorAlert: `
''Network Error''
It looks like the connection
Error/PutEditConflict: File changed on server
Error/PutForbidden: Permission denied
Error/PutUnauthorized: Authentication required
+Error/RecursiveButton: Possible Recursive Error: Button in button is not allowed
Error/RecursiveTransclusion: Recursive transclusion error in transclude widget
Error/RetrievingSkinny: Error retrieving skinny tiddler list
Error/SavingToTWEdit: Error saving to TWEdit
diff --git a/core/modules/widgets/button.js b/core/modules/widgets/button.js
index 958b6f6da..633514f45 100644
--- a/core/modules/widgets/button.js
+++ b/core/modules/widgets/button.js
@@ -18,6 +18,10 @@ var Popup = require("$:/core/modules/utils/dom/popup.js");
var ButtonWidget = function(parseTreeNode,options) {
this.initialise(parseTreeNode,options);
+ // Check if any parent is a button. Custom recursion detection for buttons in buttons
+ if(!this.hasVariable("tv-is-button","yes")) {
+ this.setVariable("tv-is-button", "yes");
+ }
};
/*
@@ -37,6 +41,17 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
// Compute attributes and execute state
this.computeAttributes();
this.execute();
+ // Check "button in button". Return early with an error message
+ // This check also prevents fatal recursion errors using the transclusion widget
+ if(this.parentWidget && this.parentWidget.hasVariable("tv-is-button","yes")) {
+ var domNode = this.document.createElement("span");
+ var textNode = this.document.createTextNode($tw.language.getString("Error/RecursiveButton"));
+ domNode.appendChild(textNode);
+ domNode.className = "tc-error";
+ parent.insertBefore(domNode,nextSibling);
+ this.domNodes.push(domNode);
+ return;
+ }
// Create element
if(this.buttonTag && $tw.config.htmlUnsafeElements.indexOf(this.buttonTag) === -1) {
tag = this.buttonTag;
@@ -74,7 +89,7 @@ ButtonWidget.prototype.render = function(parent,nextSibling) {
if(this["aria-label"]) {
domNode.setAttribute("aria-label",this["aria-label"]);
}
- if (this.role) {
+ if(this.role) {
domNode.setAttribute("role", this.role);
}
if(this.popup || this.popupTitle) {
diff --git a/editions/test/tiddlers/tests/data/transclude/Recursion-Button-Transclusion.tid b/editions/test/tiddlers/tests/data/transclude/Recursion-Button-Transclusion.tid
new file mode 100644
index 000000000..c6a08e1d2
--- /dev/null
+++ b/editions/test/tiddlers/tests/data/transclude/Recursion-Button-Transclusion.tid
@@ -0,0 +1,15 @@
+title: Transclude/Recursion/Button
+description: Transclusion recursion inside a button
+type: text/vnd.tiddlywiki-multiple
+tags: [[$:/tags/wiki-test-spec]]
+
+title: Output
+
+\whitespace trim
+<$button>
+<$transclude/>
+$button>
++
+title: ExpectedResult
+
+
\ No newline at end of file
diff --git a/editions/test/tiddlers/tests/data/transclude/Recursion-Button-in-Button.tid b/editions/test/tiddlers/tests/data/transclude/Recursion-Button-in-Button.tid
new file mode 100644
index 000000000..f90afb134
--- /dev/null
+++ b/editions/test/tiddlers/tests/data/transclude/Recursion-Button-in-Button.tid
@@ -0,0 +1,15 @@
+title: Transclude/Recursion/ButtonInButton
+description: Button in Button
+type: text/vnd.tiddlywiki-multiple
+tags: [[$:/tags/wiki-test-spec]]
+
+title: Output
+
+\whitespace trim
+<$button>Test Button
+<$button>Invalid button
+$button>
++
+title: ExpectedResult
+
+
\ No newline at end of file