diff --git a/core/language/en-GB/ControlPanel.multids b/core/language/en-GB/ControlPanel.multids index d8321edbf..93cfc3c10 100644 --- a/core/language/en-GB/ControlPanel.multids +++ b/core/language/en-GB/ControlPanel.multids @@ -206,6 +206,12 @@ Stylesheets/Caption: Stylesheets Stylesheets/Expand/Caption: Expand All Stylesheets/Hint: This is the rendered CSS of the current stylesheet tiddlers tagged with <> Stylesheets/Restore/Caption: Restore +TestCases/Caption: Test Cases +TestCases/Hint: Test cases are self contained examples for testing and learning +TestCases/All/Caption: All Test Cases +TestCases/All/Hint: All Test Cases +TestCases/Failed/Caption: Failed Test Cases +TestCases/Failed/Hint: Only Failed Test Cases Theme/Caption: Theme Theme/Prompt: Current theme: TiddlerFields/Caption: Tiddler Fields diff --git a/core/language/en-GB/Docs/PaletteColours.multids b/core/language/en-GB/Docs/PaletteColours.multids index 98addbf85..1c671a67c 100644 --- a/core/language/en-GB/Docs/PaletteColours.multids +++ b/core/language/en-GB/Docs/PaletteColours.multids @@ -65,6 +65,9 @@ sidebar-tab-foreground-selected: Sidebar tab foreground for selected tabs sidebar-tab-foreground: Sidebar tab foreground sidebar-tiddler-link-foreground-hover: Sidebar tiddler link foreground hover sidebar-tiddler-link-foreground: Sidebar tiddler link foreground +testcase-accent-level-1: Test case accent colour with no nesting +testcase-accent-level-2: Test case accent colour with 2nd level nesting +testcase-accent-level-3: Test case accent colour with 3rd level nesting or higher site-title-foreground: Site title foreground static-alert-foreground: Static alert foreground tab-background-selected: Tab background for selected tabs diff --git a/core/modules/commands/savewikifolder.js b/core/modules/commands/savewikifolder.js index c0fccd775..461ff6f04 100644 --- a/core/modules/commands/savewikifolder.js +++ b/core/modules/commands/savewikifolder.js @@ -176,7 +176,10 @@ WikiFolderMaker.prototype.saveCustomPlugin = function(pluginTiddler) { this.saveJSONFile(directory + path.sep + "plugin.info",pluginInfo); self.log("Writing " + directory + path.sep + "plugin.info: " + JSON.stringify(pluginInfo,null,$tw.config.preferences.jsonSpaces)); var pluginTiddlers = $tw.utils.parseJSONSafe(pluginTiddler.fields.text).tiddlers; // A hashmap of tiddlers in the plugin - $tw.utils.each(pluginTiddlers,function(tiddler) { + $tw.utils.each(pluginTiddlers,function(tiddler,title) { + if(!tiddler.title) { + tiddler.title = title; + } self.saveTiddler(directory,new $tw.Tiddler(tiddler)); }); }; diff --git a/core/modules/parsers/wikiparser/rules/quoteblock.js b/core/modules/parsers/wikiparser/rules/quoteblock.js index 71b689680..787b7e30f 100644 --- a/core/modules/parsers/wikiparser/rules/quoteblock.js +++ b/core/modules/parsers/wikiparser/rules/quoteblock.js @@ -3,30 +3,7 @@ title: $:/core/modules/parsers/wikiparser/rules/quoteblock.js type: application/javascript module-type: wikirule -Wiki text rule for quote blocks. For example: - -``` - <<<.optionalClass(es) optional cited from - a quote - <<< - - <<<.optionalClass(es) - a quote - <<< optional cited from -``` - -Quotes can be quoted by putting more 0) { tree.unshift({ diff --git a/core/modules/utils/dom/http.js b/core/modules/utils/dom/http.js index 05879e5a9..ddb1e17c4 100644 --- a/core/modules/utils/dom/http.js +++ b/core/modules/utils/dom/http.js @@ -283,7 +283,7 @@ exports.httpRequest = function(options) { // Set up the state change handler request.onreadystatechange = function() { if(this.readyState === 4) { - if(this.status === 200 || this.status === 201 || this.status === 204) { + if(this.status >= 200 && this.status < 300) { // Success! options.callback(null,this[returnProp],this); return; diff --git a/core/modules/widgets/data.js b/core/modules/widgets/data.js new file mode 100644 index 000000000..c46dd1fb8 --- /dev/null +++ b/core/modules/widgets/data.js @@ -0,0 +1,145 @@ +/*\ +title: $:/core/modules/widgets/data.js +type: application/javascript +module-type: widget + +Widget to dynamically represent one or more tiddlers + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget; + +var DataWidget = function(parseTreeNode,options) { + this.dataWidgetTag = parseTreeNode.type; + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +DataWidget.prototype = new Widget(); + +/* +Render this widget into the DOM +*/ +DataWidget.prototype.render = function(parent,nextSibling) { + this.parentDomNode = parent; + this.computeAttributes(); + this.execute(); + var jsonPayload = JSON.stringify(this.readDataTiddlerValues(),null,4); + var textNode = this.document.createTextNode(jsonPayload); + parent.insertBefore(textNode,nextSibling); + this.domNodes.push(textNode); +}; + +/* +Compute the internal state of the widget +*/ +DataWidget.prototype.execute = function() { + // Construct the child widgets + this.makeChildWidgets(); +}; + +/* +Read the tiddler value(s) from a data widget – must be called after the .render() method +*/ +DataWidget.prototype.readDataTiddlerValues = function() { + var self = this; + // Start with a blank object + var item = Object.create(null); + // Read any attributes not prefixed with $ + $tw.utils.each(this.attributes,function(value,name) { + if(name.charAt(0) !== "$") { + item[name] = value; + } + }); + item = new $tw.Tiddler(item); + // Deal with $tiddler, $filter or $compound-tiddler attributes + var tiddlers = [],title; + if(this.hasAttribute("$tiddler")) { + title = this.getAttribute("$tiddler"); + if(title) { + var tiddler = this.wiki.getTiddler(title); + if(tiddler) { + tiddlers.push(tiddler); + } + } + } + if(this.hasAttribute("$filter")) { + var filter = this.getAttribute("$filter"); + if(filter) { + var titles = this.wiki.filterTiddlers(filter); + $tw.utils.each(titles,function(title) { + var tiddler = self.wiki.getTiddler(title); + tiddlers.push(tiddler); + }); + } + } + if(this.hasAttribute("$compound-tiddler")) { + title = this.getAttribute("$compound-tiddler"); + if(title) { + tiddlers.push.apply(tiddlers,this.extractCompoundTiddler(title)); + } + } + // Convert the literal item to field strings + item = item.getFieldStrings(); + if(tiddlers.length === 0) { + if(Object.keys(item).length > 0 && !!item.title) { + return [item]; + } else { + return []; + } + } else { + var results = []; + $tw.utils.each(tiddlers,function(tiddler,index) { + var fields = tiddler.getFieldStrings(); + results.push($tw.utils.extend({},fields,item)); + }); + return results; + } +}; + +/* +Helper to extract tiddlers from text/vnd.tiddlywiki-multiple tiddlers +*/ +DataWidget.prototype.extractCompoundTiddler = function(title) { + var tiddler = this.wiki.getTiddler(title); + if(tiddler && tiddler.fields.type === "text/vnd.tiddlywiki-multiple") { + var text = tiddler.fields.text || "", + rawTiddlers = text.split(/\r?\n\+\r?\n/), + tiddlers = []; + $tw.utils.each(rawTiddlers,function(rawTiddler) { + var fields = Object.create(null), + split = rawTiddler.split(/\r?\n\r?\n/mg); + if(split.length >= 1) { + fields = $tw.utils.parseFields(split[0],fields); + } + if(split.length >= 2) { + fields.text = split.slice(1).join("\n\n"); + } + tiddlers.push(new $tw.Tiddler(fields)); + }); + return tiddlers; + } else { + return []; + } +}; + +/* +Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering +*/ +DataWidget.prototype.refresh = function(changedTiddlers) { + // It would be expensive to calculate whether the changedTiddlers impact the filter + // identified by the $filter attribute so we just refresh ourselves unconditionally + this.refreshSelf(); + return true; +}; + +exports.data = DataWidget; + +})(); diff --git a/core/modules/widgets/testcase.js b/core/modules/widgets/testcase.js new file mode 100644 index 000000000..abb8f03f6 --- /dev/null +++ b/core/modules/widgets/testcase.js @@ -0,0 +1,160 @@ +/*\ +title: $:/core/modules/widgets/testcase.js +type: application/javascript +module-type: widget + +Widget to display a test case + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var Widget = require("$:/core/modules/widgets/widget.js").widget; + +var TestCaseWidget = function(parseTreeNode,options) { + this.initialise(parseTreeNode,options); +}; + +/* +Inherit from the base widget class +*/ +TestCaseWidget.prototype = new Widget(); + +/* +Render this widget into the DOM +*/ +TestCaseWidget.prototype.render = function(parent,nextSibling) { + var self = this; + this.parentDomNode = parent; + this.computeAttributes(); + this.execute(); + // Create container DOM node + var domNode = this.document.createElement("div"); + this.domNodes.push(domNode); + parent.insertBefore(domNode,nextSibling); + // Render the children into a hidden DOM node + var parser = { + tree: [{ + type: "widget", + attributes: {}, + orderedAttributes: [], + children: this.parseTreeNode.children || [] + }] + }; + this.contentRoot = this.wiki.makeWidget(parser,{ + document: $tw.fakeDocument, + parentWidget: this + }); + this.contentContainer = $tw.fakeDocument.createElement("div"); + this.contentRoot.render(this.contentContainer,null); + // Create a wiki + this.testcaseWiki = new $tw.Wiki(); + // Always load the core plugin + var loadTiddler = function(title) { + var tiddler = self.wiki.getTiddler(title); + if(tiddler) { + self.testcaseWiki.addTiddler(tiddler); + } + } + loadTiddler("$:/core"); + loadTiddler("$:/plugins/tiddlywiki/codemirror"); + // Load tiddlers from child data widgets + var tiddlers = []; + this.findChildrenDataWidgets(this.contentRoot.children,"data",function(widget) { + Array.prototype.push.apply(tiddlers,widget.readDataTiddlerValues()); + }); + var jsonPayload = JSON.stringify(tiddlers); + this.testcaseWiki.addTiddlers(tiddlers); + // Unpack plugin tiddlers + this.testcaseWiki.readPluginInfo(); + this.testcaseWiki.registerPluginTiddlers("plugin"); + this.testcaseWiki.unpackPluginTiddlers(); + this.testcaseWiki.addIndexersToWiki(); + // Generate a `transclusion` variable that depends on the values of the payload tiddlers so that the template can easily make unique state tiddlers + this.setVariable("transclusion",$tw.utils.hashString(jsonPayload)); + // Generate a `payloadTiddlers` variable that contains the payload in JSON format + this.setVariable("payloadTiddlers",jsonPayload); + // Render the test rendering if required + if(this.testcaseTestOutput && this.testcaseTestExpectedResult) { + var testcaseOutputContainer = $tw.fakeDocument.createElement("div"); + var testcaseOutputWidget = this.testcaseWiki.makeTranscludeWidget(this.testcaseTestOutput,{ + document: $tw.fakeDocument, + parseAsInline: false, + parentWidget: this, + variables: { + currentTiddler: this.testcaseTestOutput + } + }); + testcaseOutputWidget.render(testcaseOutputContainer); + } + // Clear changes queue + this.testcaseWiki.clearTiddlerEventQueue(); + // Run the actions if provided + if(this.testcaseWiki.tiddlerExists(this.testcaseTestActions)) { + testcaseOutputWidget.invokeActionString(this.testcaseWiki.getTiddlerText(this.testcaseTestActions)); + testcaseOutputWidget.refresh(this.testcaseWiki.changedTiddlers,testcaseOutputContainer); + } + // Set up the test result variables + var testResult = "", + outputHTML = "", + expectedHTML = ""; + if(this.testcaseTestOutput && this.testcaseTestExpectedResult) { + outputHTML = testcaseOutputContainer.children[0].innerHTML; + expectedHTML = this.testcaseWiki.getTiddlerText(this.testcaseTestExpectedResult); + if(outputHTML === expectedHTML) { + testResult = "pass"; + } else { + testResult = "fail"; + } + this.setVariable("outputHTML",outputHTML); + this.setVariable("expectedHTML",expectedHTML); + this.setVariable("testResult",testResult); + this.setVariable("currentTiddler",this.testcaseTestOutput); + } + // Don't display anything if testHideIfPass is "yes" and the tests have passed + if(this.testcaseHideIfPass === "yes" && testResult === "pass") { + return; + } + // Render the page root template of the subwiki + var rootWidget = this.testcaseWiki.makeTranscludeWidget(this.testcaseTemplate,{ + document: this.document, + parseAsInline: false, + parentWidget: this + }); + rootWidget.render(domNode); + // Trap changes in the wiki and refresh the rendering + this.testcaseWiki.addEventListener("change",function(changes) { + rootWidget.refresh(changes,domNode); + }); +}; + +/* +Compute the internal state of the widget +*/ +TestCaseWidget.prototype.execute = function() { + this.testcaseTemplate = this.getAttribute("template","$:/core/ui/testcases/DefaultTemplate"); + this.testcaseTestOutput = this.getAttribute("testOutput"); + this.testcaseTestActions = this.getAttribute("testActions"); + this.testcaseTestExpectedResult = this.getAttribute("testExpectedResult"); + this.testcaseHideIfPass = this.getAttribute("testHideIfPass"); +}; + +/* +Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering +*/ +TestCaseWidget.prototype.refresh = function(changedTiddlers) { + var changedAttributes = this.computeAttributes(); + if($tw.utils.count(changedAttributes) > 0) { + this.refreshSelf(); + return true; + } else { + return this.contentRoot.refresh(changedTiddlers); + } +}; + +exports["testcase"] = TestCaseWidget; + +})(); diff --git a/core/modules/widgets/widget.js b/core/modules/widgets/widget.js index ea300ca0e..69f63a684 100755 --- a/core/modules/widgets/widget.js +++ b/core/modules/widgets/widget.js @@ -813,6 +813,21 @@ Widget.prototype.allowActionPropagation = function() { return true; }; +/* +Find child <$data> widgets recursively. The tag name allows aliased versions of the widget to be found too +*/ +Widget.prototype.findChildrenDataWidgets = function(children,tag,callback) { + var self = this; + $tw.utils.each(children,function(child) { + if(child.dataWidgetTag === tag) { + callback(child); + } + if(child.children) { + self.findChildrenDataWidgets(child.children,tag,callback); + } + }); +}; + /* Evaluate a variable with parameters. This is a static convenience method that attempts to evaluate a variable as a function, returning an array of strings */ diff --git a/core/palettes/Vanilla.tid b/core/palettes/Vanilla.tid index 4c660e912..c7c800046 100644 --- a/core/palettes/Vanilla.tid +++ b/core/palettes/Vanilla.tid @@ -95,6 +95,9 @@ table-footer-background: #a8a8a8 table-header-background: #f0f0f0 tag-background: #ec6 tag-foreground: #ffffff +testcase-accent-level-1: #84C5E6 +testcase-accent-level-2: #E3B740 +testcase-accent-level-3: #5FD564 tiddler-background: <> tiddler-border: <> tiddler-controls-foreground-hover: #888888 diff --git a/core/ui/ControlPanel/TestCases.tid b/core/ui/ControlPanel/TestCases.tid new file mode 100644 index 000000000..401e14113 --- /dev/null +++ b/core/ui/ControlPanel/TestCases.tid @@ -0,0 +1,10 @@ +title: $:/core/ui/ControlPanel/TestCases +tags: $:/tags/ControlPanel/Advanced +caption: {{$:/language/ControlPanel/TestCases/Caption}} + +\whitespace trim +{{$:/language/ControlPanel/TestCases/Hint}} + +
+<$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/TestCases]!has[draft.of]]" default="$:/core/ui/ControlPanel/TestCases/All"/> +
diff --git a/core/ui/ControlPanel/TestCasesAll.tid b/core/ui/ControlPanel/TestCasesAll.tid new file mode 100644 index 000000000..571fb93c2 --- /dev/null +++ b/core/ui/ControlPanel/TestCasesAll.tid @@ -0,0 +1,24 @@ +title: $:/core/ui/ControlPanel/TestCases/All +tags: $:/tags/ControlPanel/TestCases +caption: {{$:/language/ControlPanel/TestCases/All/Caption}} + +\define lingo-base() $:/language/ControlPanel/ +<> + +<$list filter="[all[tiddlers+shadows]tag[$:/tags/wiki-test-spec]type[text/vnd.tiddlywiki-multiple]] [all[tiddlers+shadows]tag[$:/tags/wiki-test-spec-failing]type[text/vnd.tiddlywiki-multiple]]"> + +

