diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8daf2f468..737d523ef 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: - master - tiddlywiki-com env: - NODE_VERSION: "12" + NODE_VERSION: "18" jobs: test: runs-on: ubuntu-latest @@ -14,7 +14,13 @@ jobs: - uses: actions/setup-node@v1 with: node-version: "${{ env.NODE_VERSION }}" - - run: "./bin/test.sh" + - run: "./bin/ci-test.sh" + - uses: actions/upload-artifact@v3 + if: always() + with: + name: playwright-report + path: playwright-report/ + retention-days: 30 build-prerelease: runs-on: ubuntu-latest if: github.ref == 'refs/heads/master' @@ -54,6 +60,7 @@ jobs: TW5_BUILD_TIDDLYWIKI: "./node_modules/tiddlywiki/tiddlywiki.js" TW5_BUILD_MAIN_EDITION: "./editions/tw5.com" TW5_BUILD_OUTPUT: "./output" + TW5_BUILD_ARCHIVE: "./output" steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 diff --git a/.gitignore b/.gitignore index 351c576ad..0ab5b300f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ tmp/ output/ node_modules/ - +/test-results/ +/playwright-report/ +/playwright/.cache/ diff --git a/bin/build-site.sh b/bin/build-site.sh index 3d1ab0ff7..a2193953d 100755 --- a/bin/build-site.sh +++ b/bin/build-site.sh @@ -5,7 +5,7 @@ # Default to the current version number for building the plugin library if [ -z "$TW5_BUILD_VERSION" ]; then - TW5_BUILD_VERSION=v5.3.2 + TW5_BUILD_VERSION=v5.3.3 fi echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]" @@ -84,10 +84,27 @@ echo -e -n "title: $:/build\ncommit: $TW5_BUILD_COMMIT\n\n$TW5_BUILD_DETAILS\n" ###################################################### # -# Core distribution +# Core distributions # ###################################################### +# Conditionally build archive if $TW5_BUILD_ARCHIVE variable is set, otherwise do nothing +# +# /archive/Empty-TiddlyWiki-.html Empty archived version +# /archive/TiddlyWiki-.html Full archived version + +if [ -n "$TW5_BUILD_ARCHIVE" ]; then + +node $TW5_BUILD_TIDDLYWIKI \ + $TW5_BUILD_MAIN_EDITION \ + --verbose \ + --version \ + --load $TW5_BUILD_OUTPUT/build.tid \ + --output $TW5_BUILD_ARCHIVE \ + --build archive \ + || exit 1 +fi + # /index.html Main site # /favicon.ico Favicon for main site # /static.html Static rendering of default tiddlers @@ -95,6 +112,7 @@ echo -e -n "title: $:/build\ncommit: $TW5_BUILD_COMMIT\n\n$TW5_BUILD_DETAILS\n" # /static/* Static single tiddlers # /static/static.css Static stylesheet # /static/favicon.ico Favicon for static pages + node $TW5_BUILD_TIDDLYWIKI \ $TW5_BUILD_MAIN_EDITION \ --verbose \ diff --git a/bin/ci-test.sh b/bin/ci-test.sh new file mode 100755 index 000000000..ffcae66b2 --- /dev/null +++ b/bin/ci-test.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +# test TiddlyWiki5 for tiddlywiki.com + +node ./tiddlywiki.js \ + ./editions/test \ + --verbose \ + --version \ + --rendertiddler $:/core/save/all test.html text/plain \ + --test \ + || exit 1 + +npm install playwright @playwright/test +npx playwright install chromium firefox --with-deps + +npx playwright test diff --git a/core/copyright.tid b/core/copyright.tid index ce0d6b02f..3f52380cc 100644 --- a/core/copyright.tid +++ b/core/copyright.tid @@ -4,7 +4,7 @@ type: text/plain TiddlyWiki created by Jeremy Ruston, (jeremy [at] jermolene [dot] com) Copyright (c) 2004-2007, Jeremy Ruston -Copyright (c) 2007-2023, UnaMesa Association +Copyright (c) 2007-2024, UnaMesa Association All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/core/language/en-GB/Fields.multids b/core/language/en-GB/Fields.multids index 1330e60a0..68804f082 100644 --- a/core/language/en-GB/Fields.multids +++ b/core/language/en-GB/Fields.multids @@ -4,6 +4,7 @@ _canonical_uri: The full URI of an external image tiddler author: Name of the author of a plugin bag: The name of the bag from which a tiddler came caption: The text to be displayed on a tab or button +class: The CSS class applied to a tiddler when rendering it - see [[Custom styles by user-class]]. Also used for [[Modals]] code-body: The view template will display the tiddler as code if set to ''yes'' color: The CSS color value associated with a tiddler component: The name of the component responsible for an [[alert tiddler|AlertMechanism]] diff --git a/core/language/en-GB/Help/commands.tid b/core/language/en-GB/Help/commands.tid index 454159b44..7551885f0 100644 --- a/core/language/en-GB/Help/commands.tid +++ b/core/language/en-GB/Help/commands.tid @@ -10,7 +10,7 @@ Sequentially run the command tokens returned from a filter Examples ``` ---commands "[enlist{$:/build-commands-as-text}]" +--commands "[enlist:raw{$:/build-commands-as-text}]" ``` ``` diff --git a/core/modules/commands/savetiddlers.js b/core/modules/commands/savetiddlers.js index d3b82d726..9c750e204 100644 --- a/core/modules/commands/savetiddlers.js +++ b/core/modules/commands/savetiddlers.js @@ -46,7 +46,7 @@ Command.prototype.execute = function() { type = tiddler.fields.type || "text/vnd.tiddlywiki", contentTypeInfo = $tw.config.contentTypeInfo[type] || {encoding: "utf8"}, filename = path.resolve(pathname,$tw.utils.encodeURIComponentExtended(title)); - fs.writeFileSync(filename,tiddler.fields.text,contentTypeInfo.encoding); + fs.writeFileSync(filename,tiddler.fields.text || "",contentTypeInfo.encoding); }); return null; }; diff --git a/core/modules/filters/encodings.js b/core/modules/filters/encodings.js index 23c743a03..a43a15f76 100644 --- a/core/modules/filters/encodings.js +++ b/core/modules/filters/encodings.js @@ -18,16 +18,20 @@ Export our filter functions exports.decodebase64 = function(source,operator,options) { var results = []; + var binary = operator.suffixes && operator.suffixes.indexOf("binary") !== -1; + var urlsafe = operator.suffixes && operator.suffixes.indexOf("urlsafe") !== -1; source(function(tiddler,title) { - results.push($tw.utils.base64Decode(title)); + results.push($tw.utils.base64Decode(title,binary,urlsafe)); }); return results; }; exports.encodebase64 = function(source,operator,options) { var results = []; + var binary = operator.suffixes && operator.suffixes.indexOf("binary") !== -1; + var urlsafe = operator.suffixes && operator.suffixes.indexOf("urlsafe") !== -1; source(function(tiddler,title) { - results.push($tw.utils.base64Encode(title)); + results.push($tw.utils.base64Encode(title,binary,urlsafe)); }); return results; }; diff --git a/core/modules/filters/json-ops.js b/core/modules/filters/json-ops.js index 51e509432..0c58964eb 100644 --- a/core/modules/filters/json-ops.js +++ b/core/modules/filters/json-ops.js @@ -213,6 +213,18 @@ function getDataItemType(data,indexes) { } } +function getItemAtIndex(item,index) { + if($tw.utils.hop(item,index)) { + return item[index]; + } else if($tw.utils.isArray(item)) { + index = $tw.utils.parseInt(index); + if(index < 0) { index = index + item.length }; + return item[index]; // Will be undefined if index was out-of-bounds + } else { + return undefined; + } +} + /* Given a JSON data structure and an array of index strings, return the value at the end of the index chain, or "undefined" if any of the index strings are invalid */ @@ -225,7 +237,7 @@ function getDataItem(data,indexes) { for(var i=0; i tiddlerInfo.changeCount) { + var self = this; + function checkIsDirty() { + // Check tiddlers that are in the store and included in the filter function + var titles = self.getSyncedTiddlers(); + for(var index=0; index tiddlerInfo.changeCount) { + return true; + } + } else { + // If the tiddler isn't known on the server then it needs to be saved to the server return true; } - } else { - // If the tiddler isn't known on the server then it needs to be saved to the server + } + } + // Check tiddlers that are known from the server but not currently in the store + titles = Object.keys(self.tiddlerInfo); + for(index=0; index 0 || updates.deletions.length > 0) { - self.processTaskQueue(); - } - } - }); - } else if(this.syncadaptor && this.syncadaptor.getSkinnyTiddlers) { - this.logger.log("Retrieving skinny tiddler list"); - cancelNextSync(); - this.syncadaptor.getSkinnyTiddlers(function(err,tiddlers) { - triggerNextSync(); - // Check for errors - if(err) { - self.displayError($tw.language.getString("Error/RetrievingSkinny"),err); - return; - } - // Keep track of which tiddlers we already know about have been reported this time - var previousTitles = Object.keys(self.tiddlerInfo); - // Process each incoming tiddler - for(var t=0; t= (this.timestampLastSyncFromServer.valueOf() + this.pollTimerInterval)))) { + return new SyncFromServerTask(this); + } + // Third, we check tiddlers that are known from the server but not currently in the store, and so need deleting on the server titles = Object.keys(this.tiddlerInfo); for(index=0; index 0) { + this.refreshSelf(); + return true; + } return false; }; diff --git a/core/modules/widgets/button.js b/core/modules/widgets/button.js index a724d8448..958b6f6da 100644 --- a/core/modules/widgets/button.js +++ b/core/modules/widgets/button.js @@ -59,6 +59,11 @@ ButtonWidget.prototype.render = function(parent,nextSibling) { $tw.utils.pushTop(classes,"tc-popup-handle"); } domNode.className = classes.join(" "); + // Assign data- attributes + this.assignAttributes(domNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); // Assign other attributes if(this.style) { domNode.setAttribute("style",this.style); @@ -250,7 +255,7 @@ ButtonWidget.prototype.updateDomNodeClasses = function() { //Add new classes from updated class attribute. $tw.utils.pushTop(domNodeClasses,newClasses); this.domNode.className = domNodeClasses.join(" "); -} +}; /* Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering @@ -260,8 +265,15 @@ ButtonWidget.prototype.refresh = function(changedTiddlers) { if(changedAttributes.actions || changedAttributes.to || changedAttributes.message || changedAttributes.param || changedAttributes.set || changedAttributes.setTo || changedAttributes.popup || changedAttributes.hover || changedAttributes.selectedClass || changedAttributes.style || changedAttributes.dragFilter || changedAttributes.dragTiddler || (this.set && changedTiddlers[this.set]) || (this.popup && changedTiddlers[this.popup]) || (this.popupTitle && changedTiddlers[this.popupTitle]) || changedAttributes.popupAbsCoords || changedAttributes.setTitle || changedAttributes.setField || changedAttributes.setIndex || changedAttributes.popupTitle || changedAttributes.disabled || changedAttributes["default"]) { this.refreshSelf(); return true; - } else if(changedAttributes["class"]) { - this.updateDomNodeClasses(); + } else { + if(changedAttributes["class"]) { + this.updateDomNodeClasses(); + } + this.assignAttributes(this.domNodes[0],{ + changedAttributes: changedAttributes, + sourcePrefix: "data-", + destPrefix: "data-" + }); } return this.refreshChildren(changedTiddlers); }; diff --git a/core/modules/widgets/checkbox.js b/core/modules/widgets/checkbox.js index fc987d815..e07513b0a 100644 --- a/core/modules/widgets/checkbox.js +++ b/core/modules/widgets/checkbox.js @@ -53,6 +53,11 @@ CheckboxWidget.prototype.render = function(parent,nextSibling) { this.labelDomNode.appendChild(this.inputDomNode); this.spanDomNode = this.document.createElement("span"); this.labelDomNode.appendChild(this.spanDomNode); + // Assign data- attributes + this.assignAttributes(this.inputDomNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); // Add a click event handler $tw.utils.addEventListeners(this.inputDomNode,[ {name: "change", handlerObject: this, handlerMethod: "handleChangeEvent"} @@ -325,6 +330,11 @@ CheckboxWidget.prototype.refresh = function(changedTiddlers) { $tw.utils.removeClass(this.labelDomNode,"tc-checkbox-checked"); } } + this.assignAttributes(this.inputDomNode,{ + changedAttributes: changedAttributes, + sourcePrefix: "data-", + destPrefix: "data-" + }); return this.refreshChildren(changedTiddlers) || refreshed; } }; @@ -332,3 +342,4 @@ CheckboxWidget.prototype.refresh = function(changedTiddlers) { exports.checkbox = CheckboxWidget; })(); + \ No newline at end of file diff --git a/core/modules/widgets/draggable.js b/core/modules/widgets/draggable.js index f759ab121..22fdc37e9 100644 --- a/core/modules/widgets/draggable.js +++ b/core/modules/widgets/draggable.js @@ -52,6 +52,11 @@ DraggableWidget.prototype.render = function(parent,nextSibling) { classes.push("tc-draggable"); } domNode.setAttribute("class",classes.join(" ")); + // Assign data- attributes and style. attributes + this.assignAttributes(domNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); // Insert the node into the DOM and render any children parent.insertBefore(domNode,nextSibling); this.renderChildren(domNode,null); @@ -108,13 +113,19 @@ DraggableWidget.prototype.updateDomNodeClasses = function() { Selectively refreshes the widget if needed. Returns true if the widget or any of its children needed re-rendering */ DraggableWidget.prototype.refresh = function(changedTiddlers) { - var changedAttributes = this.computeAttributes(), - changedAttributesCount = $tw.utils.count(changedAttributes); - if(changedAttributesCount === 1 && changedAttributes["class"]) { - this.updateDomNodeClasses(); - } else if(changedAttributesCount > 0) { + var changedAttributes = this.computeAttributes(); + if(changedAttributes.tag || changedAttributes.selector || changedAttributes.dragimagetype || changedAttributes.enable || changedAttributes.startactions || changedAttributes.endactions) { this.refreshSelf(); return true; + } else { + if(changedAttributes["class"]) { + this.assignDomNodeClasses(); + } + this.assignAttributes(this.domNodes[0],{ + changedAttributes: changedAttributes, + sourcePrefix: "data-", + destPrefix: "data-" + }); } return this.refreshChildren(changedTiddlers); }; diff --git a/core/modules/widgets/droppable.js b/core/modules/widgets/droppable.js index 104503b25..0dcba1688 100644 --- a/core/modules/widgets/droppable.js +++ b/core/modules/widgets/droppable.js @@ -42,6 +42,11 @@ DroppableWidget.prototype.render = function(parent,nextSibling) { domNode = this.document.createElement(tag); this.domNode = domNode; this.assignDomNodeClasses(); + // Assign data- attributes and style. attributes + this.assignAttributes(domNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); // Add event handlers if(this.droppableEnable) { $tw.utils.addEventListeners(domNode,[ @@ -166,8 +171,15 @@ DroppableWidget.prototype.refresh = function(changedTiddlers) { if(changedAttributes.tag || changedAttributes.enable || changedAttributes.disabledClass || changedAttributes.actions || changedAttributes.effect) { this.refreshSelf(); return true; - } else if(changedAttributes["class"]) { - this.assignDomNodeClasses(); + } else { + if(changedAttributes["class"]) { + this.assignDomNodeClasses(); + } + this.assignAttributes(this.domNodes[0],{ + changedAttributes: changedAttributes, + sourcePrefix: "data-", + destPrefix: "data-" + }); } return this.refreshChildren(changedTiddlers); }; diff --git a/core/modules/widgets/edit.js b/core/modules/widgets/edit.js index e7bd49b93..eb7758e90 100644 --- a/core/modules/widgets/edit.js +++ b/core/modules/widgets/edit.js @@ -90,7 +90,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of EditWidget.prototype.refresh = function(changedTiddlers) { var changedAttributes = this.computeAttributes(); // Refresh if an attribute has changed, or the type associated with the target tiddler has changed - if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || (changedTiddlers[this.editTitle] && this.getEditorType() !== this.editorType)) { + if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tabindex || changedAttributes.cancelPopups || changedAttributes.inputActions || changedAttributes.refreshTitle || changedAttributes.autocomplete || (this.getEditorType() !== this.editorType)) { this.refreshSelf(); return true; } else { diff --git a/core/modules/widgets/image.js b/core/modules/widgets/image.js index 533b657cc..52496fd74 100644 --- a/core/modules/widgets/image.js +++ b/core/modules/widgets/image.js @@ -100,6 +100,9 @@ ImageWidget.prototype.render = function(parent,nextSibling) { if(this.imageClass) { domNode.setAttribute("class",this.imageClass); } + if(this.imageUsemap) { + domNode.setAttribute("usemap",this.imageUsemap); + } if(this.imageWidth) { domNode.setAttribute("width",this.imageWidth); } @@ -139,6 +142,7 @@ ImageWidget.prototype.execute = function() { this.imageWidth = this.getAttribute("width"); this.imageHeight = this.getAttribute("height"); this.imageClass = this.getAttribute("class"); + this.imageUsemap = this.getAttribute("usemap"); this.imageTooltip = this.getAttribute("tooltip"); this.imageAlt = this.getAttribute("alt"); this.lazyLoading = this.getAttribute("loading"); @@ -149,7 +153,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of */ ImageWidget.prototype.refresh = function(changedTiddlers) { var changedAttributes = this.computeAttributes(); - if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.tooltip || changedTiddlers[this.imageSource]) { + if(changedAttributes.source || changedAttributes.width || changedAttributes.height || changedAttributes["class"] || changedAttributes.usemap || changedAttributes.tooltip || changedTiddlers[this.imageSource]) { this.refreshSelf(); return true; } else { diff --git a/core/modules/widgets/link.js b/core/modules/widgets/link.js index 6f199d395..f02a7cae2 100755 --- a/core/modules/widgets/link.js +++ b/core/modules/widgets/link.js @@ -43,6 +43,11 @@ LinkWidget.prototype.render = function(parent,nextSibling) { } else { // Just insert the link text var domNode = this.document.createElement("span"); + // Assign data- attributes + this.assignAttributes(domNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); parent.insertBefore(domNode,nextSibling); this.renderChildren(domNode,null); this.domNodes.push(domNode); @@ -138,6 +143,11 @@ LinkWidget.prototype.renderLink = function(parent,nextSibling) { widget: this }); } + // Assign data- attributes + this.assignAttributes(domNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); // Insert the link into the DOM and render any children parent.insertBefore(domNode,nextSibling); this.renderChildren(domNode,null); @@ -207,8 +217,7 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of */ LinkWidget.prototype.refresh = function(changedTiddlers) { var changedAttributes = this.computeAttributes(); - if(changedAttributes.to || changedTiddlers[this.to] || changedAttributes["aria-label"] || changedAttributes.tooltip || - changedAttributes["class"] || changedAttributes.tabindex || changedAttributes.draggable || changedAttributes.tag) { + if($tw.utils.count(changedAttributes) > 0 || changedTiddlers[this.to]) { this.refreshSelf(); return true; } @@ -218,3 +227,4 @@ LinkWidget.prototype.refresh = function(changedTiddlers) { exports.link = LinkWidget; })(); + \ No newline at end of file diff --git a/core/modules/widgets/list.js b/core/modules/widgets/list.js index 39c7e1b84..d4ad41995 100755 --- a/core/modules/widgets/list.js +++ b/core/modules/widgets/list.js @@ -28,6 +28,18 @@ Inherit from the base widget class */ ListWidget.prototype = new Widget(); +ListWidget.prototype.initialise = function(parseTreeNode,options) { + // Bail if parseTreeNode is undefined, meaning that the ListWidget constructor was called without any arguments so that it can be subclassed + if(parseTreeNode === undefined) { + return; + } + // First call parent constructor to set everything else up + Widget.prototype.initialise.call(this,parseTreeNode,options); + // Now look for <$list-template> and <$list-empty> widgets as immediate child widgets + // This is safe to do during initialization because parse trees never change after creation + this.findExplicitTemplates(); +} + /* Render this widget into the DOM */ @@ -38,8 +50,8 @@ ListWidget.prototype.render = function(parent,nextSibling) { $tw.modules.applyMethods("storyview",this.storyViews); } this.parentDomNode = parent; - this.computeAttributes(); - this.execute(); + var changedAttributes = this.computeAttributes(); + this.execute(changedAttributes); this.renderChildren(parent,nextSibling); // Construct the storyview var StoryView = this.storyViews[this.storyViewName]; @@ -59,7 +71,7 @@ ListWidget.prototype.render = function(parent,nextSibling) { /* Compute the internal state of the widget */ -ListWidget.prototype.execute = function() { +ListWidget.prototype.execute = function(changedAttributes) { var self = this; // Get our attributes this.template = this.getAttribute("template"); @@ -68,8 +80,10 @@ ListWidget.prototype.execute = function() { this.counterName = this.getAttribute("counter"); this.storyViewName = this.getAttribute("storyview"); this.historyTitle = this.getAttribute("history"); - // Look for <$list-template> and <$list-empty> widgets as immediate child widgets - this.findExplicitTemplates(); + // Create join template only if needed + if(this.join === undefined || (changedAttributes && changedAttributes.join)) { + this.join = this.makeJoinTemplate(); + } // Compose the list elements this.list = this.getTiddlerList(); var members = [], @@ -92,18 +106,27 @@ ListWidget.prototype.findExplicitTemplates = function() { var self = this; this.explicitListTemplate = null; this.explicitEmptyTemplate = null; + this.explicitJoinTemplate = null; + this.hasTemplateInBody = false; var searchChildren = function(childNodes) { + var foundInlineTemplate = false; $tw.utils.each(childNodes,function(node) { if(node.type === "list-template") { self.explicitListTemplate = node.children; } else if(node.type === "list-empty") { self.explicitEmptyTemplate = node.children; + } else if(node.type === "list-join") { + self.explicitJoinTemplate = node.children; } else if(node.type === "element" && node.tag === "p") { searchChildren(node.children); + foundInlineTemplate = true; + } else { + foundInlineTemplate = true; } }); + return foundInlineTemplate; }; - searchChildren(this.parseTreeNode.children); + this.hasTemplateInBody = searchChildren(this.parseTreeNode.children); } ListWidget.prototype.getTiddlerList = function() { @@ -139,6 +162,24 @@ ListWidget.prototype.getEmptyMessage = function() { } }; +/* +Compose the template for a join between list items +*/ +ListWidget.prototype.makeJoinTemplate = function() { + var parser, + join = this.getAttribute("join",""); + if(join) { + parser = this.wiki.parseText("text/vnd.tiddlywiki",join,{parseAsInline:true}) + if(parser) { + return parser.tree; + } else { + return []; + } + } else { + return this.explicitJoinTemplate; // May be null, and that's fine + } +}; + /* Compose the template for a list item */ @@ -147,6 +188,7 @@ ListWidget.prototype.makeItemTemplate = function(title,index) { var tiddler = this.wiki.getTiddler(title), isDraft = tiddler && tiddler.hasField("draft.of"), template = this.template, + join = this.join, templateTree; if(isDraft && this.editTemplate) { template = this.editTemplate; @@ -160,11 +202,11 @@ ListWidget.prototype.makeItemTemplate = function(title,index) { // Check for a <$list-item> widget if(this.explicitListTemplate) { templateTree = this.explicitListTemplate; - } else if (!this.explicitEmptyTemplate) { + } else if(this.hasTemplateInBody) { templateTree = this.parseTreeNode.children; } } - if(!templateTree) { + if(!templateTree || templateTree.length === 0) { // Default template is a link to the title templateTree = [{type: "element", tag: this.parseTreeNode.isBlock ? "div" : "span", children: [{type: "link", attributes: {to: {type: "string", value: title}}, children: [ {type: "text", text: title} @@ -172,12 +214,12 @@ ListWidget.prototype.makeItemTemplate = function(title,index) { } } // Return the list item - var parseTreeNode = {type: "listitem", itemTitle: title, variableName: this.variableName, children: templateTree}; + var parseTreeNode = {type: "listitem", itemTitle: title, variableName: this.variableName, children: templateTree, join: join}; + parseTreeNode.isLast = index === this.list.length - 1; if(this.counterName) { parseTreeNode.counter = (index + 1).toString(); parseTreeNode.counterName = this.counterName; parseTreeNode.isFirst = index === 0; - parseTreeNode.isLast = index === this.list.length - 1; } return parseTreeNode; }; @@ -193,7 +235,7 @@ ListWidget.prototype.refresh = function(changedTiddlers) { this.storyview.refreshStart(changedTiddlers,changedAttributes); } // Completely refresh if any of our attributes have changed - if(changedAttributes.filter || changedAttributes.variable || changedAttributes.counter || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.emptyMessage || changedAttributes.storyview || changedAttributes.history) { + if(changedAttributes.filter || changedAttributes.variable || changedAttributes.counter || changedAttributes.template || changedAttributes.editTemplate || changedAttributes.join || changedAttributes.emptyMessage || changedAttributes.storyview || changedAttributes.history) { this.refreshSelf(); result = true; } else { @@ -297,10 +339,29 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) { } } else { // Cycle through the list, inserting and removing list items as needed + var mustRecreateLastItem = false; + if(this.join && this.join.length) { + if(this.children.length !== this.list.length) { + mustRecreateLastItem = true; + } else if(prevList[prevList.length-1] !== this.list[this.list.length-1]) { + mustRecreateLastItem = true; + } + } + var isLast = false, wasLast = false; for(t=0; t0) { + // First re-create previosly-last item that will no longer be last + this.removeListItem(t-1); + this.insertListItem(t-1,this.list[t-1]); + } this.insertListItem(t,this.list[t]); hasRefreshed = true; } else { @@ -309,9 +370,15 @@ ListWidget.prototype.handleListChanges = function(changedTiddlers) { this.removeListItem(n); hasRefreshed = true; } - // Refresh the item we're reusing - var refreshed = this.children[t].refresh(changedTiddlers); - hasRefreshed = hasRefreshed || refreshed; + // Refresh the item we're reusing, or recreate if necessary + if(mustRecreateLastItem && (isLast || wasLast)) { + this.removeListItem(t); + this.insertListItem(t,this.list[t]); + hasRefreshed = true; + } else { + var refreshed = this.children[t].refresh(changedTiddlers); + hasRefreshed = hasRefreshed || refreshed; + } } } } @@ -401,8 +468,17 @@ ListItemWidget.prototype.execute = function() { this.setVariable(this.parseTreeNode.counterName + "-first",this.parseTreeNode.isFirst ? "yes" : "no"); this.setVariable(this.parseTreeNode.counterName + "-last",this.parseTreeNode.isLast ? "yes" : "no"); } + // Add join if needed + var children = this.parseTreeNode.children, + join = this.parseTreeNode.join; + if(join && join.length && !this.parseTreeNode.isLast) { + children = children.slice(0); + $tw.utils.each(join,function(joinNode) { + children.push(joinNode); + }) + } // Construct the child widgets - this.makeChildWidgets(); + this.makeChildWidgets(children); }; /* @@ -414,4 +490,37 @@ ListItemWidget.prototype.refresh = function(changedTiddlers) { exports.listitem = ListItemWidget; +/* +Make <$list-template> and <$list-empty> widgets that do nothing +*/ +var ListTemplateWidget = function(parseTreeNode,options) { + // Main initialisation inherited from widget.js + this.initialise(parseTreeNode,options); +}; +ListTemplateWidget.prototype = new Widget(); +ListTemplateWidget.prototype.render = function() {} +ListTemplateWidget.prototype.refresh = function() { return false; } + +exports["list-template"] = ListTemplateWidget; + +var ListEmptyWidget = function(parseTreeNode,options) { + // Main initialisation inherited from widget.js + this.initialise(parseTreeNode,options); +}; +ListEmptyWidget.prototype = new Widget(); +ListEmptyWidget.prototype.render = function() {} +ListEmptyWidget.prototype.refresh = function() { return false; } + +exports["list-empty"] = ListEmptyWidget; + +var ListJoinWidget = function(parseTreeNode,options) { + // Main initialisation inherited from widget.js + this.initialise(parseTreeNode,options); +}; +ListJoinWidget.prototype = new Widget(); +ListJoinWidget.prototype.render = function() {} +ListJoinWidget.prototype.refresh = function() { return false; } + +exports["list-join"] = ListJoinWidget; + })(); diff --git a/core/modules/widgets/radio.js b/core/modules/widgets/radio.js index 363836227..aa7a32cf1 100644 --- a/core/modules/widgets/radio.js +++ b/core/modules/widgets/radio.js @@ -40,6 +40,10 @@ RadioWidget.prototype.render = function(parent,nextSibling) { ); this.inputDomNode = this.document.createElement("input"); this.inputDomNode.setAttribute("type","radio"); + this.assignAttributes(this.inputDomNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); if(isChecked) { this.inputDomNode.checked = true; } diff --git a/core/modules/widgets/range.js b/core/modules/widgets/range.js index 4dd55dc3c..db2699cc4 100644 --- a/core/modules/widgets/range.js +++ b/core/modules/widgets/range.js @@ -50,6 +50,10 @@ RangeWidget.prototype.render = function(parent,nextSibling) { this.inputDomNode.setAttribute("disabled",true); } this.inputDomNode.value = this.getValue(); + this.assignAttributes(this.inputDomNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); // Add a click event handler $tw.utils.addEventListeners(this.inputDomNode,[ {name:"mousedown", handlerObject:this, handlerMethod:"handleMouseDownEvent"}, diff --git a/core/modules/widgets/scrollable.js b/core/modules/widgets/scrollable.js index 15b61e0c8..227c455c3 100644 --- a/core/modules/widgets/scrollable.js +++ b/core/modules/widgets/scrollable.js @@ -12,6 +12,8 @@ Scrollable widget /*global $tw: false */ "use strict"; +var DEBOUNCE_INTERVAL = 100; // Delay after last scroll event before updating the bound tiddler + var Widget = require("$:/core/modules/widgets/widget.js").widget; var ScrollableWidget = function(parseTreeNode,options) { @@ -171,6 +173,53 @@ ScrollableWidget.prototype.render = function(parent,nextSibling) { parent.insertBefore(this.outerDomNode,nextSibling); this.renderChildren(this.innerDomNode,null); this.domNodes.push(this.outerDomNode); + // If the scroll position is bound to a tiddler + if(this.scrollableBind) { + // After a delay for rendering, scroll to the bound position + this.updateScrollPositionFromBoundTiddler(); + // Set up event listener + this.currentListener = this.listenerFunction.bind(this); + this.outerDomNode.addEventListener("scroll", this.currentListener); + } +}; + +ScrollableWidget.prototype.listenerFunction = function(event) { + self = this; + clearTimeout(this.timeout); + this.timeout = setTimeout(function() { + var existingTiddler = self.wiki.getTiddler(self.scrollableBind), + newTiddlerFields = { + title: self.scrollableBind, + "scroll-left": self.outerDomNode.scrollLeft.toString(), + "scroll-top": self.outerDomNode.scrollTop.toString() + }; + if(!existingTiddler || (existingTiddler.fields["title"] !== newTiddlerFields["title"]) || (existingTiddler.fields["scroll-left"] !== newTiddlerFields["scroll-left"] || existingTiddler.fields["scroll-top"] !== newTiddlerFields["scroll-top"])) { + self.wiki.addTiddler(new $tw.Tiddler(existingTiddler,newTiddlerFields)); + } + }, DEBOUNCE_INTERVAL); +} + +ScrollableWidget.prototype.updateScrollPositionFromBoundTiddler = function() { + // Bail if we're running on the fakedom + if(!this.outerDomNode.scrollTo) { + return; + } + var tiddler = this.wiki.getTiddler(this.scrollableBind); + if(tiddler) { + var scrollLeftTo = this.outerDomNode.scrollLeft; + if(parseFloat(tiddler.fields["scroll-left"]).toString() === tiddler.fields["scroll-left"]) { + scrollLeftTo = parseFloat(tiddler.fields["scroll-left"]); + } + var scrollTopTo = this.outerDomNode.scrollTop; + if(parseFloat(tiddler.fields["scroll-top"]).toString() === tiddler.fields["scroll-top"]) { + scrollTopTo = parseFloat(tiddler.fields["scroll-top"]); + } + this.outerDomNode.scrollTo({ + top: scrollTopTo, + left: scrollLeftTo, + behavior: "instant" + }) + } }; /* @@ -178,6 +227,7 @@ Compute the internal state of the widget */ ScrollableWidget.prototype.execute = function() { // Get attributes + this.scrollableBind = this.getAttribute("bind"); this.fallthrough = this.getAttribute("fallthrough","yes"); this["class"] = this.getAttribute("class"); // Make child widgets @@ -193,7 +243,22 @@ ScrollableWidget.prototype.refresh = function(changedTiddlers) { this.refreshSelf(); return true; } - return this.refreshChildren(changedTiddlers); + // If the bound tiddler has changed, update the eventListener and update scroll position + if(changedAttributes["bind"]) { + if(this.currentListener) { + this.outerDomNode.removeEventListener("scroll", this.currentListener, false); + } + this.scrollableBind = this.getAttribute("bind"); + this.currentListener = this.listenerFunction.bind(this); + this.outerDomNode.addEventListener("scroll", this.currentListener); + } + // Refresh children + var result = this.refreshChildren(changedTiddlers); + // If the bound tiddler has changed, update scroll position + if(changedAttributes["bind"] || changedTiddlers[this.getAttribute("bind")]) { + this.updateScrollPositionFromBoundTiddler(); + } + return result; }; exports.scrollable = ScrollableWidget; diff --git a/core/modules/widgets/select.js b/core/modules/widgets/select.js index ab9bef74e..2940e3be0 100644 --- a/core/modules/widgets/select.js +++ b/core/modules/widgets/select.js @@ -40,7 +40,31 @@ SelectWidget.prototype.render = function(parent,nextSibling) { this.parentDomNode = parent; this.computeAttributes(); this.execute(); - this.renderChildren(parent,nextSibling); + //Create element + var domNode = this.document.createElement("select"); + if(this.selectClass) { + domNode.className = this.selectClass; + } + // Assign data- attributes + this.assignAttributes(domNode,{ + sourcePrefix: "data-", + destPrefix: "data-" + }); + if(this.selectMultiple) { + domNode.setAttribute("multiple","multiple"); + } + if(this.selectSize) { + domNode.setAttribute("size",this.selectSize); + } + if(this.selectTabindex) { + domNode.setAttribute("tabindex",this.selectTabindex); + } + if(this.selectTooltip) { + domNode.setAttribute("title",this.selectTooltip); + } + this.parentDomNode.insertBefore(domNode,nextSibling); + this.renderChildren(domNode,null); + this.domNodes.push(domNode); this.setSelectValue(); if(this.selectFocus == "yes") { this.getSelectDomNode().focus(); @@ -113,7 +137,7 @@ SelectWidget.prototype.setSelectValue = function() { Get the DOM node of the select element */ SelectWidget.prototype.getSelectDomNode = function() { - return this.children[0].domNodes[0]; + return this.domNodes[0]; }; // Return an array of the selected opion values @@ -149,27 +173,7 @@ SelectWidget.prototype.execute = function() { this.selectTooltip = this.getAttribute("tooltip"); this.selectFocus = this.getAttribute("focus"); // Make the child widgets - var selectNode = { - type: "element", - tag: "select", - children: this.parseTreeNode.children - }; - if(this.selectClass) { - $tw.utils.addAttributeToParseTreeNode(selectNode,"class",this.selectClass); - } - if(this.selectMultiple) { - $tw.utils.addAttributeToParseTreeNode(selectNode,"multiple","multiple"); - } - if(this.selectSize) { - $tw.utils.addAttributeToParseTreeNode(selectNode,"size",this.selectSize); - } - if(this.selectTabindex) { - $tw.utils.addAttributeToParseTreeNode(selectNode,"tabindex",this.selectTabindex); - } - if(this.selectTooltip) { - $tw.utils.addAttributeToParseTreeNode(selectNode,"title",this.selectTooltip); - } - this.makeChildWidgets([selectNode]); + this.makeChildWidgets(); }; /* @@ -178,17 +182,21 @@ Selectively refreshes the widget if needed. Returns true if the widget or any of SelectWidget.prototype.refresh = function(changedTiddlers) { var changedAttributes = this.computeAttributes(); // If we're using a different tiddler/field/index then completely refresh ourselves - if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tooltip) { + if(changedAttributes.tiddler || changedAttributes.field || changedAttributes.index || changedAttributes.tooltip || changedAttributes.tabindex) { this.refreshSelf(); return true; - // If the target tiddler value has changed, just update setting and refresh the children } else { if(changedAttributes.class) { this.selectClass = this.getAttribute("class"); this.getSelectDomNode().setAttribute("class",this.selectClass); } - + this.assignAttributes(this.getSelectDomNode(),{ + changedAttributes: changedAttributes, + sourcePrefix: "data-", + destPrefix: "data-" + }); var childrenRefreshed = this.refreshChildren(changedTiddlers); + // If the target tiddler value has changed, just update setting and refresh the children if(changedTiddlers[this.selectTitle] || childrenRefreshed) { this.setSelectValue(); } diff --git a/core/modules/widgets/widget.js b/core/modules/widgets/widget.js index 6c3997179..ea300ca0e 100755 --- a/core/modules/widgets/widget.js +++ b/core/modules/widgets/widget.js @@ -153,7 +153,7 @@ Widget.prototype.getVariableInfo = function(name,options) { } else if(variable.isFunctionDefinition) { // Function evaluations params = self.resolveVariableParameters(variable.params,actualParams); - var variables = Object.create(null); + var variables = options.variables || Object.create(null); // Apply default parameter values $tw.utils.each(variable.params,function(param,index) { if(param["default"]) { @@ -413,16 +413,34 @@ Widget.prototype.getAttribute = function(name,defaultText) { }; /* -Assign the computed attributes of the widget to a domNode +Assign the common attributes of the widget to a domNode options include: -excludeEventAttributes: ignores attributes whose name begins with "on" +sourcePrefix: prefix of attributes that are to be directly assigned (defaults to the empty string meaning all attributes) +destPrefix: prefix to be applied to attribute names that are to be directly assigned (defaults to the emtpy string which means no prefix is added) +changedAttributes: hashmap by attribute name of attributes to process (if missing, process all attributes) +excludeEventAttributes: ignores attributes whose name would begin with "on" */ Widget.prototype.assignAttributes = function(domNode,options) { options = options || {}; - var self = this; + var self = this, + changedAttributes = options.changedAttributes || this.attributes, + sourcePrefix = options.sourcePrefix || "", + destPrefix = options.destPrefix || "", + EVENT_ATTRIBUTE_PREFIX = "on"; var assignAttribute = function(name,value) { + // Process any style attributes before considering sourcePrefix and destPrefix + if(name.substr(0,6) === "style." && name.length > 6) { + domNode.style[$tw.utils.unHyphenateCss(name.substr(6))] = value; + return; + } + // Check if the sourcePrefix is a match + if(name.substr(0,sourcePrefix.length) === sourcePrefix) { + name = destPrefix + name.substr(sourcePrefix.length); + } else { + value = undefined; + } // Check for excluded attribute names - if(options.excludeEventAttributes && name.substr(0,2) === "on") { + if(options.excludeEventAttributes && name.substr(0,2).toLowerCase() === EVENT_ATTRIBUTE_PREFIX) { value = undefined; } if(value !== undefined) { @@ -432,26 +450,24 @@ Widget.prototype.assignAttributes = function(domNode,options) { namespace = "http://www.w3.org/1999/xlink"; name = name.substr(6); } - // Handle styles - if(name.substr(0,6) === "style." && name.length > 6) { - domNode.style[$tw.utils.unHyphenateCss(name.substr(6))] = value; - } else { - // Setting certain attributes can cause a DOM error (eg xmlns on the svg element) - try { - domNode.setAttributeNS(namespace,name,value); - } catch(e) { - } + // Setting certain attributes can cause a DOM error (eg xmlns on the svg element) + try { + domNode.setAttributeNS(namespace,name,value); + } catch(e) { } } - } - // Not all parse tree nodes have the orderedAttributes property + }; + // If the parse tree node has the orderedAttributes property then use that order if(this.parseTreeNode.orderedAttributes) { $tw.utils.each(this.parseTreeNode.orderedAttributes,function(attribute,index) { - assignAttribute(attribute.name,self.attributes[attribute.name]); - }); + if(attribute.name in changedAttributes) { + assignAttribute(attribute.name,self.getAttribute(attribute.name)); + } + }); + // Otherwise update each changed attribute irrespective of order } else { - $tw.utils.each(Object.keys(self.attributes).sort(),function(name) { - assignAttribute(name,self.attributes[name]); + $tw.utils.each(changedAttributes,function(value,name) { + assignAttribute(name,self.getAttribute(name)); }); } }; diff --git a/core/modules/wiki.js b/core/modules/wiki.js index 3eae3902d..430c46466 100755 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -1287,7 +1287,7 @@ exports.search = function(text,options) { console.log("Regexp error parsing /(" + text + ")/" + flags + ": ",e); } } else if(options.some) { - terms = text.trim().split(/ +/); + terms = text.trim().split(/[^\S\xA0]+/); if(terms.length === 1 && terms[0] === "") { searchTermsRegExps = null; } else { @@ -1298,7 +1298,7 @@ exports.search = function(text,options) { searchTermsRegExps.push(new RegExp("(" + regExpStr + ")",flags)); } } else { // default: words - terms = text.split(/ +/); + terms = text.split(/[^\S\xA0]+/); if(terms.length === 1 && terms[0] === "") { searchTermsRegExps = null; } else { diff --git a/core/templates/html-json-skinny-tiddler.tid b/core/templates/html-json-skinny-tiddler.tid index 1e3c032f3..6402bcee5 100644 --- a/core/templates/html-json-skinny-tiddler.tid +++ b/core/templates/html-json-skinny-tiddler.tid @@ -1,4 +1,3 @@ title: $:/core/templates/html-json-skinny-tiddler -<$list filter="[compare:number:gteq[1]] ~[!match[1]]">`,`<$text text=<>/> -<$jsontiddler tiddler=<> exclude="text" escapeUnsafeScriptChars="yes"/> +<$text text=<>/><$jsontiddler tiddler=<> exclude="text" escapeUnsafeScriptChars="yes"/> diff --git a/core/templates/html-json-tiddler.tid b/core/templates/html-json-tiddler.tid index 6b62b4ac9..2e12290a7 100644 --- a/core/templates/html-json-tiddler.tid +++ b/core/templates/html-json-tiddler.tid @@ -1,3 +1,3 @@ title: $:/core/templates/html-json-tiddler -<$list filter="[!match[1]]">`,`<$text text=<>/><$jsontiddler tiddler=<> escapeUnsafeScriptChars="yes"/> \ No newline at end of file +<$jsontiddler tiddler=<> escapeUnsafeScriptChars="yes"/> \ No newline at end of file diff --git a/core/templates/store.area.template.html.tid b/core/templates/store.area.template.html.tid index 84dd0c432..2dc115266 100644 --- a/core/templates/store.area.template.html.tid +++ b/core/templates/store.area.template.html.tid @@ -6,14 +6,12 @@ title: $:/core/templates/store.area.template.html <$list filter="[[storeAreaFormat]is[variable]getvariable[]else[json]match[json]]"> `` `` @@ -22,8 +20,8 @@ title: $:/core/templates/store.area.template.html <$reveal type="nomatch" state="$:/isEncrypted" text="yes"> `` diff --git a/core/ui/EditTemplate/body/default.tid b/core/ui/EditTemplate/body/default.tid index 68133d48e..04cc64436 100644 --- a/core/ui/EditTemplate/body/default.tid +++ b/core/ui/EditTemplate/body/default.tid @@ -1,9 +1,5 @@ title: $:/core/ui/EditTemplate/body/default -\function edit-preview-state() -[{$:/config/ShowEditPreview/PerTiddler}!match[yes]then[$:/state/showeditpreview]] :else[] +[get[text]] :else[[no]] -\end - \define config-visibility-title() $:/config/EditorToolbarButtons/Visibility/$(currentTiddler)$ \end @@ -14,15 +10,16 @@ $:/config/EditorToolbarButtons/Visibility/$(currentTiddler)$ \whitespace trim <$let + editPreviewStateTiddler={{{ [{$:/config/ShowEditPreview/PerTiddler}!match[yes]then[$:/state/showeditpreview]] :else[] }}} importTitle=<> importState=<> > <$dropzone importTitle=<> autoOpenOnImport="no" contentTypesFilter={{$:/config/Editor/ImportContentTypesFilter}} class="tc-dropzone-editor" enable={{{ [{$:/config/DragAndDrop/Enable}match[no]] :else[subfilter{$:/config/Editor/EnableImportFilter}then[yes]else[no]] }}} filesOnly="yes" actions=<> >
-
+
get[text]match[yes]then[tc-tiddler-preview]else[tc-tiddler-preview-hidden]] [[tc-tiddler-editor]] +[join[ ]] }}}> <$transclude tiddler="$:/core/ui/EditTemplate/body/editor" mode="inline"/> -<$list filter="[function[edit-preview-state]match[yes]]" variable="ignore"> +<$list filter="[get[text]match[yes]]" variable="ignore">
diff --git a/core/ui/EditTemplate/fields.tid b/core/ui/EditTemplate/fields.tid index 6a767517b..0edc33505 100644 --- a/core/ui/EditTemplate/fields.tid +++ b/core/ui/EditTemplate/fields.tid @@ -54,7 +54,7 @@ $:/config/EditTemplateFields/Visibility/$(currentField)$ \whitespace trim <$vars name={{{ [get[text]] }}}> <$reveal type="nomatch" text="" default=<>> -<$button tooltip=<>> +<$button tooltip={{$:/language/EditTemplate/Fields/Add/Button/Hint}}> <$action-sendmessage $message="tm-add-field" $name=<> $value={{{ [subfilterget[text]] }}}/> @@ -89,7 +89,7 @@ $value={{{ [subfilterget[text]] }}}/> <$button class="tc-btn-invisible" tooltip={{$:/language/EditTemplate/Field/Remove/Hint}} aria-label={{$:/language/EditTemplate/Field/Remove/Caption}}> -<$action-deletefield $field=<>/><$set name="currentTiddlerCSSescaped" value={{{ [escapecss[]] }}}><$action-sendmessage $message="tm-focus-selector" $param=<>/> +<$action-deletefield $field=<>/> {{$:/core/images/delete-button}} diff --git a/core/ui/EditorToolbar/preview.tid b/core/ui/EditorToolbar/preview.tid index 3c8cef505..ea49c971a 100644 --- a/core/ui/EditorToolbar/preview.tid +++ b/core/ui/EditorToolbar/preview.tid @@ -9,17 +9,8 @@ button-classes: tc-text-editor-toolbar-item-start-group shortcuts: ((preview)) \whitespace trim -<$let - edit-preview-state={{{ [{$:/config/ShowEditPreview/PerTiddler}!match[yes]then[$:/state/showeditpreview]] :else[] }}} -> -<$reveal state=<> type="match" text="yes" tag="span"> -{{$:/core/images/preview-open}} -<$action-setfield $tiddler=<> $value="no"/> -<$action-sendmessage $message="tm-edit-text-operation" $param="focus-editor"/> - -<$reveal state=<> type="nomatch" text="yes" tag="span"> -{{$:/core/images/preview-closed}} -<$action-setfield $tiddler=<> $value="yes"/> -<$action-sendmessage $message="tm-edit-text-operation" $param="focus-editor"/> - - + + <$transclude $tiddler={{{ [get[text]match[yes]then[$:/core/images/preview-open]else[$:/core/images/preview-closed]] }}} /> + +<$action-setfield $tiddler=<> $value={{{ [get[text]toggle[yes],[no]] }}} /> +<$action-sendmessage $message="tm-edit-text-operation" $param="focus-editor"/> \ No newline at end of file diff --git a/core/ui/ViewTemplate/subtitle.tid b/core/ui/ViewTemplate/subtitle.tid index a0436b095..a7c010287 100644 --- a/core/ui/ViewTemplate/subtitle.tid +++ b/core/ui/ViewTemplate/subtitle.tid @@ -4,11 +4,8 @@ tags: $:/tags/ViewTemplate \whitespace trim <$reveal type="nomatch" stateTitle=<> text="hide" tag="div" retain="yes" animate="yes">
-<$list filter="[all[shadows+tiddlers]tag[$:/tags/ViewTemplate/Subtitle]!has[draft.of]]" variable="subtitleTiddler" counter="indexSubtitleTiddler"> -<$list filter="[match[no]]" variable="ignore"> -  - -<$transclude tiddler=<> mode="inline"/> +<$list filter="[all[shadows+tiddlers]tag[$:/tags/ViewTemplate/Subtitle]!has[draft.of]]" variable="subtitleTiddler"> +<$transclude tiddler=<> mode="inline"/><$list-join> 
diff --git a/core/wiki/config/OfficialPluginLibrary.tid b/core/wiki/config/OfficialPluginLibrary.tid index c753568bc..286384cd1 100644 --- a/core/wiki/config/OfficialPluginLibrary.tid +++ b/core/wiki/config/OfficialPluginLibrary.tid @@ -1,6 +1,6 @@ title: $:/config/OfficialPluginLibrary tags: $:/tags/PluginLibrary -url: https://tiddlywiki.com/library/v5.3.2/index.html +url: https://tiddlywiki.com/library/v5.3.3/index.html caption: {{$:/language/OfficialPluginLibrary}} {{$:/language/OfficialPluginLibrary/Hint}} diff --git a/core/wiki/macros/colour-picker.tid b/core/wiki/macros/colour-picker.tid index 5c92af9df..1ff1e7b90 100644 --- a/core/wiki/macros/colour-picker.tid +++ b/core/wiki/macros/colour-picker.tid @@ -13,7 +13,7 @@ tags: $:/tags/Macro $(colour-picker-update-recent)$ -$actions$ +<$transclude $variable="__actions__"/> @@ -23,7 +23,7 @@ $actions$ \define colour-picker-recent-inner(actions) \whitespace trim <$set name="colour-picker-value" value="$(recentColour)$"> -<$macrocall $name="colour-picker-inner" actions="""$actions$"""/> +<$macrocall $name="colour-picker-inner" actions=<<__actions__>>/> \end @@ -31,7 +31,7 @@ $actions$ \whitespace trim {{$:/language/ColourPicker/Recent}}<$list filter="[list[$:/config/ColourPicker/Recent]]" variable="recentColour"> -<$macrocall $name="colour-picker-recent-inner" actions="""$actions$"""/> +<$macrocall $name="colour-picker-recent-inner" actions=<<__actions__>>/> \end @@ -39,13 +39,13 @@ $actions$ \whitespace trim
-<$macrocall $name="colour-picker-recent" actions="""$actions$"""/> +<$macrocall $name="colour-picker-recent" actions=<<__actions__>>/> --- <$list filter="LightPink Pink Crimson LavenderBlush PaleVioletRed HotPink DeepPink MediumVioletRed Orchid Thistle Plum Violet Magenta Fuchsia DarkMagenta Purple MediumOrchid DarkViolet DarkOrchid Indigo BlueViolet MediumPurple MediumSlateBlue SlateBlue DarkSlateBlue Lavender GhostWhite Blue MediumBlue MidnightBlue DarkBlue Navy RoyalBlue CornflowerBlue LightSteelBlue LightSlateGrey SlateGrey DodgerBlue AliceBlue SteelBlue LightSkyBlue SkyBlue DeepSkyBlue LightBlue PowderBlue CadetBlue Azure LightCyan PaleTurquoise Cyan Aqua DarkTurquoise DarkSlateGrey DarkCyan Teal MediumTurquoise LightSeaGreen Turquoise Aquamarine MediumAquamarine MediumSpringGreen MintCream SpringGreen MediumSeaGreen SeaGreen Honeydew LightGreen PaleGreen DarkSeaGreen LimeGreen Lime ForestGreen Green DarkGreen Chartreuse LawnGreen GreenYellow DarkOliveGreen YellowGreen OliveDrab Beige LightGoldenrodYellow Ivory LightYellow Yellow Olive DarkKhaki LemonChiffon PaleGoldenrod Khaki Gold Cornsilk Goldenrod DarkGoldenrod FloralWhite OldLace Wheat Moccasin Orange PapayaWhip BlanchedAlmond NavajoWhite AntiqueWhite Tan BurlyWood Bisque DarkOrange Linen Peru PeachPuff SandyBrown Chocolate SaddleBrown Seashell Sienna LightSalmon Coral OrangeRed DarkSalmon Tomato MistyRose Salmon Snow LightCoral RosyBrown IndianRed Red Brown FireBrick DarkRed Maroon White WhiteSmoke Gainsboro LightGrey Silver DarkGrey Grey DimGrey Black" variable="colour-picker-value"> -<$macrocall $name="colour-picker-inner" actions="""$actions$"""/> +<$macrocall $name="colour-picker-inner" actions=<<__actions__>>/> --- @@ -54,7 +54,7 @@ $actions$ <$edit-text tiddler="$:/config/ColourPicker/New" type="color" tag="input"/> <$set name="colour-picker-value" value={{$:/config/ColourPicker/New}}> -<$macrocall $name="colour-picker-inner" actions="""$actions$"""/> +<$macrocall $name="colour-picker-inner" actions=<<__actions__>>/>
diff --git a/core/wiki/macros/image-picker.tid b/core/wiki/macros/image-picker.tid index 464a53e7b..5f09ced0d 100644 --- a/core/wiki/macros/image-picker.tid +++ b/core/wiki/macros/image-picker.tid @@ -5,13 +5,13 @@ title: $:/core/macros/image-picker type: text/vnd.tiddlywiki \define image-picker-thumbnail(actions) -<$button tag="a" tooltip="""$(imageTitle)$""">$actions$<$transclude tiddler=<>/> +<$button tag="a" tooltip="""$(imageTitle)$"""><$transclude $variable="__actions__"/><$transclude tiddler=<>/> \end \define image-picker-list(filter,actions) \whitespace trim <$list filter="""$filter$""" variable="imageTitle"> -<$macrocall $name="image-picker-thumbnail" actions="""$actions$"""/> +<$macrocall $name="image-picker-thumbnail" actions=<<__actions__>>/> \end @@ -25,15 +25,15 @@ type: text/vnd.tiddlywiki {{$:/language/SystemTiddlers/Include/Prompt}} <$reveal state=<> type="match" text="hide" default="hide" tag="div"> -<$macrocall $name="image-picker-list" filter="""$filter$ +[!is[system]]""" actions="""$actions$"""/> +<$macrocall $name="image-picker-list" filter="""$filter$ +[!is[system]]""" actions=<<__actions__>>/> <$reveal state=<> type="nomatch" text="hide" default="hide" tag="div"> -<$macrocall $name="image-picker-list" filter="""$filter$""" actions="""$actions$"""/> +<$macrocall $name="image-picker-list" filter="""$filter$""" actions=<<__actions__>>/>
\end \define image-picker-include-tagged-images(actions) -<$macrocall $name="image-picker" filter="[all[shadows+tiddlers]is[image]] [all[shadows+tiddlers]tag[$:/tags/Image]] -[type[application/pdf]] +[!has[draft.of]sort[title]]" actions="""$actions$"""/> +<$macrocall $name="image-picker" filter="[all[shadows+tiddlers]is[image]] [all[shadows+tiddlers]tag[$:/tags/Image]] -[type[application/pdf]] +[!has[draft.of]sort[title]]" actions=<<__actions__>>/> \end diff --git a/core/wiki/macros/list.tid b/core/wiki/macros/list.tid index 5464ecad1..c9dd2ad71 100644 --- a/core/wiki/macros/list.tid +++ b/core/wiki/macros/list.tid @@ -4,17 +4,17 @@ tags: $:/tags/Macro \define list-links(filter,type:"ul",subtype:"li",class:"",emptyMessage,field:"caption") \whitespace trim <$genesis $type=<<__type__>> class=<<__class__>>> -<$list filter=<<__filter__>> emptyMessage=<<__emptyMessage__>>> -<$genesis $type=<<__subtype__>>> -<$link to={{!!title}}> -<$let tv-wikilinks="no"> -<$transclude field=<<__field__>>> -<$view field="title"/> - - - - - + <$list filter=<<__filter__>> emptyMessage=<<__emptyMessage__>>> + <$genesis $type=<<__subtype__>>> + <$link to={{!!title}}> + <$let tv-wikilinks="no"> + <$transclude field=<<__field__>>> + <$view field="title"/> + + + + + \end @@ -25,34 +25,42 @@ tags: $:/tags/Macro \define list-links-draggable(tiddler,field:"list",emptyMessage,type:"ul",subtype:"li",class:"",itemTemplate) \whitespace trim -<$vars targetTiddler="""$tiddler$""" targetField="""$field$"""> -<$genesis $type=<<__type__>> class="$class$"> -<$list filter="[list[$tiddler$!!$field$]]" emptyMessage=<<__emptyMessage__>>> -<$droppable actions=<> tag="""$subtype$""" enable=<>> -
-
-<$transclude tiddler="""$itemTemplate$"""> -<$link to={{!!title}}> -<$let tv-wikilinks="no"> -<$transclude field="caption"> -<$view field="title"/> - - - - -
- - -<$tiddler tiddler=""> -<$droppable actions=<> tag="div" enable=<>> -
-{{$:/core/images/blank}} -
-
- - - - + <$vars targetTiddler="""$tiddler$""" targetField="""$field$"""> + <$genesis $type=<<__type__>> class="$class$"> + <$list filter="[list[$tiddler$!!$field$]]" emptyMessage=<<__emptyMessage__>>> + <$droppable + actions=<> + tag="""$subtype$""" + enable=<> + > +
+
+ <$transclude tiddler="""$itemTemplate$"""> + <$link to={{!!title}}> + <$let tv-wikilinks="no"> + <$transclude field="caption"> + <$view field="title"/> + + + + +
+ + + <$tiddler tiddler=""> + <$droppable + actions=<> + tag="div" + enable=<> + > +
+ {{$:/core/images/blank}} +
+
+ + + + \end @@ -60,50 +68,59 @@ tags: $:/tags/Macro \whitespace trim <$set name="order" filter="[<__tag__>tagging[]]"> - -<$list filter="[<__tag__>tagging[]]"> -<$action-deletefield $field="list-before"/> -<$action-deletefield $field="list-after"/> - - -<$action-listops $tiddler=<<__tag__>> $field="list" $filter="+[enlist] +[insertbefore,]"/> - - - - -<$list filter="[!contains:tags<__tag__>]"> -<$fieldmangler tiddler=<>> -<$action-sendmessage $message="tm-add-tag" $param=<<__tag__>>/> - - + + <$list filter="[<__tag__>tagging[]]"> + <$action-deletefield $field="list-before"/> + <$action-deletefield $field="list-after"/> + + + <$action-listops $tiddler=<<__tag__>> $field="list" $filter="+[enlist] +[insertbefore,]"/> + + + + + <$list filter="[!contains:tags<__tag__>]"> + <$fieldmangler tiddler=<>> + <$action-sendmessage $message="tm-add-tag" $param=<<__tag__>>/> + + \end \define list-tagged-draggable(tag,subFilter,emptyMessage,itemTemplate,elementTag:"div",storyview:"") \whitespace trim -<$set name="tag" value=<<__tag__>>> -<$list filter="[<__tag__>tagging[]$subFilter$]" emptyMessage=<<__emptyMessage__>> storyview=<<__storyview__>>> -<$genesis $type=<<__elementTag__>> class="tc-menu-list-item"> -<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<>> -<$genesis $type=<<__elementTag__>> class="tc-droppable-placeholder"/> -<$genesis $type=<<__elementTag__>>> -<$transclude tiddler="""$itemTemplate$"""> -<$link to={{!!title}}> -<$view field="title"/> - - - - - - -<$tiddler tiddler=""> -<$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<>> -<$genesis $type=<<__elementTag__>> class="tc-droppable-placeholder"/> -<$genesis $type=<<__elementTag__>> style="height:0.5em;"> - - - - + <$set name="tag" value=<<__tag__>>> + <$list + filter="[<__tag__>tagging[]$subFilter$]" + emptyMessage=<<__emptyMessage__>> + storyview=<<__storyview__>> + > + <$genesis $type=<<__elementTag__>> class="tc-menu-list-item"> + <$droppable + actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" + enable=<> + > + <$genesis $type=<<__elementTag__>> class="tc-droppable-placeholder"/> + <$genesis $type=<<__elementTag__>>> + <$transclude tiddler="""$itemTemplate$"""> + <$link to={{!!title}}> + <$view field="title"/> + + + + + + + <$tiddler tiddler=""> + <$droppable + actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" + enable=<> + > + <$genesis $type=<<__elementTag__>> class="tc-droppable-placeholder"/> + <$genesis $type=<<__elementTag__>> style="height:0.5em;"/> + + + \end diff --git a/core/wiki/macros/tabs.tid b/core/wiki/macros/tabs.tid index bc8a0255f..1805bc9be 100644 --- a/core/wiki/macros/tabs.tid +++ b/core/wiki/macros/tabs.tid @@ -4,7 +4,15 @@ code-body: yes \define tabs-button() \whitespace trim -<$button set=<> setTo=<> default=<<__default__>> selectedClass="tc-tab-selected" tooltip={{!!tooltip}} role="switch"> +<$button + set=<> + setTo=<> + default=<<__default__>> + selectedClass="tc-tab-selected" + tooltip={{!!tooltip}} + role="switch" + data-tab-title=<> +> <$tiddler tiddler=<>> <$set name="tv-wikilinks" value="no"> <$transclude tiddler=<<__buttonTemplate__>> mode="inline"> diff --git a/core/wiki/macros/tag-picker.tid b/core/wiki/macros/tag-picker.tid index e1b1a7139..ede53ec26 100644 --- a/core/wiki/macros/tag-picker.tid +++ b/core/wiki/macros/tag-picker.tid @@ -16,7 +16,7 @@ second-search-filter: [tags[]is[system]search:titlesort[]] emptyMessage="<$action-listops $tiddler=<> $field=<<__tagField__>> $subfilter='-[]'/>" > <$action-listops $tiddler=<> $field=<<__tagField__>> $subfilter="[trim[]]"/> - $actions$ + <$transclude $variable="__actions__"/> <> @@ -102,7 +102,7 @@ second-search-filter: [tags[]is[system]search:titlesort[]] <$set name="tag" value={{{ [get[text]] }}}> <$button set=<> setTo="" class=""> <$action-listops $tiddler=<> $field=<<__tagField__>> $subfilter="[trim[]]"/> - $actions$ + <$transclude $variable="__actions__"/> <$set name="currentTiddlerCSSEscaped" value={{{ [escapecss[]] }}}> <><$action-sendmessage $message="tm-focus-selector" $param=<>/> diff --git a/core/wiki/macros/toc.tid b/core/wiki/macros/toc.tid index a925e7ee5..a3f5b002a 100644 --- a/core/wiki/macros/toc.tid +++ b/core/wiki/macros/toc.tid @@ -118,7 +118,7 @@ tags: $:/tags/Macro <$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item" >
  • >> <$link to={{{ [get[target]else] }}}> - <$list filter="[all[current]tagging[]$sort$limit[1]]" variable="ignore" emptyMessage="<$button class='tc-btn-invisible'>{{$:/core/images/blank}}"> + <$list filter="[all[current]tagging[]$sort$limit[1]] -[subfilter<__exclude__>]" variable="ignore" emptyMessage="<$button class='tc-btn-invisible'>{{$:/core/images/blank}}"> <$reveal type="nomatch" stateTitle=<> text="open"> <$button setTitle=<> setTo="open" class="tc-btn-invisible tc-popup-keep"> <$transclude tiddler=<> /> @@ -145,7 +145,7 @@ tags: $:/tags/Macro <$qualify name="toc-state" title={{{ [[$:/state/toc]addsuffix<__path__>addsuffix[-]addsuffix] }}}> <$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item">
  • >> - <$list filter="[all[current]tagging[]$sort$limit[1]]" variable="ignore" emptyMessage="""<$button class="tc-btn-invisible">{{$:/core/images/blank}}<>"""> + <$list filter="[all[current]tagging[]$sort$limit[1]] -[subfilter<__exclude__>]" variable="ignore" emptyMessage="""<$button class="tc-btn-invisible">{{$:/core/images/blank}}<>"""> <$reveal type="nomatch" stateTitle=<> text="open"> <$button setTitle=<> setTo="open" class="tc-btn-invisible tc-popup-keep"> <$transclude tiddler=<> /> diff --git a/editions/de-AT/tiddlers/HelloThere.tid b/editions/de-AT/tiddlers/HelloThere.tid index 60c2147f8..aa8bf52e7 100644 --- a/editions/de-AT/tiddlers/HelloThere.tid +++ b/editions/de-AT/tiddlers/HelloThere.tid @@ -21,8 +21,8 @@ Willkommen bei ''~TiddlyWiki'', dem einzigartigen [[nicht-linearen|Philosophy vo Anders, als bei herkömmlichen Online-Diensten, lässt Ihnen ~TiddlyWiki die Freiheit, wo sie ihre Daten speichern. Da ~TiddlyWiki alle Daten als simplen Text speichert, sind Notizen, die Sie heute machen, garantiert in Jahrzehnten noch einfach lesbar.
    - -{{$:/core/images/mail}} ~TiddlyWiki Mailing List + +{{$:/core/images/help}} ~TiddlyWiki Forum {{$:/core/images/twitter}} @~TiddlyWiki on Twitter diff --git a/editions/de-AT/tiddlers/community/Fur_Entwickler.tid b/editions/de-AT/tiddlers/community/Fur_Entwickler.tid index 0c656c8dc..2dbc185d7 100644 --- a/editions/de-AT/tiddlers/community/Fur_Entwickler.tid +++ b/editions/de-AT/tiddlers/community/Fur_Entwickler.tid @@ -7,5 +7,5 @@ type: text/vnd.tiddlywiki Es gibt mehrere Ressourcen für Entwickler, um mehr über das TiddlyWiki Projekt zu erfahren, zu diskutieren und vor allem mitzuhelfen. * [[tiddlywiki.com/dev|https://tiddlywiki.com/dev]] Offizielle Entwickler Doku. -* [[TiddlyWikiDev group|http://groups.google.com/group/TiddlyWikiDev]] Google Diskussionsforum für Entwickler. +* [[TiddlyWikiDev group|https://talk.tiddlywiki.org/c/devs/]] Diskussionsforum für Entwickler. * https://github.com/Jermolene/TiddlyWiki5 .. Github Repository. diff --git a/editions/es-ES/tiddlers/Forums.tid b/editions/es-ES/tiddlers/Forums.tid index 63ecc4339..57af7d0f2 100644 --- a/editions/es-ES/tiddlers/Forums.tid +++ b/editions/es-ES/tiddlers/Forums.tid @@ -12,7 +12,9 @@ Son listas de correo en las que hablamos de ~TiddlyWiki: pedimos ayuda, anunciam Puedes participar a través de la página web asociada, o suscribirte via mail. -!!Usuarios +!! Usuarios + +[[Foro oficial de TiddlyWiki| https://talk.tiddlywiki.org/]] [[Grupo principal de TiddlyWiki| http://groups.google.com/group/TiddlyWiki]] @@ -25,10 +27,7 @@ o síguenos [[en Twitter|http://twitter.com/TiddlyWiki]] si quieres recibir las !! Desarrolladores -[[Grupo de desarrollo de TiddlyWiki|http://groups.google.com/group/TiddlyWikiDev]] - ->No necesitas tener cuenta en Google para acceder al grupo. Suscríbete igualmente enviando un mail a: -*mailto:tiddlywikidev+subscribe@googlegroups.com. +[[Foro de desarrollo de TiddlyWiki|https://talk.tiddlywiki.org/c/devs]] Accede a nuestra [[página de desarrollo|https://github.com/Jermolene/TiddlyWiki5]] en GitHub y haz tu contribución. diff --git a/editions/es-ES/tiddlers/HelloThere.tid b/editions/es-ES/tiddlers/HelloThere.tid index 0b1cea4bb..c08a27e7d 100644 --- a/editions/es-ES/tiddlers/HelloThere.tid +++ b/editions/es-ES/tiddlers/HelloThere.tid @@ -20,8 +20,8 @@ BIenvenido a TiddlyWiki, un bloc de notas [[no lineal|Philosophy of Tiddlers]] Al revés que los servicios online convencionales, TiddlyWiki te deja escoger dónde quieres guardar tus datos, garantizándote que, por más que pase el tiempo, podrás seguir usando en el futuro las notas que tomes hoy.
    - -{{$:/core/images/mail}} ~TiddlyWiki en Google Groups + +{{$:/core/images/mail}} Foro oficial de ~TiddlyWiki {{$:/core/images/video}} ~TiddlyWiki en ~YouTube diff --git a/editions/es-ES/tiddlers/Typography.tid b/editions/es-ES/tiddlers/Typography.tid index 58edf9220..47d3a05ce 100644 --- a/editions/es-ES/tiddlers/Typography.tid +++ b/editions/es-ES/tiddlers/Typography.tid @@ -8,7 +8,7 @@ type: text/vnd.tiddlywiki Se recomienda el uso de las [[macros de documentación|Documentation Macros]] para facilitar las futuras tareas de mantenimiento del texto frente a nuevos cambios y actualizaciones. -Se recomienda precaución en el uso arbitrario de estilos directos de formato (''negrita'', //cursiva// ...etc). Si se puede usar una macro, conviene usarla. Si no existe la macro adecuada, se puede crear o, si no se sabe cómo, pedir su creación en el [[Grupo de Google|http://groups.google.com/group/TiddlyWiki]]. +Se recomienda precaución en el uso arbitrario de estilos directos de formato (''negrita'', //cursiva// ...etc). Si se puede usar una macro, conviene usarla. Si no existe la macro adecuada, se puede crear o, si no se sabe cómo, pedir su creación en el [[Foro de TiddlyWiki|https://talk.tiddlywiki.org/]]. Por el mismo motivo, se aconseja el uso de acentos graves `...` para transcribir fragmentos de código y ~WikiText, pero no para nombres de cosas tales como campos, operadores, variables o widgets. Estas tienen su macro correspondiente. diff --git a/editions/prerelease/tiddlers/Release 5.3.2.tid b/editions/prerelease/tiddlers/Release 5.3.2.tid deleted file mode 100644 index b21851cb6..000000000 --- a/editions/prerelease/tiddlers/Release 5.3.2.tid +++ /dev/null @@ -1,122 +0,0 @@ -caption: 5.3.2 -created: 20231016122502955 -modified: 20231016122502955 -tags: ReleaseNotes -title: Release 5.3.2 -type: text/vnd.tiddlywiki -description: Under development - -//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.1...master]]// - -! Major Improvements - -!! Conditional Shortcut Syntax - -<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7710">> a new [[shortcut syntax|Conditional Shortcut Syntax]] for concisely expressing if-then-else logic. For example: - -``` -<% if [match[Elephant]] %> - It is an elephant -<% else %> - <% if [match[Giraffe]] %> - It is a giraffe - <% else %> - It is completely unknown - <% endif %> -<% endif %> -``` - -!! Explicit Templates for the ListWidget - -<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7784">> support for `<$list-template>` and `<$list-empty>` as immediate children of the <<.wid "ListWidget">> widget to specify the list item template and/or the empty template. Note that the <<.attr "emptyMessage">> and <<.attr "template">> attributes take precedence if they are present. For example: - -``` -<$list filter=<>> - <$list-template> - <$text text=<>/> - - <$list-empty> - None! - - -``` - -!! jsonset operator - -<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7742">> [[jsonset Operator]] for setting values within JSON objects - -!! QR Code Reader - -<<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/7746">> QR Code plugin to be able to read QR codes and a number of other bar code formats - -! Translation improvement - -Improvements to the following translations: - -* Chinese -* Polish -* Spanish - -! Plugin Improvements - -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/1be8f0a9336952d4745d2bd4f2327e353580a272">> Comments Plugin to use predefined palette colours -* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7785">> Evernote Importer Plugin to support images and other attachments - -! Widget Improvements - -* - -! Usability Improvements - -* <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/pull/7747">> editor preview button to automatically focus the editor -* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7764">> file type names in the export menu - -! Hackability Improvements - -* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7737">> an automatic build of the external core TiddlyWiki at https://tiddlywiki.com/empty-external-core.html -* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7690">> the default page layout to better support CSS grid and flexbox layouts - -! Bug Fixes - -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/issues/7665">> `{{}}` generating a recursion error -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7758">> ordering of Vanilla stylesheets -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/fa9bfa07a095548eb2f8339b0b1b816d2e6794ef">> missing closing tag in tag-pill-inner macro -* <<.link-badge-removed "https://github.com/Jermolene/TiddlyWiki5/issues/7732">> invalid "type" attribute from textarea elements generated by the EditTextWidget -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7749">> editor "type" dropdown state tiddlers -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7712">> handling of "counter-last" variable when appending items with the ListWidget -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/6088">> upgrade download link in Firefox -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7698">> refreshing of transcluded functions -* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7789">> resizing of height of textareas in control panel - -! Node.js Improvements - -* - -! Performance Improvements - -* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7702">> performance of predefined patterns with [[all Operator]] -* <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/issues/7671">> favicon format to PNG - -! Developer Improvements - -* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7751">> global hook handling to support removing hooks - -! Acknowledgements - -[[@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 """ -AnthonyMuscio -BramChen -BuckarooBanzay -BurningTreeC -EvidentlyCube -joebordes -kookma -linonetwo -mateuszwilczek -pmario -rmunn -simonbaird -T1mL3arn -""">> diff --git a/editions/prerelease/tiddlers/Release 5.3.4.tid b/editions/prerelease/tiddlers/Release 5.3.4.tid new file mode 100644 index 000000000..562909a93 --- /dev/null +++ b/editions/prerelease/tiddlers/Release 5.3.4.tid @@ -0,0 +1,61 @@ +caption: 5.3.4 +created: 20231223102229103 +modified: 20231223102229103 +tags: ReleaseNotes +title: Release 5.3.4 +type: text/vnd.tiddlywiki +description: Under development + +//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.3...master]]// + +! Major Improvements + + +! Translation improvements + +Improvements to the following translations: + +* + +! Plugin Improvements + +* + +! Widget Improvements + +* + +! Usability Improvements + +* + +! Hackability Improvements + +* + +! Bug Fixes + +* + +! Node.js Improvements + +* + +! Performance Improvements + +* + +! Developer Improvements + +* + +! Infrastructure Improvements + +* + +! Acknowledgements + +[[@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 """ +""">> diff --git a/editions/prerelease/tiddlers/system/PrereleaseOfficialPluginLibrary.tid b/editions/prerelease/tiddlers/system/PrereleaseOfficialPluginLibrary.tid index dfd8a6153..d5cdcec63 100644 --- a/editions/prerelease/tiddlers/system/PrereleaseOfficialPluginLibrary.tid +++ b/editions/prerelease/tiddlers/system/PrereleaseOfficialPluginLibrary.tid @@ -1,6 +1,6 @@ title: $:/config/OfficialPluginLibrary tags: $:/tags/PluginLibrary -url: https://tiddlywiki.com/prerelease/library/v5.3.2/index.html +url: https://tiddlywiki.com/prerelease/library/v5.3.3/index.html caption: {{$:/language/OfficialPluginLibrary}} (Prerelease) The prerelease version of the official ~TiddlyWiki plugin library at tiddlywiki.com. Plugins, themes and language packs are maintained by the core team. diff --git a/editions/prerelease/tiddlers/system/temp-my-scroll-position.tid b/editions/prerelease/tiddlers/system/temp-my-scroll-position.tid new file mode 100644 index 000000000..c4a164070 --- /dev/null +++ b/editions/prerelease/tiddlers/system/temp-my-scroll-position.tid @@ -0,0 +1,3 @@ +title: $:/my-scroll-position +scroll-left: 0 +scroll-top: 100 diff --git a/editions/test/playwright.spec.js b/editions/test/playwright.spec.js new file mode 100644 index 000000000..1d8c624c7 --- /dev/null +++ b/editions/test/playwright.spec.js @@ -0,0 +1,25 @@ +const { test, expect } = require('@playwright/test'); +const {resolve} = require('path'); + +const indexPath = resolve(__dirname, 'output', 'test.html'); +const crossPlatformIndexPath = indexPath.replace(/^\/+/, ''); + + +test('get started link', async ({ page }) => { + // The tests can take a while to run + const timeout = 1000 * 30; + test.setTimeout(timeout); + + // Load the generated test TW html + await page.goto(`file:///${crossPlatformIndexPath}`); + + // Sanity check + await expect(page.locator('.tc-site-title'), "Expected correct page title to verify the test page was loaded").toHaveText('TiddlyWiki5'); + + // Wait for jasmine results bar to appear + await expect(page.locator('.jasmine-overall-result'), "Expected jasmine test results bar to be present").toBeVisible({timeout}); + + // Assert the tests have passed + await expect(page.locator('.jasmine-overall-result.jasmine-failed'), "Expected jasmine tests to not have failed").not.toBeVisible(); + await expect(page.locator('.jasmine-overall-result.jasmine-passed'), "Expected jasmine tests to have passed").toBeVisible(); +}); diff --git a/editions/test/tiddlers/tests/data/functions/FunctionFilterrunVariables.tid b/editions/test/tiddlers/tests/data/functions/FunctionFilterrunVariables.tid new file mode 100644 index 000000000..5226e9f05 --- /dev/null +++ b/editions/test/tiddlers/tests/data/functions/FunctionFilterrunVariables.tid @@ -0,0 +1,24 @@ +title: Functions/FunctionFilterrunVariables +description: Functions in filter runs that set variables +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Idiosyncrasy +caption: Idiosyncrasy Caption Field + ++ +title: Output + +\whitespace trim +\procedure demo-subfilter() [] +\function .demo-function() [] + +<$let currentTiddler="Idiosyncrasy"> +<$text text={{{ [get[caption]!is[blank]else] :map[subfilter] }}}/>, +<$text text={{{ [get[caption]!is[blank]else] :map[.demo-function[]] }}}/> + + ++ +title: ExpectedResult + +

    Idiosyncrasy Caption Field,Idiosyncrasy Caption Field

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/functions/FunctionFilterrunVariables2.tid b/editions/test/tiddlers/tests/data/functions/FunctionFilterrunVariables2.tid new file mode 100644 index 000000000..07b8c412a --- /dev/null +++ b/editions/test/tiddlers/tests/data/functions/FunctionFilterrunVariables2.tid @@ -0,0 +1,20 @@ +title: Functions/FunctionFilterrunVariables2 +description: Functions in filter runs that set variables +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Apple +cost: 5 + ++ +title: Output + +\whitespace trim +\function .doublecost() [get[cost]multiply[2]] + +<$text text={{{ [[Apple]] :map[.doublecost[]] }}}/> + ++ +title: ExpectedResult + +10 \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/list-widget/WithEmptyParagraphTemplate.tid b/editions/test/tiddlers/tests/data/list-widget/WithEmptyParagraphTemplate.tid new file mode 100644 index 000000000..7730f525a --- /dev/null +++ b/editions/test/tiddlers/tests/data/list-widget/WithEmptyParagraphTemplate.tid @@ -0,0 +1,13 @@ +title: ListWidget/WithEmptyParagraphTemplate +description: List widget with an empty paragraph as inline template +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + ++ +title: Output + +<$list filter="1">

    ++ +title: ExpectedResult + +

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/list-widget/WithJoinTemplate.tid b/editions/test/tiddlers/tests/data/list-widget/WithJoinTemplate.tid new file mode 100644 index 000000000..f1b6f25e9 --- /dev/null +++ b/editions/test/tiddlers/tests/data/list-widget/WithJoinTemplate.tid @@ -0,0 +1,30 @@ +title: ListWidget/WithJoinTemplate +description: List widget with join template and $list-empty +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + ++ +title: Output + +\whitespace trim + +\procedure test(filter) +<$list filter=<>> + Item:<> + + <$list-empty> + None! + + + <$list-join>, + +\end + +<> + +<> + ++ +title: ExpectedResult + +

    Item:1,Item:2,Item:3

    None!

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/list-widget/WithJoinTemplateInBlockMode.tid b/editions/test/tiddlers/tests/data/list-widget/WithJoinTemplateInBlockMode.tid new file mode 100644 index 000000000..c12f4c801 --- /dev/null +++ b/editions/test/tiddlers/tests/data/list-widget/WithJoinTemplateInBlockMode.tid @@ -0,0 +1,32 @@ +title: ListWidget/WithJoinTemplateInBlockMode +description: List widget with join template and $list-empty in block mode +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + ++ +title: Output + +\whitespace trim + +\procedure test(filter) +<$list filter=<>> + + Item:<> + + <$list-empty> + None! + + + <$list-join>
    + +\end + +<> + +<> + ++ +title: ExpectedResult +comment: I wish there was a good way to get rid of these extraneous paragraph elements + +

    Item:1


    Item:2


    Item:3

    None! \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/macros/EndInBody.tid b/editions/test/tiddlers/tests/data/macros/EndInBody.tid new file mode 100644 index 000000000..e93a0917d --- /dev/null +++ b/editions/test/tiddlers/tests/data/macros/EndInBody.tid @@ -0,0 +1,16 @@ +title: Macros/EndInBody +description: \end line starting with non-whitespace is part of macro body +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\define hello() + hello \end +\end + +Out: <> ++ +title: ExpectedResult + +

    Out: hello \end

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/macros/IndentedEnd.tid b/editions/test/tiddlers/tests/data/macros/IndentedEnd.tid new file mode 100644 index 000000000..97f2c4197 --- /dev/null +++ b/editions/test/tiddlers/tests/data/macros/IndentedEnd.tid @@ -0,0 +1,16 @@ +title: Macros/IndentedEnd +description: \end line starting with whitespace ends a macro body +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\define hello() + hello \end + \end + +Out: <> ++ +title: ExpectedResult + +

    Out: hello \end

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/macros/MismatchedNamedEnd.tid b/editions/test/tiddlers/tests/data/macros/MismatchedNamedEnd.tid new file mode 100644 index 000000000..b66821753 --- /dev/null +++ b/editions/test/tiddlers/tests/data/macros/MismatchedNamedEnd.tid @@ -0,0 +1,16 @@ +title: Macros/MismatchedNamedEnd +description: Mismatched named end is part of the body +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\define hello() + \end goodbye +\end + +Out: <> ++ +title: ExpectedResult + +

    Out: \end goodbye

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/macros/WhitespaceOnlyWithEnd.tid b/editions/test/tiddlers/tests/data/macros/WhitespaceOnlyWithEnd.tid new file mode 100644 index 000000000..6998672ba --- /dev/null +++ b/editions/test/tiddlers/tests/data/macros/WhitespaceOnlyWithEnd.tid @@ -0,0 +1,18 @@ +title: Macros/WhitespaceOnlyWithEnd +description: The /end should be detected when macro definition contains only whitespace +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\define max() +\end +Nothing +\end + +Out: <> ++ +title: ExpectedResult + +

    Nothing +\end

    Out:

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/macros/WhitespaceOnlyWithEnd2.tid b/editions/test/tiddlers/tests/data/macros/WhitespaceOnlyWithEnd2.tid new file mode 100644 index 000000000..60db278d6 --- /dev/null +++ b/editions/test/tiddlers/tests/data/macros/WhitespaceOnlyWithEnd2.tid @@ -0,0 +1,15 @@ +title: Macros/WhitespaceOnlyWithEnd2 +description: Line with \end can start with whitespace +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\define empty() + \end + +Out: <> ++ +title: ExpectedResult + +

    Out:

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/macros/NestedMacros-indented.tid b/editions/test/tiddlers/tests/data/macros/nested-macros/NestedMacros-indented.tid similarity index 100% rename from editions/test/tiddlers/tests/data/macros/NestedMacros-indented.tid rename to editions/test/tiddlers/tests/data/macros/nested-macros/NestedMacros-indented.tid diff --git a/editions/test/tiddlers/tests/data/macros/NestedMacros-indented2.tid b/editions/test/tiddlers/tests/data/macros/nested-macros/NestedMacros-indented2.tid similarity index 100% rename from editions/test/tiddlers/tests/data/macros/NestedMacros-indented2.tid rename to editions/test/tiddlers/tests/data/macros/nested-macros/NestedMacros-indented2.tid diff --git a/editions/test/tiddlers/tests/data/macros/NestedMacros.tid b/editions/test/tiddlers/tests/data/macros/nested-macros/NestedMacros.tid similarity index 100% rename from editions/test/tiddlers/tests/data/macros/NestedMacros.tid rename to editions/test/tiddlers/tests/data/macros/nested-macros/NestedMacros.tid diff --git a/editions/test/tiddlers/tests/data/macros/TrailingNewlines.tid b/editions/test/tiddlers/tests/data/macros/trailing-newlines/TrailingNewlines.tid similarity index 100% rename from editions/test/tiddlers/tests/data/macros/TrailingNewlines.tid rename to editions/test/tiddlers/tests/data/macros/trailing-newlines/TrailingNewlines.tid diff --git a/editions/test/tiddlers/tests/data/pragmas/WhitespaceAfterPragma.tid b/editions/test/tiddlers/tests/data/pragmas/WhitespaceAfterPragma.tid new file mode 100644 index 000000000..34b7b12ff --- /dev/null +++ b/editions/test/tiddlers/tests/data/pragmas/WhitespaceAfterPragma.tid @@ -0,0 +1,64 @@ +title: Pragmas/WhitespaceAfterPragma +description: parsermode pragma +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +<$wikify name="parsetree" text={{Text}} mode="inline" output="parsetree"> +<$text text=<>/> + ++ +title: Text + +\procedure this-is-a-definition() Something + + + + +Now! + ++ +title: ExpectedResult + +

    +[ + { + "type": "set", + "attributes": { + "name": { + "name": "name", + "type": "string", + "value": "this-is-a-definition" + }, + "value": { + "name": "value", + "type": "string", + "value": "Something" + } + }, + "children": [ + { + "type": "text", + "text": "Now!\n", + "start": 48, + "end": 53 + } + ], + "params": [], + "orderedAttributes": [ + { + "name": "name", + "type": "string", + "value": "this-is-a-definition" + }, + { + "name": "value", + "type": "string", + "value": "Something" + } + ], + "isProcedureDefinition": true + } +] +

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/pragmas/WhitespaceNoPragma.tid b/editions/test/tiddlers/tests/data/pragmas/WhitespaceNoPragma.tid new file mode 100644 index 000000000..2a7041750 --- /dev/null +++ b/editions/test/tiddlers/tests/data/pragmas/WhitespaceNoPragma.tid @@ -0,0 +1,32 @@ +title: Pragmas/WhitespaceNoPragma +description: parsermode pragma +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +<$wikify name="parsetree" text={{Text}} mode="inline" output="parsetree"> +<$text text=<>/> + ++ +title: Text + + + + + +Now! + ++ +title: ExpectedResult + +

    +[ + { + "type": "text", + "text": "\n\n\n\nNow!\n", + "start": 0, + "end": 9 + } +] +

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/procedures/EndInBody.tid b/editions/test/tiddlers/tests/data/procedures/EndInBody.tid new file mode 100644 index 000000000..3aa13fad5 --- /dev/null +++ b/editions/test/tiddlers/tests/data/procedures/EndInBody.tid @@ -0,0 +1,16 @@ +title: Procedures/EndInBody +description: \end line starting with non-whitespace is part of procedure body +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\procedure hello() + hello \end +\end + +Out: <> ++ +title: ExpectedResult + +

    Out: hello \end

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/procedures/IndentedEnd.tid b/editions/test/tiddlers/tests/data/procedures/IndentedEnd.tid new file mode 100644 index 000000000..664be3446 --- /dev/null +++ b/editions/test/tiddlers/tests/data/procedures/IndentedEnd.tid @@ -0,0 +1,16 @@ +title: Procedures/IndentedEnd +description: \end line starting with whitespace ends a procedure body +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\procedure hello() + hello \end + \end + +Out: <> ++ +title: ExpectedResult + +

    Out: hello \end

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/procedures/MismatchedNamedEnd.tid b/editions/test/tiddlers/tests/data/procedures/MismatchedNamedEnd.tid new file mode 100644 index 000000000..0b5385c6e --- /dev/null +++ b/editions/test/tiddlers/tests/data/procedures/MismatchedNamedEnd.tid @@ -0,0 +1,16 @@ +title: Procedures/MismatchedNamedEnd +description: Mismatched named end is part of the body +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\procedure hello() + \end goodbye +\end + +Out: <> ++ +title: ExpectedResult + +

    Out: \end goodbye

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/procedures/WhitespaceOnlyWithEnd.tid b/editions/test/tiddlers/tests/data/procedures/WhitespaceOnlyWithEnd.tid new file mode 100644 index 000000000..51f0b87df --- /dev/null +++ b/editions/test/tiddlers/tests/data/procedures/WhitespaceOnlyWithEnd.tid @@ -0,0 +1,18 @@ +title: Procedures/WhitespaceOnlyWithEnd +description: The /end should be detected when procedure definition contains only whitespace +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\procedure max() +\end +Nothing +\end + +Out: <> ++ +title: ExpectedResult + +

    Nothing +\end

    Out:

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/procedures/WhitespaceOnlyWithEnd2.tid b/editions/test/tiddlers/tests/data/procedures/WhitespaceOnlyWithEnd2.tid new file mode 100644 index 000000000..54d3ebbf6 --- /dev/null +++ b/editions/test/tiddlers/tests/data/procedures/WhitespaceOnlyWithEnd2.tid @@ -0,0 +1,15 @@ +title: Procedures/WhitespaceOnlyWithEnd2 +description: Line with \end can start with whitespace +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\procedure empty() + \end + +Out: <> ++ +title: ExpectedResult + +

    Out:

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/transclude/CustomWidget-CodeblockOverride-TextParser.tid b/editions/test/tiddlers/tests/data/transclude/CustomWidget-CodeblockOverride-TextParser.tid new file mode 100644 index 000000000..484f0c4a3 --- /dev/null +++ b/editions/test/tiddlers/tests/data/transclude/CustomWidget-CodeblockOverride-TextParser.tid @@ -0,0 +1,20 @@ +title: Transclude/CustomWidget/CodeblockOverride-TextParser +description: Test that overriding codeblock widget does not impact text parser +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\widget $codeblock(code) +<$transclude $variable="copy-to-clipboard" src=<>/> +<$genesis $type="$codeblock" $remappable="no" code=<>/> +\end + +\procedure myvariable() hello + +<$transclude $variable="myvariable" $type="text/plain" $output="text/plain"/> ++ +title: ExpectedResult + +

    hello

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/DataAttributes/ButtonWidget-DataAttributes.tid b/editions/test/tiddlers/tests/data/widgets/DataAttributes/ButtonWidget-DataAttributes.tid new file mode 100644 index 000000000..da3d7080a --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/DataAttributes/ButtonWidget-DataAttributes.tid @@ -0,0 +1,27 @@ +title: Widgets/DataAttributes/ButtonWidget +description: Data Attributes for ButtonWidget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$button tag="div" class="myclass" data-title="mytiddler" style.color="red" onclick="clicked"> +my tiddler + +<$button tag="div" class="myclass" data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}}> +hello + ++ +title: Actions + +<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/> ++ +title: Temp +color: black + +Title1 ++ +title: ExpectedResult + +

    my tiddler
    hello

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/DataAttributes/CheckboxWidget-DataAttributes.tid b/editions/test/tiddlers/tests/data/widgets/DataAttributes/CheckboxWidget-DataAttributes.tid new file mode 100644 index 000000000..521fa3a13 --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/DataAttributes/CheckboxWidget-DataAttributes.tid @@ -0,0 +1,22 @@ +title: Widgets/DataAttributes/CheckboxWidget +description: Data Attributes for CheckboxWidget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$checkbox tag="done" data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}} onclick="clicked"> Is it done? ++ +title: Actions + +<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/> ++ +title: Temp +color: black + +Title1 ++ +title: ExpectedResult + +

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/DataAttributes/DraggableWidget-DataAttributes.tid b/editions/test/tiddlers/tests/data/widgets/DataAttributes/DraggableWidget-DataAttributes.tid new file mode 100644 index 000000000..feeb89ded --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/DataAttributes/DraggableWidget-DataAttributes.tid @@ -0,0 +1,27 @@ +title: Widgets/DataAttributes/DraggableWidget +description: Data Attributes for DraggableWidget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$draggable tag="div" class="myclass" data-title="mytiddler" style.color="red" onclick="clicked"> +my tiddler + +<$draggable tag="div" class="myclass" data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}}> +hello + ++ +title: Actions + +<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/> ++ +title: Temp +color: black + +Title1 ++ +title: ExpectedResult + +

    my tiddler
    hello

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/DataAttributes/DroppableWidget-DataAttributes.tid b/editions/test/tiddlers/tests/data/widgets/DataAttributes/DroppableWidget-DataAttributes.tid new file mode 100644 index 000000000..3c7284eb1 --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/DataAttributes/DroppableWidget-DataAttributes.tid @@ -0,0 +1,27 @@ +title: Widgets/DataAttributes/DroppableWidget +description: Data Attributes for DroppableWidget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$droppable tag="div" class="myclass" data-title="mytiddler" style.color="red" onclick="clicked"> +my tiddler + +<$droppable tag="div" class="myclass" data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}}> +hello + ++ +title: Actions + +<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/> ++ +title: Temp +color: black + +Title1 ++ +title: ExpectedResult + +

    my tiddler
    hello

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/DataAttributes/LinkWidget-DataAttributes.tid b/editions/test/tiddlers/tests/data/widgets/DataAttributes/LinkWidget-DataAttributes.tid new file mode 100644 index 000000000..e99e265bb --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/DataAttributes/LinkWidget-DataAttributes.tid @@ -0,0 +1,27 @@ +title: Widgets/DataAttributes/LinkWidget +description: Data Attributes for LinkWidget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$link data-id="mytiddler" style.color="red" to="Temp" onclick="clicked"> +link to Temp + +<$link tag="button" data-id={{Temp}} style.color={{{ [[Temp]get[color]] }}} to="SomeTiddler"> +some tiddler + ++ +title: Actions + +<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/> ++ +title: Temp +color: black + +Title1 ++ +title: ExpectedResult + +

    link to Temp

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/DataAttributes/OrderedStyleAttributes.tid b/editions/test/tiddlers/tests/data/widgets/DataAttributes/OrderedStyleAttributes.tid new file mode 100644 index 000000000..2f6d2cb1a --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/DataAttributes/OrderedStyleAttributes.tid @@ -0,0 +1,15 @@ +title: Widgets/DataAttributes/OrderedStyleAttributes +description: Ordered style attributes +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +
    +hello +
    ++ +title: ExpectedResult + +

    hello

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/DataAttributes/SelectWidget-DataAttributes.tid b/editions/test/tiddlers/tests/data/widgets/DataAttributes/SelectWidget-DataAttributes.tid new file mode 100644 index 000000000..e2006312b --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/DataAttributes/SelectWidget-DataAttributes.tid @@ -0,0 +1,27 @@ +title: Widgets/DataAttributes/SelectWidget +description: Data Attributes for SelectWidget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$select tiddler='New Tiddler' class="myclass" field='text' default='Choose a new text' data-title={{Temp}} style.color={{{ [[Temp]get[color]] }}} onclick="clicked"> + + + + + ++ +title: Actions + +<$action-setfield $tiddler="Temp" $field="text" $value="Title2" color="red"/> ++ +title: Temp +color: black + +Title1 ++ +title: ExpectedResult + +

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/ElementWidgetEventAttributes.tid b/editions/test/tiddlers/tests/data/widgets/ElementWidgetEventAttributes.tid new file mode 100644 index 000000000..4c2f6eb04 --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/ElementWidgetEventAttributes.tid @@ -0,0 +1,15 @@ +title: Widgets/ElementWidgetEventAttributes +description: Element widget should not support event attributes starting with "on" +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +
    +TiddlyWiki +
    ++ +title: ExpectedResult + +

    TiddlyWiki

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/ElementWidgetStyleAttributes.tid b/editions/test/tiddlers/tests/data/widgets/ElementWidgetStyleAttributes.tid new file mode 100644 index 000000000..a36a51323 --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/ElementWidgetStyleAttributes.tid @@ -0,0 +1,15 @@ +title: Widgets/ElementWidgetStyleAttributes +description: Element widget should support style.* attributes +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +
    +TiddlyWiki +
    ++ +title: ExpectedResult + +

    TiddlyWiki

    \ No newline at end of file diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index 9e2f53b1a..727f64ca4 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -365,6 +365,7 @@ Tests the filtering mechanism. expect(wiki.filterTiddlers("[sort[title]first[8]]").join(",")).toBe("$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three"); expect(wiki.filterTiddlers("[sort[title]first[x]]").join(",")).toBe("$:/ShadowPlugin"); expect(wiki.filterTiddlers("[sort[title]last[]]").join(",")).toBe("TiddlerOne"); + expect(wiki.filterTiddlers("[sort[title]last[0]]").join(",")).toBe(""); expect(wiki.filterTiddlers("[sort[title]last[2]]").join(",")).toBe("Tiddler Three,TiddlerOne"); expect(wiki.filterTiddlers("[sort[title]last[8]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three,TiddlerOne"); expect(wiki.filterTiddlers("[sort[title]last[x]]").join(",")).toBe("TiddlerOne"); diff --git a/editions/test/tiddlers/tests/test-json-filters.js b/editions/test/tiddlers/tests/test-json-filters.js index b2f2c8e82..bfb8a4504 100644 --- a/editions/test/tiddlers/tests/test-json-filters.js +++ b/editions/test/tiddlers/tests/test-json-filters.js @@ -53,6 +53,11 @@ describe("json filter tests", function() { expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[2]]")).toEqual(["true"]); expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[3]]")).toEqual(["false"]); expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[4]]")).toEqual(["null"]); + expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-5]]")).toEqual(["five"]); + expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-4]]")).toEqual(["six"]); + expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-3]]")).toEqual(["true"]); + expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-2]]")).toEqual(["false"]); + expect(wiki.filterTiddlers("[{First}jsonget[d],[f],[-1]]")).toEqual(["null"]); }); it("should support the jsonextract operator", function() { @@ -70,6 +75,11 @@ describe("json filter tests", function() { expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[2]]")).toEqual(["true"]); expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[3]]")).toEqual(["false"]); expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[4]]")).toEqual(["null"]); + expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-5]]")).toEqual(['"five"']); + expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-4]]")).toEqual(['"six"']); + expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-3]]")).toEqual(["true"]); + expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-2]]")).toEqual(["false"]); + expect(wiki.filterTiddlers("[{First}jsonextract[d],[f],[-1]]")).toEqual(["null"]); }); it("should support the jsonindexes operator", function() { @@ -85,6 +95,11 @@ describe("json filter tests", function() { expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[2]]")).toEqual([]); expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[3]]")).toEqual([]); expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[4]]")).toEqual([]); + expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-5]]")).toEqual([]); + expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-4]]")).toEqual([]); + expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-3]]")).toEqual([]); + expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-2]]")).toEqual([]); + expect(wiki.filterTiddlers("[{First}jsonindexes[d],[f],[-1]]")).toEqual([]); }); it("should support the jsontype operator", function() { @@ -101,9 +116,15 @@ describe("json filter tests", function() { expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[2]]")).toEqual(["boolean"]); expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[3]]")).toEqual(["boolean"]); expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[4]]")).toEqual(["null"]); + expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-5]]")).toEqual(["string"]); + expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-4]]")).toEqual(["string"]); + expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-3]]")).toEqual(["boolean"]); + expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-2]]")).toEqual(["boolean"]); + expect(wiki.filterTiddlers("[{First}jsontype[d],[f],[-1]]")).toEqual(["null"]); }); it("should support the jsonset operator", function() { + expect(wiki.filterTiddlers("[jsonset[a],[aa]]")).toEqual(['"First"','"Second"','"Third"']); expect(wiki.filterTiddlers("[{First}jsonset[]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']); expect(wiki.filterTiddlers("[{First}jsonset[],[Antelope]]")).toEqual(['"Antelope"']); expect(wiki.filterTiddlers("[{First}jsonset:number[],[not a number]]")).toEqual(['0']); @@ -115,6 +136,7 @@ describe("json filter tests", function() { expect(wiki.filterTiddlers("[{First}jsonset:null[id]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":null}']); expect(wiki.filterTiddlers("[{First}jsonset:array[d],[f],[5]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null,[]]}}']); expect(wiki.filterTiddlers("[{First}jsonset:object[d],[f],[5]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null,{}]}}']); + expect(wiki.filterTiddlers("[{First}jsonset:number[d],[f],[-1],[42]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,42]}}']); expect(wiki.filterTiddlers("[{First}jsonset[missing],[id],[Antelope]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}']); expect(wiki.filterTiddlers("[{First}jsonset:json[\"Antelope\"]]")).toEqual(['"Antelope"']); expect(wiki.filterTiddlers("[{First}jsonset:json[id],[{\"a\":313}]]")).toEqual(['{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":{"a":313}}']); diff --git a/editions/test/tiddlers/tests/test-utils.js b/editions/test/tiddlers/tests/test-utils.js index d41d5047a..91ddf86f4 100644 --- a/editions/test/tiddlers/tests/test-utils.js +++ b/editions/test/tiddlers/tests/test-utils.js @@ -48,6 +48,29 @@ describe("Utility tests", function() { expect($tw.utils.base64Decode($tw.utils.base64Encode(booksEmoji))).toBe(booksEmoji, "should round-trip correctly"); }); + it("should handle base64 encoding emojis in URL-safe variant", function() { + var booksEmoji = "📚"; + expect($tw.utils.base64Encode(booksEmoji, false, true)).toBe("8J-Tmg==", "if surrogate pairs are correctly treated as a single code unit then base64 should be 8J+Tmg=="); + expect($tw.utils.base64Decode("8J-Tmg==", false, true)).toBe(booksEmoji); + expect($tw.utils.base64Decode($tw.utils.base64Encode(booksEmoji, false, true), false, true)).toBe(booksEmoji, "should round-trip correctly"); + }); + + it("should handle base64 encoding binary data", function() { + var binaryData = "\xff\xfe\xfe\xff"; + var encoded = $tw.utils.base64Encode(binaryData,true); + expect(encoded).toBe("//7+/w=="); + var decoded = $tw.utils.base64Decode(encoded,true); + expect(decoded).toBe(binaryData, "Binary data did not round-trip correctly"); + }); + + it("should handle base64 encoding binary data in URL-safe variant", function() { + var binaryData = "\xff\xfe\xfe\xff"; + var encoded = $tw.utils.base64Encode(binaryData,true,true); + expect(encoded).toBe("__7-_w=="); + var decoded = $tw.utils.base64Decode(encoded,true,true); + expect(decoded).toBe(binaryData, "Binary data did not round-trip correctly"); + }); + it("should handle stringifying a string array", function() { var str = $tw.utils.stringifyList; expect(str([])).toEqual(""); diff --git a/editions/test/tiddlers/tests/test-widget.js b/editions/test/tiddlers/tests/test-widget.js index 4da9e20b0..0d1351f31 100755 --- a/editions/test/tiddlers/tests/test-widget.js +++ b/editions/test/tiddlers/tests/test-widget.js @@ -527,6 +527,45 @@ describe("Widget module", function() { expect(wrapper.children[0].children[15].sequenceNumber).toBe(53); }); + var testListJoin = function(oldList, newList) { + return function() { + var wiki = new $tw.Wiki(); + // Add some tiddlers + wiki.addTiddler({title: "Numbers", text: "", list: oldList}); + var text = "<$list filter='[list[Numbers]]' variable='item' join=', '><>"; + var widgetNode = createWidgetNode(parseText(text,wiki),wiki); + // Render the widget node to the DOM + var wrapper = renderWidgetNode(widgetNode); + // Test the rendering + expect(wrapper.innerHTML).toBe("

    " + oldList.split(' ').join(', ') + "

    "); + // Change the list and ensure new rendering is still right + wiki.addTiddler({title: "Numbers", text: "", list: newList}); + refreshWidgetNode(widgetNode,wrapper,["Numbers"]); + expect(wrapper.innerHTML).toBe("

    " + newList.split(' ').join(', ') + "

    "); + } + } + + it("the list widget with join should update correctly when empty list gets one item", testListJoin("", "1")); + it("the list widget with join should update correctly when empty list gets two items", testListJoin("", "1 2")); + it("the list widget with join should update correctly when single-item list is appended to", testListJoin("1", "1 2")); + it("the list widget with join should update correctly when single-item list is prepended to", testListJoin("1", "2 1")); + it("the list widget with join should update correctly when list is appended", testListJoin("1 2 3 4", "1 2 3 4 5")); + it("the list widget with join should update correctly when last item is removed", testListJoin("1 2 3 4", "1 2 3")); + it("the list widget with join should update correctly when first item is inserted", testListJoin("1 2 3 4", "0 1 2 3 4")); + it("the list widget with join should update correctly when first item is removed", testListJoin("1 2 3 4", "2 3 4")); + it("the list widget with join should update correctly when first two items are swapped", testListJoin("1 2 3 4", "2 1 3 4")); + it("the list widget with join should update correctly when last two items are swapped", testListJoin("1 2 3 4", "1 2 4 3")); + it("the list widget with join should update correctly when last item is moved to the front", testListJoin("1 2 3 4", "4 1 2 3")); + it("the list widget with join should update correctly when last item is moved to the middle", testListJoin("1 2 3 4", "1 4 2 3")); + it("the list widget with join should update correctly when first item is moved to the back", testListJoin("1 2 3 4", "2 3 4 1")); + it("the list widget with join should update correctly when middle item is moved to the back", testListJoin("1 2 3 4", "1 3 4 2")); + it("the list widget with join should update correctly when the last item disappears at the same time as other edits 1", testListJoin("1 3 4", "1 2 3")); + it("the list widget with join should update correctly when the last item disappears at the same time as other edits 2", testListJoin("1 3 4", "1 3 2")); + it("the list widget with join should update correctly when the last item disappears at the same time as other edits 3", testListJoin("1 3 4", "2 1 3")); + it("the list widget with join should update correctly when the last item disappears at the same time as other edits 4", testListJoin("1 3 4", "2 3 1")); + it("the list widget with join should update correctly when the last item disappears at the same time as other edits 5", testListJoin("1 3 4", "3 1 2")); + it("the list widget with join should update correctly when the last item disappears at the same time as other edits 6", testListJoin("1 3 4", "3 2 1")); + var testCounterLast = function(oldList, newList) { return function() { var wiki = new $tw.Wiki(); diff --git a/editions/tw5.com/tiddlers/concepts/Macros.tid b/editions/tw5.com/tiddlers/concepts/Macros.tid index 8377046f6..884551996 100644 --- a/editions/tw5.com/tiddlers/concepts/Macros.tid +++ b/editions/tw5.com/tiddlers/concepts/Macros.tid @@ -1,9 +1,15 @@ created: 20140211171341271 -modified: 20230419103154328 +modified: 20230922094937115 tags: Concepts Reference title: Macros type: text/vnd.tiddlywiki +!! Important + +<<.from-version "5.3.0">> Macros have been [[superseded|Macro Pitfalls]] by [[Procedures]], [[Functions]] and [[Custom Widgets]] which together provide more robust and flexible ways to encapsulate and re-use code. + +For text substitutions it is now recommended to use: [[Substituted Attribute Values]], [[substitute Operator]] and [[Transclusion and Substitution]] + !! Introduction A <<.def macro>> is a named snippet of text. They are typically defined with the [[Pragma: \define]]: @@ -26,8 +32,6 @@ The parameters that are specified in the macro call are substituted for special * `$parameter-name$` is replaced with the value of the named parameter * `$(variable-name)$` is replaced with the value of the named [[variable|Variables]]). -<<.from-version "5.3.0">> Macros have been [[superseded|Macro Pitfalls]] by [[Procedures]], [[Custom Widgets]] and [[Functions]] which together provide more robust and flexible ways to encapsulate and re-use code. It is now recommended to only use macros when textual substitution is specifically required. - !! How Macros Work Macros are implemented as a special kind of [[variable|Variables]]. The only thing that distinguishes them from ordinary variables is the way that the parameters are handled. diff --git a/editions/tw5.com/tiddlers/concepts/TiddlerFields.tid b/editions/tw5.com/tiddlers/concepts/TiddlerFields.tid index 0f9b830a7..22017c570 100644 --- a/editions/tw5.com/tiddlers/concepts/TiddlerFields.tid +++ b/editions/tw5.com/tiddlers/concepts/TiddlerFields.tid @@ -24,7 +24,7 @@ The standard fields are: Other fields used by the core are: |!Field Name |!Description | -|`class` |<> | +|`class` |<<.from-version "5.1.16">> <> | |`code-body` |<<.from-version "5.2.1">> <> | |`color` |<> | |`description` |<> | diff --git a/editions/tw5.com/tiddlers/concepts/Title List.tid b/editions/tw5.com/tiddlers/concepts/Title List.tid index a49a665ad..67bb4e339 100644 --- a/editions/tw5.com/tiddlers/concepts/Title List.tid +++ b/editions/tw5.com/tiddlers/concepts/Title List.tid @@ -1,5 +1,5 @@ created: 20150117152418000 -modified: 20220523075540462 +modified: 20231019155036098 tags: Concepts title: Title List type: text/vnd.tiddlywiki @@ -15,3 +15,7 @@ Title lists are used in various places, including PermaLinks and the ListField. They are in fact the simplest case of a [[filter|Filters]], and are thus a way of expressing a [[selection of titles|Title Selection]]. <<.warning """The [[Title List]] format cannot reliably represent items that contain certain specific character sequences such as `]] `. Thus it should not be used where there is a possibility of such sequences occurring.""">> + +See also: + +* The [[format Operator]] with the 'titlelist' suffix conditionally wraps double square brackets around a string if it contains whitespace diff --git a/editions/tw5.com/tiddlers/filters/decodebase64 Operator.tid b/editions/tw5.com/tiddlers/filters/decodebase64 Operator.tid index 6760af8d3..0d3a61920 100644 --- a/editions/tw5.com/tiddlers/filters/decodebase64 Operator.tid +++ b/editions/tw5.com/tiddlers/filters/decodebase64 Operator.tid @@ -1,6 +1,7 @@ caption: decodebase64 op-input: a [[selection of titles|Title Selection]] op-output: the input with base 64 decoding applied +op-suffix: optional: `binary` to produce binary output, `urlsafe` for URL-safe input op-parameter: op-parameter-name: op-purpose: apply base 64 decoding to a string @@ -11,6 +12,10 @@ from-version: 5.2.6 See Mozilla Developer Network for details of [[base 64 encoding|https://developer.mozilla.org/en-US/docs/Glossary/Base64]]. TiddlyWiki uses [[library code from @nijikokun|https://gist.github.com/Nijikokun/5192472]] to handle the conversion. -The input strings must be base64 encoded. The output strings are binary data. +The input strings must be base64 encoded. The output strings are the text (or binary data) decoded from base64 format. + +The optional `binary` suffix, if present, changes how the input is processed. The input is normally assumed to be [[UTF-8|https://developer.mozilla.org/en-US/docs/Glossary/UTF-8]] text encoded in base64 form (such as what the <<.op "encodebase64">> operator produces), so only certain byte sequences in the input are valid. If the input is binary data encoded in base64 format (such as an image, audio file, video file, etc.), then use the optional `binary` suffix, which will allow all byte sequences. Note that the output will then be binary, ''not'' text, and should probably not be passed into further filter operators. + +The optional `urlsafe` suffix, if present, causes the decoder to assume that the base64 input uses `-` and `_` instead of `+` and `/` for the 62nd and 63rd characters of the base64 "alphabet", which is usually referred to as "URL-safe base64" or "bae64url". <<.operator-examples "decodebase64">> diff --git a/editions/tw5.com/tiddlers/filters/encodebase64 Operator.tid b/editions/tw5.com/tiddlers/filters/encodebase64 Operator.tid index ddbc73078..a7943d726 100644 --- a/editions/tw5.com/tiddlers/filters/encodebase64 Operator.tid +++ b/editions/tw5.com/tiddlers/filters/encodebase64 Operator.tid @@ -1,6 +1,7 @@ caption: encodebase64 op-input: a [[selection of titles|Title Selection]] op-output: the input with base 64 encoding applied +op-suffix: optional: `binary` to treat input as binary data, `urlsafe` for URL-safe output op-parameter: op-parameter-name: op-purpose: apply base 64 encoding to a string @@ -11,6 +12,10 @@ from-version: 5.2.6 See Mozilla Developer Network for details of [[base 64 encoding|https://developer.mozilla.org/en-US/docs/Glossary/Base64]]. TiddlyWiki uses [[library code from @nijikokun|https://gist.github.com/Nijikokun/5192472]] to handle the conversion. -The input strings are interpreted as binary data. The output strings are base64 encoded. +The input strings are interpreted as [[UTF-8 encoded|https://developer.mozilla.org/en-US/docs/Glossary/UTF-8]] text (or binary data instead if the `binary` suffix is present). The output strings are base64 encoded. + +The optional `binary` suffix, if present, causes the input string to be interpreted as binary data instead of text. Normally, an extra UTF-8 encoding step will be added before the base64 output is produced, so that emojis and other Unicode characters will be encoded correctly. If the input is binary data, such as an image, audio file, video, etc., then the UTF-8 encoding step would produce incorrect results, so using the `binary` suffix causes the UTF-8 encoding step to be skipped. + +The optional `urlsafe` suffix, if present, will use the alternate "URL-safe" base64 encoding, where `-` and `_` are used instead of `+` and `/` respectively, allowing the result to be used in URL query parameters or filenames. <<.operator-examples "encodebase64">> diff --git a/editions/tw5.com/tiddlers/filters/examples/jsonset.tid b/editions/tw5.com/tiddlers/filters/examples/jsonset.tid new file mode 100644 index 000000000..8cd1d1d61 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/jsonset.tid @@ -0,0 +1,59 @@ +created: 20231204112944341 +modified: 20231204115056732 +tags: [[Operator Examples]] [[jsonset Operator]] +title: jsonset Operator (Examples) + +<$let object-a="""{ + "a": "one", + "b": "", + "c": "three", + "d": { + "e": "four", + "f": [ + "five", + "six", + true, + false, + null + ], + "g": { + "x": "max", + "y": "may", + "z": "maize" + } + } +} +""" +object-b="""{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}}"""> + +The examples below assume the following JSON object is contained in the variable `object-a`: + +
    <>
    + +<<.operator-example 1 "[jsonset[d],[Jaguar]]">> +<<.operator-example 2 "[jsonset[d],[f],[Panther]]">> +<<.operator-example 3 "[jsonset[d],[f],[-1],[Elephant]]">> +<<.operator-example 4 "[jsonset[d],[f],[-2],[Elephant]]">> +<<.operator-example 5 "[jsonset[d],[f],[-4],[Elephant]]">> +<<.operator-example 6 "[jsonset[Panther]]" "If only a single parameter is specified, it replaces the entire JSON object">> +<<.operator-example 7 "[jsonset[]]" "If only a single blank parameter is specified, no changes are made to the JSON object">> + + +The examples below assume the following JSON object is contained in the variable `object-b`: + +
    <>
    + +<<.operator-example 8 "[jsonset[]]" "If only a single blank parameter is specified, no changes are made to the JSON object">> +<<.operator-example 9 "[jsonset[],[Antelope]]" "If the property to be set is blank, the entire JSON object is replaced">> +<<.operator-example 10 "[jsonset:number[],[not a number]]" "invalid numbers are interpreted as zero">> +<<.operator-example 11 "[jsonset[id],[Antelope]]" "nonexistent top level properties are added to the object">> +<<.operator-example 19 "[jsonset[missing],[id],[Antelope]]" "nonexistent nested properties are are ignored">> +<<.operator-example 12 "[jsonset:notatype[id],[Antelope]]" "invalid type suffix is interpreted as the default string type">> +<<.operator-example 13 "[jsonset:boolean[id],[false]]">> +<<.operator-example 14 "[jsonset:boolean[id],[Antelope]]" "invalid boolean value causes no assignment to be made">> +<<.operator-example 15 "[jsonset:number[id],[42]]">> +<<.operator-example 16 "[jsonset:null[id]]">> +<<.operator-example 17 "[jsonset:array[d],[f],[5]]">> +<<.operator-example 18 "[jsonset:object[d],[f],[5]]">> + +<<.operator-example 20 "[] [] :and[jsonset[b],[two]]" "If the input consists of multiple JSON objects with matching properties, the value is set for all of them">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/examples/jsonstringify Operator (Examples).tid b/editions/tw5.com/tiddlers/filters/examples/jsonstringify Operator (Examples).tid new file mode 100644 index 000000000..39113ea38 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/jsonstringify Operator (Examples).tid @@ -0,0 +1,11 @@ +created: 20230922121858167 +modified: 20230922122333325 +tags: [[Operator Examples]] [[jsonstringify Operator]] +title: jsonstringify Operator (Examples) +type: text/vnd.tiddlywiki + +Compare the encoding of quotes and control characters in the first example with the analogue [[example for the stringify operator|stringify Operator (Examples)]]. +<<.operator-example 1 """[[Backslash \, double quote ", single quote ', tab , line feed +]] +[jsonstringify[]]""">> +<<.operator-example 2 """[[Accents and emojis -> äñøßπ ⌛🎄🍪🍓 without suffix]] +[jsonstringify[]]""">> +<<.operator-example 3 """[[Accents and emojis -> äñøßπ ⌛🎄🍪🍓 with rawunicode suffix]] +[jsonstringify:rawunicode[]]""">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/examples/stringify_Operator_(Examples).tid b/editions/tw5.com/tiddlers/filters/examples/stringify_Operator_(Examples).tid index cc5a51429..4b67993b0 100644 --- a/editions/tw5.com/tiddlers/filters/examples/stringify_Operator_(Examples).tid +++ b/editions/tw5.com/tiddlers/filters/examples/stringify_Operator_(Examples).tid @@ -1,9 +1,11 @@ created: 20161017154944352 -modified: 20230919124059118 +modified: 20230922122319674 tags: [[Operator Examples]] [[stringify Operator]] title: stringify Operator (Examples) type: text/vnd.tiddlywiki -<<.operator-example 1 """[[Title with "double quotes" and single ' and \backslash]] +[stringify[]]""">> +Compare the encoding of quotes and control characters in the first example with the analogue [[example for the jsonstringify operator|jsonstringify Operator (Examples)]]. +<<.operator-example 1 """[[Backslash \, double quote ", single quote ', tab , line feed +]] +[stringify[]]""">> <<.operator-example 2 """[[Accents and emojis -> äñøßπ ⌛🎄🍪🍓 without suffix]] +[stringify[]]""">> <<.operator-example 3 """[[Accents and emojis -> äñøßπ ⌛🎄🍪🍓 with rawunicode suffix]] +[stringify:rawunicode[]]""">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/jsonextract.tid b/editions/tw5.com/tiddlers/filters/jsonextract.tid index 15517e110..27724205f 100644 --- a/editions/tw5.com/tiddlers/filters/jsonextract.tid +++ b/editions/tw5.com/tiddlers/filters/jsonextract.tid @@ -53,6 +53,14 @@ The <<.op jsonextract>> operator uses multiple operands to specify the indexes o [jsonextract[d],[g]] --> {"x":"max","y":"may","z":"maize"} ``` +<<.from-version "5.3.2">> Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on: + +``` +[jsonextract[d],[f],[-1]] --> null +[jsonextract[d],[f],[-2]] --> false +[jsonextract[d],[f],[-4]] --> "six" +``` + Indexes can be dynamically composed from variables and transclusions: ``` diff --git a/editions/tw5.com/tiddlers/filters/jsonget.tid b/editions/tw5.com/tiddlers/filters/jsonget.tid index d9caa680e..c50cbd6f2 100644 --- a/editions/tw5.com/tiddlers/filters/jsonget.tid +++ b/editions/tw5.com/tiddlers/filters/jsonget.tid @@ -51,6 +51,14 @@ The <<.op jsonget>> operator uses multiple operands to specify the indexes of th [jsonget[d],[f],[0]] --> "five" ``` +<<.from-version "5.3.2">> Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on: + +``` +[jsonget[d],[f],[-1]] --> null +[jsonget[d],[f],[-2]] --> false +[jsonget[d],[f],[-4]] --> "six" +``` + Indexes can be dynamically composed from variables and transclusions: ``` diff --git a/editions/tw5.com/tiddlers/filters/jsonset.tid b/editions/tw5.com/tiddlers/filters/jsonset.tid index 9f70f6eb4..1cfd076c2 100644 --- a/editions/tw5.com/tiddlers/filters/jsonset.tid +++ b/editions/tw5.com/tiddlers/filters/jsonset.tid @@ -1,22 +1,31 @@ +caption: jsonset created: 20230915121010948 -modified: 20230915121010948 +modified: 20231204115203428 +op-input: a selection of JSON objects +op-output: the JSON objects with the specified value assigned to the specified property +op-parameter: one or more indexes of the property to modify, if applicable followed by the value to be assigned +op-purpose: set the value of a property in JSON objects +op-suffix: data type of the value to be assigned to the property tags: [[Filter Operators]] [[JSON Operators]] title: jsonset Operator -caption: jsonset -op-purpose: set the value of a property in JSON strings -op-input: a selection of JSON strings -op-parameter: one or more indexes of the property to retrieve and sometimes a value to assign -op-output: the JSON strings with the specified property assigned -<<.from-version "5.3.2">> See [[JSON in TiddlyWiki]] for background. - -The <<.op jsonset>> operator is used to set a property value in JSON strings. See also the following related operators: +<<.from-version "5.3.2">> The <<.op jsonset>> operator is used to set a property value in JSON strings. See [[JSON in TiddlyWiki]] for background. See also the following related operators: * <<.olink jsonget>> to retrieve the values of a property in JSON data * <<.olink jsontype>> to retrieve the type of a JSON value * <<.olink jsonindexes>> to retrieve the names of the fields of a JSON object, or the indexes of a JSON array * <<.olink jsonextract>> to retrieve a JSON value as a string of JSON +The type of the value to be assigned to the property can be optionally specified with a suffix: + +* ''string'': default, the string is specified as the final operand +* ''boolean'': the boolean value is true if the final operand is the string "true" and false if the final operand is the string "false", any other value for the final string results prevents the property from being assigned +* ''number'': the numeric value is taken from the final operand, invalid numbers are interpreted as zero +* ''json'': the JSON string value is taken from the final operand, invalid JSON prevents the property from being assigned +* ''object'': an empty object is assigned to the property, the final operand is ignored +* ''array'': an empty array is assigned to the property, the final operand is ignored +* ''null'': the special value null is assigned to the property, the final operand is ignored + Properties within a JSON object are identified by a sequence of indexes. In the following example, the value at `[a]` is `one`, and the value at `[d][f][0]` is `five`. ``` @@ -42,55 +51,14 @@ Properties within a JSON object are identified by a sequence of indexes. In the } ``` -The following examples assume that this JSON data is contained in a variable called `jsondata`. +The <<.op jsonset>> operator uses multiple parameters to specify the indexes of the property to set. When used to assign strings (default behaviour if no suffix is specified) the final operand is interpreted as the value to assign. -The <<.op jsonset>> operator uses multiple operands to specify the indexes of the property to set. When used to assign strings the final operand is interpreted as the value to assign. For example: +Negative indexes are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on. -``` -[jsonset[d],[Jaguar]] --> {"a": "one","b": "","c": "three","d": "Jaguar"} -[jsonset[d],[f],[Panther]] --> {"a": "one","b": "","c": "three","d": "{"e": "four","f": "Panther","g": {"x": "max","y": "may","z": "maize"}}"} -``` +Indexes can be dynamically composed from variables and transclusions, e.g. `[jsonset,{!!field},[0],{CurrentResult}]`. -Indexes can be dynamically composed from variables and transclusions: +In the special case where only a single parameter is defined, the operator replaces the entire input object with the the value of that parameter. If the single parameter is blank, the operation is ignored and no assignment takes place. -``` -[jsonset,{!!field},[0],{CurrentResult}] -``` +If the input consists of multiple JSON objects with matching properties, the value is set for all of them. -The data type of the value to be assigned to the property can be specified with an optional suffix: - -|!Suffix |!Description | -|''string'' |The string is specified as the final operand | -|''boolean'' |The boolean value is true if the final operand is the string "true" and false if the final operand is the string "false". Any other value for the final string results prevents the property from being assigned | -|''number'' |The numeric value is taken from the final operand. Invalid numbers are interpreted as zero | -|''json'' |The JSON string value is taken from the final operand. Invalid JSON prevents the property from being assigned | -|''object'' |An empty object is assigned to the property. The final operand is not used as a value | -|''array'' |An empty array is assigned to the property. The final operand is not used as a value | -|''null'' |The special value null is assigned to the property. The final operand is not used as a value | - -For example: - -``` -Input string: -{"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}} - -[jsonset[]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}} -[jsonset[],[Antelope]] --> "Antelope" -[jsonset:number[],[not a number]] --> 0 -[jsonset[id],[Antelope]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":"Antelope"} -[jsonset:notatype[id],[Antelope]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":"Antelope"} -[jsonset:boolean[id],[false]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":false} -[jsonset:boolean[id],[Antelope]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}} -[jsonset:number[id],[42]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":42} -[jsonset:null[id]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]},"id":null} -[jsonset:array[d],[f],[5]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null,[]]}} -[jsonset:object[d],[f],[5]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null,{}]}} -[jsonset[missing],[id],[Antelope]] --> {"a":"one","b":"","c":1.618,"d":{"e":"four","f":["five","six",true,false,null]}} -``` - -A subtlety is that the special case of a single operand sets the value of that operand as the new JSON string, entirely replacing the input object. If that operand is blank, the operation is ignored and no assignment takes place. Thus: - -``` -[jsonset[Panther]] --> "Panther" -[jsonset[]] --> {"a": "one","b": "","c": "three","d": "{"e": "four","f": ["five", "six", true, false, null],"g": {"x": "max","y": "may","z": "maize"}}"} -``` +<<.operator-examples "jsonset">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/jsonstringify Operator.tid b/editions/tw5.com/tiddlers/filters/jsonstringify Operator.tid index 748a851bb..edd48432a 100644 --- a/editions/tw5.com/tiddlers/filters/jsonstringify Operator.tid +++ b/editions/tw5.com/tiddlers/filters/jsonstringify Operator.tid @@ -1,12 +1,35 @@ caption: jsonstringify created: 20171029155051467 from-version: 5.1.14 -modified: 20230919124826880 +modified: 20230922121404577 +op-input: a [[selection of titles|Title Selection]] +op-output: the input with JSON string encodings applied op-parameter: op-parameter-name: -op-purpose: deprecated, use <<.olink stringify>> instead +op-purpose: apply JSON string encoding to a string, see also the similar <<.olink stringify>> +op-suffix: <<.from-version "5.1.23">> optionally, the keyword `rawunicode` op-suffix-name: R tags: [[Filter Operators]] [[String Operators]] title: jsonstringify Operator type: text/vnd.tiddlywiki +The following substitutions are made: + +|!Character |!Replacement |!Condition | +|`\` |`\\` |Always | +|`"` |`\"` |Always | +|Carriage return (0x0d) |`\r` |Always | +|Line feed (0x0a) |`\n` |Always | +|Backspace (0x08) |`\b` |Always | +|Form field (0x0c) |`\f` |Always | +|Tab (0x09) |`\t` |Always| +|Characters from 0x00 to 0x1f, except listed above |`\u####` where #### is four hex digits |Always | +|Characters from from 0x80 to 0xffff|`\u####` where #### is four hex digits |If `rawunicode` suffix is not present (default) | +|Characters from 0x80 to 0xffff|<<.from-version "5.1.23">> Unchanged |If `rawunicode` suffix is present | + +<<.from-version "5.1.23">> If the suffix `rawunicode` is present, Unicode characters above 0x80 (such as ß, ä, ñ or 🎄) will be passed through unchanged. Without the suffix, they will be substituted with `\u` codes, which was the default behavior before 5.1.23. Characters outside the Basic Multilingual Plane, such as 🎄 and other emojis, will be encoded as a UTF-16 surrogate pair, i.e. with two `\u` sequences. + +<<.note """Mind the differences compared to <<.olink stringify>> in encoding of single quotes and control characters (0x00 to 0x1f). +""">> + +<<.operator-examples "jsonstringify">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/jsontype.tid b/editions/tw5.com/tiddlers/filters/jsontype.tid index b88f865dd..6bff01914 100644 --- a/editions/tw5.com/tiddlers/filters/jsontype.tid +++ b/editions/tw5.com/tiddlers/filters/jsontype.tid @@ -61,6 +61,14 @@ The <<.op jsontype>> operator uses multiple operands to specify the indexes of t [jsontype[d],[f],[2]] --> "boolean" ``` +<<.from-version "5.3.2">> Negative indexes into an array are counted from the end, so -1 means the last item, -2 the next-to-last item, and so on: + +``` +[jsontype[d],[f],[-1]] --> "null" +[jsontype[d],[f],[-2]] --> "boolean" +[jsontype[d],[f],[-4]] --> "string" +``` + Indexes can be dynamically composed from variables and transclusions: ``` diff --git a/editions/tw5.com/tiddlers/filters/stringify_Operator.tid b/editions/tw5.com/tiddlers/filters/stringify_Operator.tid index 73dabb1c2..6178517f7 100644 --- a/editions/tw5.com/tiddlers/filters/stringify_Operator.tid +++ b/editions/tw5.com/tiddlers/filters/stringify_Operator.tid @@ -1,12 +1,12 @@ caption: stringify created: 20161017153038029 from-version: 5.1.14 -modified: 20230919130847809 +modified: 20230922121406947 op-input: a [[selection of titles|Title Selection]] op-output: the input with ~JavaScript string encodings applied op-parameter: op-parameter-name: -op-purpose: apply ~JavaScript string encoding to a string +op-purpose: apply ~JavaScript string encoding to a string, see also the similar <<.olink jsonstringify>> op-suffix: <<.from-version "5.1.23">> optionally, the keyword `rawunicode` op-suffix-name: R tags: [[Filter Operators]] [[String Operators]] @@ -18,19 +18,16 @@ The following substitutions are made: |!Character |!Replacement |!Condition | |`\` |`\\` |Always | |`"` |`\"` |Always | -|Carriage return (0x0d) |`\r` |Always | +|`'` |`\'` |Always | |Line feed (0x0a) |`\n` |Always | -|Backspace (0x08) |`\b` |Always | -|Form field (0x0c) |`\f` |Always | -|Tab (0x09) |`\t` |Always | -|Characters from 0x00 to 0x1f |`\x##` where ## is two hex digits |Always | +|Carriage return (0x0d) |`\r` |Always | +|Characters from 0x00 to 0x1f, except listed above |`\x##` where ## is two hex digits |Always | |Characters from 0x80 to 0xffff|`\u####` where #### is four hex digits |If `rawunicode` suffix is not present (default) | |Characters from 0x80 to 0xffff|<<.from-version "5.1.23">> Unchanged |If `rawunicode` suffix is present | -<<.from-version "5.1.23">> If the suffix `rawunicode` is present, Unicode characters above 0x80 (such as ß, ä, ñ or 🎄) will be passed through unchanged. Without the suffix, they will be substituted with `\u` codes, which was the default behavior before 5.1.23. +<<.from-version "5.1.23">> If the suffix `rawunicode` is present, Unicode characters above 0x80 (such as ß, ä, ñ or 🎄) will be passed through unchanged. Without the suffix, they will be substituted with `\u` codes, which was the default behavior before 5.1.23. Characters outside the Basic Multilingual Plane, such as 🎄 and other emojis, will be encoded as a UTF-16 surrogate pair, i.e. with two `\u` sequences. -<<.note """Characters outside the Basic Multilingual Plane, such as 🎄 and other emojis, will be encoded as a UTF-16 surrogate pair, i.e. with two `\u` sequences.""">> - -<<.olink jsonstringify>> is considered deprecated, as it duplicates the functionality of <<.op stringify>>. +<<.note """Mind the differences compared to <<.olink jsonstringify>> in encoding of single quotes and control characters (0x00 to 0x1f). +""">> <<.operator-examples "stringify">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/hellothere/HelloThere.tid b/editions/tw5.com/tiddlers/hellothere/HelloThere.tid index f29cb780d..5f8d5fd34 100644 --- a/editions/tw5.com/tiddlers/hellothere/HelloThere.tid +++ b/editions/tw5.com/tiddlers/hellothere/HelloThere.tid @@ -1,6 +1,6 @@ created: 20130822170200000 list: [[A Gentle Guide to TiddlyWiki]] [[Discover TiddlyWiki]] [[Some of the things you can do with TiddlyWiki]] [[Ten reasons to switch to TiddlyWiki]] Examples [[What happened to the original TiddlyWiki?]] -modified: 20230820112855583 +modified: 20231223102201587 tags: TableOfContents title: HelloThere type: text/vnd.tiddlywiki @@ -35,7 +35,7 @@ TiddlyWiki lets you choose where to keep your data, guaranteeing that in the dec
    <$link to="中文社区 - Chinese Community" class="tc-btn-big-green tc-card"> -中文社区 - Chinese Community +中文社区
    Chinese Community
    diff --git a/editions/tw5.com/tiddlers/howtos/Constructing JSON tiddlers.tid b/editions/tw5.com/tiddlers/howtos/Constructing JSON tiddlers.tid index ff4c7927c..93d78ac16 100644 --- a/editions/tw5.com/tiddlers/howtos/Constructing JSON tiddlers.tid +++ b/editions/tw5.com/tiddlers/howtos/Constructing JSON tiddlers.tid @@ -1,5 +1,5 @@ created: 20220427174702859 -modified: 20230809113620964 +modified: 20230922122551197 tags: [[JSON in TiddlyWiki]] Learning title: Constructing JSON tiddlers @@ -13,4 +13,4 @@ At a high level, we have several ways to generate JSON data in TiddlyWiki's own * [[jsontiddler Macro]] * [[jsontiddlers Macro]] -When constructing JSON data manually, the [[stringify Operator]] is needed to ensure that any special characters are properly escaped. +When constructing JSON data manually, the [[jsonstringify Operator]] is needed to ensure that any special characters are properly escaped. diff --git a/editions/tw5.com/tiddlers/images/New Release Banner.png b/editions/tw5.com/tiddlers/images/New Release Banner.png index daa1db094..6577e8923 100644 Binary files a/editions/tw5.com/tiddlers/images/New Release Banner.png and b/editions/tw5.com/tiddlers/images/New Release Banner.png differ diff --git a/editions/tw5.com/tiddlers/macros/CoreMacros.tid b/editions/tw5.com/tiddlers/macros/CoreMacros.tid index 56d43bd2f..01879e3ac 100644 --- a/editions/tw5.com/tiddlers/macros/CoreMacros.tid +++ b/editions/tw5.com/tiddlers/macros/CoreMacros.tid @@ -5,4 +5,4 @@ type: text/vnd.tiddlywiki The following [[macros|Macros]] are built into ~TiddlyWiki's core: -<> +<> diff --git a/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-delete-tiddler.tid b/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-delete-tiddler.tid index 879faaa10..b3d95e7e5 100644 --- a/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-delete-tiddler.tid +++ b/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-delete-tiddler.tid @@ -11,4 +11,6 @@ The `tm-delete-tiddler` message deletes the specified tiddler and removes it fro |param |Title of the tiddler that is to be deleted | |tiddlerTitle |Fallback title that is used if ''param'' isn't specified (automatically set by the ButtonWidget) | -The delete tiddler message is usually generated with the ButtonWidget and is handled by the NavigatorWidget. \ No newline at end of file +The delete tiddler message is usually generated with the ButtonWidget and is handled by the NavigatorWidget. + +Use the [[ActionDeleteTiddlerWidget]] to delete a named tiddler without getting the "Do you wish to delete the tiddler" prompt. diff --git a/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-open-window.tid b/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-open-window.tid index d820aea01..77a072395 100644 --- a/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-open-window.tid +++ b/editions/tw5.com/tiddlers/messages/WidgetMessage_ tm-open-window.tid @@ -1,6 +1,6 @@ caption: tm-open-window created: 20160424181447704 -modified: 20220301162140993 +modified: 20230831201518773 tags: Messages title: WidgetMessage: tm-open-window type: text/vnd.tiddlywiki @@ -20,10 +20,17 @@ The `tm-open-window` [[message|Messages]] opens a tiddler in a new //browser// w The `tm-open-window` message is best generated with the ActionSendMessageWidget, which in turn is triggered by a widget such as the ButtonWidget. The message is handled by the core itself. -<<.tip """When used with the ActionSendMessageWidget, <<.param 'param'>> becomes <<.param '$param'>> """>> -<<.tip """Parameters <<.param template>>, <<.param windowTitle>>, <<.param width>>, <<.param height>>, <<.param left>> and <<.param top>> require the ActionSendMessageWidget.""">> -<<.tip """<<.from-version 5.2.2>> To close a window opened with tm-open-window use [[WidgetMessage: tm-close-window]]""">> -<<.tip """<<.from-version 5.2.2>> To open a tiddler in more than one new window, use a unique value for <<.param windowID>>""">> +<<.tip """When used with the ActionSendMessageWidget, <<.param 'param'>> becomes <<.param '$param'>>.
    +Parameters <<.param template>>, <<.param windowTitle>>, <<.param width>>, <<.param height>>, <<.param left>> and <<.param top>> require the ActionSendMessageWidget. """>> + +<<.tip """<<.from-version 5.2.2>> +To close a window opened with tm-open-window use [[WidgetMessage: tm-close-window]]
    +To open a tiddler in more than one new window, use a unique value for <<.param windowID>> +""">> + +<<.tip """<<.from-version 5.3.2>> +If the new window is hidden by other windows, clicking the "open" button again will bring it to the foreground and set focus to the new window. This behaviour should be consistent for all browsers now +""">> <$macrocall $name='wikitext-example-without-html' src=""" diff --git a/editions/tw5.com/tiddlers/pragmas/Pragma_ _define.tid b/editions/tw5.com/tiddlers/pragmas/Pragma_ _define.tid index 6058b7905..883f1e450 100644 --- a/editions/tw5.com/tiddlers/pragmas/Pragma_ _define.tid +++ b/editions/tw5.com/tiddlers/pragmas/Pragma_ _define.tid @@ -1,5 +1,5 @@ created: 20220917112233317 -modified: 20230419103154328 +modified: 20231217185535715 tags: Pragmas title: Pragma: \define type: text/vnd.tiddlywiki @@ -48,4 +48,6 @@ $caption$ <> """>> +<<.warning """If macros are nested, textual substitution will only occur for the outermost macro. Thi is because by the time the inner macros are processed all the substitutions will have already occurred""">> + A more formal [[presentation|Macro Definition Syntax]] of this syntax is also available. diff --git a/editions/tw5.com/tiddlers/releasenotes/BetaReleases.tid b/editions/tw5.com/tiddlers/releasenotes/BetaReleases.tid index c7c815ec9..aa16ab7b2 100644 --- a/editions/tw5.com/tiddlers/releasenotes/BetaReleases.tid +++ b/editions/tw5.com/tiddlers/releasenotes/BetaReleases.tid @@ -1,9 +1,9 @@ created: 20131109105400007 -modified: 20211117230125737 +modified: 20231220113054682 tags: Releases BetaReleaseNotes title: BetaReleases type: text/vnd.tiddlywiki Here are the details of the beta releases of TiddlyWiki5. See [[TiddlyWiki5 Versioning]] for details of how releases are named. -<> +<]" "Release 5.0.18-beta" "$:/state/tab2" "tc-vertical" "ReleaseTemplate">> diff --git a/editions/tw5.com/tiddlers/releasenotes/Release 5.3.2.tid b/editions/tw5.com/tiddlers/releasenotes/Release 5.3.2.tid new file mode 100644 index 000000000..e2f3637cb --- /dev/null +++ b/editions/tw5.com/tiddlers/releasenotes/Release 5.3.2.tid @@ -0,0 +1,184 @@ +caption: 5.3.2 +created: 20231213080637781 +modified: 20231213080637781 +released: 20231213080637781 +tags: ReleaseNotes +title: Release 5.3.2 +type: text/vnd.tiddlywiki +description: Under development + +//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.1...v5.3.2]]// + +<<.banner-credits + credit:"""Congratulations to [[catter-fly|https://talk.tiddlywiki.org/u/catter-fly]] for their winning design for the banner for this release (here is the [[competition thread|https://talk.tiddlywiki.org/t/banner-image-competition-for-v5-3-2/8569]]). +""" + url:"https://raw.githubusercontent.com/Jermolene/TiddlyWiki5/51862f812851afda0ed3540f8463f51def0d4f9a/editions/tw5.com/tiddlers/images/New%20Release%20Banner.png" +>> +! Major Improvements + +!! Conditional Shortcut Syntax + +<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7710">> a new [[shortcut syntax|Conditional Shortcut Syntax]] for concisely expressing if-then-else logic. This is the first of a new type of wikitext syntax based on tokens delimited with `<%` and `%>`. We plan to introduce other structures using the same format such as a "case" statement. + +These new token-based shortcuts allow a richer structure and expressivity than existing features such as widgets or pragmas. For example: + +``` +<% if [match[Elephant]] %> + It is an elephant +<% elseif [match[Giraffe]] %> + It is a giraffe +<% else %> + It is completely unknown +<% endif %> +``` + +Behind the scenes, the conditional shortcut syntax is rendered as the equivalent [[ListWidgets|ListWidget]]. + +!! Explicit Templates for the ListWidget + +<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7784">> support for `<$list-template>` and `<$list-empty>` as immediate children of the <<.wid "ListWidget">> widget to specify the list item template and/or the empty template. + +This new feature is designed to replace a common pattern of using the `emptyMessage` attribute of the ListWidget to render complex wikitext that thus has to be quoted. Working with wikitext within quotes is awkward and error prone. The new structure can be somewhat faster because it allows the empty message to be parsed in advanced of rendering. + +For example: + +``` +<$list filter=<>> + <$list-template> + <$text text=<>/> + + <$list-empty> + None! + + +``` + +Note that the <<.attr "emptyMessage">> and <<.attr "template">> attributes take precedence if they are present. + +!! Joiners for the ListWidget + +<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7694">> a <<.attr "join">> attribute to the <<.wid "ListWidget">> widget to insert a short piece of text between list items. This is both easier to use and faster than using the <<.attr "counter">> attribute for the same purpose. So if your list looked like this: + +``` +<$list filter=<> counter="counter" variable="item"> +<$text text=<>/><$list filter="[match[no]]" variable="ignore"><$text text=", "/> + +``` + +You can replace it with: + +``` +<$list filter=<> variable="item" join=", "><$text text=<>/> +``` + +If the joiner text that you need is long and awkward to write in an attribute, you can use the new `<$list-join>` widget. Like `<$list-template>` and `<$list-empty>`, it must be an immediate child of the <<.wid "ListWidget">>: + +``` +<$list filter=<> variable="item"><$text text=<>/><$list-join>, and also let's not forget +``` + +!! jsonset operator + +<<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7742">> [[jsonset Operator]] for setting values within JSON objects + +!! QR Code Reader + +<<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/7746">> QR Code plugin to be able to read QR codes and a number of other bar code formats + +! Translation improvements + +Improvements to the following translations: + +* Chinese +* Polish +* Spanish + +! Plugin Improvements + +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/1be8f0a9336952d4745d2bd4f2327e353580a272">> Comments Plugin to use predefined palette colours +* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7785">> Evernote Importer Plugin to support images and other attachments +* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7790">> `$floating` attribute to Dynannotate Plugin to support popups that do not disappear when another part of the screen is clicked. Instead they have to dismissed manually + +! Widget Improvements + +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7734">> ImageWidget encoding for more image types +* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/7634">> ImageWidget to add a "usemap" attribute +* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7649">> the ScrollableWidget to allow the scroll position to be bound to a tiddler, so that changes to the tiddler affect the scroll position, and vice versa + +! Usability Improvements + +* <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/pull/7747">> editor preview button to automatically focus the editor +* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7764">> file type names in the export menu + +! Hackability Improvements + +* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/7769">> all the relevant core widgets to allow arbitrary `data-*` attributes and `style.*` attributes to be applied to the generated DOM nodes. This is useful for passing data to the EventCatcherWidget +* <<.link-badge-extended "https://github.com/Jermolene/TiddlyWiki5/pull/7849">> [[jsonextract Operator]], [[jsonget Operator]], [[jsonset Operator]] and [[jsontype Operator]] to allow negative indexes into arrays to be counted from the end of the array +* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7690">> the default page layout to better support CSS grid and flexbox layouts +* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7787">> the editor to use grid layout, simplifying customisation + +! Bug Fixes + +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7758">> ordering of Vanilla stylesheets +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/commit/fa9bfa07a095548eb2f8339b0b1b816d2e6794ef">> missing closing tag in tag-pill-inner macro +* <<.link-badge-removed "https://github.com/Jermolene/TiddlyWiki5/issues/7732">> invalid "type" attribute from textarea elements generated by the EditTextWidget +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7749">> editor "type" dropdown state tiddlers +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7712">> handling of "counter-last" variable when appending items with the ListWidget +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/6088">> upgrade download link in Firefox +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7698">> refreshing of transcluded functions +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7789">> resizing of height of textareas in control panel +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7683">> [[encodebase64 Operator]] and [[decodebase64 Operator]] to work properly with binary data +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7708">> [[WidgetMessage: tm-open-window]] when opening an existing window to bring it to the front and focus it +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7809">> behaviour of [[last Operator]] when zero items selected +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7806">> incorrectly setting focus on field name input field when deleting field using the delete field button +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7802">> [[Table-of-Contents Macros]] to not show expander icon for a sublist that has all children excluded +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7794">> overflow of [[CodeMirror Plugin]] editor within grid container +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7835">> wikitest parser removing whitespace when parsing pragmas +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7842">> tooltip for editor add field button +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7844">> plain text parser being susceptible to the CodeBlockWidget being redefined +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7855">> pragmas not working within the action string of several core macros + +! Node.js Improvements + +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7843">> a significant flaw in the synchronisation algorithm used by the client-server configuration. The flaw could lead to tiddlers temporarily disappearing from the browser + +! Performance Improvements + +* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7702">> performance of predefined patterns with [[all Operator]] +* <<.link-badge-updated "https://github.com/Jermolene/TiddlyWiki5/issues/7671">> favicon format to PNG + +! Developer Improvements + +* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7751">> global hook handling to support removing hooks +* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7539">> some useful npm scripts to `package.json` + +! Infrastructure Improvements + +* <<.link-badge-improved "https://github.com/Jermolene/TiddlyWiki5/pull/7820">> Continuous Integration tests to use Playwright to run our browser-based tests +* <<.link-badge-added "https://github.com/Jermolene/TiddlyWiki5/pull/7737">> an automatic build of the external core TiddlyWiki at https://tiddlywiki.com/empty-external-core.html + +! Acknowledgements + +[[@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 """ +AnthonyMuscio +BramChen +BuckarooBanzay +BurningTreeC +CrossEye +EvidentlyCube +Gk0Wk +joebordes +kookma +linonetwo +mateuszwilczek +oflg +pille1842 +pmario +rmunn +saqimtiaz +simonbaird +T1mL3arn +yaisog +""">> diff --git a/editions/tw5.com/tiddlers/releasenotes/Release 5.3.3.tid b/editions/tw5.com/tiddlers/releasenotes/Release 5.3.3.tid new file mode 100644 index 000000000..b6d1cc451 --- /dev/null +++ b/editions/tw5.com/tiddlers/releasenotes/Release 5.3.3.tid @@ -0,0 +1,30 @@ +caption: 5.3.3 +created: 20231223102201587 +modified: 20231223102201587 +released: 20231223102201587 +tags: ReleaseNotes +title: Release 5.3.3 +type: text/vnd.tiddlywiki +description: Under development + +//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.2...v5.3.3]]// + +<<.banner-credits + credit:"""Congratulations to [[catter-fly|https://talk.tiddlywiki.org/u/catter-fly]] for their winning design for the banner for this release (here is the [[competition thread|https://talk.tiddlywiki.org/t/banner-image-competition-for-v5-3-2/8569]]). +""" + url:"https://raw.githubusercontent.com/Jermolene/TiddlyWiki5/5cb31b7adb0a6b226c0c215ddbed62e297ce89e1/editions/tw5.com/tiddlers/images/New%20Release%20Banner.png" +>> + +This is a bug fix release to address a number of bugs that were introduced with [[Release 5.3.2]]. + +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7903">> handling of a list widget with an empty paragraph as inline template +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7900">> broken per-tiddler previews +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7897">> missing comma before skinny tiddlers in JSON store area +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7895">> handling of whitespace immediately after pragmas +* <<.link-badge-fixed "https://github.com/Jermolene/TiddlyWiki5/pull/7905">> SelectWidget handling of classes and rendering typo + +Since v5.3.3 replaces v5.3.2 after only a couple of weeks, here is the release note for v5.3.2. + +! Release Note for v5.3.2 + +{{Release 5.3.2}} \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/releasenotes/alpha/AlphaReleases.tid b/editions/tw5.com/tiddlers/releasenotes/alpha/AlphaReleases.tid index 4e2a6a451..0797ad7e0 100644 --- a/editions/tw5.com/tiddlers/releasenotes/alpha/AlphaReleases.tid +++ b/editions/tw5.com/tiddlers/releasenotes/alpha/AlphaReleases.tid @@ -1,9 +1,9 @@ created: 20131109105400007 -modified: 20211117225858830 +modified: 20231220113044942 tags: Releases AlphaReleaseNotes title: AlphaReleases type: text/vnd.tiddlywiki Here are the details of the alpha releases of TiddlyWiki5. See [[TiddlyWiki5 Versioning]] for details of how releases are named. -<> +<]" "Release 5.0.1-alpha" "$:/state/tab2" "tc-vertical" "ReleaseTemplate">> diff --git a/editions/tw5.com/tiddlers/system/tw5.com-styles.tid b/editions/tw5.com/tiddlers/system/tw5.com-styles.tid index a0c9083af..dd60694f4 100644 --- a/editions/tw5.com/tiddlers/system/tw5.com-styles.tid +++ b/editions/tw5.com/tiddlers/system/tw5.com-styles.tid @@ -148,6 +148,7 @@ type: text/vnd.tiddlywiki } .tc-cards.tc-small { + text-align: center; font-size: 0.7em; } diff --git a/editions/tw5.com/tiddlers/widgets/BrowseWidget.tid b/editions/tw5.com/tiddlers/widgets/BrowseWidget.tid index 28012bd68..b0364a71a 100644 --- a/editions/tw5.com/tiddlers/widgets/BrowseWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/BrowseWidget.tid @@ -1,6 +1,6 @@ caption: browse created: 20131024141900000 -modified: 20200421221304177 +modified: 20231113093304323 tags: Widgets title: BrowseWidget type: text/vnd.tiddlywiki @@ -20,6 +20,8 @@ The content of the <<.wid BrowseWidget>> widget is ignored. |accept |<<.from-version "5.1.23">> Optional comma delimited [[list of file accepted extensions|https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#Unique_file_type_specifiers]] and/or MIME types | |message |Optional override of widget message to be generated. The files will be passed in the JavaScript object `event.target.files` | |param |Optional parameter to be passed with the custom message | +|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML element | +|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML element | On iPhone/iPad choosing the multiple option will remove the ability to take photographs/videos directly into TiddlyWiki. diff --git a/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid b/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid index da61838af..d74c09575 100644 --- a/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/ButtonWidget.tid @@ -1,6 +1,6 @@ caption: button created: 20131024141900000 -modified: 20220810192251345 +modified: 20231113093304323 tags: Widgets TriggeringWidgets title: ButtonWidget type: text/vnd.tiddlywiki @@ -41,6 +41,8 @@ The content of the `<$button>` widget is displayed within the button. |aria-label |Optional [[Accessibility]] label | |tooltip |Optional tooltip | |class |An optional CSS class name to be assigned to the HTML element| +|data-* |<<.from-version "5.3.2">> Optional [[data attributes|https://developer.mozilla.org/en-US/docs/Learn/HTML/Howto/Use_data_attributes]] to be assigned to the HTML element | +|style.* |<<.from-version "5.3.2">> Optional [[CSS properties|https://developer.mozilla.org/en-US/docs/Web/CSS/Reference]] to be assigned to the HTML element | |style |An optional CSS style attribute to be assigned to the HTML element | |tag |An optional html tag to use instead of the default "button" | |dragTiddler |An optional tiddler title making the button draggable and identifying the payload tiddler. See DraggableWidget for details | diff --git a/editions/tw5.com/tiddlers/widgets/CheckboxWidget.tid b/editions/tw5.com/tiddlers/widgets/CheckboxWidget.tid index 47e83e875..00ecbb6f8 100644 --- a/editions/tw5.com/tiddlers/widgets/CheckboxWidget.tid +++ b/editions/tw5.com/tiddlers/widgets/CheckboxWidget.tid @@ -3,7 +3,7 @@ colors: red orange yellow blue created: 20131024141900000 fruits: bananas oranges grapes list: [[CheckboxWidget (tag Mode)]] [[CheckboxWidget (field Mode)]] [[CheckboxWidget (listField Mode)]] [[CheckboxWidget (index Mode)]] [[CheckboxWidget (listIndex Mode)]] [[CheckboxWidget (filter Mode)]] [[CheckboxWidget (indeterminate)]] -modified: 20230316192632667 +modified: 20231113093304323 tags: Widgets TriggeringWidgets title: CheckboxWidget type: text/vnd.tiddlywiki @@ -38,5 +38,7 @@ The content of the `<$checkbox>` widget is displayed within an HTML `