mirror of
				https://github.com/Jermolene/TiddlyWiki5
				synced 2025-10-31 07:32:59 +00:00 
			
		
		
		
	Support global macros via the importvariables widget
The new importvariables widget imports macro/variable definitions from the specified tiddlers and makes them available to its children. Allows us to split PageMacros up into separate tiddlers. We still support loading macros from $:/core/ui/PageMacros to help people upgrading. Fixes #644 and #559
This commit is contained in:
		| @@ -21,7 +21,7 @@ exports.synchronous = true; | |||||||
| // Default story and history lists | // Default story and history lists | ||||||
| var PAGE_TITLE_TITLE = "$:/core/wiki/title" | var PAGE_TITLE_TITLE = "$:/core/wiki/title" | ||||||
| var PAGE_STYLESHEET_TITLE = "$:/core/ui/PageStylesheet"; | var PAGE_STYLESHEET_TITLE = "$:/core/ui/PageStylesheet"; | ||||||
| var PAGE_TEMPLATE_TITLE = "$:/core/ui/PageMacros"; | var PAGE_TEMPLATE_TITLE = "$:/core/ui/PageTemplate"; | ||||||
|  |  | ||||||
| // Time (in ms) that we defer refreshing changes to draft tiddlers | // Time (in ms) that we defer refreshing changes to draft tiddlers | ||||||
| var DRAFT_TIDDLER_TIMEOUT = 400; | var DRAFT_TIDDLER_TIMEOUT = 400; | ||||||
| @@ -49,7 +49,7 @@ exports.startup = function() { | |||||||
| 			$tw.styleElement.innerHTML = $tw.styleContainer.textContent; | 			$tw.styleElement.innerHTML = $tw.styleContainer.textContent; | ||||||
| 		} | 		} | ||||||
| 	})); | 	})); | ||||||
| 	// Display the $:/core/ui/PageMacros tiddler to kick off the display | 	// Display the $:/core/ui/PageTemplate tiddler to kick off the display | ||||||
| 	$tw.perf.report("mainRender",function() { | 	$tw.perf.report("mainRender",function() { | ||||||
| 		$tw.pageWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TEMPLATE_TITLE,{document: document, parentWidget: $tw.rootWidget}); | 		$tw.pageWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TEMPLATE_TITLE,{document: document, parentWidget: $tw.rootWidget}); | ||||||
| 		$tw.pageContainer = document.createElement("div"); | 		$tw.pageContainer = document.createElement("div"); | ||||||
|   | |||||||
							
								
								
									
										115
									
								
								core/modules/widgets/importvariables.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								core/modules/widgets/importvariables.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | |||||||
|  | /*\ | ||||||
|  | title: $:/core/modules/widgets/importvariables.js | ||||||
|  | type: application/javascript | ||||||
|  | module-type: widget | ||||||
|  |  | ||||||
|  | Import variable definitions from other tiddlers | ||||||
|  |  | ||||||
|  | \*/ | ||||||
|  | (function(){ | ||||||
|  |  | ||||||
|  | /*jslint node: true, browser: true */ | ||||||
|  | /*global $tw: false */ | ||||||
|  | "use strict"; | ||||||
|  |  | ||||||
|  | var Widget = require("$:/core/modules/widgets/widget.js").widget; | ||||||
|  |  | ||||||
|  | var ImportVariablesWidget = function(parseTreeNode,options) { | ||||||
|  | 	this.initialise(parseTreeNode,options); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Inherit from the base widget class | ||||||
|  | */ | ||||||
|  | ImportVariablesWidget.prototype = new Widget(); | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Render this widget into the DOM | ||||||
|  | */ | ||||||
|  | ImportVariablesWidget.prototype.render = function(parent,nextSibling) { | ||||||
|  | 	this.parentDomNode = parent; | ||||||
|  | 	this.computeAttributes(); | ||||||
|  | 	this.execute(); | ||||||
|  | 	this.renderChildren(parent,nextSibling); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Compute the internal state of the widget | ||||||
|  | */ | ||||||
|  | ImportVariablesWidget.prototype.execute = function(tiddlerList) { | ||||||
|  | 	var self = this; | ||||||
|  | 	// Get our parameters | ||||||
|  | 	this.filter = this.getAttribute("filter"); | ||||||
|  | 	// Compute the filter | ||||||
|  | 	this.tiddlerList = tiddlerList || this.wiki.filterTiddlers(this.filter,this); | ||||||
|  | 	// Accumulate the <$set> widgets from each tiddler | ||||||
|  | 	var widgetStackStart,widgetStackEnd; | ||||||
|  | 	function addWidgetNode(widgetNode) { | ||||||
|  | 		if(widgetNode) { | ||||||
|  | 			if(!widgetStackStart && !widgetStackEnd) { | ||||||
|  | 				widgetStackStart = widgetNode; | ||||||
|  | 				widgetStackEnd = widgetNode; | ||||||
|  | 			} else { | ||||||
|  | 				widgetStackEnd.children = [widgetNode]; | ||||||
|  | 				widgetStackEnd = widgetNode; | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	$tw.utils.each(this.tiddlerList,function(title) { | ||||||
|  | 		var parser = self.wiki.parseTiddler(title); | ||||||
|  | 		if(parser) { | ||||||
|  | 			var parseTreeNode = parser.tree[0]; | ||||||
|  | 			while(parseTreeNode && parseTreeNode.type === "set") { | ||||||
|  | 				addWidgetNode({ | ||||||
|  | 					type: "set", | ||||||
|  | 					attributes: parseTreeNode.attributes, | ||||||
|  | 					params: parseTreeNode.params | ||||||
|  | 				}); | ||||||
|  | 				parseTreeNode = parseTreeNode.children[0]; | ||||||
|  | 			} | ||||||
|  | 		}  | ||||||
|  | 	}); | ||||||
|  | 	// Add our own children to the end of the pile | ||||||
|  | 	var parseTreeNodes; | ||||||
|  | 	if(widgetStackStart && widgetStackEnd) { | ||||||
|  | 		parseTreeNodes = [widgetStackStart]; | ||||||
|  | 		widgetStackEnd.children = this.parseTreeNode.children; | ||||||
|  | 	} else { | ||||||
|  | 		parseTreeNodes = this.parseTreeNode.children; | ||||||
|  | 	} | ||||||
|  | 	// Construct the child widgets | ||||||
|  | 	this.makeChildWidgets(parseTreeNodes); | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | /* | ||||||
|  | Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering | ||||||
|  | */ | ||||||
|  | ImportVariablesWidget.prototype.refresh = function(changedTiddlers) { | ||||||
|  | 	// Recompute our attributes and the filter list | ||||||
|  | 	var changedAttributes = this.computeAttributes(), | ||||||
|  | 		tiddlerList = this.wiki.filterTiddlers(this.getAttribute("filter"),this); | ||||||
|  | 	// Refresh if the filter has changed, or the list of tiddlers has changed, or any of the tiddlers in the list has changed | ||||||
|  | 	function haveListedTiddlersChanged() { | ||||||
|  | 		var changed = false; | ||||||
|  | 		tiddlerList.forEach(function(title) { | ||||||
|  | 			if(changedTiddlers[title]) { | ||||||
|  | 				changed = true; | ||||||
|  | 			} | ||||||
|  | 		}); | ||||||
|  | 		return changed; | ||||||
|  | 	} | ||||||
|  | 	if(changedAttributes.filter || !$tw.utils.isArrayEqual(this.tiddlerList,tiddlerList) || haveListedTiddlersChanged()) { | ||||||
|  | 		// Compute the filter | ||||||
|  | console.log("Refreshing importvariables with filter=",this.getAttribute("filter")); | ||||||
|  | 		this.removeChildDomNodes(); | ||||||
|  | 		this.execute(tiddlerList); | ||||||
|  | 		this.renderChildren(this.parentDomNode,this.findNextSiblingDomNode()); | ||||||
|  | 		return true; | ||||||
|  | 	} else { | ||||||
|  | 		return this.refreshChildren(changedTiddlers);		 | ||||||
|  | 	} | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | exports.importvariables = ImportVariablesWidget; | ||||||
|  |  | ||||||
|  | })(); | ||||||
| @@ -21,6 +21,6 @@ type: text/vnd.tiddlywiki-html | |||||||
| </head> | </head> | ||||||
| <body class="tw-body"> | <body class="tw-body"> | ||||||
| {{$:/StaticBanner||$:/core/templates/html-tiddler}} | {{$:/StaticBanner||$:/core/templates/html-tiddler}} | ||||||
| {{$:/core/ui/PageMacros||$:/core/templates/html-tiddler}} | {{$:/core/ui/PageTemplate||$:/core/templates/html-tiddler}} | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|   | |||||||
| @@ -15,7 +15,9 @@ title: $:/core/templates/static.tiddler.html | |||||||
| <body class="tw-body"> | <body class="tw-body"> | ||||||
| `{{$:/StaticBanner||$:/core/templates/html-tiddler}}` | `{{$:/StaticBanner||$:/core/templates/html-tiddler}}` | ||||||
| <section class="story-river"> | <section class="story-river"> | ||||||
| `<$view tiddler="$:/core/ui/ViewTemplate" format="htmlwikified"/>` | `<$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]"> | ||||||
|  | <$view tiddler="$:/core/ui/ViewTemplate" format="htmlwikified"/> | ||||||
|  | </$importvariables>` | ||||||
| </section> | </section> | ||||||
| </body> | </body> | ||||||
| </html> | </html> | ||||||
|   | |||||||
| @@ -4,6 +4,8 @@ title: $:/core/ui/PageTemplate | |||||||
| tw-page-container tw-page-view-$(themeTitle)$ tw-language-$(languageTitle)$ | tw-page-container tw-page-view-$(themeTitle)$ tw-language-$(languageTitle)$ | ||||||
| \end | \end | ||||||
|  |  | ||||||
|  | <$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]"> | ||||||
|  |  | ||||||
| <$set name="themeTitle" value={{$:/view}}> | <$set name="themeTitle" value={{$:/view}}> | ||||||
|  |  | ||||||
| <$set name="currentTiddler" value={{$:/language}}> | <$set name="currentTiddler" value={{$:/language}}> | ||||||
| @@ -37,3 +39,5 @@ tw-page-container tw-page-view-$(themeTitle)$ tw-language-$(languageTitle)$ | |||||||
| </$set> | </$set> | ||||||
|  |  | ||||||
| </$set> | </$set> | ||||||
|  |  | ||||||
|  | </$importvariables> | ||||||
|   | |||||||
							
								
								
									
										10
									
								
								core/wiki/macros/CSS.tid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								core/wiki/macros/CSS.tid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | title: $:/core/macros/CSS | ||||||
|  | tags: $:/tags/Macro | ||||||
|  |  | ||||||
|  | \define colour(name) | ||||||
|  | <$transclude tiddler={{$:/palette}} index="$name$"/> | ||||||
|  | \end | ||||||
|  |  | ||||||
|  | \define color(name) | ||||||
|  | <<colour $name$>> | ||||||
|  | \end | ||||||
							
								
								
									
										10
									
								
								core/wiki/macros/lingo.tid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								core/wiki/macros/lingo.tid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | |||||||
|  | title: $:/core/macros/lingo | ||||||
|  | tags: $:/tags/Macro | ||||||
|  |  | ||||||
|  | \define lingo-base() | ||||||
|  | $:/lingo/ | ||||||
|  | \end | ||||||
|  |  | ||||||
|  | \define lingo(title) | ||||||
|  | {{$(lingo-base)$$title$}} | ||||||
|  | \end | ||||||
| @@ -1,12 +1,5 @@ | |||||||
| title: $:/core/ui/PageMacros | title: $:/core/macros/tabs | ||||||
| 
 | tags: $:/tags/Macro | ||||||
| \define colour(name) |  | ||||||
| <$transclude tiddler={{$:/palette}} index="$name$"/> |  | ||||||
| \end |  | ||||||
| 
 |  | ||||||
| \define color(name) |  | ||||||
| <<colour $name$>> |  | ||||||
| \end |  | ||||||
| 
 | 
 | ||||||
| \define tabs(tabsList,default,state:"$:/state/tab",class) | \define tabs(tabsList,default,state:"$:/state/tab",class) | ||||||
| <div class="tw-tab-set $class$"> | <div class="tw-tab-set $class$"> | ||||||
| @@ -27,40 +20,3 @@ title: $:/core/ui/PageMacros | |||||||
| </div> | </div> | ||||||
| </div> | </div> | ||||||
| \end | \end | ||||||
| 
 |  | ||||||
| \define wikitext-example(src) |  | ||||||
| ``` |  | ||||||
| $src$ |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Renders as: |  | ||||||
| 
 |  | ||||||
| $src$ |  | ||||||
| 
 |  | ||||||
| In HTML: |  | ||||||
| 
 |  | ||||||
| $$$text/vnd.tiddlywiki>text/html |  | ||||||
| $src$ |  | ||||||
| $$$ |  | ||||||
| 
 |  | ||||||
| \end |  | ||||||
| 
 |  | ||||||
| \define wikitext-example-without-html(src) |  | ||||||
| ``` |  | ||||||
| $src$ |  | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Renders as: |  | ||||||
| 
 |  | ||||||
| $src$ |  | ||||||
| 
 |  | ||||||
| \end |  | ||||||
| 
 |  | ||||||
| \define lingo-base() |  | ||||||
| $:/lingo/ |  | ||||||
| \end |  | ||||||
| 
 |  | ||||||
| \define lingo(title) |  | ||||||
| {{$(lingo-base)$$title$}} |  | ||||||
| \end |  | ||||||
| {{$:/core/ui/PageTemplate}} |  | ||||||
| @@ -23,4 +23,4 @@ It also determines the standard UI flow: | |||||||
| # The core then issues a store change event which triggers the refresh cycle | # The core then issues a store change event which triggers the refresh cycle | ||||||
| # Each widget in the tree then gets a chance to refresh itself to reflect the changes in the store if they need to | # Each widget in the tree then gets a chance to refresh itself to reflect the changes in the store if they need to | ||||||
|  |  | ||||||
| From a technical perspective, TiddlyWiki is a fairly classic MVC architecture, with strict separation of concerns. The model is the tiddler store, the view is a rendering tree (such as the one created from [[$:/core/ui/PageMacros]] in startup.js), and the controller is the core code itself. | From a technical perspective, TiddlyWiki is a fairly classic MVC architecture, with strict separation of concerns. The model is the tiddler store, the view is a rendering tree (such as the one created from [[$:/core/ui/PageTemplate]] in startup.js), and the controller is the core code itself. | ||||||
|   | |||||||
| @@ -6,7 +6,7 @@ type: text/vnd.tiddlywiki | |||||||
|  |  | ||||||
| The StateMechanism in TiddlyWiki is at the heart of how complex user interfaces can be built from WikiText. | The StateMechanism in TiddlyWiki is at the heart of how complex user interfaces can be built from WikiText. | ||||||
|  |  | ||||||
| In the browser, the TiddlyWiki display is produced by dynamically rendering the tiddler [[$:/core/ui/PageMacros]]. Through various transclusions and other widgets it renders the entire user interface. The dynamic rendering is accomplished by a mechanism called "binding": any changes to the tiddlers in the store are dynamically reflected in the browser display. | In the browser, the TiddlyWiki display is produced by dynamically rendering the tiddler [[$:/core/ui/PageTemplate]]. Through various transclusions and other widgets it renders the entire user interface. The dynamic rendering is accomplished by a mechanism called "binding": any changes to the tiddlers in the store are dynamically reflected in the browser display. | ||||||
|  |  | ||||||
| The stack of templates that make up the TiddlyWiki display are complex but we'll focus on the line that displays the main story column: | The stack of templates that make up the TiddlyWiki display are complex but we'll focus on the line that displays the main story column: | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										30
									
								
								editions/tw5.com/tiddlers/system/wikitext-macros.tid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								editions/tw5.com/tiddlers/system/wikitext-macros.tid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,30 @@ | |||||||
|  | title: $:/editions/tw5.com/wikitext-macros | ||||||
|  | tags: $:/tags/Macro | ||||||
|  |  | ||||||
|  | \define wikitext-example(src) | ||||||
|  | ``` | ||||||
|  | $src$ | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Renders as: | ||||||
|  |  | ||||||
|  | $src$ | ||||||
|  |  | ||||||
|  | In HTML: | ||||||
|  |  | ||||||
|  | $$$text/vnd.tiddlywiki>text/html | ||||||
|  | $src$ | ||||||
|  | $$$ | ||||||
|  |  | ||||||
|  | \end | ||||||
|  |  | ||||||
|  | \define wikitext-example-without-html(src) | ||||||
|  | ``` | ||||||
|  | $src$ | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | Renders as: | ||||||
|  |  | ||||||
|  | $src$ | ||||||
|  |  | ||||||
|  | \end | ||||||
							
								
								
									
										32
									
								
								editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | created: 20140612142500000 | ||||||
|  | modified: 20140612175900970 | ||||||
|  | tags: widget | ||||||
|  | title: ImportVariablesWidget | ||||||
|  | type: text/vnd.tiddlywiki | ||||||
|  |  | ||||||
|  | ! Introduction | ||||||
|  |  | ||||||
|  | The ImportVariablesWidget imports macro and variable definitions from a list of other tiddlers and makes them available to its children. For example: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | <$importvariables filter="[tag[mySpecialMacros]]"> | ||||||
|  | All the macros defined in tiddlers with the tag "mySpecialMacros" are available here | ||||||
|  | </$importvariables> | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | ! Attributes and Content | ||||||
|  |  | ||||||
|  | The content of the importvariables widget is the scope within which the imported variable definitions are available. | ||||||
|  |  | ||||||
|  | |!Attribute |!Description | | ||||||
|  | |filter |[[Tiddler filter|TiddlerFilters]] defining the tiddlers from which macro definitions will be imported | | ||||||
|  |  | ||||||
|  | ! Global Macros | ||||||
|  |  | ||||||
|  | So-called global macros are implemented within the main page template ([[$:/core/ui/PageTemplate]]) by wrapping the page content in the following importvariables widget: | ||||||
|  |  | ||||||
|  | ``` | ||||||
|  | <$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]"> | ||||||
|  | ... | ||||||
|  | </$importvariables> | ||||||
|  | ``` | ||||||
		Reference in New Issue
	
	Block a user
	 Jermolene
					Jermolene