+ +<$link> + +<$text text=<>/> + + + +

+ +<$transclude + $tiddler="$:/core/ui/TestCaseTemplate" +/> + + diff --git a/core/ui/ControlPanel/TestCasesFailed.tid b/core/ui/ControlPanel/TestCasesFailed.tid new file mode 100644 index 000000000..4ab2d062d --- /dev/null +++ b/core/ui/ControlPanel/TestCasesFailed.tid @@ -0,0 +1,15 @@ +title: $:/core/ui/ControlPanel/TestCases/Failed +tags: $:/tags/ControlPanel/TestCases +caption: {{$:/language/ControlPanel/TestCases/Failed/Caption}} + +\define lingo-base() $:/language/ControlPanel/ +<> + +<$list filter="[all[tiddlers+shadows]tag[$:/tags/wiki-test-spec]type[text/vnd.tiddlywiki-multiple]] [all[tiddlers+shadows]tag[$:/tags/wiki-test-spec-failing]type[text/vnd.tiddlywiki-multiple]]"> + +<$transclude + $tiddler="$:/core/ui/TestCaseTemplate" + hideIfPass="yes" +/> + + diff --git a/core/ui/TestCaseTemplate.tid b/core/ui/TestCaseTemplate.tid new file mode 100644 index 000000000..74b6ab27d --- /dev/null +++ b/core/ui/TestCaseTemplate.tid @@ -0,0 +1,18 @@ +title: $:/core/ui/TestCaseTemplate + +\parameters (hideIfPass:"no") +\whitespace trim +<$let + linkTarget="yes" + displayFormat={{!!display-format}} +> + <$testcase + testOutput="Output" + testExpectedResult="ExpectedResult" + testActions="Actions" + testHideIfPass=<> + > + <$data $compound-tiddler=<>/> + <$data title="Description" text={{!!description}}/> + + diff --git a/core/ui/TestCases/DefaultTemplate.tid b/core/ui/TestCases/DefaultTemplate.tid new file mode 100644 index 000000000..679620969 --- /dev/null +++ b/core/ui/TestCases/DefaultTemplate.tid @@ -0,0 +1,64 @@ +title: $:/core/ui/testcases/DefaultTemplate + +\whitespace trim +\procedure linkcatcherActions() +<%if [has[title]] %> + <$qualify title=<> name="qualifiedState"> + <$action-setfield $tiddler=<> text=<>/> + +<%endif%> +\end + +<$let + state={{{ [] }}} +> +
+
+

