diff --git a/core/modules/widgets/macrocall.js b/core/modules/widgets/macrocall.js index 02bc8cce5..23522629b 100644 --- a/core/modules/widgets/macrocall.js +++ b/core/modules/widgets/macrocall.js @@ -48,7 +48,9 @@ MacroCallWidget.prototype.execute = function() { } }); // Get the macro value - var text = this.getVariable(this.parseTreeNode.name || this.getAttribute("$name"),{params: params}), + var macroName = this.parseTreeNode.name || this.getAttribute("$name"), + variableInfo = this.getVariableInfo(macroName,{params: params}), + text = variableInfo.text, parseTreeNodes; // Are we rendering to HTML? if(this.renderOutput === "text/html") { @@ -56,6 +58,21 @@ MacroCallWidget.prototype.execute = function() { var parser = this.wiki.parseText(this.parseType,text, {parseAsInline: !this.parseTreeNode.isBlock}); parseTreeNodes = parser ? parser.tree : []; + // Wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__" + var attributes = {}; + $tw.utils.each(variableInfo.params,function(param) { + var name = "__" + param.name + "__"; + attributes[name] = { + name: name, + type: "string", + value: param.value + }; + }); + parseTreeNodes = [{ + type: "vars", + attributes: attributes, + children: parseTreeNodes + }]; } else { // Otherwise, we'll render the text var plainText = this.wiki.renderText("text/plain",this.parseType,text,{parentWidget: this}); diff --git a/core/modules/widgets/widget.js b/core/modules/widgets/widget.js index 07c5aa7cc..a55d8adf3 100755 --- a/core/modules/widgets/widget.js +++ b/core/modules/widgets/widget.js @@ -83,52 +83,73 @@ options: see below Options include params: array of {name:, value:} for each parameter defaultValue: default value if the variable is not defined + +Returns an object with the following fields: + +params: array of {name:,value:} of parameters passed to wikitext variables +text: text of variable, with parameters properly substituted */ -Widget.prototype.getVariable = function(name,options) { +Widget.prototype.getVariableInfo = function(name,options) { options = options || {}; var actualParams = options.params || [], parentWidget = this.parentWidget; // Check for the variable defined in the parent widget (or an ancestor in the prototype chain) if(parentWidget && name in parentWidget.variables) { var variable = parentWidget.variables[name], - value = variable.value; + value = variable.value, + params = this.resolveVariableParameters(variable.params,actualParams); // Substitute any parameters specified in the definition - value = this.substituteVariableParameters(value,variable.params,actualParams); + $tw.utils.each(params,function(param) { + value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value); + }); value = this.substituteVariableReferences(value); - return value; + return { + text: value, + params: params + }; } // If the variable doesn't exist in the parent widget then look for a macro module - return this.evaluateMacroModule(name,actualParams,options.defaultValue); + return { + text: this.evaluateMacroModule(name,actualParams,options.defaultValue) + }; }; -Widget.prototype.substituteVariableParameters = function(text,formalParams,actualParams) { - if(formalParams) { - var nextAnonParameter = 0, // Next candidate anonymous parameter in macro call - paramInfo, paramValue; - // Step through each of the parameters in the macro definition - for(var p=0; p """/> +!! Parameters as Variables + +The parameters to a wikitext macro are also available as special variables named as the parameter name wrapped in double underscores. For example, the example above could also be expressed as: + +``` +\define sayhi(name:"Bugs Bunny") Hi, I'm <$text text=<<__name__>>/>. +``` + +Accessing parameters as variables only works in macros that are wikified and not, for example, when a macro is used as an attribute value. The advantage of the technique is that it avoids the parameter value being substituted into the macro as a literal string, which in turn can help avoid issues with parameters that contain quotes. + +For example, consider this macro. It is intended to wrap a DIV around another macro invocation, passing through the single parameter to the inner macro: + +``` +\define related-tags(base-tag) +
+<$macrocall $name="anothermacro" param="""$base-tag$"""/> +
+\end +``` + +The code above will fail if the macro is invoked with the argument containing triple double quotes (for example `<>`). Using parameter variables offers a workaround: + +``` +\define related-tags(base-tag) +
+<$macrocall $name="anothermacro" param=<<__base-tag__>>/> +
+\end +``` + !! Scope Macros are available to the tiddler that defines them, plus any tiddlers that it transcludes.