mirror of
				https://github.com/Jermolene/TiddlyWiki5
				synced 2025-10-31 07:32:59 +00:00 
			
		
		
		
	Fixes to enable the transclude widget itself to be overridden
There are two big changes here: Replace the previous "ts-wrapper" mechanism, which we had been using to redefine custom widgets inside their definitions to prevent recursive calls. Now we've got the genesis widget we can instead control recursion through a new "$remappable" attribute that allows the custom widget mechanism to be skipped. We also extend the slot widget to allow a depth to be specified; it then reaches up by the indicated number of transclusion widgets to find the one from which it should retrieve the slot value.
This commit is contained in:
		| @@ -12,8 +12,13 @@ Parse tree utility functions. | |||||||
| /*global $tw: false */ | /*global $tw: false */ | ||||||
| "use strict"; | "use strict"; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Add attribute to parse tree node | ||||||
|  | Can be invoked as (node,name,value) or (node,attr) | ||||||
|  | */ | ||||||
| exports.addAttributeToParseTreeNode = function(node,name,value) { | exports.addAttributeToParseTreeNode = function(node,name,value) { | ||||||
| 	var attribute = {name: name, type: "string", value: value}; | 	var attribute = typeof name === "object" ? name : {name: name, type: "string", value: value}; | ||||||
|  | 	name = attribute.name; | ||||||
| 	node.attributes = node.attributes || {}; | 	node.attributes = node.attributes || {}; | ||||||
| 	node.orderedAttributes = node.orderedAttributes || []; | 	node.orderedAttributes = node.orderedAttributes || []; | ||||||
| 	node.attributes[name] = attribute; | 	node.attributes[name] = attribute; | ||||||
|   | |||||||
| @@ -41,6 +41,7 @@ GenesisWidget.prototype.execute = function() { | |||||||
| 	// Collect attributes | 	// Collect attributes | ||||||
| 	this.genesisType = this.getAttribute("$type","element"); | 	this.genesisType = this.getAttribute("$type","element"); | ||||||
| 	this.genesisTag = this.getAttribute("$tag","div"); | 	this.genesisTag = this.getAttribute("$tag","div"); | ||||||
|  | 	this.genesisRemappable = this.getAttribute("$remappable","yes") === "yes"; | ||||||
| 	this.genesisNames = this.getAttribute("$names",""); | 	this.genesisNames = this.getAttribute("$names",""); | ||||||
| 	this.genesisValues = this.getAttribute("$values",""); | 	this.genesisValues = this.getAttribute("$values",""); | ||||||
| 	// Construct parse tree | 	// Construct parse tree | ||||||
| @@ -49,7 +50,8 @@ GenesisWidget.prototype.execute = function() { | |||||||
| 		tag: this.genesisTag, | 		tag: this.genesisTag, | ||||||
| 		attributes: {}, | 		attributes: {}, | ||||||
| 		orderedAttributes: [], | 		orderedAttributes: [], | ||||||
| 		children: this.parseTreeNode.children || [] | 		children: this.parseTreeNode.children || [], | ||||||
|  | 		isNotRemappable: !this.genesisRemappable | ||||||
| 	}]; | 	}]; | ||||||
| 	// Apply attributes in $names/$values | 	// Apply attributes in $names/$values | ||||||
| 	this.attributeNames = []; | 	this.attributeNames = []; | ||||||
|   | |||||||
| @@ -54,8 +54,9 @@ ParametersWidget.prototype.execute = function() { | |||||||
| 				value = transclusionWidget.getTransclusionParameter(name,index,self.getAttribute(name)); | 				value = transclusionWidget.getTransclusionParameter(name,index,self.getAttribute(name)); | ||||||
| 			self.setVariable(name,value); | 			self.setVariable(name,value); | ||||||
| 		}); | 		}); | ||||||
| 		this.setVariable("paramNames",$tw.utils.stringifyList(transclusionWidget.getTransclusionParameterNames())); | 		$tw.utils.each(transclusionWidget.getTransclusionMetaVariables(),function(value,name) { | ||||||
| 		this.setVariable("paramValues",$tw.utils.stringifyList(transclusionWidget.getTransclusionParameterValues())); | 			self.setVariable(name,value); | ||||||
|  | 		}); | ||||||
| 	} | 	} | ||||||
| 	// Construct the child widgets | 	// Construct the child widgets | ||||||
| 	this.makeChildWidgets(); | 	this.makeChildWidgets(); | ||||||
|   | |||||||
| @@ -43,13 +43,24 @@ Compute the internal state of the widget | |||||||
| SlotWidget.prototype.execute = function() { | SlotWidget.prototype.execute = function() { | ||||||
| 	var self = this; | 	var self = this; | ||||||
| 	this.slotName = this.getAttribute("$name"); | 	this.slotName = this.getAttribute("$name"); | ||||||
| 	// Find the parent transclusion | 	this.slotDepth = parseInt(this.getAttribute("$depth","1"),10) || 1; | ||||||
| 	var transclusionWidget = this.parentWidget; | 	// Find the parent transclusions | ||||||
| 	while(transclusionWidget && !(transclusionWidget instanceof TranscludeWidget)) { | 	var pointer = this.parentWidget, | ||||||
| 		transclusionWidget = transclusionWidget.parentWidget; | 		depth = this.slotDepth; | ||||||
|  | 	while(pointer) { | ||||||
|  | 		if(pointer instanceof TranscludeWidget) { | ||||||
|  | 			depth--; | ||||||
|  | 			if(depth === 0) { | ||||||
|  | 				break; | ||||||
| 			} | 			} | ||||||
|  | 		} | ||||||
|  | 		pointer = pointer.parentWidget; | ||||||
|  | 	} | ||||||
|  | 	var parseTreeNodes = [{type: "text", attributes: {text: {type: "string", value: "Missing slot reference!"}}}]; | ||||||
|  | 	if(pointer instanceof TranscludeWidget) { | ||||||
| 		// Get the parse tree nodes comprising the slot contents | 		// Get the parse tree nodes comprising the slot contents | ||||||
| 	var parseTreeNodes = transclusionWidget.getTransclusionSlotValue(this.slotName,this.parseTreeNode.children); | 		parseTreeNodes = pointer.getTransclusionSlotValue(this.slotName,this.parseTreeNode.children); | ||||||
|  | 	} | ||||||
| 	// Construct the child widgets | 	// Construct the child widgets | ||||||
| 	this.makeChildWidgets(parseTreeNodes); | 	this.makeChildWidgets(parseTreeNodes); | ||||||
| }; | }; | ||||||
| @@ -59,7 +70,7 @@ Refresh the widget by ensuring our attributes are up to date | |||||||
| */ | */ | ||||||
| SlotWidget.prototype.refresh = function(changedTiddlers) { | SlotWidget.prototype.refresh = function(changedTiddlers) { | ||||||
| 	var changedAttributes = this.computeAttributes(); | 	var changedAttributes = this.computeAttributes(); | ||||||
| 	if(changedAttributes["$name"]) { | 	if(changedAttributes["$name"] || changedAttributes["$depth"]) { | ||||||
| 		this.refreshSelf(); | 		this.refreshSelf(); | ||||||
| 		return true; | 		return true; | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -46,13 +46,7 @@ TranscludeWidget.prototype.execute = function() { | |||||||
| 		parseTreeNodes = target.parseTreeNodes; | 		parseTreeNodes = target.parseTreeNodes; | ||||||
| 	this.sourceText = target.source; | 	this.sourceText = target.source; | ||||||
| 	this.sourceType = target.type; | 	this.sourceType = target.type; | ||||||
| 	// Wrap the transcluded content if required | 	this.parseAsInline = target.parseAsInline; | ||||||
| 	if(this.slotValueParseTrees["ts-wrapper"]) { |  | ||||||
| 		this.slotValueParseTrees["ts-wrapped"] = parseTreeNodes; |  | ||||||
| 		parseTreeNodes = this.slotValueParseTrees["ts-wrapper"]; |  | ||||||
| 		this.sourceTest = undefined; |  | ||||||
| 		this.sourceType = undefined; |  | ||||||
| 	} |  | ||||||
| 	// Set context variables for recursion detection | 	// Set context variables for recursion detection | ||||||
| 	var recursionMarker = this.makeRecursionMarker(); | 	var recursionMarker = this.makeRecursionMarker(); | ||||||
| 	if(this.recursionMarker === "yes") { | 	if(this.recursionMarker === "yes") { | ||||||
| @@ -135,6 +129,7 @@ TranscludeWidget.prototype.collectSlotValueParameters = function() { | |||||||
| 	if(this.legacyMode) { | 	if(this.legacyMode) { | ||||||
| 		this.slotValueParseTrees["ts-missing"] = this.parseTreeNode.children; | 		this.slotValueParseTrees["ts-missing"] = this.parseTreeNode.children; | ||||||
| 	} else { | 	} else { | ||||||
|  | 		this.slotValueParseTrees["ts-raw"] = this.parseTreeNode.children; | ||||||
| 		var noValueWidgetsFound = true, | 		var noValueWidgetsFound = true, | ||||||
| 			searchParseTreeNodes = function(nodes) { | 			searchParseTreeNodes = function(nodes) { | ||||||
| 				$tw.utils.each(nodes,function(node) { | 				$tw.utils.each(nodes,function(node) { | ||||||
| @@ -171,11 +166,11 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { | |||||||
| 	if(this.transcludeVariable) { | 	if(this.transcludeVariable) { | ||||||
| 		var variableInfo = this.getVariableInfo(this.transcludeVariable).srcVariable; | 		var variableInfo = this.getVariableInfo(this.transcludeVariable).srcVariable; | ||||||
| 		if(variableInfo) { | 		if(variableInfo) { | ||||||
| 			var mode = this.parseTreeNode.isBlock ? "blockParser" : "inlineParser"; | 			var mode = parseAsInline ? "inlineParser" : "blockParser"; | ||||||
| 			if(variableInfo[mode]) { | 			if(variableInfo[mode]) { | ||||||
| 				parser = variableInfo[mode]; | 				parser = variableInfo[mode]; | ||||||
| 			} else { | 			} else { | ||||||
| 				parser = this.wiki.parseText(this.transcludeType,variableInfo.value || "",{parseAsInline: !this.parseTreeNode.isBlock}); | 				parser = this.wiki.parseText(this.transcludeType,variableInfo.value || "",{parseAsInline: parseAsInline}); | ||||||
| 				variableInfo[mode] = parser; | 				variableInfo[mode] = parser; | ||||||
| 			} | 			} | ||||||
| 			if(parser && variableInfo.isFunctionDefinition) { | 			if(parser && variableInfo.isFunctionDefinition) { | ||||||
| @@ -206,6 +201,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { | |||||||
| 		return { | 		return { | ||||||
| 			parser: parser, | 			parser: parser, | ||||||
| 			parseTreeNodes: parser.tree, | 			parseTreeNodes: parser.tree, | ||||||
|  | 			parseAsInline: parseAsInline, | ||||||
| 			text: parser.source, | 			text: parser.source, | ||||||
| 			type: parser.type | 			type: parser.type | ||||||
| 		}; | 		}; | ||||||
| @@ -213,6 +209,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { | |||||||
| 		return { | 		return { | ||||||
| 			parser: null, | 			parser: null, | ||||||
| 			parseTreeNodes: (this.slotValueParseTrees["ts-missing"] || []), | 			parseTreeNodes: (this.slotValueParseTrees["ts-missing"] || []), | ||||||
|  | 			parseAsInline: parseAsInline, | ||||||
| 			text: null, | 			text: null, | ||||||
| 			type: null | 			type: null | ||||||
| 		}; | 		}; | ||||||
| @@ -234,6 +231,17 @@ TranscludeWidget.prototype.getTransclusionParameter = function(name,index,defaul | |||||||
| 	return defaultValue; | 	return defaultValue; | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Get a hashmap of the special variables to be provided by the parameters widget | ||||||
|  | */ | ||||||
|  | TranscludeWidget.prototype.getTransclusionMetaVariables = function() { | ||||||
|  | 	return { | ||||||
|  | 		paramNames: $tw.utils.stringifyList(this.getTransclusionParameterNames()), | ||||||
|  | 		paramValues: $tw.utils.stringifyList(this.getTransclusionParameterValues()), | ||||||
|  | 		parseAsInline: this.parseAsInline ? "yes" : "no" | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  |  | ||||||
| /* | /* | ||||||
| Get an array of the names of all the provided transclusion parameters | Get an array of the names of all the provided transclusion parameters | ||||||
| */ | */ | ||||||
| @@ -248,7 +256,7 @@ TranscludeWidget.prototype.getTransclusionParameterValues = function() { | |||||||
| 	var self = this, | 	var self = this, | ||||||
| 		values = []; | 		values = []; | ||||||
| 	$tw.utils.each(Object.keys(this.stringParametersByName),function(name) { | 	$tw.utils.each(Object.keys(this.stringParametersByName),function(name) { | ||||||
| 		values.push(self.stringParametersByName[name]); | 		values.push(self.stringParametersByName[name] || ""); | ||||||
| 	}); | 	}); | ||||||
| 	return values; | 	return values; | ||||||
| }; | }; | ||||||
| @@ -282,6 +290,7 @@ TranscludeWidget.prototype.makeRecursionMarker = function() { | |||||||
| }; | }; | ||||||
|  |  | ||||||
| TranscludeWidget.prototype.parserNeedsRefresh = function() { | TranscludeWidget.prototype.parserNeedsRefresh = function() { | ||||||
|  | 	// TODO: Doesn't consider transcluded variables | ||||||
| 	var parserInfo = this.wiki.getTextReferenceParserInfo(this.transcludeTitle,this.transcludeField,this.transcludeIndex,{subTiddler:this.transcludeSubTiddler}); | 	var parserInfo = this.wiki.getTextReferenceParserInfo(this.transcludeTitle,this.transcludeField,this.transcludeIndex,{subTiddler:this.transcludeSubTiddler}); | ||||||
| 	return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType) | 	return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType) | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -401,49 +401,23 @@ Widget.prototype.makeChildWidget = function(parseTreeNode,options) { | |||||||
| 	options = options || {}; | 	options = options || {}; | ||||||
| 	// Check whether this node type is defined by a custom macro definition | 	// Check whether this node type is defined by a custom macro definition | ||||||
| 	var variableDefinitionName = "<$" + parseTreeNode.type + ">"; | 	var variableDefinitionName = "<$" + parseTreeNode.type + ">"; | ||||||
| 	if(parseTreeNode.type !== "transclude" && this.variables[variableDefinitionName] && this.variables[variableDefinitionName].value) { | 	if(!parseTreeNode.isNotRemappable && this.variables[variableDefinitionName] && this.variables[variableDefinitionName].value) { | ||||||
| 		var newParseTreeNode = { | 		var newParseTreeNode = { | ||||||
| 			type: "transclude", | 			type: "transclude", | ||||||
| 			attributes: { |  | ||||||
| 				"$variable": {name: "$variable", type: "string", value: variableDefinitionName} |  | ||||||
| 			}, |  | ||||||
| 			children: [ | 			children: [ | ||||||
| 				{ | 				{ | ||||||
| 					type: "value", | 					type: "value", | ||||||
| 					attributes: { |  | ||||||
| 						"$name": {name: "$name", type: "string", value: "ts-body"} |  | ||||||
| 					}, |  | ||||||
| 					children: parseTreeNode.children | 					children: parseTreeNode.children | ||||||
| 				}, |  | ||||||
| 				{ |  | ||||||
| 					type: "value", |  | ||||||
| 					attributes: { |  | ||||||
| 						"$name": {name: "$name", type: "string", value: "ts-wrapper"} |  | ||||||
| 					}, |  | ||||||
| 					children: [ |  | ||||||
| 						{ |  | ||||||
| 							type: "setvariable", |  | ||||||
| 							attributes: { |  | ||||||
| 								"name": {name: "name", type: "string", value: variableDefinitionName}, |  | ||||||
| 								"value": {name: "value", type: "string", value: ""} |  | ||||||
| 							}, |  | ||||||
| 							children: [ |  | ||||||
| 								{ |  | ||||||
| 									type: "slot", |  | ||||||
| 									attributes: { |  | ||||||
| 										"$name": {name: "$name", type: "string", value: "ts-wrapped"} |  | ||||||
| 				} | 				} | ||||||
| 								} | 			], | ||||||
| 							] | 			isBlock: parseTreeNode.isBlock | ||||||
| 						} |  | ||||||
| 					] |  | ||||||
| 				} |  | ||||||
| 			] |  | ||||||
| 		}; | 		}; | ||||||
|  | 		$tw.utils.addAttributeToParseTreeNode(newParseTreeNode,"$variable",variableDefinitionName); | ||||||
|  | 		$tw.utils.addAttributeToParseTreeNode(newParseTreeNode.children[0],"$name","ts-body"); | ||||||
| 		$tw.utils.each(parseTreeNode.attributes,function(attr,name) { | 		$tw.utils.each(parseTreeNode.attributes,function(attr,name) { | ||||||
| 			// If the attribute starts with a dollar then add an extra dollar so that it doesn't clash with the $xxx attributes of transclude | 			// If the attribute starts with a dollar then add an extra dollar so that it doesn't clash with the $xxx attributes of transclude | ||||||
| 			name = name.charAt(0) === "$" ? "$" + name : name; | 			name = name.charAt(0) === "$" ? "$" + name : name; | ||||||
| 			newParseTreeNode.attributes[name] = attr; | 			$tw.utils.addAttributeToParseTreeNode(newParseTreeNode,$tw.utils.extend({},attr,{name: name})); | ||||||
| 		}); | 		}); | ||||||
| 		parseTreeNode = newParseTreeNode; | 		parseTreeNode = newParseTreeNode; | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -17,7 +17,7 @@ title: Definition | |||||||
|  |  | ||||||
| \whitespace trim | \whitespace trim | ||||||
| \function <$codeblock>(code) | \function <$codeblock>(code) | ||||||
| <$codeblock code={{{ [<code>addprefix[£]addsuffix[@]] }}}/> | <$genesis $type="codeblock" $remappable="no" code={{{ [<code>addprefix[£]addsuffix[@]] }}}/> | ||||||
| \end | \end | ||||||
| + | + | ||||||
| title: Subject | title: Subject | ||||||
|   | |||||||
| @@ -24,9 +24,9 @@ title: TiddlerOne | |||||||
| 		Whale | 		Whale | ||||||
| 	</$slot> | 	</$slot> | ||||||
| \end | \end | ||||||
| <$transclude $tiddler="TiddlerZero"> | <$genesis $type="transclude" $remappable="no" $$tiddler="TiddlerZero"> | ||||||
| 	Crocodile | 	Crocodile | ||||||
| </$transclude> | </$genesis> | ||||||
| + | + | ||||||
| title: ExpectedResult | title: ExpectedResult | ||||||
|  |  | ||||||
|   | |||||||
| @@ -15,10 +15,12 @@ title: TiddlerOne | |||||||
| <!-- Redefine the <$text> widget by defining a transcludable variable with that name --> | <!-- Redefine the <$text> widget by defining a transcludable variable with that name --> | ||||||
| \function <$text>(text:'Jaguar') | \function <$text>(text:'Jaguar') | ||||||
| \whitespace trim | \whitespace trim | ||||||
| <$text text=<<text>>/> | <$genesis $type="text" $remappable="no" text=<<text>>/> | ||||||
| <$slot $name="ts-body"> | <$set name="<$text>" value=""> | ||||||
|  | 	<$slot $name="ts-body"> | ||||||
| 		Whale | 		Whale | ||||||
| </$slot> | 	</$slot> | ||||||
|  | </$set> | ||||||
| \end | \end | ||||||
| <$text text="Dingo"> | <$text text="Dingo"> | ||||||
| 	Crocodile | 	Crocodile | ||||||
|   | |||||||
| @@ -6,6 +6,8 @@ tags: [[$:/tags/wiki-test-spec]] | |||||||
| title: Output | title: Output | ||||||
|  |  | ||||||
| \whitespace trim | \whitespace trim | ||||||
|  | <$transclude $tiddler="TiddlerOne" 0="" 1="" 2=""/> | ||||||
|  |  | ||||||
| {{TiddlerOne}} | {{TiddlerOne}} | ||||||
| {{TiddlerOne|Ferret}} | {{TiddlerOne|Ferret}} | ||||||
| {{TiddlerOne|Butterfly|Moth}} | {{TiddlerOne|Butterfly|Moth}} | ||||||
| @@ -17,7 +19,7 @@ title: TiddlerOne | |||||||
| \whitespace trim | \whitespace trim | ||||||
| \parameters(zero:'Jaguar',one:'Lizard',two:'Mole') | \parameters(zero:'Jaguar',one:'Lizard',two:'Mole') | ||||||
| <$list filter="[enlist<paramNames>]" counter="counter"> | <$list filter="[enlist<paramNames>]" counter="counter"> | ||||||
| {<$text text={{{ [enlist<paramNames>nth<counter>] }}}/>:<$text text={{{ [enlist<paramValues>nth<counter>] }}}/>} | {<$text text={{{ [enlist:raw<paramNames>nth<counter>] }}}/>:<$text text={{{ [enlist:raw<paramValues>nth<counter>] }}}/>} | ||||||
| </$list> | </$list> | ||||||
| + | + | ||||||
| title: TiddlerTwo | title: TiddlerTwo | ||||||
| @@ -28,4 +30,4 @@ title: TiddlerTwo | |||||||
| + | + | ||||||
| title: ExpectedResult | title: ExpectedResult | ||||||
|  |  | ||||||
| <p></p><p>{0:Ferret}</p><p>{0:Butterfly}{1:Moth}</p><p>{0:Beetle}{1:Scorpion}{2:Snake}</p><p>({zero:Beetle}{one:Scorpion}{two:Snake})</p> | <p>{0:}{1:}{2:}</p><p></p><p>{0:Ferret}</p><p>{0:Butterfly}{1:Moth}</p><p>{0:Beetle}{1:Scorpion}{2:Snake}</p><p>({zero:Beetle}{one:Scorpion}{two:Snake})</p> | ||||||
		Reference in New Issue
	
	Block a user
	 jeremy@jermolene.com
					jeremy@jermolene.com