+ <$genesis $type={{{ [!match[]then[$link]else[div]] }}}> + <%if [!match[]] %> + !match[fail]then[tc-test-case-result-icon-pass]] [match[fail]then[tc-test-case-result-icon-fail]] +[join[ ]] }}}> + <%if [!match[fail]] %> + {{$:/core/images/done-button}} + <%else%> + {{$:/core/images/close-button}} + <%endif%> + + <%endif%> + <$view tiddler="Description" mode="inline"/> + +

+
+ <%if [[Narrative]is[tiddler]] %> +
+ <$transclude $tiddler="Narrative" mode="block"/> +
+ <%endif%> + <%if [match[fail]] %> +
+
+ TEST FAILED +
+
+ <$diff-text source=<> dest=<>/> +
+
+ <%endif%> +
+
+ <$macrocall $name="tabs" tabsList="[all[tiddlers]sort[]] -[prefix] -Description -Narrative -ExpectedResult -Output Output +[putfirst[]] -[has[plugin-type]]" state=<> default="Output" template="$:/core/ui/testcases/DefaultTemplate/SourceTabs"/> +
+
+
+
+ <%if [!match[]else[wikitext]match[plaintext]] %> +
<$view tiddler="Output" format="plainwikified" mode="block"/>
+ <%else%> + <$linkcatcher actions=<>> + <$transclude $tiddler="Output" $mode="block"/> + + <%endif%> +
+
+
+ diff --git a/core/ui/TestCases/DefaultTemplateSourceTabs.tid b/core/ui/TestCases/DefaultTemplateSourceTabs.tid new file mode 100644 index 000000000..68c62c1f6 --- /dev/null +++ b/core/ui/TestCases/DefaultTemplateSourceTabs.tid @@ -0,0 +1,24 @@ +title: $:/core/ui/testcases/DefaultTemplate/SourceTabs + +\whitespace trim +\procedure body() +<$list filter="[fields[]] -text +[limit[1]]" variable="ignore"> + + + <$list filter="[fields[]sort[]] -text -title title +[putfirst[]]" variable="fieldName"> + + + + + + +
+ <$text text=<>/> + + <$view tiddler=<> field=<>/> +
+ +<$edit class="tc-edit-texteditor" tiddler=<>/> +\end + +<$transclude $variable="body" $mode="inline"/> diff --git a/core/ui/TestCases/RawJSONTemplate.tid b/core/ui/TestCases/RawJSONTemplate.tid new file mode 100644 index 000000000..fe9c583e8 --- /dev/null +++ b/core/ui/TestCases/RawJSONTemplate.tid @@ -0,0 +1,4 @@ +title: $:/core/ui/testcases/RawJSONTemplate + +\whitespace trim +<$text text=<>/> diff --git a/core/wiki/config/ViewTemplateBodyFilters.multids b/core/wiki/config/ViewTemplateBodyFilters.multids index ff9fe7250..e1dd62880 100644 --- a/core/wiki/config/ViewTemplateBodyFilters.multids +++ b/core/wiki/config/ViewTemplateBodyFilters.multids @@ -1,6 +1,7 @@ title: $:/config/ViewTemplateBodyFilters/ tags: $:/tags/ViewTemplateBodyFilter +testcase: [tag[$:/tags/wiki-test-spec]type[text/vnd.tiddlywiki-multiple]then[$:/core/ui/TestCaseTemplate]] [tag[$:/tags/wiki-test-spec-failing]type[text/vnd.tiddlywiki-multiple]then[$:/core/ui/TestCaseTemplate]] stylesheet: [tag[$:/tags/Stylesheet]then[$:/core/ui/ViewTemplate/body/rendered-plain-text]] core-ui-tags: [tag[$:/tags/PageTemplate]] [tag[$:/tags/EditTemplate]] [tag[$:/tags/ViewTemplate]] [tag[$:/tags/KeyboardShortcut]] [tag[$:/tags/ImportPreview]] [tag[$:/tags/EditPreview]][tag[$:/tags/EditorToolbar]] [tag[$:/tags/Actions]] :then[[$:/core/ui/ViewTemplate/body/code]] system: [prefix[$:/boot/]] [prefix[$:/config/]] [prefix[$:/core/macros]] [prefix[$:/core/save/]] [prefix[$:/core/templates/]] [prefix[$:/info/]] [prefix[$:/language/]] [prefix[$:/languages/]] [prefix[$:/snippets/]] [prefix[$:/state/]] [prefix[$:/status/]] [prefix[$:/info/]] [prefix[$:/temp/]] +[!is[image]limit[1]then[$:/core/ui/ViewTemplate/body/code]] diff --git a/core/wiki/macros/testcase.tid b/core/wiki/macros/testcase.tid new file mode 100644 index 000000000..a04cb540d --- /dev/null +++ b/core/wiki/macros/testcase.tid @@ -0,0 +1,10 @@ +title: $:/core/macros/testcase +tags: $:/tags/Macro $:/tags/Global + +\whitespace trim + +\procedure testcase(tiddler) +<$tiddler tiddler=<>> +<$transclude $tiddler="$:/core/ui/TestCaseTemplate"> + +\end diff --git a/core/wiki/tags/ViewTemplateBodyFilter.tid b/core/wiki/tags/ViewTemplateBodyFilter.tid index 7b9fb7fd8..0143c1f88 100644 --- a/core/wiki/tags/ViewTemplateBodyFilter.tid +++ b/core/wiki/tags/ViewTemplateBodyFilter.tid @@ -1,2 +1,2 @@ title: $:/tags/ViewTemplateBodyFilter -list: $:/config/ViewTemplateBodyFilters/hide-body $:/config/ViewTemplateBodyFilters/code-body $:/config/ViewTemplateBodyFilters/stylesheet $:/config/ViewTemplateBodyFilters/core-ui-advanced-search $:/config/ViewTemplateBodyFilters/core-ui-tags $:/config/ViewTemplateBodyFilters/system $:/config/ViewTemplateBodyFilters/import $:/config/ViewTemplateBodyFilters/plugin $:/config/ViewTemplateBodyFilters/default \ No newline at end of file +list: $:/config/ViewTemplateBodyFilters/testcase $:/config/ViewTemplateBodyFilters/hide-body $:/config/ViewTemplateBodyFilters/code-body $:/config/ViewTemplateBodyFilters/stylesheet $:/config/ViewTemplateBodyFilters/core-ui-advanced-search $:/config/ViewTemplateBodyFilters/core-ui-tags $:/config/ViewTemplateBodyFilters/system $:/config/ViewTemplateBodyFilters/import $:/config/ViewTemplateBodyFilters/plugin $:/config/ViewTemplateBodyFilters/default \ No newline at end of file diff --git a/editions/prerelease/tiddlers/Release 5.3.4.tid b/editions/prerelease/tiddlers/Release 5.3.4.tid index 562909a93..638afaaa2 100644 --- a/editions/prerelease/tiddlers/Release 5.3.4.tid +++ b/editions/prerelease/tiddlers/Release 5.3.4.tid @@ -10,12 +10,23 @@ description: Under development ! Major Improvements +<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7734">> several new features that together allow interactive learning tours to be created and presented in TiddlyWiki. + +The demo TiddlyWiki interactive tour can be seen at https://tiddlywiki.com/prerelease/tour + +The new features include: + +* The new Tour Plugin itself +* The new Confetti Plugin that allows animated bursts of confetti to be displayed +* Improvements to the Dynannotate Plugin to add the ability to highlight screen elements using an animated spotlight effect ! Translation improvements Improvements to the following translations: -* +* Chinese +* Macedonian +* Polish ! Plugin Improvements @@ -23,23 +34,44 @@ Improvements to the following translations: ! Widget Improvements -* +* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/8115">> ''$timestamp'' attribute to ActionDeleteFieldWidget + +! Filter Improvements + +* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7966">> new [[backtranscludes Operator]] ! Usability Improvements -* +* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/issues/8121">> new keyboard shortcut for refreshing the page ! Hackability Improvements -* +* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7966">> button to the JavaScript error popup allowing tiddlers to be saved to a local JSON file +* <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/issues/8120">> to latest version of modern-normalize 2.0.0 ! Bug Fixes -* +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7933">> TiddlyWikiClassic build process +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7935">> LinkWidget not refreshing when the `to` attribute changes +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/3460">> parsing bug with empty procedures/macros +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7907">> functions to use variables set by filter runs +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7943">> edit widget not refreshing when the editor type changes +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7922">> editor preview width +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/9bf3c0602d4fd3fe5ac7411db697b51f87a79056">> [[WidgetMessage: tm-http-request]] not returning data in the event of an error +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/8150">> [[WidgetMessage: tm-http-request]] incorrectly interpreting 2XX status codes +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7949">> processing of path separators in `tiddlywiki.files` files on Windows +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7975">> incorrect state reference in advanced search +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7985">> clipping of popups in preview pane +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/8039">> JavaScript error when attempting to export missing tiddlers to a CSV file +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7909">> imported procedures defaulting to `\whitespace trim` +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/801ed0ea1164aab4f88547322f9d73704388143f">> crash with [[cycle Operator]] if the the step size is larger than the number of operands +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/8095">> proper DOCTYPE for the open window template +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7945">> theme font size settings to open in new window CSS ! Node.js Improvements -* +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/8141">> usage of "Cache-Control" header +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7878">> SaveCommand not overwriting files when required ! Performance Improvements @@ -58,4 +90,29 @@ Improvements to the following translations: [[@Jermolene|https://github.com/Jermolene]] would like to thank the contributors to this release who have generously given their time to help improve TiddlyWiki: <<.contributors """ +andjar +AnthonyMuscio +bimlas +BramChen +btheado +BurningTreeC +catter-fly +eschlon +etardiff +flibbles +FSpark +hoelzro +jinix6 +joshuafontany +linonetwo +mateuszwilczek +mklauber +oeyoews +pmario +PotOfCoffee2Go +rmunn +saqimtiaz +sarna +Telumire +yaisog """>> diff --git a/editions/test/tiddlers/HelloThere.tid b/editions/test/tiddlers/HelloThere.tid index d41f45fe2..74ea616e5 100644 --- a/editions/test/tiddlers/HelloThere.tid +++ b/editions/test/tiddlers/HelloThere.tid @@ -3,3 +3,7 @@ title: HelloThere This is TiddlyWiki's browser-based test runner for version <>. See the bottom of this page for the test results. https://tiddlywiki.com/ + +! Test Cases + +{{$:/core/ui/ControlPanel/TestCases}} diff --git a/editions/test/tiddlers/tests/from-tw5.com/tiddlywiki.files b/editions/test/tiddlers/tests/from-tw5.com/tiddlywiki.files new file mode 100644 index 000000000..c8ce1656e --- /dev/null +++ b/editions/test/tiddlers/tests/from-tw5.com/tiddlywiki.files @@ -0,0 +1,5 @@ +{ + "directories": [ + "../../../../tw5.com/tiddlers/testcases" + ] +} \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/concepts/CompoundTiddlers.tid b/editions/tw5.com/tiddlers/concepts/CompoundTiddlers.tid new file mode 100644 index 000000000..c4a049a6a --- /dev/null +++ b/editions/tw5.com/tiddlers/concepts/CompoundTiddlers.tid @@ -0,0 +1,31 @@ +title: CompoundTiddlers +modified: 20240507221902644 +created: 20240507221902644 +tags: Concepts + +Compound tiddlers are a special type of tiddler that can store one or more payload tiddlers. The tiddlers within a compound tiddler are only accessible via special operations, typically with the TestCaseWidget. + +The compound tiddler format is extremely simple, and includes the notable flaw that it does not permit tiddlers that contain a plus sign (`+`) on a line by itself. It is not intended as a general purpose way of storing tiddler data. + +Compound tiddlers are identified by having their type field set to `text/vnd.tiddlywiki-multiple`. + +The content of a compound tiddler consists of a sequence of tiddlers separated by a plus sign (`+`) on a line by itself. Each tiddler uses the same format as [[.tid files|TiddlerFiles]]. + +For example: + +``` +title: First +tags: one two + +This is the first tiddler ++ +title: Second +tags: three four + +This is the second tiddler ++ +title: third +tags: five six + +This is the third tiddler +``` diff --git a/editions/tw5.com/tiddlers/concepts/TestCaseTiddlers.tid b/editions/tw5.com/tiddlers/concepts/TestCaseTiddlers.tid new file mode 100644 index 000000000..ff84d6800 --- /dev/null +++ b/editions/tw5.com/tiddlers/concepts/TestCaseTiddlers.tid @@ -0,0 +1,27 @@ +title: TestCaseTiddlers +modified: 20240507221902644 +created: 20240507221902644 +tags: Concepts + +Test case tiddlers encapsulate one or more tiddlers that can be displayed as a [[test case|TestCaseWidget]]: an independent embedded wiki that can be used for testing or learning purposes. + +Test case tiddlers are formatted as CompoundTiddlers, allowing them to contain multiple tiddlers packed into one. + +Test case tiddlers have the following fields: + +|!Field |!Description | +|<<.field type>> | Should be set to `text/vnd.tiddlywiki-multiple` | +|<<.field tags>> | Test cases are tagged [[$:/tags/wiki-test-spec]]. Test cases that intentionally fail are tagged [[$:/tags/wiki-test-spec-failing]] | +|<<.field description>> |Descriptive heading for the test, intended to make it easy to identify the test | +|<<.field display-format>> |Optional, defaults to `wikitext`. Set to `plaintext` to cause the output to be rended as plain text | + +Test case tiddlers with the appropriate tag are shown in $:/ControlPanel + +Some payload tiddlers are set aside for special purposes: + +|!Tiddler |!Description | +|''Narrative'' |Narrative description of the test, intended to explain the purpose and operation of the test | +|''Output'' |The tiddler that produces the test output | +|''~ExpectedResult'' |HTML of expected result of rendering the ''Output'' tiddler | + + diff --git a/editions/tw5.com/tiddlers/concepts/TiddlerFields.tid b/editions/tw5.com/tiddlers/concepts/TiddlerFields.tid index 22017c570..049782a42 100644 --- a/editions/tw5.com/tiddlers/concepts/TiddlerFields.tid +++ b/editions/tw5.com/tiddlers/concepts/TiddlerFields.tid @@ -1,11 +1,11 @@ created: 20130825213300000 -modified: 20220109101407050 +modified: 20240416103247799 tags: Concepts title: TiddlerFields type: text/vnd.tiddlywiki \define lingo-base() $:/language/Docs/Fields/ -~TiddlerFields are name:value pairs that make up a [[tiddler|Tiddlers]]. Field names must be lowercase letters, digits or the characters `-` (dash), `_` (underscore) and `.` (period). +~TiddlerFields are name:value pairs that make up a [[tiddler|Tiddlers]]. Field names may contain any combination of characters (prior to [[v5.2.0|Release 5.2.0]], fields were constrained to be lowercase letters, digits or the characters `-` (dash), `_` (underscore) and `.` (period)). The standard fields are: diff --git a/editions/tw5.com/tiddlers/filters/syntax/Filter Run Prefix.tid b/editions/tw5.com/tiddlers/filters/syntax/Filter Run Prefix.tid index 4387ba9cb..5eb487b6d 100644 --- a/editions/tw5.com/tiddlers/filters/syntax/Filter Run Prefix.tid +++ b/editions/tw5.com/tiddlers/filters/syntax/Filter Run Prefix.tid @@ -4,7 +4,7 @@ tags: [[Filter Expression]] title: Filter Run Prefix type: text/vnd.tiddlywiki -There are 2 types of filter run prefixes that are interchangeable. Named prefixes and shortcut prefixes. +There are 2 types of filter run prefixes that are interchangeable; [[named prefixes|Named Filter Run Prefix]] and [[shortcut prefixes|Shortcut Filter Run Prefix]]. <$railroad text=""" \start none diff --git a/editions/tw5.com/tiddlers/hellothere/HelloThere.tid b/editions/tw5.com/tiddlers/hellothere/HelloThere.tid index 91ee04e2f..956deec8c 100644 --- a/editions/tw5.com/tiddlers/hellothere/HelloThere.tid +++ b/editions/tw5.com/tiddlers/hellothere/HelloThere.tid @@ -5,7 +5,7 @@ tags: TableOfContents title: HelloThere type: text/vnd.tiddlywiki - !!.tc-hero-heading ''Welcome to TiddlyWiki, a unique [[non-linear|Philosophy of Tiddlers]] notebook for [[capturing|Creating and editing tiddlers]], [[organising|Structuring TiddlyWiki]] and [[sharing|Sharing your tiddlers with others]] complex information'' +!!.tc-hero-heading ''Welcome to TiddlyWiki, a unique [[non-linear|Philosophy of Tiddlers]] notebook for [[capturing|Creating and editing tiddlers]], [[organising|Structuring TiddlyWiki]] and [[sharing|Sharing your tiddlers with others]] complex information'' Use it to keep your [[to-do list|TaskManagementExample]], to plan an [[essay or novel|"TiddlyWiki for Scholars" by Alberto Molina]], or to organise your wedding. Record every thought that crosses your brain, or build a flexible and responsive website. diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/ImportCompound.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportCompound.tid new file mode 100644 index 000000000..20e967316 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportCompound.tid @@ -0,0 +1,33 @@ +title: TestCases/DataWidget/ImportCompound +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Importing a compound payload tiddler and adding custom fields +display-format: plaintext + +title: Narrative + +Using the data widget to import a tiddler stored in a compound tiddler ++ +title: Output + +<$data $compound-tiddler="Compound" custom="Alpha"/> ++ +title: Compound +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Payload Tiddler +tags: Alpha Beta Gamma + +This is a payload tiddler from a compound tiddler ++ +title: ExpectedResult + +

[ + { + "title": "Payload Tiddler", + "tags": "Alpha Beta Gamma", + "text": "This is a payload tiddler from a compound tiddler", + "custom": "Alpha" + } +]

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedFilter.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedFilter.tid new file mode 100644 index 000000000..3e7416fe3 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedFilter.tid @@ -0,0 +1,49 @@ +title: TestCases/DataWidget/ImportedFilter +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Imported filter definition +display-format: plaintext + +title: Narrative + +Using the data widget to create copies of all the tiddlers with the title prefix "Day: T", adding the field "custom" set to "Beta" ++ +title: Output + +<$data $filter="[prefix[Day: T]]" custom="Beta"/> ++ +title: Day: Monday +text: Today is Monday ++ +title: Day: Tuesday +text: Today is Tuesday ++ +title: Day: Wednesday +text: Today is Wednesday ++ +title: Day: Thursday +text: Today is Thursday ++ +title: Day: Friday +text: Today is Friday ++ +title: Day: Saturday +text: Today is Saturday ++ +title: Day: Sunday +text: Today is Sunday ++ +title: ExpectedResult + +

[ + { + "title": "Day: Thursday", + "text": "Today is Thursday", + "custom": "Beta" + }, + { + "title": "Day: Tuesday", + "text": "Today is Tuesday", + "custom": "Beta" + } +]

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedTiddler.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedTiddler.tid new file mode 100644 index 000000000..02b89726c --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/ImportedTiddler.tid @@ -0,0 +1,29 @@ +title: TestCases/DataWidget/ImportedTiddler +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Imported tiddler definition +display-format: plaintext + +title: Narrative + +Using the data widget to create a tiddler that is a copy of the tiddler "Hello" with the addition of the field "custom" set to "Alpha" ++ +title: Output + +<$data $tiddler="Hello" custom="Alpha"/> ++ +title: Hello +modifier: JoeBloggs + +This is the Hello tiddler ++ +title: ExpectedResult + +

[ + { + "title": "Hello", + "modifier": "JoeBloggs", + "text": "This is the Hello tiddler", + "custom": "Alpha" + } +]

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/Refreshing.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/Refreshing.tid new file mode 100644 index 000000000..92fc32090 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/Refreshing.tid @@ -0,0 +1,30 @@ +title: TestCases/DataWidget/Refreshing +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Refreshing the data widget +display-format: plaintext + +title: Narrative + +Verifying that the JSON output of the data widget is correctly refreshed when the data changes ++ +title: Output + +<$data title="Epsilon" text={{Subject}}/> ++ +title: Subject + +Nothing ++ +title: Actions + +<$action-setfield $tiddler="Subject" text="Theta"/> ++ +title: ExpectedResult + +

[ + { + "title": "Epsilon", + "text": "Theta" + } +]

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/DataWidget/SimpleTiddler.tid b/editions/tw5.com/tiddlers/testcases/DataWidget/SimpleTiddler.tid new file mode 100644 index 000000000..633ecb0b8 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/DataWidget/SimpleTiddler.tid @@ -0,0 +1,22 @@ +title: TestCases/DataWidget/SimpleTiddler +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Simple tiddler definition +display-format: plaintext + +title: Narrative + +Using the data widget to create a tiddler with the title "Epsilon" and the text "Theta" ++ +title: Output + +<$data title="Epsilon" text="Theta"/> ++ +title: ExpectedResult + +

[ + { + "title": "Epsilon", + "text": "Theta" + } +]

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/TestCaseWidget/FailingTest.tid b/editions/tw5.com/tiddlers/testcases/TestCaseWidget/FailingTest.tid new file mode 100644 index 000000000..bd9126e03 --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/TestCaseWidget/FailingTest.tid @@ -0,0 +1,15 @@ +title: TestCases/TestCaseWidget/FailingTest +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec-failing]] +description: An example of a failing test + +title: Narrative + +This test case intentionally fails to show how failures are displayed. ++ +title: Output + +The sum is <$text text={{{ [[2]add[2]] }}}/>. ++ +title: ExpectedResult +text:

