From 9ab0c84140bbc0f31cf65e632bddac616bbadbda Mon Sep 17 00:00:00 2001 From: Jermolene Date: Thu, 12 Jun 2014 18:01:33 +0100 Subject: [PATCH] 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 --- core/modules/startup/render.js | 4 +- core/modules/widgets/importvariables.js | 115 ++++++++++++++++++ core/templates/static.template.html.tid | 2 +- core/templates/static.tiddler.html.tid | 4 +- core/ui/PageTemplate.tid | 4 + core/wiki/macros/CSS.tid | 10 ++ core/wiki/macros/lingo.tid | 10 ++ .../PageMacros.tid => wiki/macros/tabs.tid} | 48 +------- .../dev/TiddlyWiki for Developers.tid | 2 +- .../tiddlers/mechanisms/StateMechanism.tid | 2 +- .../tiddlers/system/wikitext-macros.tid | 30 +++++ .../widgets/ImportVariablesWidget.tid | 32 +++++ 12 files changed, 211 insertions(+), 52 deletions(-) create mode 100644 core/modules/widgets/importvariables.js create mode 100644 core/wiki/macros/CSS.tid create mode 100644 core/wiki/macros/lingo.tid rename core/{ui/PageMacros.tid => wiki/macros/tabs.tid} (62%) create mode 100644 editions/tw5.com/tiddlers/system/wikitext-macros.tid create mode 100644 editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid diff --git a/core/modules/startup/render.js b/core/modules/startup/render.js index f677788d0..de01c868b 100644 --- a/core/modules/startup/render.js +++ b/core/modules/startup/render.js @@ -21,7 +21,7 @@ exports.synchronous = true; // Default story and history lists var PAGE_TITLE_TITLE = "$:/core/wiki/title" 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 var DRAFT_TIDDLER_TIMEOUT = 400; @@ -49,7 +49,7 @@ exports.startup = function() { $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.pageWidgetNode = $tw.wiki.makeTranscludeWidget(PAGE_TEMPLATE_TITLE,{document: document, parentWidget: $tw.rootWidget}); $tw.pageContainer = document.createElement("div"); diff --git a/core/modules/widgets/importvariables.js b/core/modules/widgets/importvariables.js new file mode 100644 index 000000000..f1494e41c --- /dev/null +++ b/core/modules/widgets/importvariables.js @@ -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; + +})(); diff --git a/core/templates/static.template.html.tid b/core/templates/static.template.html.tid index 0c9f0cf36..a4da2cda0 100644 --- a/core/templates/static.template.html.tid +++ b/core/templates/static.template.html.tid @@ -21,6 +21,6 @@ type: text/vnd.tiddlywiki-html {{$:/StaticBanner||$:/core/templates/html-tiddler}} -{{$:/core/ui/PageMacros||$:/core/templates/html-tiddler}} +{{$:/core/ui/PageTemplate||$:/core/templates/html-tiddler}} diff --git a/core/templates/static.tiddler.html.tid b/core/templates/static.tiddler.html.tid index c9d711868..679c82dac 100644 --- a/core/templates/static.tiddler.html.tid +++ b/core/templates/static.tiddler.html.tid @@ -15,7 +15,9 @@ title: $:/core/templates/static.tiddler.html `{{$:/StaticBanner||$:/core/templates/html-tiddler}}`
-`<$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"/> +`
diff --git a/core/ui/PageTemplate.tid b/core/ui/PageTemplate.tid index 2d72617d7..889286af5 100644 --- a/core/ui/PageTemplate.tid +++ b/core/ui/PageTemplate.tid @@ -4,6 +4,8 @@ title: $:/core/ui/PageTemplate tw-page-container tw-page-view-$(themeTitle)$ tw-language-$(languageTitle)$ \end +<$importvariables filter="[[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]]"> + <$set name="themeTitle" value={{$:/view}}> <$set name="currentTiddler" value={{$:/language}}> @@ -37,3 +39,5 @@ tw-page-container tw-page-view-$(themeTitle)$ tw-language-$(languageTitle)$ + + diff --git a/core/wiki/macros/CSS.tid b/core/wiki/macros/CSS.tid new file mode 100644 index 000000000..58b0252a2 --- /dev/null +++ b/core/wiki/macros/CSS.tid @@ -0,0 +1,10 @@ +title: $:/core/macros/CSS +tags: $:/tags/Macro + +\define colour(name) +<$transclude tiddler={{$:/palette}} index="$name$"/> +\end + +\define color(name) +<> +\end diff --git a/core/wiki/macros/lingo.tid b/core/wiki/macros/lingo.tid new file mode 100644 index 000000000..3c761cf01 --- /dev/null +++ b/core/wiki/macros/lingo.tid @@ -0,0 +1,10 @@ +title: $:/core/macros/lingo +tags: $:/tags/Macro + +\define lingo-base() +$:/lingo/ +\end + +\define lingo(title) +{{$(lingo-base)$$title$}} +\end diff --git a/core/ui/PageMacros.tid b/core/wiki/macros/tabs.tid similarity index 62% rename from core/ui/PageMacros.tid rename to core/wiki/macros/tabs.tid index 56d18f782..cd5ed50f9 100644 --- a/core/ui/PageMacros.tid +++ b/core/wiki/macros/tabs.tid @@ -1,12 +1,5 @@ -title: $:/core/ui/PageMacros - -\define colour(name) -<$transclude tiddler={{$:/palette}} index="$name$"/> -\end - -\define color(name) -<> -\end +title: $:/core/macros/tabs +tags: $:/tags/Macro \define tabs(tabsList,default,state:"$:/state/tab",class)
@@ -27,40 +20,3 @@ title: $:/core/ui/PageMacros
\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}} diff --git a/editions/tw5.com/tiddlers/dev/TiddlyWiki for Developers.tid b/editions/tw5.com/tiddlers/dev/TiddlyWiki for Developers.tid index ee2e295f7..c81277f11 100644 --- a/editions/tw5.com/tiddlers/dev/TiddlyWiki for Developers.tid +++ b/editions/tw5.com/tiddlers/dev/TiddlyWiki for Developers.tid @@ -23,4 +23,4 @@ It also determines the standard UI flow: # 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 -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. diff --git a/editions/tw5.com/tiddlers/mechanisms/StateMechanism.tid b/editions/tw5.com/tiddlers/mechanisms/StateMechanism.tid index f7f28a33d..4366c57c2 100644 --- a/editions/tw5.com/tiddlers/mechanisms/StateMechanism.tid +++ b/editions/tw5.com/tiddlers/mechanisms/StateMechanism.tid @@ -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. -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: diff --git a/editions/tw5.com/tiddlers/system/wikitext-macros.tid b/editions/tw5.com/tiddlers/system/wikitext-macros.tid new file mode 100644 index 000000000..f919d3fce --- /dev/null +++ b/editions/tw5.com/tiddlers/system/wikitext-macros.tid @@ -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 diff --git a/editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid b/editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid new file mode 100644 index 000000000..5465c037e --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/ImportVariablesWidget.tid @@ -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 + +``` + +! 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]]"> +... + +```