mirror of
				https://github.com/Jermolene/TiddlyWiki5
				synced 2025-10-30 23:23:02 +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 */ | ||||
| "use strict"; | ||||
|  | ||||
| /* | ||||
| Add attribute to parse tree node | ||||
| Can be invoked as (node,name,value) or (node,attr) | ||||
| */ | ||||
| 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.orderedAttributes = node.orderedAttributes || []; | ||||
| 	node.attributes[name] = attribute; | ||||
|   | ||||
| @@ -41,6 +41,7 @@ GenesisWidget.prototype.execute = function() { | ||||
| 	// Collect attributes | ||||
| 	this.genesisType = this.getAttribute("$type","element"); | ||||
| 	this.genesisTag = this.getAttribute("$tag","div"); | ||||
| 	this.genesisRemappable = this.getAttribute("$remappable","yes") === "yes"; | ||||
| 	this.genesisNames = this.getAttribute("$names",""); | ||||
| 	this.genesisValues = this.getAttribute("$values",""); | ||||
| 	// Construct parse tree | ||||
| @@ -49,7 +50,8 @@ GenesisWidget.prototype.execute = function() { | ||||
| 		tag: this.genesisTag, | ||||
| 		attributes: {}, | ||||
| 		orderedAttributes: [], | ||||
| 		children: this.parseTreeNode.children || [] | ||||
| 		children: this.parseTreeNode.children || [], | ||||
| 		isNotRemappable: !this.genesisRemappable | ||||
| 	}]; | ||||
| 	// Apply attributes in $names/$values | ||||
| 	this.attributeNames = []; | ||||
|   | ||||
| @@ -54,8 +54,9 @@ ParametersWidget.prototype.execute = function() { | ||||
| 				value = transclusionWidget.getTransclusionParameter(name,index,self.getAttribute(name)); | ||||
| 			self.setVariable(name,value); | ||||
| 		}); | ||||
| 		this.setVariable("paramNames",$tw.utils.stringifyList(transclusionWidget.getTransclusionParameterNames())); | ||||
| 		this.setVariable("paramValues",$tw.utils.stringifyList(transclusionWidget.getTransclusionParameterValues())); | ||||
| 		$tw.utils.each(transclusionWidget.getTransclusionMetaVariables(),function(value,name) { | ||||
| 			self.setVariable(name,value); | ||||
| 		}); | ||||
| 	} | ||||
| 	// Construct the child widgets | ||||
| 	this.makeChildWidgets(); | ||||
|   | ||||
| @@ -43,13 +43,24 @@ Compute the internal state of the widget | ||||
| SlotWidget.prototype.execute = function() { | ||||
| 	var self = this; | ||||
| 	this.slotName = this.getAttribute("$name"); | ||||
| 	// Find the parent transclusion | ||||
| 	var transclusionWidget = this.parentWidget; | ||||
| 	while(transclusionWidget && !(transclusionWidget instanceof TranscludeWidget)) { | ||||
| 		transclusionWidget = transclusionWidget.parentWidget; | ||||
| 	this.slotDepth = parseInt(this.getAttribute("$depth","1"),10) || 1; | ||||
| 	// Find the parent transclusions | ||||
| 	var pointer = this.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 | ||||
| 	var parseTreeNodes = transclusionWidget.getTransclusionSlotValue(this.slotName,this.parseTreeNode.children); | ||||
| 		parseTreeNodes = pointer.getTransclusionSlotValue(this.slotName,this.parseTreeNode.children); | ||||
| 	} | ||||
| 	// Construct the child widgets | ||||
| 	this.makeChildWidgets(parseTreeNodes); | ||||
| }; | ||||
| @@ -59,7 +70,7 @@ Refresh the widget by ensuring our attributes are up to date | ||||
| */ | ||||
| SlotWidget.prototype.refresh = function(changedTiddlers) { | ||||
| 	var changedAttributes = this.computeAttributes(); | ||||
| 	if(changedAttributes["$name"]) { | ||||
| 	if(changedAttributes["$name"] || changedAttributes["$depth"]) { | ||||
| 		this.refreshSelf(); | ||||
| 		return true; | ||||
| 	} | ||||
|   | ||||
| @@ -46,13 +46,7 @@ TranscludeWidget.prototype.execute = function() { | ||||
| 		parseTreeNodes = target.parseTreeNodes; | ||||
| 	this.sourceText = target.source; | ||||
| 	this.sourceType = target.type; | ||||
| 	// Wrap the transcluded content if required | ||||
| 	if(this.slotValueParseTrees["ts-wrapper"]) { | ||||
| 		this.slotValueParseTrees["ts-wrapped"] = parseTreeNodes; | ||||
| 		parseTreeNodes = this.slotValueParseTrees["ts-wrapper"]; | ||||
| 		this.sourceTest = undefined; | ||||
| 		this.sourceType = undefined; | ||||
| 	} | ||||
| 	this.parseAsInline = target.parseAsInline; | ||||
| 	// Set context variables for recursion detection | ||||
| 	var recursionMarker = this.makeRecursionMarker(); | ||||
| 	if(this.recursionMarker === "yes") { | ||||
| @@ -135,6 +129,7 @@ TranscludeWidget.prototype.collectSlotValueParameters = function() { | ||||
| 	if(this.legacyMode) { | ||||
| 		this.slotValueParseTrees["ts-missing"] = this.parseTreeNode.children; | ||||
| 	} else { | ||||
| 		this.slotValueParseTrees["ts-raw"] = this.parseTreeNode.children; | ||||
| 		var noValueWidgetsFound = true, | ||||
| 			searchParseTreeNodes = function(nodes) { | ||||
| 				$tw.utils.each(nodes,function(node) { | ||||
| @@ -171,11 +166,11 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { | ||||
| 	if(this.transcludeVariable) { | ||||
| 		var variableInfo = this.getVariableInfo(this.transcludeVariable).srcVariable; | ||||
| 		if(variableInfo) { | ||||
| 			var mode = this.parseTreeNode.isBlock ? "blockParser" : "inlineParser"; | ||||
| 			var mode = parseAsInline ? "inlineParser" : "blockParser"; | ||||
| 			if(variableInfo[mode]) { | ||||
| 				parser = variableInfo[mode]; | ||||
| 			} 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; | ||||
| 			} | ||||
| 			if(parser && variableInfo.isFunctionDefinition) { | ||||
| @@ -206,6 +201,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { | ||||
| 		return { | ||||
| 			parser: parser, | ||||
| 			parseTreeNodes: parser.tree, | ||||
| 			parseAsInline: parseAsInline, | ||||
| 			text: parser.source, | ||||
| 			type: parser.type | ||||
| 		}; | ||||
| @@ -213,6 +209,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { | ||||
| 		return { | ||||
| 			parser: null, | ||||
| 			parseTreeNodes: (this.slotValueParseTrees["ts-missing"] || []), | ||||
| 			parseAsInline: parseAsInline, | ||||
| 			text: null, | ||||
| 			type: null | ||||
| 		}; | ||||
| @@ -234,6 +231,17 @@ TranscludeWidget.prototype.getTransclusionParameter = function(name,index,defaul | ||||
| 	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 | ||||
| */ | ||||
| @@ -248,7 +256,7 @@ TranscludeWidget.prototype.getTransclusionParameterValues = function() { | ||||
| 	var self = this, | ||||
| 		values = []; | ||||
| 	$tw.utils.each(Object.keys(this.stringParametersByName),function(name) { | ||||
| 		values.push(self.stringParametersByName[name]); | ||||
| 		values.push(self.stringParametersByName[name] || ""); | ||||
| 	}); | ||||
| 	return values; | ||||
| }; | ||||
| @@ -282,6 +290,7 @@ TranscludeWidget.prototype.makeRecursionMarker = 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}); | ||||
| 	return (this.sourceText === undefined || parserInfo.sourceText !== this.sourceText || parserInfo.parserType !== this.parserType) | ||||
| }; | ||||
|   | ||||
| @@ -401,49 +401,23 @@ Widget.prototype.makeChildWidget = function(parseTreeNode,options) { | ||||
| 	options = options || {}; | ||||
| 	// Check whether this node type is defined by a custom macro definition | ||||
| 	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 = { | ||||
| 			type: "transclude", | ||||
| 			attributes: { | ||||
| 				"$variable": {name: "$variable", type: "string", value: variableDefinitionName} | ||||
| 			}, | ||||
| 			children: [ | ||||
| 				{ | ||||
| 					type: "value", | ||||
| 					attributes: { | ||||
| 						"$name": {name: "$name", type: "string", value: "ts-body"} | ||||
| 					}, | ||||
| 					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) { | ||||
| 			// 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; | ||||
| 			newParseTreeNode.attributes[name] = attr; | ||||
| 			$tw.utils.addAttributeToParseTreeNode(newParseTreeNode,$tw.utils.extend({},attr,{name: name})); | ||||
| 		}); | ||||
| 		parseTreeNode = newParseTreeNode; | ||||
| 	} | ||||
|   | ||||
| @@ -17,7 +17,7 @@ title: Definition | ||||
|  | ||||
| \whitespace trim | ||||
| \function <$codeblock>(code) | ||||
| <$codeblock code={{{ [<code>addprefix[£]addsuffix[@]] }}}/> | ||||
| <$genesis $type="codeblock" $remappable="no" code={{{ [<code>addprefix[£]addsuffix[@]] }}}/> | ||||
| \end | ||||
| + | ||||
| title: Subject | ||||
|   | ||||
| @@ -24,9 +24,9 @@ title: TiddlerOne | ||||
| 		Whale | ||||
| 	</$slot> | ||||
| \end | ||||
| <$transclude $tiddler="TiddlerZero"> | ||||
| <$genesis $type="transclude" $remappable="no" $$tiddler="TiddlerZero"> | ||||
| 	Crocodile | ||||
| </$transclude> | ||||
| </$genesis> | ||||
| + | ||||
| title: ExpectedResult | ||||
|  | ||||
|   | ||||
| @@ -15,10 +15,12 @@ title: TiddlerOne | ||||
| <!-- Redefine the <$text> widget by defining a transcludable variable with that name --> | ||||
| \function <$text>(text:'Jaguar') | ||||
| \whitespace trim | ||||
| <$text text=<<text>>/> | ||||
| <$slot $name="ts-body"> | ||||
| <$genesis $type="text" $remappable="no" text=<<text>>/> | ||||
| <$set name="<$text>" value=""> | ||||
| 	<$slot $name="ts-body"> | ||||
| 		Whale | ||||
| </$slot> | ||||
| 	</$slot> | ||||
| </$set> | ||||
| \end | ||||
| <$text text="Dingo"> | ||||
| 	Crocodile | ||||
|   | ||||
| @@ -6,6 +6,8 @@ tags: [[$:/tags/wiki-test-spec]] | ||||
| title: Output | ||||
|  | ||||
| \whitespace trim | ||||
| <$transclude $tiddler="TiddlerOne" 0="" 1="" 2=""/> | ||||
|  | ||||
| {{TiddlerOne}} | ||||
| {{TiddlerOne|Ferret}} | ||||
| {{TiddlerOne|Butterfly|Moth}} | ||||
| @@ -17,7 +19,7 @@ title: TiddlerOne | ||||
| \whitespace trim | ||||
| \parameters(zero:'Jaguar',one:'Lizard',two:'Mole') | ||||
| <$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> | ||||
| + | ||||
| title: TiddlerTwo | ||||
| @@ -28,4 +30,4 @@ title: TiddlerTwo | ||||
| + | ||||
| 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