The sum is not 8.

diff --git a/editions/tw5.com/tiddlers/testcases/TestCaseWidget/currentTiddler.tid b/editions/tw5.com/tiddlers/testcases/TestCaseWidget/currentTiddler.tid new file mode 100644 index 000000000..824a2b17f --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/TestCaseWidget/currentTiddler.tid @@ -0,0 +1,16 @@ +description: currentTiddler should be properly set +tags: $:/tags/wiki-test-spec +title: TestCases/TestCaseTiddler/currentTiddler +type: text/vnd.tiddlywiki-multiple + +title: Narrative + +currentTiddler variable in Output tiddler should be "Output" ++ +title: Output + +<$text text=<>> ++ +title: ExpectedResult + +

Output

\ No newline at end of file diff --git a/editions/tw5.com/tiddlers/testcases/TranscludeWidget/SimpleTransclusion.tid b/editions/tw5.com/tiddlers/testcases/TranscludeWidget/SimpleTransclusion.tid new file mode 100644 index 000000000..e7ef05e6d --- /dev/null +++ b/editions/tw5.com/tiddlers/testcases/TranscludeWidget/SimpleTransclusion.tid @@ -0,0 +1,23 @@ +title: TestCases/TranscludeWidget/SimpleTransclusion +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] +description: Simple transclusion + +title: Narrative + +This test case demonstrates transclusion of and links to other tiddlers. ++ +title: Output + +Good morning, my [[name|Name]] is {{Name}} and I [[live in|Address]] {{Address}} ++ +title: Name + +Robert Rabbit ++ +title: Address + +14 Carrot Street, Vegetabletown ++ +title: ExpectedResult +text:

Good morning, my name is Robert Rabbit and I live in 14 Carrot Street, Vegetabletown

diff --git a/editions/tw5.com/tiddlers/webserver/Using the external JavaScript template.tid b/editions/tw5.com/tiddlers/webserver/Using the external JavaScript template.tid index 75b5f1484..75d38cc29 100644 --- a/editions/tw5.com/tiddlers/webserver/Using the external JavaScript template.tid +++ b/editions/tw5.com/tiddlers/webserver/Using the external JavaScript template.tid @@ -1,5 +1,5 @@ created: 20180905075846391 -modified: 20230319130830880 +modified: 20240413045138914 tags: [[WebServer Guides]] title: Using the external JavaScript template type: text/vnd.tiddlywiki @@ -20,7 +20,7 @@ The remaining inefficiency when working in the client server configuration is th ! Using the external ~JavaScript template with the client-server configuration -The mechanism is activated by setting the [[root-tiddler|WebServer Parameter: root-tiddler]] parameter to `$:/core/save/all-external-js`. This template externalises ~TiddlyWiki's core ~JavaScript into a separate file. For example, the following command will start your server with caching enabled. It will transfer the wiki with two GET requests, and the core can be cached by the browser. +The mechanism is activated by setting the [[root-tiddler|WebServer Parameter: root-tiddler]] parameter to `$:/core/save/all-external-js`. This template externalises ~TiddlyWiki's core ~JavaScript into a separate file. For example, the following command will [[start your server with caching enabled|WebServer Parameter: use-browser-cache]]. It will transfer the wiki with two GET requests, and the core can be cached by the browser. ``` tiddlywiki YOUR_WIKI_FOLDER --listen 'root-tiddler=$:/core/save/all-external-js' use-browser-cache=yes diff --git a/editions/tw5.com/tiddlers/webserver/Using the integrated static file server.tid b/editions/tw5.com/tiddlers/webserver/Using the integrated static file server.tid index 515730a3b..91544551e 100644 --- a/editions/tw5.com/tiddlers/webserver/Using the integrated static file server.tid +++ b/editions/tw5.com/tiddlers/webserver/Using the integrated static file server.tid @@ -1,5 +1,5 @@ created: 20180703095630828 -modified: 20180703100445719 +modified: 20240413045124764 tags: [[WebServer Guides]] title: Using the integrated static file server type: text/vnd.tiddlywiki @@ -13,4 +13,19 @@ Static files can be referenced directly: * `[ext[./files/a-big-document.pdf]]` - to make a link to a PDF * `[img[./files/a-big-image.png]]` - to embed an image -Alternatively, the ''_canonical_uri'' field can be used to reference the files as [[external tiddlers|ExternalImages]]. \ No newline at end of file +Alternatively, the ''_canonical_uri'' field can be used to reference the files as [[external tiddlers|ExternalImages]]. + +If [[WebServer Parameter: use-browser-cache]] is used, these files will be cached by the client's browser to save on bandwidth. In this case, the `cache busting strategy` can be used to make sure the client always has the latest updated files. + + +<<< +https://javascript.plainenglish.io/what-is-cache-busting-55366b3ac022 + +!! Cache Busting + +There are a couple different ways of changing the names of files so that they will load when they change. One way is to use version numbers and have them somewhere in the file name when loading. You could have a subdirectory for every version, `v1/index.js` `v2/index.css` . You could also have the version in queries in the URLs, `index.js?v1` , `index.css?v2` . + +Another way is to change the name of the file, `index.v1.js` , `index.v2.css` . These ways are not as manageable because this can become very hard once you have a ton of files that are being changed. + +A more popular and manageable way is to keep hashes inside the file names. Hashes, if you don’t know, are fixed length character representations of any content and they are irreversible, meaning you can get the hash from the file but you can’t get the file from the hash. Hashes are perfect for this, because when a file changes its hash will change, so if we keep the hash inside the filename `index.[someHashHere].js` browsers will detect it and load it instead of an old file. +<<< diff --git a/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ use-browse-cache.tid b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ use-browse-cache.tid new file mode 100644 index 000000000..2d0693aa2 --- /dev/null +++ b/editions/tw5.com/tiddlers/webserver/WebServer Parameter_ use-browse-cache.tid @@ -0,0 +1,25 @@ +caption: use-browse-cache +created: 20240413042652008 +modified: 20240413050841387 +tags: [[WebServer Parameters]] +title: WebServer Parameter: use-browser-cache +type: text/vnd.tiddlywiki + +The [[web server configuration parameter|WebServer Parameters]] ''use-browser-cache=yes'' activates 200 OK browser caching via the `Cache-Control` header and a smart a Etag header: + +* The server javascript creates an MD5 `hash` object. +* Adds the data of the current `request:response` (for example: json text or an image binary) to the hash object. +* Adds the current `headers` of the response to the hash object. +* If the response data has an `encoding` value, adds the encoding to the hash object. +* Calculates the final MD5 hash string as a `contentDigest` javascript variable, and saves it as an `Etag: "<>"` header. + +If the incoming request contains a header named `if-none-match`, then the server will check the generated Etag against all values. + +If any `if-none-match` value DOES match the current Etag, the server will send a `304 NOT MODIFIED` response with the current response headers, instead of the data with a `200 OK` response. + +This saves bandwidth, as the client can be sure they have already received the exact data and has it in their current cache. + +If ''use-browser-cache=no'' (or any other value including null), then the server will return a `Cache-Control: no-store` header by default. + +If any customer server route module defines custom `Cache-Control` header behavior, then the server will pass that header through instead of the default. + diff --git a/editions/tw5.com/tiddlers/webserver/WebServer.tid b/editions/tw5.com/tiddlers/webserver/WebServer.tid index c1ab5ae2a..afa3fe749 100644 --- a/editions/tw5.com/tiddlers/webserver/WebServer.tid +++ b/editions/tw5.com/tiddlers/webserver/WebServer.tid @@ -1,5 +1,5 @@ created: 20180626150526207 -modified: 20181216181934282 +modified: 20240413043741157 tags: ListenCommand ServerCommand Features title: WebServer type: text/vnd.tiddlywiki diff --git a/editions/tw5.com/tiddlers/widgets/DataWidget.tid b/editions/tw5.com/tiddlers/widgets/DataWidget.tid new file mode 100644 index 000000000..8aaf1efaf --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/DataWidget.tid @@ -0,0 +1,42 @@ +caption: data +created: 20240507221902644 +modified: 20240507221902644 +tags: Widgets +title: DataWidget +type: text/vnd.tiddlywiki + +! Introduction + +The data widget is used with the <<.wlink TestCaseWidget>> widget and the [[Innerwiki Plugin]] to specify payload tiddlers that are to be included in the test case or innerwiki. + +! Content and Attributes + +The content of the data widget is ignored. It supports the following attributes: + +|!Attribute |!Description | +|<<.attr $tiddler>> |Optional title of a tiddler to be used as a payload tiddler (optional) | +|<<.attr $filter>> |Optional filter string identifying tiddlers to be used as payload tiddlers (optional) | +|<<.attr $compound-tiddler>> |Optional title of a tiddler containing payload tiddlers in `text/vnd.tiddlywiki-multiple` format (see below) | +|//any attribute
not starting
with $// |Field values to be assigned to the payload tiddler(s) | + +The data widget is not rendered when used within the <<.wlink TestCaseWidget>> widget or the [[Innerwiki Plugin]] but for ease of testing, when used elsewhere it renders a JSON representation of the payload tiddlers. + +Without any of the attributes <<.attr $tiddler>>, <<.attr $filter>> or <<.attr $compound-tiddler>>, any attributes whose name does not start with $ are used as the field values for creating a single new tiddler. + +<> + +If any of the attributes <<.attr $tiddler>>, <<.attr $filter>> or <<.attr $compound-tiddler>> are specified then they are used to generate base tiddlers that are then modified with the addition of fields derived from any attributes whose name does not start with $. + +The attribute <<.attr $tiddler>> is used to ingest a single tiddler from the wiki containing the data widget: + +<> + +The attribute <<.attr $filter>> is used to ingest multiple tiddlers from the wiki containing the data widget: + +<> + +! Compound Tiddlers + +[[Compound tiddlers|CompoundTiddlers]] provide a way to easily create multiple tiddlers from within a single tiddler. They are contained in tiddlers of type `text/vnd.tiddlywiki-multiple`. The text field consists of a series of tiddlers in the same format as `.tid` files, each separated by a line containing a single `+` character. + +<> diff --git a/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid b/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid new file mode 100644 index 000000000..a73403890 --- /dev/null +++ b/editions/tw5.com/tiddlers/widgets/TestCaseWidget.tid @@ -0,0 +1,101 @@ +caption: testcase +created: 20240507221902644 +modified: 20240507221902644 +tags: Widgets +title: TestCaseWidget +type: text/vnd.tiddlywiki + +! Introduction + +The <<.wid testcase>> widget is designed to present interactive example test cases that are useful for learning and testing. It functions by creating an independent subwiki loaded with the specified payload tiddlers and then rendering a specified template from within the subwiki. The <<.wid testcase>> widget can optionally also be used to run and verify test results within the subwiki. + +This makes it possible to run independent tests that also serve as documentation examples. + +The <<.wid testcase>> widget can be used directly as documented below, but it is generally easier to create [[TestCaseTiddlers]]. These are special CompoundTiddlers that can contain multiple payload tiddlers making up a test case. + +!! Features + +Here is an example of a test case showing the default split view with the source tiddlers on the left and the tiddler titled `Output` rendered on the right. + +<> + +Notice also that clicking on links within the output pane will switch to the tab containing that tiddler. + +The text of the payload tiddlers listed on the left are editable, with the results being immediately reflected in the preview pane on the right. However, if the <<.wid testcase>> widget is refreshed then the modifications are lost. + +The green tick at the top left of a test case indicates that a test has been set up and that it passes. + +If the test fails, a red cross is shown, and there is a display of the differences between the actual results and the expected results: + +<> + +! Limitations + +The <<.wid testcase>> widget creates a lightweight TiddlyWiki environment that is a parasite of the main wiki. Because it is not a full, independent TiddlyWiki environment, there are some important limitations: + +* Output is rendered into a DIV, and so cannot be styled independently of the host wiki +* Any changes to the wiki made interactively by the user are volatile, and are lost when the <<.wid testcase>> widget is refreshed +* Startup actions are not supported +* Only plugins available in the host wiki can be included in the test case + +If these limitations are a problem, the [[Innerwiki Plugin]] offers the ability to embed a fully independent subwiki via an `