diff --git a/bin/build-site.sh b/bin/build-site.sh index a9133c2b7..0618e1bb1 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.0 + TW5_BUILD_VERSION=v5.3.1 fi echo "Using TW5_BUILD_VERSION as [$TW5_BUILD_VERSION]" @@ -107,7 +107,7 @@ node $TW5_BUILD_TIDDLYWIKI \ # /empty.html Empty # /empty.hta For Internet Explorer node $TW5_BUILD_TIDDLYWIKI \ - $TW5_BUILD_MAIN_EDITION \ + ./editions/empty \ --verbose \ --output $TW5_BUILD_OUTPUT \ --build empty \ diff --git a/boot/boot.js b/boot/boot.js index a9fb91b74..0aa089aa0 100644 --- a/boot/boot.js +++ b/boot/boot.js @@ -1150,7 +1150,7 @@ $tw.Wiki = function(options) { shadowTiddlerTitles = null, getShadowTiddlerTitles = function() { if(!shadowTiddlerTitles) { - shadowTiddlerTitles = Object.keys(shadowTiddlers); + shadowTiddlerTitles = Object.keys(shadowTiddlers).sort(function(a,b) {return a.localeCompare(b);}); } return shadowTiddlerTitles; }, @@ -2080,7 +2080,11 @@ $tw.loadPluginFolder = function(filepath,excludeRegExp) { console.log("Warning: missing plugin.info file in " + filepath); return null; } - var pluginInfo = $tw.utils.parseJSONSafe(fs.readFileSync(infoPath,"utf8")); + var pluginInfo = $tw.utils.parseJSONSafe(fs.readFileSync(infoPath,"utf8"),function() {return null;}); + if(!pluginInfo) { + console.log("warning: invalid JSON in plugin.info file at " + infoPath); + pluginInfo = {}; + } // Read the plugin files var pluginFiles = $tw.loadTiddlersFromPath(filepath,excludeRegExp); // Save the plugin tiddlers into the plugin info @@ -2197,7 +2201,11 @@ $tw.loadWikiTiddlers = function(wikiPath,options) { pluginFields; // Bail if we don't have a wiki info file if(fs.existsSync(wikiInfoPath)) { - wikiInfo = $tw.utils.parseJSONSafe(fs.readFileSync(wikiInfoPath,"utf8")); + wikiInfo = $tw.utils.parseJSONSafe(fs.readFileSync(wikiInfoPath,"utf8"),function() {return null;}); + if(!wikiInfo) { + console.log("warning: invalid JSON in tiddlywiki.info file at " + wikiInfoPath); + wikiInfo = {}; + } } else { return null; } diff --git a/core/acknowledgements.tid b/core/acknowledgements.tid index e7acee129..cb54e3d23 100644 --- a/core/acknowledgements.tid +++ b/core/acknowledgements.tid @@ -3,7 +3,7 @@ title: $:/Acknowledgements TiddlyWiki incorporates code from these fine OpenSource projects: * [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]] -* [[The Jasmine JavaScript Test Framework|http://pivotal.github.io/jasmine/]] +* [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]] * [[Normalize.css by Nicolas Gallagher|http://necolas.github.io/normalize.css/]] And media from these projects: diff --git a/core/images/network-activity.tid b/core/images/network-activity.tid new file mode 100644 index 000000000..2efdfd4d4 --- /dev/null +++ b/core/images/network-activity.tid @@ -0,0 +1,11 @@ +title: $:/core/images/network-activity +tags: $:/tags/Image + + +<$list filter="[{$:/state/http-requests}match[0]]" variable="ignore"> + + +<$list filter="[{$:/state/http-requests}!match[0]]" variable="ignore"> + + + \ No newline at end of file diff --git a/core/images/new-journal-button.tid b/core/images/new-journal-button.tid index 3b04d5786..5b793deb5 100755 --- a/core/images/new-journal-button.tid +++ b/core/images/new-journal-button.tid @@ -1,6 +1,4 @@ title: $:/core/images/new-journal-button tags: $:/tags/Image -<$parameters size="22pt" day=<>> -> height=<> class="tc-image-new-journal-button tc-image-button" viewBox="0 0 128 128"><$text text=<>/> - \ No newline at end of file +<$parameters size="22pt" day=<>>> height=<> class="tc-image-new-journal-button tc-image-button" viewBox="0 0 128 128"><$text text=<>/> \ No newline at end of file diff --git a/core/language/en-GB/Buttons.multids b/core/language/en-GB/Buttons.multids index 85a71ac08..fa769d117 100644 --- a/core/language/en-GB/Buttons.multids +++ b/core/language/en-GB/Buttons.multids @@ -67,6 +67,8 @@ More/Caption: more More/Hint: More actions NewHere/Caption: new here NewHere/Hint: Create a new tiddler tagged with this one +NetworkActivity/Caption: network activity +NetworkActivity/Hint: Cancel all network activity NewJournal/Caption: new journal NewJournal/Hint: Create a new journal tiddler NewJournalHere/Caption: new journal here diff --git a/core/language/en-GB/Help/savewikifolder.tid b/core/language/en-GB/Help/savewikifolder.tid index bda1d19a3..5c6405ad2 100644 --- a/core/language/en-GB/Help/savewikifolder.tid +++ b/core/language/en-GB/Help/savewikifolder.tid @@ -4,7 +4,7 @@ description: Saves a wiki to a new wiki folder <<.from-version "5.1.20">> Saves the current wiki as a wiki folder, including tiddlers, plugins and configuration: ``` ---savewikifolder [] +--savewikifolder [] [ [=] ]* ``` * The target wiki folder must be empty or non-existent @@ -12,8 +12,23 @@ description: Saves a wiki to a new wiki folder * Plugins from the official plugin library are replaced with references to those plugins in the `tiddlywiki.info` file * Custom plugins are unpacked into their own folder +The following options are supported: + +* ''filter'': a filter expression that defines the tiddlers to include in the output. +* ''explodePlugins'': defaults to "yes" +** ''yes'' will "explode" plugins into separate tiddler files and save them to the plugin directory within the wiki folder +** ''no'' will suppress exploding plugins into their constituent tiddler files. It will save the plugin as a single JSON tiddler in the tiddlers folder + +Note that both ''explodePlugins'' options will produce wiki folders that build the same exact same original wiki. The difference lies in how plugins are represented in the wiki folder. + A common usage is to convert a TiddlyWiki HTML file into a wiki folder: ``` tiddlywiki --load ./mywiki.html --savewikifolder ./mywikifolder ``` + +Save the plugin to the tiddlers directory of the target wiki folder: + +``` +tiddlywiki --load ./mywiki.html --savewikifolder ./mywikifolder explodePlugins=no +``` \ No newline at end of file diff --git a/core/language/en-GB/Misc.multids b/core/language/en-GB/Misc.multids index 52545bd86..2c10d1acb 100644 --- a/core/language/en-GB/Misc.multids +++ b/core/language/en-GB/Misc.multids @@ -25,6 +25,8 @@ Encryption/RepeatPassword: Repeat password Encryption/PasswordNoMatch: Passwords do not match Encryption/SetPassword: Set password Error/Caption: Error +Error/DeserializeOperator/MissingOperand: Filter Error: Missing operand for 'deserialize' operator +Error/DeserializeOperator/UnknownDeserializer: Filter Error: Unknown deserializer provided as operand for the 'deserialize' operator Error/Filter: Filter error Error/FilterSyntax: Syntax error in filter expression Error/FilterRunPrefix: Filter Error: Unknown prefix for filter run diff --git a/core/language/en-GB/SiteTitle.tid b/core/language/en-GB/SiteTitle.tid index 9f522664a..a32da7dfe 100644 --- a/core/language/en-GB/SiteTitle.tid +++ b/core/language/en-GB/SiteTitle.tid @@ -1,3 +1,3 @@ title: $:/SiteTitle -My ~TiddlyWiki \ No newline at end of file +My TiddlyWiki \ No newline at end of file diff --git a/core/modules/commands/savewikifolder.js b/core/modules/commands/savewikifolder.js index 48e9be56a..c0fccd775 100644 --- a/core/modules/commands/savewikifolder.js +++ b/core/modules/commands/savewikifolder.js @@ -5,7 +5,14 @@ module-type: command Command to save the current wiki as a wiki folder ---savewikifolder [] +--savewikifolder [ [=] ]* + +The following options are supported: + +* ''filter'': a filter expression defining the tiddlers to be included in the output +* ''explodePlugins'': set to "no" to suppress exploding plugins into their constituent shadow tiddlers (defaults to "yes") + +Supports backward compatibility with --savewikifolder [] [ [=] ]* \*/ (function(){ @@ -35,14 +42,28 @@ Command.prototype.execute = function() { if(this.params.length < 1) { return "Missing wiki folder path"; } - var wikifoldermaker = new WikiFolderMaker(this.params[0],this.params[1],this.commander); + var regFilter = /^[a-zA-Z0-9\.\-_]+=/g, // dynamic parameters + namedParames, + tiddlerFilter, + options = {}; + if (regFilter.test(this.params[1])) { + namedParames = this.commander.extractNamedParameters(this.params.slice(1)); + tiddlerFilter = namedParames.filter || "[all[tiddlers]]"; + } else { + namedParames = this.commander.extractNamedParameters(this.params.slice(2)); + tiddlerFilter = this.params[1]; + } + tiddlerFilter = tiddlerFilter || "[all[tiddlers]]"; + options.explodePlugins = namedParames.explodePlugins || "yes"; + var wikifoldermaker = new WikiFolderMaker(this.params[0],tiddlerFilter,this.commander,options); return wikifoldermaker.save(); }; -function WikiFolderMaker(wikiFolderPath,wikiFilter,commander) { +function WikiFolderMaker(wikiFolderPath,wikiFilter,commander,options) { this.wikiFolderPath = wikiFolderPath; - this.wikiFilter = wikiFilter || "[all[tiddlers]]"; + this.wikiFilter = wikiFilter; this.commander = commander; + this.explodePlugins = options.explodePlugins; this.wiki = commander.wiki; this.savedPaths = []; // So that we can detect filename clashes } @@ -93,10 +114,13 @@ WikiFolderMaker.prototype.save = function() { self.log("Adding built-in plugin: " + libraryDetails.name); newWikiInfo[libraryDetails.type] = newWikiInfo[libraryDetails.type] || []; $tw.utils.pushTop(newWikiInfo[libraryDetails.type],libraryDetails.name); - } else { + } else if(self.explodePlugins !== "no") { // A custom plugin self.log("Processing custom plugin: " + title); self.saveCustomPlugin(tiddler); + } else if(self.explodePlugins === "no") { + self.log("Processing custom plugin to tiddlders folder: " + title); + self.saveTiddler("tiddlers", tiddler); } } else { // Ordinary tiddler diff --git a/core/modules/filterrunprefixes/then.js b/core/modules/filterrunprefixes/then.js new file mode 100644 index 000000000..9f6d5c76a --- /dev/null +++ b/core/modules/filterrunprefixes/then.js @@ -0,0 +1,32 @@ +/*\ +title: $:/core/modules/filterrunprefixes/then.js +type: application/javascript +module-type: filterrunprefix + +Replace results of previous runs unless empty + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter prefix function +*/ +exports.then = function(operationSubFunction) { + return function(results,source,widget) { + if(results.length !== 0) { + // Only run if previous run(s) produced results + var thisRunResult = operationSubFunction(source,widget); + if(thisRunResult.length !== 0) { + // Replace results only if this run actually produces a result + results.clear(); + results.pushTop(thisRunResult); + } + } + }; +}; + +})(); diff --git a/core/modules/filters.js b/core/modules/filters.js index b705c994c..aa82a352a 100644 --- a/core/modules/filters.js +++ b/core/modules/filters.js @@ -12,6 +12,8 @@ Adds tiddler filtering methods to the $tw.Wiki object. /*global $tw: false */ "use strict"; +var widgetClass = require("$:/core/modules/widgets/widget.js").widget; + /* Maximum permitted filter recursion depth */ var MAX_FILTER_DEPTH = 300; @@ -269,7 +271,7 @@ exports.compileFilter = function(filterString) { operand.value = self.getTextReference(operand.text,"",currTiddlerTitle); } else if(operand.variable) { var varTree = $tw.utils.parseFilterVariable(operand.text); - operand.value = widget.evaluateVariable(varTree.name,{params: varTree.params, source: source})[0] || ""; + operand.value = widgetClass.evaluateVariable(widget,varTree.name,{params: varTree.params, source: source})[0] || ""; } else { operand.value = operand.text; } diff --git a/core/modules/filters/deserialize.js b/core/modules/filters/deserialize.js new file mode 100644 index 000000000..5511f29e8 --- /dev/null +++ b/core/modules/filters/deserialize.js @@ -0,0 +1,39 @@ +/*\ +title: $:/core/modules/filters/deserialize.js +type: application/javascript +module-type: filteroperator +Filter operator for deserializing string data into JSON representing tiddlers +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports["deserialize"] = function(source,operator,options) { + var results = [], + deserializer; + if(operator.operand) { + // Get the deserializer identified by the operand + deserializer = $tw.Wiki.tiddlerDeserializerModules[operator.operand]; + if(deserializer) { + source(function(tiddler,title) { + var tiddlers; + try { + tiddlers = deserializer(title); + } catch(e) { + // Return an empty array if we could not extract any tiddlers + tiddlers = []; + } + results.push(JSON.stringify(tiddlers)); + }); + } else { + return [$tw.language.getString("Error/DeserializeOperator/UnknownDeserializer")]; + } + } else { + return [$tw.language.getString("Error/DeserializeOperator/MissingOperand")]; + } + return results; +} + +})(); \ No newline at end of file diff --git a/core/modules/filters/function.js b/core/modules/filters/function.js index f6a8c034d..79210fb78 100644 --- a/core/modules/filters/function.js +++ b/core/modules/filters/function.js @@ -17,9 +17,13 @@ Export our filter function */ exports.function = function(source,operator,options) { var functionName = operator.operands[0], - variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName); + params = []; + $tw.utils.each(operator.operands.slice(1),function(param) { + params.push({value: param}); + }); + var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(functionName,{params: params, source: source}); if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { - return options.widget.evaluateVariable(functionName,{params: operator.operands.slice(1), source: source}); + return variableInfo.resultList ? variableInfo.resultList : [variableInfo.text]; } // Return the input list if the function wasn't found var results = []; diff --git a/core/modules/filters/substitute.js b/core/modules/filters/substitute.js new file mode 100644 index 000000000..655ef7321 --- /dev/null +++ b/core/modules/filters/substitute.js @@ -0,0 +1,36 @@ +/*\ +title: $:/core/modules/filters/substitute.js +type: application/javascript +module-type: filteroperator + +Filter operator for substituting variables and embedded filter expressions with their corresponding values + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.substitute = function(source,operator,options) { + var results = [], + operands = []; + $tw.utils.each(operator.operands,function(operand,index){ + operands.push({ + name: (index + 1).toString(), + value: operand + }); + }); + source(function(tiddler,title) { + if(title) { + results.push(options.wiki.getSubstitutedText(title,options.widget,{substitutions:operands})); + } + }); + return results; +}; + +})(); + \ No newline at end of file diff --git a/core/modules/filters/unknown.js b/core/modules/filters/unknown.js index 1cc5d16e2..6ae5baaf0 100644 --- a/core/modules/filters/unknown.js +++ b/core/modules/filters/unknown.js @@ -22,9 +22,13 @@ Export our filter function exports["[unknown]"] = function(source,operator,options) { // Check for a user defined filter operator if(operator.operator.indexOf(".") !== -1) { - var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator); - if(variableInfo && variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { - var list = options.widget.evaluateVariable(operator.operator,{params: operator.operands, source: source}); + var params = []; + $tw.utils.each(operator.operands,function(param) { + params.push({value: param}); + }); + var variableInfo = options.widget && options.widget.getVariableInfo && options.widget.getVariableInfo(operator.operator,{params: params, source: source}); + if(variableInfo && variableInfo.srcVariable) { + var list = variableInfo.resultList ? variableInfo.resultList : [variableInfo.text]; if(operator.prefix === "!") { var results = []; source(function(tiddler,title) { diff --git a/core/modules/parsers/parseutils.js b/core/modules/parsers/parseutils.js index 6a0902c6f..1f86dd909 100644 --- a/core/modules/parsers/parseutils.js +++ b/core/modules/parsers/parseutils.js @@ -305,10 +305,11 @@ exports.parseAttribute = function(source,pos) { start: pos }; // Define our regexps - var reAttributeName = /([^\/\s>"'=]+)/g, - reUnquotedAttribute = /([^\/\s<>"'=]+)/g, + var reAttributeName = /([^\/\s>"'`=]+)/g, + reUnquotedAttribute = /([^\/\s<>"'`=]+)/g, reFilteredValue = /\{\{\{([\S\s]+?)\}\}\}/g, - reIndirectValue = /\{\{([^\}]+)\}\}/g; + reIndirectValue = /\{\{([^\}]+)\}\}/g, + reSubstitutedValue = /(?:```([\s\S]*?)```|`([^`]|[\S\s]*?)`)/g; // Skip whitespace pos = $tw.utils.skipWhiteSpace(source,pos); // Get the attribute name @@ -361,8 +362,15 @@ exports.parseAttribute = function(source,pos) { node.type = "macro"; node.value = macroInvocation; } else { - node.type = "string"; - node.value = "true"; + var substitutedValue = $tw.utils.parseTokenRegExp(source,pos,reSubstitutedValue); + if(substitutedValue) { + pos = substitutedValue.end; + node.type = "substituted"; + node.rawValue = substitutedValue.match[1] || substitutedValue.match[2]; + } else { + node.type = "string"; + node.value = "true"; + } } } } diff --git a/core/modules/parsers/wikiparser/rules/wikilinkprefix.js b/core/modules/parsers/wikiparser/rules/wikilinkprefix.js new file mode 100644 index 000000000..60cb3d992 --- /dev/null +++ b/core/modules/parsers/wikiparser/rules/wikilinkprefix.js @@ -0,0 +1,40 @@ +/*\ +title: $:/core/modules/parsers/wikiparser/rules/wikilinkprefix.js +type: application/javascript +module-type: wikirule + +Wiki text inline rule for suppressed wiki links. For example: + +``` +~SuppressedLink +``` + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +exports.name = "wikilinkprefix"; +exports.types = {inline: true}; + +exports.init = function(parser) { + this.parser = parser; + // Regexp to match + this.matchRegExp = new RegExp($tw.config.textPrimitives.unWikiLink + $tw.config.textPrimitives.wikiLink,"mg"); +}; + +/* +Parse the most recent match +*/ +exports.parse = function() { + // Get the details of the match + var linkText = this.match[0]; + // Move past the wikilink + this.parser.pos = this.matchRegExp.lastIndex; + // Return the link without unwikilink character as plain text + return [{type: "text", text: linkText.substr(1)}]; +}; + +})(); diff --git a/core/modules/startup/plugins.js b/core/modules/startup/plugins.js index cad61b104..ab74214b9 100644 --- a/core/modules/startup/plugins.js +++ b/core/modules/startup/plugins.js @@ -15,6 +15,7 @@ Startup logic concerned with managing plugins // Export name and synchronous status exports.name = "plugins"; exports.after = ["load-modules"]; +exports.before = ["startup"]; exports.synchronous = true; var TITLE_REQUIRE_RELOAD_DUE_TO_PLUGIN_CHANGE = "$:/status/RequireReloadDueToPluginChange"; diff --git a/core/modules/startup/rootwidget.js b/core/modules/startup/rootwidget.js index 1175f6f25..716275cda 100644 --- a/core/modules/startup/rootwidget.js +++ b/core/modules/startup/rootwidget.js @@ -20,6 +20,39 @@ exports.before = ["story"]; exports.synchronous = true; exports.startup = function() { + // Install the HTTP client event handler + $tw.httpClient = new $tw.utils.HttpClient(); + var getPropertiesWithPrefix = function(properties,prefix) { + var result = Object.create(null); + $tw.utils.each(properties,function(value,name) { + if(name.indexOf(prefix) === 0) { + result[name.substring(prefix.length)] = properties[name]; + } + }); + return result; + }; + $tw.rootWidget.addEventListener("tm-http-request",function(event) { + var params = event.paramObject || {}; + $tw.httpClient.initiateHttpRequest({ + wiki: event.widget.wiki, + url: params.url, + method: params.method, + body: params.body, + binary: params.binary, + oncompletion: params.oncompletion, + onprogress: params.onprogress, + bindStatus: params["bind-status"], + bindProgress: params["bind-progress"], + variables: getPropertiesWithPrefix(params,"var-"), + headers: getPropertiesWithPrefix(params,"header-"), + passwordHeaders: getPropertiesWithPrefix(params,"password-header-"), + queryStrings: getPropertiesWithPrefix(params,"query-"), + passwordQueryStrings: getPropertiesWithPrefix(params,"password-query-") + }); + }); + $tw.rootWidget.addEventListener("tm-http-cancel-all-requests",function(event) { + $tw.httpClient.cancelAllHttpRequests(); + }); // Install the modal message mechanism $tw.modal = new $tw.utils.Modal($tw.wiki); $tw.rootWidget.addEventListener("tm-modal",function(event) { diff --git a/core/modules/utils/dom/http.js b/core/modules/utils/dom/http.js index 6e07b1040..083381d8d 100644 --- a/core/modules/utils/dom/http.js +++ b/core/modules/utils/dom/http.js @@ -3,7 +3,7 @@ title: $:/core/modules/utils/dom/http.js type: application/javascript module-type: utils -Browser HTTP support +HTTP support \*/ (function(){ @@ -13,12 +13,220 @@ Browser HTTP support "use strict"; /* -A quick and dirty HTTP function; to be refactored later. Options are: +Manage tm-http-request events. Options include: +wiki: Reference to the wiki to be used for state tiddler tracking +stateTrackerTitle: Title of tiddler to be used for state tiddler tracking +*/ +function HttpClient(options) { + options = options || {}; + this.nextId = 1; + this.wiki = options.wiki || $tw.wiki; + this.stateTrackerTitle = options.stateTrackerTitle || "$:/state/http-requests"; + this.requests = []; // Array of {id: string,request: HttpClientRequest} + this.updateRequestTracker(); +} + +/* +Return the index into this.requests[] corresponding to a given ID. Returns null if not found +*/ +HttpClient.prototype.getRequestIndex = function(targetId) { + var targetIndex = null; + $tw.utils.each(this.requests,function(requestInfo,index) { + if(requestInfo.id === targetId) { + targetIndex = index; + } + }); + return targetIndex; +}; + +/* +Update the state tiddler that is tracking the outstanding requests +*/ +HttpClient.prototype.updateRequestTracker = function() { + this.wiki.addTiddler({title: this.stateTrackerTitle, text: "" + this.requests.length}); +}; + +HttpClient.prototype.initiateHttpRequest = function(options) { + var self = this, + id = this.nextId, + request = new HttpClientRequest(options); + this.nextId += 1; + this.requests.push({id: id, request: request}); + this.updateRequestTracker(); + request.send(function(err) { + var targetIndex = self.getRequestIndex(id); + if(targetIndex !== null) { + self.requests.splice(targetIndex,1); + self.updateRequestTracker(); + } + }); + return id; +}; + +HttpClient.prototype.cancelAllHttpRequests = function() { + var self = this; + if(this.requests.length > 0) { + for(var t=this.requests.length - 1; t--; t>=0) { + var requestInfo = this.requests[t]; + requestInfo.request.cancel(); + } + } + this.requests = []; + this.updateRequestTracker(); +}; + +HttpClient.prototype.cancelHttpRequest = function(targetId) { + var targetIndex = this.getRequestIndex(targetId); + if(targetIndex !== null) { + this.requests[targetIndex].request.cancel(); + this.requests.splice(targetIndex,1); + this.updateRequestTracker(); + } +}; + +/* +Initiate an HTTP request. Options: +wiki: wiki to be used for executing action strings +url: URL for request +method: method eg GET, POST +body: text of request body +binary: set to "yes" to force binary processing of response payload +oncompletion: action string to be invoked on completion +onprogress: action string to be invoked on progress updates +bindStatus: optional title of tiddler to which status ("pending", "complete", "error") should be written +bindProgress: optional title of tiddler to which the progress of the request (0 to 100) should be bound +variables: hashmap of variable name to string value passed to action strings +headers: hashmap of header name to header value to be sent with the request +passwordHeaders: hashmap of header name to password store name to be sent with the request +queryStrings: hashmap of query string parameter name to parameter value to be sent with the request +passwordQueryStrings: hashmap of query string parameter name to password store name to be sent with the request +*/ +function HttpClientRequest(options) { + var self = this; + console.log("Initiating an HTTP request",options) + this.wiki = options.wiki; + this.completionActions = options.oncompletion; + this.progressActions = options.onprogress; + this.bindStatus = options["bindStatus"]; + this.bindProgress = options["bindProgress"]; + this.method = options.method || "GET"; + this.body = options.body || ""; + this.binary = options.binary || ""; + this.variables = options.variables; + var url = options.url; + $tw.utils.each(options.queryStrings,function(value,name) { + url = $tw.utils.setQueryStringParameter(url,name,value); + }); + $tw.utils.each(options.passwordQueryStrings,function(value,name) { + url = $tw.utils.setQueryStringParameter(url,name,$tw.utils.getPassword(value) || ""); + }); + this.url = url; + this.requestHeaders = {}; + $tw.utils.each(options.headers,function(value,name) { + self.requestHeaders[name] = value; + }); + $tw.utils.each(options.passwordHeaders,function(value,name) { + self.requestHeaders[name] = $tw.utils.getPassword(value) || ""; + }); +} + +HttpClientRequest.prototype.send = function(callback) { + var self = this, + setBinding = function(title,text) { + if(title) { + self.wiki.addTiddler(new $tw.Tiddler({title: title, text: text})); + } + }; + if(this.url) { + setBinding(this.bindStatus,"pending"); + setBinding(this.bindProgress,"0"); + // Set the request tracker tiddler + var requestTrackerTitle = this.wiki.generateNewTitle("$:/temp/HttpRequest"); + this.wiki.addTiddler({ + title: requestTrackerTitle, + tags: "$:/tags/HttpRequest", + text: JSON.stringify({ + url: this.url, + type: this.method, + status: "inprogress", + headers: this.requestHeaders, + data: this.body + }) + }); + this.xhr = $tw.utils.httpRequest({ + url: this.url, + type: this.method, + headers: this.requestHeaders, + data: this.body, + returnProp: this.binary === "" ? "responseText" : "response", + responseType: this.binary === "" ? "text" : "arraybuffer", + callback: function(err,data,xhr) { + var hasSucceeded = xhr.status >= 200 && xhr.status < 300, + completionCode = hasSucceeded ? "complete" : "error", + headers = {}; + $tw.utils.each(xhr.getAllResponseHeaders().split("\r\n"),function(line) { + var pos = line.indexOf(":"); + if(pos !== -1) { + headers[line.substr(0,pos)] = line.substr(pos + 1).trim(); + } + }); + setBinding(self.bindStatus,completionCode); + setBinding(self.bindProgress,"100"); + var resultVariables = { + status: xhr.status.toString(), + statusText: xhr.statusText, + error: (err || "").toString(), + data: (data || "").toString(), + headers: JSON.stringify(headers) + }; + /* Convert data from binary to base64 */ + if (xhr.responseType === "arraybuffer") { + var binary = "", + bytes = new Uint8Array(data), + len = bytes.byteLength; + for (var i=0; i widgets from each tiddler $tw.utils.each(this.tiddlerList,function(title) { - var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true}); + var parser = widgetPointer.wiki.parseTiddler(title,{parseAsInline:true, configTrimWhiteSpace:true}); if(parser) { var parseTreeNode = parser.tree[0]; while(parseTreeNode && ["setvariable","set","parameters"].indexOf(parseTreeNode.type) !== -1) { diff --git a/core/modules/widgets/select.js b/core/modules/widgets/select.js index cd789423f..ab9bef74e 100644 --- a/core/modules/widgets/select.js +++ b/core/modules/widgets/select.js @@ -145,6 +145,7 @@ SelectWidget.prototype.execute = function() { this.selectDefault = this.getAttribute("default"); this.selectMultiple = this.getAttribute("multiple", false); this.selectSize = this.getAttribute("size"); + this.selectTabindex = this.getAttribute("tabindex"); this.selectTooltip = this.getAttribute("tooltip"); this.selectFocus = this.getAttribute("focus"); // Make the child widgets @@ -162,6 +163,9 @@ SelectWidget.prototype.execute = function() { 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); } diff --git a/core/modules/widgets/transclude.js b/core/modules/widgets/transclude.js index f50e32c64..7b4b9581d 100755 --- a/core/modules/widgets/transclude.js +++ b/core/modules/widgets/transclude.js @@ -41,16 +41,17 @@ TranscludeWidget.prototype.execute = function() { this.collectAttributes(); this.collectStringParameters(); this.collectSlotFillParameters(); - // Get the parse tree nodes that we are transcluding + // Get the target text and parse tree nodes that we are transcluding var target = this.getTransclusionTarget(), - parseTreeNodes = target.parseTreeNodes; + parseTreeNodes; this.sourceText = target.text; this.parserType = target.type; this.parseAsInline = target.parseAsInline; // Process the transclusion according to the output type switch(this.transcludeOutput || "text/html") { case "text/html": - // No further processing required + // Return the parse tree nodes + parseTreeNodes = target.parseTreeNodes; break; case "text/raw": // Just return the raw text @@ -158,7 +159,7 @@ TranscludeWidget.prototype.collectSlotFillParameters = function() { }; /* -Get transcluded parse tree nodes as an object {parser:,text:,type:} +Get transcluded parse tree nodes as an object {text:,type:,parseTreeNodes:,parseAsInline:} */ TranscludeWidget.prototype.getTransclusionTarget = function() { var self = this; @@ -177,24 +178,8 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { var variableInfo = this.getVariableInfo(this.transcludeVariable,{params: this.getOrderedTransclusionParameters()}), srcVariable = variableInfo && variableInfo.srcVariable; if(variableInfo.text) { - if(srcVariable.isFunctionDefinition) { - // Function to return parameters by name or position - var fnGetParam = function(name,index) { - // Parameter names starting with dollar must be escaped to double dollars - if(name.charAt(0) === "$") { - name = "$" + name; - } - // Look for the parameter by name - if(self.hasAttribute(name)) { - return self.getAttribute(name); - // Look for the parameter by index - } else if(self.hasAttribute(index + "")) { - return self.getAttribute(index + ""); - } else { - return undefined; - } - }, - result = this.evaluateVariable(this.transcludeVariable,{params: fnGetParam})[0] || ""; + if(srcVariable && srcVariable.isFunctionDefinition) { + var result = (variableInfo.resultList ? variableInfo.resultList[0] : variableInfo.text) || ""; parser = { tree: [{ type: "text", @@ -223,7 +208,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { if(variableInfo.isCacheable && srcVariable[cacheKey]) { parser = srcVariable[cacheKey]; } else { - parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable.configTrimWhiteSpace}); + parser = this.wiki.parseText(this.transcludeType,variableInfo.text || "",{parseAsInline: parseAsInline, configTrimWhiteSpace: srcVariable && srcVariable.configTrimWhiteSpace}); if(variableInfo.isCacheable) { srcVariable[cacheKey] = parser; } @@ -231,7 +216,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { } if(parser) { // Add parameters widget for procedures and custom widgets - if(srcVariable.isProcedureDefinition || srcVariable.isWidgetDefinition) { + if(srcVariable && (srcVariable.isProcedureDefinition || srcVariable.isWidgetDefinition)) { parser = { tree: [ { @@ -250,7 +235,7 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { } $tw.utils.addAttributeToParseTreeNode(parser.tree[0],name,param["default"]) }); - } else { + } else if(srcVariable && (srcVariable.isMacroDefinition || !srcVariable.isFunctionDefinition)) { // For macros and ordinary variables, wrap the parse tree in a vars widget assigning the parameters to variables named "__paramname__" parser = { tree: [ @@ -286,7 +271,6 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { // Return the parse tree if(parser) { return { - parser: parser, parseTreeNodes: parser.tree, parseAsInline: parseAsInline, text: parser.source, @@ -295,7 +279,6 @@ TranscludeWidget.prototype.getTransclusionTarget = function() { } else { // If there's no parse tree then return the missing slot value return { - parser: null, parseTreeNodes: (this.slotFillParseTrees["ts-missing"] || []), parseAsInline: parseAsInline, text: null, diff --git a/core/modules/widgets/widget.js b/core/modules/widgets/widget.js index 6f9a8e4e1..3b9a1de25 100755 --- a/core/modules/widgets/widget.js +++ b/core/modules/widgets/widget.js @@ -13,7 +13,7 @@ Widget base class "use strict"; /* Maximum permitted depth of the widget tree for recursion detection */ -var MAX_WIDGET_TREE_DEPTH = 1000; +var MAX_WIDGET_TREE_DEPTH = 500; /* Create a widget object for a parse tree node @@ -112,14 +112,18 @@ Get the prevailing value of a context variable name: name of variable options: see below Options include + params: array of {name:, value:} for each parameter defaultValue: default value if the variable is not defined +source: optional source iterator for evaluating function invocations allowSelfAssigned: if true, includes the current widget in the context chain instead of just the parent Returns an object with the following fields: -params: array of {name:,value:} of parameters passed to wikitext variables +params: array of {name:,value:} or {value:} of parameters to be applied text: text of variable, with parameters properly substituted +resultList: result of variable evaluation as an array +srcVariable: reference to the object defining the variable */ Widget.prototype.getVariableInfo = function(name,options) { options = options || {}; @@ -135,7 +139,8 @@ Widget.prototype.getVariableInfo = function(name,options) { if(variable) { var originalValue = variable.value, value = originalValue, - params = []; + params = [], + resultList = [value]; // Only substitute parameter and variable references if this variable was defined with the \define pragma if(variable.isMacroDefinition) { params = self.resolveVariableParameters(variable.params,actualParams); @@ -144,10 +149,28 @@ Widget.prototype.getVariableInfo = function(name,options) { value = $tw.utils.replaceString(value,new RegExp("\\$" + $tw.utils.escapeRegExp(param.name) + "\\$","mg"),param.value); }); value = self.substituteVariableReferences(value,options); + resultList = [value]; + } else if(variable.isFunctionDefinition) { + // Function evaluations + params = self.resolveVariableParameters(variable.params,actualParams); + var variables = Object.create(null); + // Apply default parameter values + $tw.utils.each(variable.params,function(param,index) { + if(param["default"]) { + variables[param.name] = param["default"]; + } + }); + // Parameters are an array of {value:} or {name:, value:} pairs + $tw.utils.each(params,function(param) { + variables[param.name] = param.value; + }); + resultList = this.wiki.filterTiddlers(value,this.makeFakeWidgetWithVariables(variables),options.source); + value = resultList[0] || ""; } return { text: value, params: params, + resultList: resultList, srcVariable: variable, isCacheable: originalValue === value }; @@ -159,7 +182,7 @@ Widget.prototype.getVariableInfo = function(name,options) { } return { text: text, - srcVariable: {} + resultList: [text] }; }; @@ -317,62 +340,11 @@ Widget.prototype.makeFakeWidgetWithVariables = function(variables) { }; }, makeFakeWidgetWithVariables: self.makeFakeWidgetWithVariables, - evaluateVariable: self.evaluateVariable, resolveVariableParameters: self.resolveVariableParameters, wiki: self.wiki }; }; -/* -Evaluate a variable and associated actual parameters and return the resulting array. -The way that the variable is evaluated depends upon its type: -* Functions are evaluated as parameterised filter strings -* Macros are returned as plain text with substitution of parameters -* Procedures and widgets are returned as plain text - -Options are: -params - the actual parameters – may be one of: - * an array of values that may be an anonymous string value, or a {name:, value:} pair - * a hashmap of {name: value} pairs - * a function invoked with parameters (name,index) that returns a parameter value by name or position -source - iterator for source tiddlers -*/ -Widget.prototype.evaluateVariable = function(name,options) { - options = options || {}; - var params = options.params || []; - // Get the details of the variable (includes processing text substitution for macros - var variableInfo = this.getVariableInfo(name,{params: params,defaultValue: ""}); - // Process function parameters - var variables = Object.create(null); - if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { - // Apply default parameter values - $tw.utils.each(variableInfo.srcVariable.params,function(param,index) { - if(param["default"]) { - variables[param.name] = param["default"]; - } - }); - if($tw.utils.isArray(params)) { - // Parameters are an array of values or {name:, value:} pairs - $tw.utils.each(this.resolveVariableParameters(variableInfo.srcVariable.params,params),function(param) { - variables[param.name] = param.value; - }); - } else if(typeof params === "function") { - // Parameters are passed via a function - $tw.utils.each(variableInfo.srcVariable.params,function(param,index) { - variables[param.name] = params(param.name,index) || param["default"] || ""; - }); - } else { - // Parameters are a hashmap - $tw.utils.each(params,function(value,name) { - variables[name] = value; - }); - } - return this.wiki.filterTiddlers(variableInfo.text,this.makeFakeWidgetWithVariables(variables),options.source); - } else { - return [variableInfo.text]; - } -}; - /* Compute the current values of the attributes of the widget. Returns a hashmap of the names of the attributes that have changed. Options include: @@ -406,13 +378,9 @@ Widget.prototype.computeAttribute = function(attribute) { value = this.wiki.getTextReference(attribute.textReference,"",this.getVariable("currentTiddler")) || ""; } else if(attribute.type === "macro") { var variableInfo = this.getVariableInfo(attribute.value.name,{params: attribute.value.params}); - if(variableInfo.srcVariable && variableInfo.srcVariable.isFunctionDefinition) { - // It is a function definition. Go through each of the defined parameters, and make a variable with the value of the corresponding provided parameter - var paramArray = this.resolveVariableParameters(variableInfo.srcVariable.params,attribute.value.params); - value = this.evaluateVariable(attribute.value.name,{params: paramArray})[0] || ""; - } else { - value = variableInfo.text; - } + value = variableInfo.text; + } else if(attribute.type === "substituted") { + value = this.wiki.getSubstitutedText(attribute.rawValue,this) || ""; } else { // String attribute value = attribute.value; } @@ -751,23 +719,46 @@ Widget.prototype.findFirstDomNode = function() { }; /* -Remove any DOM nodes created by this widget or its children +Entry into destroy procedure +*/ +Widget.prototype.destroyChildren = function() { + $tw.utils.each(this.children,function(childWidget) { + childWidget.destroy(); + }); +}; +/* +Legacy entry into destroy procedure */ Widget.prototype.removeChildDomNodes = function() { - // If this widget has directly created DOM nodes, delete them and exit. This assumes that any child widgets are contained within the created DOM nodes, which would normally be the case + this.destroy(); +}; +/* +Default destroy +*/ +Widget.prototype.destroy = function() { + // call children to remove their resources + this.destroyChildren(); + // remove our resources + this.children = []; + this.removeLocalDomNodes(); +}; + +/* +Remove any DOM nodes created by this widget +*/ +Widget.prototype.removeLocalDomNodes = function() { + // If this widget has directly created DOM nodes, delete them and exit. if(this.domNodes.length > 0) { $tw.utils.each(this.domNodes,function(domNode) { - domNode.parentNode.removeChild(domNode); + if(domNode.parentNode) { + domNode.parentNode.removeChild(domNode); + } }); this.domNodes = []; - } else { - // Otherwise, ask the child widgets to delete their DOM nodes - $tw.utils.each(this.children,function(childWidget) { - childWidget.removeChildDomNodes(); - }); } }; + /* Invoke the action widgets that are descendents of the current widget. */ @@ -829,6 +820,20 @@ Widget.prototype.allowActionPropagation = function() { return true; }; +/* +Evaluate a variable with parameters. This is a static convenience method that attempts to evaluate a variable as a function, returning an array of strings +*/ +Widget.evaluateVariable = function(widget,name,options) { + var result; + if(widget.getVariableInfo) { + var variableInfo = widget.getVariableInfo(name,options); + result = variableInfo.resultList || [variableInfo.text]; + } else { + result = [widget.getVariable(name)]; + } + return result; +}; + exports.widget = Widget; })(); diff --git a/core/modules/wiki.js b/core/modules/wiki.js index 8cb12cc39..3eae3902d 100755 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -1063,6 +1063,34 @@ exports.getTextReferenceParserInfo = function(title,field,index,options) { return parserInfo; } +/* +Parse a block of text of a specified MIME type + text: text on which to perform substitutions + widget + options: see below +Options include: + substitutions: an optional array of substitutions +*/ +exports.getSubstitutedText = function(text,widget,options) { + options = options || {}; + text = text || ""; + var self = this, + substitutions = options.substitutions || [], + output; + // Evaluate embedded filters and substitute with first result + output = text.replace(/\$\{([\S\s]+?)\}\$/g, function(match,filter) { + return self.filterTiddlers(filter,widget)[0] || ""; + }); + // Process any substitutions provided in options + $tw.utils.each(substitutions,function(substitute) { + output = $tw.utils.replaceString(output,new RegExp("\\$" + $tw.utils.escapeRegExp(substitute.name) + "\\$","mg"),substitute.value); + }); + // Substitute any variable references with their values + return output.replace(/\$\(([^\)\$]+)\)\$/g, function(match,varname) { + return widget.getVariable(varname,{defaultValue: ""}) + }); +}; + /* Make a widget tree for a parse tree parser: parser object @@ -1415,6 +1443,14 @@ exports.checkTiddlerText = function(title,targetText,options) { return text === targetText; } +/* +Execute an action string without an associated context widget +*/ +exports.invokeActionString = function(actions,event,variables,options) { + var widget = this.makeWidget(null,{parentWidget: options.parentWidget}); + widget.invokeActionString(actions,null,event,variables); +}; + /* Read an array of browser File objects, invoking callback(tiddlerFieldsArray) once they're all read */ diff --git a/core/palettes/Vanilla.tid b/core/palettes/Vanilla.tid index d84b4ec83..4c660e912 100644 --- a/core/palettes/Vanilla.tid +++ b/core/palettes/Vanilla.tid @@ -54,6 +54,7 @@ modal-footer-background: #f5f5f5 modal-footer-border: #dddddd modal-header-border: #eeeeee muted-foreground: #bbb +network-activity-foreground: #448844 notification-background: #ffffdd notification-border: #999999 page-background: #f4f4f4 diff --git a/core/templates/exporters/StaticRiver.tid b/core/templates/exporters/StaticRiver.tid index a22cfb98a..3b70c9d11 100644 --- a/core/templates/exporters/StaticRiver.tid +++ b/core/templates/exporters/StaticRiver.tid @@ -3,6 +3,7 @@ tags: $:/tags/Exporter description: {{$:/language/Exporters/StaticRiver}} extension: .html +\define tv-config-static() yes \define tv-wikilink-template() #$uri_encoded$ \define tv-config-toolbar-icons() no \define tv-config-toolbar-text() no diff --git a/core/templates/server/static.tiddler.html.tid b/core/templates/server/static.tiddler.html.tid index a8409e50f..6c9fd80a5 100644 --- a/core/templates/server/static.tiddler.html.tid +++ b/core/templates/server/static.tiddler.html.tid @@ -1,6 +1,7 @@ title: $:/core/templates/server/static.tiddler.html \whitespace trim +\define tv-config-static() yes \define tv-wikilink-template() $uri_encoded$ \import [subfilter{$:/core/config/GlobalImportFilter}] diff --git a/core/templates/static.template.html.tid b/core/templates/static.template.html.tid index 5da5fb752..8b6482846 100644 --- a/core/templates/static.template.html.tid +++ b/core/templates/static.template.html.tid @@ -1,6 +1,7 @@ title: $:/core/templates/static.template.html type: text/vnd.tiddlywiki-html +\define tv-config-static() yes \define tv-wikilink-template() static/$uri_doubleencoded$.html \define tv-config-toolbar-icons() no \define tv-config-toolbar-text() no diff --git a/core/templates/static.tiddler.html.tid b/core/templates/static.tiddler.html.tid index f90818464..a3297ee78 100644 --- a/core/templates/static.tiddler.html.tid +++ b/core/templates/static.tiddler.html.tid @@ -1,6 +1,7 @@ title: $:/core/templates/static.tiddler.html \define tv-wikilink-template() $uri_doubleencoded$.html +\define tv-config-static() yes \define tv-config-toolbar-icons() no \define tv-config-toolbar-text() no \define tv-config-toolbar-class() tc-btn-invisible diff --git a/core/ui/AlertTemplate.tid b/core/ui/AlertTemplate.tid index ae15818a0..d67586b8d 100644 --- a/core/ui/AlertTemplate.tid +++ b/core/ui/AlertTemplate.tid @@ -1,3 +1,4 @@ +code-body: yes title: $:/core/ui/AlertTemplate \whitespace trim diff --git a/core/ui/ControlPanel/Settings.tid b/core/ui/ControlPanel/Settings.tid index f4a4b13c2..74004ffa0 100644 --- a/core/ui/ControlPanel/Settings.tid +++ b/core/ui/ControlPanel/Settings.tid @@ -2,18 +2,6 @@ title: $:/core/ui/ControlPanel/Settings tags: $:/tags/ControlPanel caption: {{$:/language/ControlPanel/Settings/Caption}} -\define lingo-base() $:/language/ControlPanel/Settings/ - -<> - -<$list filter="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/Settings]]"> - -
- -!! <$link><$transclude field="caption"/> - -<$transclude/> - -
- - +
+<$macrocall $name="tabs" tabsList="[all[shadows+tiddlers]tag[$:/tags/ControlPanel/SettingsTab]!has[draft.of]]" default="$:/core/ui/ControlPanel/Settings/TiddlyWiki" explicitState="$:/state/tab--697582678"/> +
\ No newline at end of file diff --git a/plugins/tiddlywiki/codemirror/ui/controlpanel/tiddlywiki.tid b/core/ui/ControlPanel/TiddlyWiki.tid similarity index 96% rename from plugins/tiddlywiki/codemirror/ui/controlpanel/tiddlywiki.tid rename to core/ui/ControlPanel/TiddlyWiki.tid index f88865997..40be32139 100644 --- a/plugins/tiddlywiki/codemirror/ui/controlpanel/tiddlywiki.tid +++ b/core/ui/ControlPanel/TiddlyWiki.tid @@ -1,6 +1,7 @@ title: $:/core/ui/ControlPanel/Settings/TiddlyWiki tags: $:/tags/ControlPanel/SettingsTab caption: TiddlyWiki +list-before: \define lingo-base() $:/language/ControlPanel/Settings/ diff --git a/core/ui/EditTemplate.tid b/core/ui/EditTemplate.tid index 5aed61a73..6ad84a139 100644 --- a/core/ui/EditTemplate.tid +++ b/core/ui/EditTemplate.tid @@ -1,3 +1,4 @@ +code-body: yes title: $:/core/ui/EditTemplate \define delete-edittemplate-state-tiddlers() diff --git a/core/ui/EditTemplate/tags.tid b/core/ui/EditTemplate/tags.tid index 8d829b30e..5084478b4 100644 --- a/core/ui/EditTemplate/tags.tid +++ b/core/ui/EditTemplate/tags.tid @@ -14,8 +14,8 @@ color:$(foregroundColor)$; \define tag-body-inner(colour,fallbackTarget,colourA,colourB,icon,tagField:"tags") \whitespace trim <$vars foregroundColor=<> backgroundColor="""$colour$"""> -> class="tc-tag-label tc-tag-list-item tc-small-gap-right"> -<$transclude tiddler="""$icon$"""/><$view field="title" format="text" /> +> class="tc-tag-label tc-tag-list-item tc-small-gap-right" data-tag-title=<>> +<$transclude tiddler="""$icon$"""/><$view field="title" format="text"/> <$button class="tc-btn-invisible tc-remove-tag-button" style=<>><$action-listops $tiddler=<> $field=<<__tagField__>> $subfilter="-[{!!title}]"/>{{$:/core/images/close-button}} diff --git a/core/ui/EditorToolbar/link-dropdown.tid b/core/ui/EditorToolbar/link-dropdown.tid index e2766935b..d2887a180 100644 --- a/core/ui/EditorToolbar/link-dropdown.tid +++ b/core/ui/EditorToolbar/link-dropdown.tid @@ -18,7 +18,7 @@ title: $:/core/ui/EditorToolbar/link-dropdown \define external-link() \whitespace trim -<$button class="tc-btn-invisible" style="width: auto; display: inline-block; background-colour: inherit;" actions=<>> +<$button class="tc-btn-invisible tc-btn-mini" style="width: auto; display: inline-block; background-colour: inherit;" actions=<>> {{$:/core/images/chevron-right}} \end @@ -45,7 +45,7 @@ title: $:/core/ui/EditorToolbar/link-dropdown <$reveal tag="span" state=<> type="nomatch" text=""> <> -<$button class="tc-btn-invisible" style="width: auto; display: inline-block; background-colour: inherit;"> +<$button class="tc-btn-invisible tc-btn-mini" style="width: auto; display: inline-block; background-colour: inherit;"> <><$set name="cssEscapedTitle" value={{{ [escapecss[]] }}}><$action-sendmessage $message="tm-focus-selector" $param=<>/> {{$:/core/images/close-button}} diff --git a/core/ui/ImportPreviews/Text.tid b/core/ui/ImportPreviews/Text.tid index 7832eb8b8..b37c109ad 100644 --- a/core/ui/ImportPreviews/Text.tid +++ b/core/ui/ImportPreviews/Text.tid @@ -1,5 +1,6 @@ title: $:/core/ui/ImportPreviews/Text tags: $:/tags/ImportPreview caption: {{$:/language/Import/Listing/Preview/Text}} +code-body: yes <$transclude tiddler=<> subtiddler=<> mode="block"/> diff --git a/core/ui/MoreSideBar/Tags.tid b/core/ui/MoreSideBar/Tags.tid index 0a4727bc3..b1b67bb67 100644 --- a/core/ui/MoreSideBar/Tags.tid +++ b/core/ui/MoreSideBar/Tags.tid @@ -3,15 +3,14 @@ tags: $:/tags/MoreSideBar caption: {{$:/language/SideBar/Tags/Caption}} \whitespace trim - <$let tv-config-toolbar-icons="yes" tv-config-toolbar-text="yes" tv-config-toolbar-class="">
- {{$:/core/ui/Buttons/tag-manager}} + {{$:/core/ui/Buttons/tag-manager}}
<$list filter={{$:/core/Filters/AllTags!!filter}}>
- <$transclude tiddler="$:/core/ui/TagTemplate"/> + <$transclude tiddler="$:/core/ui/TagTemplate"/>

diff --git a/core/ui/PageControls/network-activity.tid b/core/ui/PageControls/network-activity.tid new file mode 100644 index 000000000..763365f37 --- /dev/null +++ b/core/ui/PageControls/network-activity.tid @@ -0,0 +1,16 @@ +title: $:/core/ui/Buttons/network-activity +tags: $:/tags/PageControls +caption: {{$:/core/images/network-activity}} {{$:/language/Buttons/NetworkActivity/Caption}} +description: {{$:/language/Buttons/NetworkActivity/Hint}} + +\whitespace trim +<$button message="tm-http-cancel-all-requests" tooltip={{$:/language/Buttons/NetworkActivity/Hint}} aria-label={{$:/language/Buttons/NetworkActivity/Caption}} class=<>> +<$list filter="[match[yes]]"> +{{$:/core/images/network-activity}} + +<$list filter="[match[yes]]"> + +<$text text={{$:/language/Buttons/NetworkActivity/Caption}}/> + + + \ No newline at end of file diff --git a/core/ui/PageStylesheet.tid b/core/ui/PageStylesheet.tid index 0b32df5f8..f21909f09 100644 --- a/core/ui/PageStylesheet.tid +++ b/core/ui/PageStylesheet.tid @@ -1,4 +1,5 @@ title: $:/core/ui/PageStylesheet +code-body: yes \import [subfilter{$:/core/config/GlobalImportFilter}] \whitespace trim diff --git a/core/ui/PageTemplate.tid b/core/ui/PageTemplate.tid index f0ab4852a..38b4c915b 100644 --- a/core/ui/PageTemplate.tid +++ b/core/ui/PageTemplate.tid @@ -2,6 +2,7 @@ title: $:/core/ui/PageTemplate name: {{$:/language/PageTemplate/Name}} description: {{$:/language/PageTemplate/Description}} icon: $:/core/images/layout-button +code-body: yes \whitespace trim \import [subfilter{$:/core/config/GlobalImportFilter}] diff --git a/core/ui/RootTemplate.tid b/core/ui/RootTemplate.tid index 1fd7319ba..4a7443c79 100644 --- a/core/ui/RootTemplate.tid +++ b/core/ui/RootTemplate.tid @@ -1,4 +1,5 @@ title: $:/core/ui/RootTemplate +code-body: yes <$transclude tiddler={{{ [{$:/layout}has[text]] ~[[$:/core/ui/PageTemplate]] }}} mode="inline"/> diff --git a/core/ui/StoryTiddlerTemplate.tid b/core/ui/StoryTiddlerTemplate.tid index 7cc26a849..a2b2f5558 100644 --- a/core/ui/StoryTiddlerTemplate.tid +++ b/core/ui/StoryTiddlerTemplate.tid @@ -1,3 +1,4 @@ title: $:/core/ui/StoryTiddlerTemplate +code-body: yes <$transclude tiddler={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/StoryTiddlerTemplateFilter]!is[draft]get[text]] :and[has[title]else[$:/core/ui/ViewTemplate]] }}} /> diff --git a/core/ui/TagPickerTagTemplate.tid b/core/ui/TagPickerTagTemplate.tid index 9545725b3..9e8689153 100644 --- a/core/ui/TagPickerTagTemplate.tid +++ b/core/ui/TagPickerTagTemplate.tid @@ -2,22 +2,29 @@ title: $:/core/ui/TagPickerTagTemplate \whitespace trim <$button class=<> tag="a" tooltip={{$:/language/EditTemplate/Tags/Add/Button/Hint}}> -<$list filter="[minlength[1]]"> -<$action-listops $tiddler=<> $field=<> $subfilter="[]"/> - -<$set name="currentTiddlerCSSEscaped" value={{{ [escapecss[]] }}}> -<$action-sendmessage $message="tm-focus-selector" $param=<> preventScroll="true"/> - -<> -<$list filter="[minlength[1]]"> -<$action-setfield $tiddler=<> text="yes"/> - -<> -<$set name="backgroundColor" value={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}}> -<$wikify name="foregroundColor" text="""<$macrocall $name="contrastcolour" target=<> fallbackTarget=<> colourA=<> colourB=<>/>"""> ->> -{{||$:/core/ui/TiddlerIcon}}<$view field="title" format="text"/> - - - + <$list filter="[minlength[1]]"> + <$action-listops $tiddler=<> $field=<> $subfilter="[]"/> + + <$set name="currentTiddlerCSSEscaped" value={{{ [escapecss[]] }}}> + <$action-sendmessage $message="tm-focus-selector" $param=<> preventScroll="true"/> + + <> + <$list filter="[minlength[1]]"> + <$action-setfield $tiddler=<> text="yes"/> + + <> + <$set name="backgroundColor" + value={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} + > + <$wikify name="foregroundColor" + text="""<$macrocall $name="contrastcolour" target=<> fallbackTarget=<> colourA=<> colourB=<>/>""" + > + > + data-tag-title=<> + > + {{||$:/core/ui/TiddlerIcon}}<$view field="title" format="text"/> + + + diff --git a/core/ui/TagTemplate.tid b/core/ui/TagTemplate.tid index f137f22a0..49e836671 100644 --- a/core/ui/TagTemplate.tid +++ b/core/ui/TagTemplate.tid @@ -3,16 +3,23 @@ title: $:/core/ui/TagTemplate \whitespace trim >> <$set name="transclusion" value=<>> -<$macrocall $name="tag-pill-body" tag=<> icon={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} colour={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} palette={{$:/palette}} element-tag="""$button""" element-attributes="""popup=<> dragFilter='[all[current]tagging[]]' tag='span'"""/> -<$reveal state=<> type="popup" position="below" animate="yes" class="tc-drop-down"> -<$set name="tv-show-missing-links" value="yes"> -<$transclude tiddler="$:/core/ui/ListItemTemplate"/> - -<$list filter="[all[shadows+tiddlers]tag[$:/tags/TagDropdown]!has[draft.of]]" variable="listItem"> -<$transclude tiddler=<>/> - -
-<$macrocall $name="list-tagged-draggable" tag=<>/> - + <$macrocall $name="tag-pill-body" + tag=<> + icon={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} + colour={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} + palette={{$:/palette}} + element-tag="$button" + element-attributes="""popup=<> dragFilter="[all[current]tagging[]]" tag='span'""" + /> + <$reveal state=<> type="popup" position="below" animate="yes" class="tc-drop-down"> + <$set name="tv-show-missing-links" value="yes"> + <$transclude tiddler="$:/core/ui/ListItemTemplate"/> + + <$list filter="[all[shadows+tiddlers]tag[$:/tags/TagDropdown]!has[draft.of]]" variable="listItem"> + <$transclude tiddler=<>/> + +
+ <$macrocall $name="list-tagged-draggable" tag=<>/> +
diff --git a/core/ui/ViewTemplate.tid b/core/ui/ViewTemplate.tid index dcba5c953..9fa67816e 100644 --- a/core/ui/ViewTemplate.tid +++ b/core/ui/ViewTemplate.tid @@ -1,4 +1,5 @@ title: $:/core/ui/ViewTemplate +code-body: yes \whitespace trim \define folded-state() diff --git a/core/ui/ViewTemplate/body/default.tid b/core/ui/ViewTemplate/body/default.tid index 083684879..5416c6e07 100644 --- a/core/ui/ViewTemplate/body/default.tid +++ b/core/ui/ViewTemplate/body/default.tid @@ -1,4 +1,5 @@ title: $:/core/ui/ViewTemplate/body/default +code-body: yes <$transclude> diff --git a/core/wiki/config/OfficialPluginLibrary.tid b/core/wiki/config/OfficialPluginLibrary.tid index 20bb13d0d..4aa985b87 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.2.8/index.html +url: https://tiddlywiki.com/library/v5.3.1/index.html caption: {{$:/language/OfficialPluginLibrary}} {{$:/language/OfficialPluginLibrary/Hint}} diff --git a/core/wiki/config/PageControlButtons.multids b/core/wiki/config/PageControlButtons.multids index a437251f5..b66f11cc0 100644 --- a/core/wiki/config/PageControlButtons.multids +++ b/core/wiki/config/PageControlButtons.multids @@ -13,6 +13,7 @@ core/ui/Buttons/language: hide core/ui/Buttons/tag-manager: hide core/ui/Buttons/manager: hide core/ui/Buttons/more-page-actions: hide +core/ui/Buttons/network-activity: hide core/ui/Buttons/new-journal: hide core/ui/Buttons/new-image: hide core/ui/Buttons/palette: hide diff --git a/core/wiki/config/ViewTemplateBodyFilters.multids b/core/wiki/config/ViewTemplateBodyFilters.multids index 6348cc036..ff9fe7250 100644 --- a/core/wiki/config/ViewTemplateBodyFilters.multids +++ b/core/wiki/config/ViewTemplateBodyFilters.multids @@ -2,7 +2,8 @@ title: $:/config/ViewTemplateBodyFilters/ tags: $:/tags/ViewTemplateBodyFilter stylesheet: [tag[$:/tags/Stylesheet]then[$:/core/ui/ViewTemplate/body/rendered-plain-text]] -system: [prefix[$:/boot/]] [prefix[$:/config/]] [prefix[$:/core/macros]] [prefix[$:/core/save/]] [prefix[$:/core/templates/]] [prefix[$:/core/ui/]split[/]count[]compare:number:eq[4]] [prefix[$:/info/]] [prefix[$:/language/]] [prefix[$:/languages/]] [prefix[$:/snippets/]] [prefix[$:/state/]] [prefix[$:/status/]] [prefix[$:/info/]] [prefix[$:/temp/]] +[!is[image]limit[1]then[$:/core/ui/ViewTemplate/body/code]] +core-ui-tags: [tag[$:/tags/PageTemplate]] [tag[$:/tags/EditTemplate]] [tag[$:/tags/ViewTemplate]] [tag[$:/tags/KeyboardShortcut]] [tag[$:/tags/ImportPreview]] [tag[$:/tags/EditPreview]][tag[$:/tags/EditorToolbar]] [tag[$:/tags/Actions]] :then[[$:/core/ui/ViewTemplate/body/code]] +system: [prefix[$:/boot/]] [prefix[$:/config/]] [prefix[$:/core/macros]] [prefix[$:/core/save/]] [prefix[$:/core/templates/]] [prefix[$:/info/]] [prefix[$:/language/]] [prefix[$:/languages/]] [prefix[$:/snippets/]] [prefix[$:/state/]] [prefix[$:/status/]] [prefix[$:/info/]] [prefix[$:/temp/]] +[!is[image]limit[1]then[$:/core/ui/ViewTemplate/body/code]] code-body: [field:code-body[yes]then[$:/core/ui/ViewTemplate/body/code]] import: [field:plugin-type[import]then[$:/core/ui/ViewTemplate/body/import]] plugin: [has[plugin-type]then[$:/core/ui/ViewTemplate/body/plugin]] diff --git a/core/wiki/config/wikilink.tid b/core/wiki/config/wikilink.tid index 9a395abd6..6cd5cd8cb 100644 --- a/core/wiki/config/wikilink.tid +++ b/core/wiki/config/wikilink.tid @@ -1,3 +1,3 @@ title: $:/config/WikiParserRules/Inline/wikilink -enable \ No newline at end of file +disable \ No newline at end of file diff --git a/core/wiki/macros/tag.tid b/core/wiki/macros/tag.tid index 3616fb97d..03dd8cb98 100644 --- a/core/wiki/macros/tag.tid +++ b/core/wiki/macros/tag.tid @@ -9,26 +9,50 @@ color:$(foregroundColor)$; \define tag-pill-inner(tag,icon,colour,fallbackTarget,colourA,colourB,element-tag,element-attributes,actions) +\whitespace trim <$vars foregroundColor=<> - backgroundColor="""$colour$""" -><$element-tag$ + backgroundColor=<<__colour__>> +> +<$element-tag$ $element-attributes$ class="tc-tag-label tc-btn-invisible" style=<> ->$actions$<$transclude tiddler="""$icon$"""/><$view tiddler=<<__tag__>> field="title" format="text" /> +> + <<__actions__>> + <$transclude tiddler=<<__icon__>>/> + <$view tiddler=<<__tag__>> field="title" format="text" /> + \end \define tag-pill-body(tag,icon,colour,palette,element-tag,element-attributes,actions) -<$macrocall $name="tag-pill-inner" tag=<<__tag__>> icon="""$icon$""" colour="""$colour$""" fallbackTarget={{$palette$##tag-background}} colourA={{$palette$##foreground}} colourB={{$palette$##background}} element-tag="""$element-tag$""" element-attributes="""$element-attributes$""" actions="""$actions$"""/> +\whitespace trim +<$macrocall $name="tag-pill-inner" + tag=<<__tag__>> + icon=<<__icon__>> + colour=<<__colour__>> + fallbackTarget={{$palette$##tag-background}} + colourA={{$palette$##foreground}} + colourB={{$palette$##background}} + element-tag=<<__element-tag__>> + element-attributes=<<__element-attributes__>> + actions=<<__actions__>> +/> \end \define tag-pill(tag,element-tag:"span",element-attributes:"",actions:"") \whitespace trim >> -<$let currentTiddler=<<__tag__>>> -<$macrocall $name="tag-pill-body" tag=<<__tag__>> icon={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} colour={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} palette={{$:/palette}} element-tag="""$element-tag$""" element-attributes="""$element-attributes$""" actions="""$actions$"""/> - + <$let currentTiddler=<<__tag__>>> + <$macrocall $name="tag-pill-body" + tag=<<__tag__>> + icon={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} + colour={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} + palette={{$:/palette}} + element-tag=<<__element-tag__>> + element-attributes=<<__element-attributes__>> + actions=<<__actions__>>/> + \end diff --git a/core/wiki/macros/toc.tid b/core/wiki/macros/toc.tid index 6b8a83295..528c0e63c 100644 --- a/core/wiki/macros/toc.tid +++ b/core/wiki/macros/toc.tid @@ -19,9 +19,9 @@ tags: $:/tags/Macro \define toc-body(tag,sort:"",itemClassFilter,exclude,path) \whitespace trim
    - <$list filter="""[all[shadows+tiddlers]tag<__tag__>!has[draft.of]$sort$] -[<__tag__>] -[enlist<__exclude__>]"""> + <$list filter="""[all[shadows+tiddlers]tag<__tag__>!has[draft.of]$sort$] -[<__tag__>] -[subfilter<__exclude__>]"""> <$let item=<> path={{{ [<__path__>addsuffix[/]addsuffix<__tag__>] }}}> - <$set name="excluded" filter="""[enlist<__exclude__>] [<__tag__>]"""> + <$set name="excluded" filter="[subfilter<__exclude__>] [<__tag__>]"> <$set name="toc-item-class" filter=<<__itemClassFilter__>> emptyValue="toc-item-selected" value="toc-item">
  1. >> <$list filter="[all[current]toc-link[no]]" emptyMessage="<$link to={{{ [get[target]else] }}}><>"> @@ -36,8 +36,8 @@ tags: $:/tags/Macro
\end -\define toc(tag,sort:"",itemClassFilter:"") -<$macrocall $name="toc-body" tag=<<__tag__>> sort=<<__sort__>> itemClassFilter=<<__itemClassFilter__>> /> +\define toc(tag,sort:"",itemClassFilter:"", exclude) +<$macrocall $name="toc-body" tag=<<__tag__>> sort=<<__sort__>> itemClassFilter=<<__itemClassFilter__>> exclude=<<__exclude__>>/> \end \define toc-linked-expandable-body(tag,sort:"",itemClassFilter,exclude,path) @@ -75,7 +75,7 @@ tags: $:/tags/Macro
  • >> <$reveal type="nomatch" stateTitle=<> text="open"> <$button setTitle=<> setTo="open" class="tc-btn-invisible tc-popup-keep"> - <$transclude tiddler=<> /> + <$transclude tiddler=<> /> <> @@ -100,9 +100,9 @@ tags: $:/tags/Macro \define toc-expandable(tag,sort:"",itemClassFilter:"",exclude,path) \whitespace trim <$let tag=<<__tag__>> sort=<<__sort__>> itemClassFilter=<<__itemClassFilter__>> path={{{ [<__path__>addsuffix[/]addsuffix<__tag__>] }}}> - <$set name="excluded" filter="""[enlist<__exclude__>] [<__tag__>]"""> + <$set name="excluded" filter="[subfilter<__exclude__>] [<__tag__>]">
      - <$list filter="""[all[shadows+tiddlers]tag<__tag__>!has[draft.of]$sort$] -[<__tag__>] -[enlist<__exclude__>]"""> + <$list filter="""[all[shadows+tiddlers]tag<__tag__>!has[draft.of]$sort$] -[<__tag__>] -[subfilter<__exclude__>]"""> <$list filter="[all[current]toc-link[no]]" emptyMessage=<> > <$macrocall $name="toc-unlinked-expandable-body" tag=<<__tag__>> sort=<<__sort__>> itemClassFilter="""itemClassFilter""" exclude=<> path=<> /> @@ -174,9 +174,9 @@ tags: $:/tags/Macro \define toc-selective-expandable(tag,sort:"",itemClassFilter,exclude,path) \whitespace trim <$let tag=<<__tag__>> sort=<<__sort__>> itemClassFilter=<<__itemClassFilter__>> path={{{ [<__path__>addsuffix[/]addsuffix<__tag__>] }}}> - <$set name="excluded" filter="[enlist<__exclude__>] [<__tag__>]"> + <$set name="excluded" filter="[subfilter<__exclude__>] [<__tag__>]">
        - <$list filter="""[all[shadows+tiddlers]tag<__tag__>!has[draft.of]$sort$] -[<__tag__>] -[enlist<__exclude__>]"""> + <$list filter="""[all[shadows+tiddlers]tag<__tag__>!has[draft.of]$sort$] -[<__tag__>] -[subfilter<__exclude__>]"""> <$list filter="[all[current]toc-link[no]]" variable="ignore" emptyMessage=<> > <$macrocall $name="toc-unlinked-selective-expandable-body" tag=<<__tag__>> sort=<<__sort__>> itemClassFilter=<<__itemClassFilter__>> exclude=<> path=<>/> @@ -186,13 +186,13 @@ tags: $:/tags/Macro \end -\define toc-tabbed-external-nav(tag,sort:"",selectedTiddler:"$:/temp/toc/selectedTiddler",unselectedText,missingText,template:"") +\define toc-tabbed-external-nav(tag,sort:"",selectedTiddler:"$:/temp/toc/selectedTiddler",unselectedText,missingText,template:"",exclude) \whitespace trim <$tiddler tiddler={{{ [<__selectedTiddler__>get[text]] }}}>
        <$linkcatcher to=<<__selectedTiddler__>>>
        - <$macrocall $name="toc-selective-expandable" tag=<<__tag__>> sort=<<__sort__>> itemClassFilter="[all[current]] -[<__selectedTiddler__>get[text]]"/> + <$macrocall $name="toc-selective-expandable" tag=<<__tag__>> sort=<<__sort__>> itemClassFilter="[all[current]] -[<__selectedTiddler__>get[text]]" exclude=<<__exclude__>>/>
        @@ -210,9 +210,9 @@ tags: $:/tags/Macro \end -\define toc-tabbed-internal-nav(tag,sort:"",selectedTiddler:"$:/temp/toc/selectedTiddler",unselectedText,missingText,template:"") +\define toc-tabbed-internal-nav(tag,sort:"",selectedTiddler:"$:/temp/toc/selectedTiddler",unselectedText,missingText,template:"",exclude) \whitespace trim <$linkcatcher to=<<__selectedTiddler__>>> - <$macrocall $name="toc-tabbed-external-nav" tag=<<__tag__>> sort=<<__sort__>> selectedTiddler=<<__selectedTiddler__>> unselectedText=<<__unselectedText__>> missingText=<<__missingText__>> template=<<__template__>>/> + <$macrocall $name="toc-tabbed-external-nav" tag=<<__tag__>> sort=<<__sort__>> selectedTiddler=<<__selectedTiddler__>> unselectedText=<<__unselectedText__>> missingText=<<__missingText__>> template=<<__template__>> exclude=<<__exclude__>> /> \end diff --git a/core/wiki/tags/PageControls.tid b/core/wiki/tags/PageControls.tid index c6234751c..c0f1cb233 100644 --- a/core/wiki/tags/PageControls.tid +++ b/core/wiki/tags/PageControls.tid @@ -1,2 +1,2 @@ title: $:/tags/PageControls -list: [[$:/core/ui/Buttons/home]] [[$:/core/ui/Buttons/close-all]] [[$:/core/ui/Buttons/fold-all]] [[$:/core/ui/Buttons/unfold-all]] [[$:/core/ui/Buttons/permaview]] [[$:/core/ui/Buttons/new-tiddler]] [[$:/core/ui/Buttons/new-journal]] [[$:/core/ui/Buttons/new-image]] [[$:/core/ui/Buttons/import]] [[$:/core/ui/Buttons/export-page]] [[$:/core/ui/Buttons/control-panel]] [[$:/core/ui/Buttons/advanced-search]] [[$:/core/ui/Buttons/manager]] [[$:/core/ui/Buttons/tag-manager]] [[$:/core/ui/Buttons/language]] [[$:/core/ui/Buttons/palette]] [[$:/core/ui/Buttons/theme]] [[$:/core/ui/Buttons/layout]] [[$:/core/ui/Buttons/storyview]] [[$:/core/ui/Buttons/encryption]] [[$:/core/ui/Buttons/timestamp]] [[$:/core/ui/Buttons/full-screen]] [[$:/core/ui/Buttons/print]] [[$:/core/ui/Buttons/save-wiki]] [[$:/core/ui/Buttons/refresh]] [[$:/core/ui/Buttons/more-page-actions]] +list: [[$:/core/ui/Buttons/home]] [[$:/core/ui/Buttons/close-all]] [[$:/core/ui/Buttons/fold-all]] [[$:/core/ui/Buttons/unfold-all]] [[$:/core/ui/Buttons/permaview]] [[$:/core/ui/Buttons/new-tiddler]] [[$:/core/ui/Buttons/new-journal]] [[$:/core/ui/Buttons/new-image]] [[$:/core/ui/Buttons/import]] [[$:/core/ui/Buttons/export-page]] [[$:/core/ui/Buttons/control-panel]] [[$:/core/ui/Buttons/advanced-search]] [[$:/core/ui/Buttons/manager]] [[$:/core/ui/Buttons/tag-manager]] [[$:/core/ui/Buttons/language]] [[$:/core/ui/Buttons/palette]] [[$:/core/ui/Buttons/theme]] [[$:/core/ui/Buttons/layout]] [[$:/core/ui/Buttons/storyview]] [[$:/core/ui/Buttons/encryption]] [[$:/core/ui/Buttons/timestamp]] [[$:/core/ui/Buttons/full-screen]] [[$:/core/ui/Buttons/print]] [[$:/core/ui/Buttons/save-wiki]] [[$:/core/ui/Buttons/refresh]] [[$:/core/ui/Buttons/network-activity]] [[$:/core/ui/Buttons/more-page-actions]] diff --git a/core/wiki/tags/ViewTemplateBodyFilter.tid b/core/wiki/tags/ViewTemplateBodyFilter.tid index ab7ff262a..7b9fb7fd8 100644 --- a/core/wiki/tags/ViewTemplateBodyFilter.tid +++ b/core/wiki/tags/ViewTemplateBodyFilter.tid @@ -1,3 +1,2 @@ title: $:/tags/ViewTemplateBodyFilter -list: $:/config/ViewTemplateBodyFilters/hide-body $:/config/ViewTemplateBodyFilters/code-body $:/config/ViewTemplateBodyFilters/stylesheet $:/config/ViewTemplateBodyFilters/system $:/config/ViewTemplateBodyFilters/import $:/config/ViewTemplateBodyFilters/plugin $:/config/ViewTemplateBodyFilters/default - +list: $:/config/ViewTemplateBodyFilters/hide-body $:/config/ViewTemplateBodyFilters/code-body $:/config/ViewTemplateBodyFilters/stylesheet $:/config/ViewTemplateBodyFilters/core-ui-advanced-search $:/config/ViewTemplateBodyFilters/core-ui-tags $:/config/ViewTemplateBodyFilters/system $:/config/ViewTemplateBodyFilters/import $:/config/ViewTemplateBodyFilters/plugin $:/config/ViewTemplateBodyFilters/default \ No newline at end of file diff --git a/editions/de-AT/tiddlers/external/tiddlywiki.files b/editions/de-AT/tiddlers/external/tiddlywiki.files index 0c59b7bc5..fd714a0df 100644 --- a/editions/de-AT/tiddlers/external/tiddlywiki.files +++ b/editions/de-AT/tiddlers/external/tiddlywiki.files @@ -3,7 +3,7 @@ { "file": "../../../tw5.com/tiddlers/images/New Release Banner.png", "fields": { - "type": "image/jpg", + "type": "image/jpeg", "title": "New Release Banner", "tags": "picture" } diff --git a/editions/dev/tiddlers/Widget `destroy` method examples.tid b/editions/dev/tiddlers/Widget `destroy` method examples.tid new file mode 100644 index 000000000..5ff04bdd0 --- /dev/null +++ b/editions/dev/tiddlers/Widget `destroy` method examples.tid @@ -0,0 +1,36 @@ +created: 20230601123245916 +modified: 20230601125015463 +title: Widget `destroy` method examples +type: text/vnd.tiddlywiki + +!! When using a v-dom library + +Virtual DOM libraries manages its internal state and apply state to DOM periodically, this is so called [["controlled" component|https://react.dev/learn/sharing-state-between-components#controlled-and-uncontrolled-components]]. When Tiddlywiki remove a DOM element controlled by a v-dom library, it may throws error. + +So when creating a plugin providing v-dom library binding, you need to tell v-dom library (for example, React.js) the DOM element is removed. We will use `destroy` method for this. + +```js + render() { + // ...other render related code + if (this.root === undefined || this.containerElement === undefined) { + // initialize the v-dom library + this.root = ReactDom.createRoot(document.createElement('div')); + } + } + + destroy() { + // end the lifecycle of v-dom library + this.root && this.root.unmount(); + } +``` + +The `destroy` method will be called by parent widget. If you widget don't have any child widget, you can just write your own tear down logic. If it may have some child widget, don't forget to call original `destroy` method in the `Widget` class to destroy children widgets. + +```js +Widget.prototype.destroy(); +this.root && this.root.unmount(); +/** if you are using ESNext +super.destroy(); +this.root?.unmount(); +*/ +``` \ No newline at end of file diff --git a/editions/dev/tiddlers/from tw5.com/mechanisms/TestingMechanism.tid b/editions/dev/tiddlers/from tw5.com/mechanisms/TestingMechanism.tid index c334ab256..37659629f 100644 --- a/editions/dev/tiddlers/from tw5.com/mechanisms/TestingMechanism.tid +++ b/editions/dev/tiddlers/from tw5.com/mechanisms/TestingMechanism.tid @@ -2,7 +2,7 @@ modified: 20141013085608911 tags: Mechanisms title: TestingMechanism -TiddlyWiki5 incorporates the Jasmine JavaScript testing framework (see http://pivotal.github.io/jasmine/). It allows the same tests to be run both in the browser and under Node.js. +TiddlyWiki5 incorporates the Jasmine JavaScript testing framework (see https://jasmine.github.io/). It allows the same tests to be run both in the browser and under Node.js. ! TiddlyWiki5 Testing Components diff --git a/editions/dev/tiddlers/from tw5.com/moduletypes/WidgetModules.tid b/editions/dev/tiddlers/from tw5.com/moduletypes/WidgetModules.tid index 1a8bf5edf..0b0b3f33a 100644 --- a/editions/dev/tiddlers/from tw5.com/moduletypes/WidgetModules.tid +++ b/editions/dev/tiddlers/from tw5.com/moduletypes/WidgetModules.tid @@ -1,7 +1,8 @@ -title: WidgetModules +created: 20131101130700000 +modified: 20230601130631884 tags: dev moduletypes -created: 201311011307 -modified: 201311011307 +title: WidgetModules +type: text/vnd.tiddlywiki ! Introduction @@ -78,4 +79,10 @@ The individual methods defined by the widget object are documented in the source !! Widget `refreshChildren` method !! Widget `findNextSiblingDomNode` method !! Widget `findFirstDomNode` method +!! Widget `destroy` method + +<<.from-version "5.3.0">> Gets called when any parent widget is unmounted from the widget tree. + +[[Examples|Widget `destroy` method examples]] + !! Widget `removeChildDomNodes` method diff --git a/editions/dev/tiddlers/system/configWikiParserRulesInlineWikilink.tid b/editions/dev/tiddlers/system/configWikiParserRulesInlineWikilink.tid new file mode 100644 index 000000000..9a395abd6 --- /dev/null +++ b/editions/dev/tiddlers/system/configWikiParserRulesInlineWikilink.tid @@ -0,0 +1,3 @@ +title: $:/config/WikiParserRules/Inline/wikilink + +enable \ No newline at end of file diff --git a/editions/dev/tiddlers/system/doc-styles.tid b/editions/dev/tiddlers/system/doc-styles.tid new file mode 100644 index 000000000..24234d47a --- /dev/null +++ b/editions/dev/tiddlers/system/doc-styles.tid @@ -0,0 +1,40 @@ +created: 20150117152612000 +modified: 20230325101137075 +tags: $:/tags/Stylesheet +title: $:/editions/tw5.com/doc-styles +type: text/vnd.tiddlywiki + +a.doc-from-version.tc-tiddlylink { + display: inline-block; + border-radius: 1em; + background: <>; + color: <>; + fill: <>; + padding: 0 0.4em; + font-size: 0.7em; + text-transform: uppercase; + font-weight: bold; + line-height: 1.5; + vertical-align: text-bottom; +} + +a.doc-deprecated-version.tc-tiddlylink { + display: inline-block; + border-radius: 1em; + background: red; + color: <>; + fill: <>; + padding: 0 0.4em; + font-size: 0.7em; + text-transform: uppercase; + font-weight: bold; + line-height: 1.5; + vertical-align: text-bottom; +} + +.doc-deprecated-version svg, +.doc-from-version svg { + width: 1em; + height: 1em; + vertical-align: text-bottom; +} diff --git a/editions/dev/tiddlers/system/version-macros.tid b/editions/dev/tiddlers/system/version-macros.tid new file mode 100644 index 000000000..0fb7dcf12 --- /dev/null +++ b/editions/dev/tiddlers/system/version-macros.tid @@ -0,0 +1,14 @@ +code-body: yes +created: 20161008085627406 +modified: 20221007122259593 +tags: $:/tags/Macro +title: $:/editions/tw5.com/version-macros +type: text/vnd.tiddlywiki + +\define .from-version(version) +<$link to={{{ [<__version__>addprefix[Release ]] }}} class="doc-from-version">{{$:/core/images/warning}} New in: <$text text=<<__version__>>/> +\end + +\define .deprecated-since(version, superseded:"TODO-Link") +<$link to="Deprecated - What does it mean" class="doc-deprecated-version tc-btn-invisible">{{$:/core/images/warning}} Deprecated since: <$text text=<<__version__>>/> (see <$link to=<<__superseded__>>><$text text=<<__superseded__>>/>) +\end diff --git a/editions/es-ES/tiddlers/$__Acknowledgements.tid b/editions/es-ES/tiddlers/$__Acknowledgements.tid index 48074d928..d907bc9e6 100644 --- a/editions/es-ES/tiddlers/$__Acknowledgements.tid +++ b/editions/es-ES/tiddlers/$__Acknowledgements.tid @@ -6,7 +6,7 @@ type: text/vnd.tiddlywiki TiddlyWiki incorpora código de los siguientes proyectos OpenSource: * [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]] -* [[The Jasmine JavaScript Test Framework|http://pivotal.github.io/jasmine/]] +* [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]] * [[Normalize.css by Nicolas Gallagher|http://necolas.github.io/normalize.css/]] ...y materiales de estos otros proyectos: diff --git a/editions/fr-FR/tiddlers/$__Acknowledgements.tid b/editions/fr-FR/tiddlers/$__Acknowledgements.tid index 1adf217a0..6e9f6e0e5 100644 --- a/editions/fr-FR/tiddlers/$__Acknowledgements.tid +++ b/editions/fr-FR/tiddlers/$__Acknowledgements.tid @@ -6,7 +6,7 @@ type: text/vnd.tiddlywiki TiddlyWiki intègre du code provenant de ces excellents projets OpenSource<> * [[The Stanford Javascript Crypto Library|http://bitwiseshiftleft.github.io/sjcl/]] -* [[The Jasmine JavaScript Test Framework|http://pivotal.github.io/jasmine/]] +* [[The Jasmine JavaScript Test Framework|https://jasmine.github.io/]] * [[Normalize.css by Nicolas Gallagher|http://necolas.github.io/normalize.css/]] Et des contenus provenenant de ces sources<> diff --git a/editions/prerelease/tiddlers/Release 5.3.1.tid b/editions/prerelease/tiddlers/Release 5.3.1.tid new file mode 100644 index 000000000..dcea04fb5 --- /dev/null +++ b/editions/prerelease/tiddlers/Release 5.3.1.tid @@ -0,0 +1,57 @@ +caption: 5.3.1 +created: 20230701133439630 +modified: 20230701133439630 +tags: ReleaseNotes +title: Release 5.3.1 +type: text/vnd.tiddlywiki + +//[[See GitHub for detailed change history of this release|https://github.com/Jermolene/TiddlyWiki5/compare/v5.3.0...master]]// + +! Overview of v5.3.1 + + +! Plugin Improvements + +* + +! Translation improvement + +Improvements to the following translations: + +* Chinese +* Polish + +! Usability Improvements + +* + +! Widget Improvements + +* + +! Filter improvements + +* + +! Hackability Improvements + +* + +! Bug Fixes + +* + +! Node.js Improvements + +* + +! Performance 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/PrereleaseLocalPluginLibrary.tid b/editions/prerelease/tiddlers/system/PrereleaseLocalPluginLibrary.tid index d8f641602..a42bdc774 100644 --- a/editions/prerelease/tiddlers/system/PrereleaseLocalPluginLibrary.tid +++ b/editions/prerelease/tiddlers/system/PrereleaseLocalPluginLibrary.tid @@ -1,6 +1,6 @@ title: $:/config/LocalPluginLibrary tags: $:/tags/PluginLibrary -url: http://127.0.0.1:8080/prerelease/library/v5.2.2/index.html +url: http://127.0.0.1:8080/prerelease/library/v5.3.1/index.html caption: {{$:/language/OfficialPluginLibrary}} (Prerelease Local) A locally installed version of the official ~TiddlyWiki plugin library at tiddlywiki.com for testing and debugging. //Requires a local web server to share the library// diff --git a/editions/prerelease/tiddlers/system/PrereleaseOfficialPluginLibrary.tid b/editions/prerelease/tiddlers/system/PrereleaseOfficialPluginLibrary.tid index 510d876d0..555b81ab6 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.2.8/index.html +url: https://tiddlywiki.com/prerelease/library/v5.3.1/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/configWikiParserRulesInlineWikilink.tid b/editions/prerelease/tiddlers/system/configWikiParserRulesInlineWikilink.tid new file mode 100644 index 000000000..9a395abd6 --- /dev/null +++ b/editions/prerelease/tiddlers/system/configWikiParserRulesInlineWikilink.tid @@ -0,0 +1,3 @@ +title: $:/config/WikiParserRules/Inline/wikilink + +enable \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/deserializers/case 6.tid b/editions/test/tiddlers/tests/data/deserializers/case 6.tid new file mode 100644 index 000000000..b6e653c39 --- /dev/null +++ b/editions/test/tiddlers/tests/data/deserializers/case 6.tid @@ -0,0 +1,8 @@ +title: dezerializer test data case 6 +type: application/json + +[ + {"created":"20230601125557184","text":"Before you start storing important information in ~TiddlyWiki it is vital to make sure that you can reliably save changes. See https://tiddlywiki.com/#GettingStarted for details\n\n","title":"GettingStarted","modified":"20230601125601619"}, + {"created":"20230601125507054","text":"Welcome to \"TiddlyWiki\".\n\nThis is a test tiddler.","tags":"","title":"Hello There \"Welcome\"","modified":"20230601125551144"}, + {"title":"TiddlyWiki","created":"20130822170700000","modified":"20170127221451610","tags":"Concepts","type":"text/vnd.tiddlywiki","text":"~TiddlyWiki is a rich, interactive tool for manipulating complex data with structure that doesn't easily fit into conventional tools like spreadsheets or wordprocessors.\n\n~TiddlyWiki is designed to fit around your brain, helping you deal with the things that won't fit."} +] diff --git a/editions/test/tiddlers/tests/data/filters/substitute.tid b/editions/test/tiddlers/tests/data/filters/substitute.tid new file mode 100644 index 000000000..873d8e0ba --- /dev/null +++ b/editions/test/tiddlers/tests/data/filters/substitute.tid @@ -0,0 +1,40 @@ +title: Filters/substitute +description: Test substitute operator +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: substitute filter data 1 +tags: Hello There [[Welcome to TiddlyWiki]] GettingStarted + +TiddlyWiki ++ +title: substitute filter data 2 + +The output of the filter `[[substitute filter data 1]tags[]]` is ${[[substitute filter data 1]tags[]]}$. ++ +title: substitute filter data 3 + +Welcome to $(projectname)$ $1$ $2$ $3$. Tiddlers starting with `substitute`: ${[prefix[substitute]format:titlelist[]join[ ]]}$. ++ +title: Output + +\whitespace trim +<$let projectname="TiddlyWiki"> +(<$text text={{{ [[]substitute[]] }}}/>) +(<$text text={{{ [[Hello There, welcome to $TiddlyWiki$]substitute[]] }}}/>) +(<$text text={{{ [[Welcome to $(projectname)$]substitute[]] }}}/>) +(<$text text={{{ [[Welcome to $(projectname)$ $1$]substitute[today]] }}}/>) +(<$text text={{{ [[This is not a valid embedded filter ${ hello )$]substitute[]] }}}/>) +(<$text text={{{ [{substitute filter data 2}substitute[]] }}}/>) +(<$text text={{{ [{substitute filter data 3}substitute[every],[day]] }}}/>) + ++ +title: ExpectedResult + +

        () +(Hello There, welcome to $TiddlyWiki$) +(Welcome to TiddlyWiki) +(Welcome to TiddlyWiki today) +(This is not a valid embedded filter ${ hello )$) +(The output of the filter `[[substitute filter data 1]tags[]]` is Hello.) +(Welcome to TiddlyWiki every day $3$. Tiddlers starting with `substitute`: [[substitute filter data 1]] [[substitute filter data 2]] [[substitute filter data 3]].)

        \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/importvariables/WithSetWidgets.tid b/editions/test/tiddlers/tests/data/importvariables/WithSetWidgets.tid new file mode 100644 index 000000000..5d351583e --- /dev/null +++ b/editions/test/tiddlers/tests/data/importvariables/WithSetWidgets.tid @@ -0,0 +1,23 @@ +title: ImportVariables/WithSetWidgets +description: Import variables defined with a set widget +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\import Definitions +<$text text=<>/>, +<$text text=<>/> ++ +title: Definitions + +\whitespace trim +<$set name="one" value="elephant"> +<$set name="two" value="giraffe"> + + ++ +title: ExpectedResult + +

        elephant,giraffe

        \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/importvariables/WithSetWidgets2.tid b/editions/test/tiddlers/tests/data/importvariables/WithSetWidgets2.tid new file mode 100644 index 000000000..23fa58c57 --- /dev/null +++ b/editions/test/tiddlers/tests/data/importvariables/WithSetWidgets2.tid @@ -0,0 +1,22 @@ +title: ImportVariables/WithSetWidgets2 +description: Import variables defined with a set widget without whitespace pragma +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\import Definitions +<$text text=<>/>, +<$text text=<>/> ++ +title: Definitions + +<$set name="one" value="elephant"> +<$set name="two" value="giraffe"> + + ++ +title: ExpectedResult + +

        elephant,giraffe

        \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/importvariables/WithSetWidgetsAndMacros.tid b/editions/test/tiddlers/tests/data/importvariables/WithSetWidgetsAndMacros.tid new file mode 100644 index 000000000..eaa81f38c --- /dev/null +++ b/editions/test/tiddlers/tests/data/importvariables/WithSetWidgetsAndMacros.tid @@ -0,0 +1,29 @@ +title: ImportVariables/WithSetWidgetsAndMacros +description: Import variables defined with a set widget without whitespace pragma +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +\import Definitions +<$text text=<>/>, +<$text text=<
        >/>, +<$text text=<>/>, +<$text text=<>/> ++ +title: Definitions + +\define name() Bugs Bunny +\procedure address() +Bunny Hill +\end + +<$set name="one" value="elephant"> +<$set name="two" value="giraffe"> + + ++ +title: ExpectedResult + +

        Bugs Bunny,Bunny Hill,elephant,giraffe

        \ No newline at end of file diff --git a/editions/test/tiddlers/tests/data/widgets/SubstitutedAttributes.tid b/editions/test/tiddlers/tests/data/widgets/SubstitutedAttributes.tid new file mode 100644 index 000000000..9d5538573 --- /dev/null +++ b/editions/test/tiddlers/tests/data/widgets/SubstitutedAttributes.tid @@ -0,0 +1,21 @@ +title: Widgets/SubstitutedAttributes +description: Attributes specified as string that should have substitution performed. +type: text/vnd.tiddlywiki-multiple +tags: [[$:/tags/wiki-test-spec]] + +title: Output + +\whitespace trim +<$set name="var with spaces" value="spaces"> +<$let project="TiddlyWiki" disabled="true" var-with-dashes="dashes"> +
        +
        + + + ++ +title: ExpectedResult + +

        \ No newline at end of file diff --git a/editions/test/tiddlers/tests/test-deserialize-operator.js b/editions/test/tiddlers/tests/test-deserialize-operator.js new file mode 100644 index 000000000..c629de3ae --- /dev/null +++ b/editions/test/tiddlers/tests/test-deserialize-operator.js @@ -0,0 +1,44 @@ +/*\ +title: test-deserialize-operator.js +type: application/javascript +tags: [[$:/tags/test-spec]] + +Tests deserialize[] filter operator with various core deserializers + +\*/ +(function(){ + + /* jslint node: true, browser: true */ + /* eslint-env node, browser, jasmine */ + /* eslint no-mixed-spaces-and-tabs: ["error", "smart-tabs"]*/ + /* global $tw, require */ + "use strict"; + + + describe("deserialize operator tests", function() { + + it("should support the deserialize[] operator", function() { + //Unknown deserializer as operand + expect($tw.wiki.filterTiddlers("[{dezerializer test data case 4}deserialize[unknown/deserializer]]")).toEqual([$tw.language.getString("Error/DeserializeOperator/UnknownDeserializer")]); + + //Missing operand + expect($tw.wiki.filterTiddlers("[{dezerializer test data case 4}deserialize[]]")).toEqual([$tw.language.getString("Error/DeserializeOperator/MissingOperand")]); + + //Deserialize TiddlyWiki file + expect($tw.wiki.filterTiddlers("[{dezerializer test data case 4}deserialize[text/html]]")).toEqual(['[{"type":"text/vnd.tiddlywiki","text":"Abacus","title":"Hello \\"There\\""},{"title":"Hello \\"There\\"","text":"Calculator"}]']); + expect($tw.wiki.filterTiddlers("[{dezerializer test data case 5}deserialize[text/html]]")).toEqual(['[{"type":"text/vnd.tiddlywiki","text":"Abacus","title":"Hello \\"There\\""},{"title":"Hello \\"There\\"","text":"Calculator"},{"title":"Hello \\"There\\"","text":"Protractor"}]']); + + // Deserialize JSON payload containing tiddlers + expect($tw.wiki.filterTiddlers("[{dezerializer test data case 6}deserialize[application/json]]")).toEqual( [ `[{"created":"20230601125557184","text":"Before you start storing important information in ~TiddlyWiki it is vital to make sure that you can reliably save changes. See https://tiddlywiki.com/#GettingStarted for details\\n\\n","title":"GettingStarted","modified":"20230601125601619"},{"created":"20230601125507054","text":"Welcome to \\"TiddlyWiki\\".\\n\\nThis is a test tiddler.","tags":"","title":"Hello There \\"Welcome\\"","modified":"20230601125551144"},{"title":"TiddlyWiki","created":"20130822170700000","modified":"20170127221451610","tags":"Concepts","type":"text/vnd.tiddlywiki","text":"~TiddlyWiki is a rich, interactive tool for manipulating complex data with structure that doesn't easily fit into conventional tools like spreadsheets or wordprocessors.\\n\\n~TiddlyWiki is designed to fit around your brain, helping you deal with the things that won't fit."}]` ]); + expect($tw.wiki.filterTiddlers("[{dezerializer test data case 6}deserialize[application/json]jsonindexes[]] :map[{dezerializer test data case 6}jsonget,[title]]")).toEqual([ 'GettingStarted', 'Hello There "Welcome"', 'TiddlyWiki' ]); + + //Deserialize TiddlyWiki file with an mismatched deserializer + expect($tw.wiki.filterTiddlers("[{dezerializer test data case 5}deserialize[application/json]]")).toEqual([jasmine.stringMatching('JSON error')]); + }); + }); + +})(); + + + + \ 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 49a790166..9e2f53b1a 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -99,14 +99,14 @@ Tests the filtering mechanism. }, "TiddlerSix": { title: "TiddlerSix", - text: "Missing inaction from TiddlerOne", + text: "Missing inaction from [[TiddlerOne]]", filter: "[[one]] [[a a]] [subfilter{hasList!!list}]", tags: [] }, "TiddlerSeventh": { title: "TiddlerSeventh", text: "", - list: "TiddlerOne [[Tiddler Three]] [[a fourth tiddler]] MissingTiddler", + list: "[[TiddlerOne]] [[Tiddler Three]] [[a fourth tiddler]] [[MissingTiddler]]", tags: ["one"] }, "Tiddler8": { @@ -144,7 +144,7 @@ Tests the filtering mechanism. modified: "201304152211" },{ title: "Tiddler Three", - text: "The speed of sound in light\n\nThere is no TiddlerZero but TiddlerSix", + text: "The speed of sound in light\n\nThere is no [[TiddlerZero]] but [[TiddlerSix]]", tags: ["one","two"], cost: "56", value: "80", @@ -252,9 +252,9 @@ Tests the filtering mechanism. }); it("should handle the lookup operator", function() { - expect(wiki.filterTiddlers("Six Seventh 8 +[lookup[Tiddler]]").join(",")).toBe("Missing inaction from TiddlerOne,,Tidd"); - expect(wiki.filterTiddlers("Six Seventh 8 +[lookup:8[Tiddler]]").join(",")).toBe("Missing inaction from TiddlerOne,8,Tidd"); - expect(wiki.filterTiddlers("Six Seventh 8 +[lookup:8[Tiddler],[text]]").join(",")).toBe("Missing inaction from TiddlerOne,8,Tidd"); + expect(wiki.filterTiddlers("Six Seventh 8 +[lookup[Tiddler]]").join(",")).toBe("Missing inaction from [[TiddlerOne]],,Tidd"); + expect(wiki.filterTiddlers("Six Seventh 8 +[lookup:8[Tiddler]]").join(",")).toBe("Missing inaction from [[TiddlerOne]],8,Tidd"); + expect(wiki.filterTiddlers("Six Seventh 8 +[lookup:8[Tiddler],[text]]").join(",")).toBe("Missing inaction from [[TiddlerOne]],8,Tidd"); expect(wiki.filterTiddlers("Six Seventh 8 +[lookup[Tiddler],[tags]]").join(",")).toBe(",one,one"); }); @@ -397,8 +397,8 @@ Tests the filtering mechanism. expect(wiki.filterTiddlers("[all[shadows]tag[two]]").join(",")).toBe("$:/TiddlerFive"); expect(wiki.filterTiddlers("[all[shadows+tiddlers]tag[two]]").join(",")).toBe("$:/TiddlerFive,$:/TiddlerTwo,Tiddler Three"); expect(wiki.filterTiddlers("[all[tiddlers+shadows]tag[two]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three,$:/TiddlerFive"); - expect(wiki.filterTiddlers("[all[shadows+tiddlers]]").join(",")).toBe("$:/TiddlerFive,TiddlerSix,TiddlerSeventh,Tiddler8,$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three,TiddlerOne"); - expect(wiki.filterTiddlers("[all[tiddlers+shadows]]").join(",")).toBe("$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three,TiddlerOne,$:/TiddlerFive,TiddlerSix,TiddlerSeventh,Tiddler8"); + expect(wiki.filterTiddlers("[all[shadows+tiddlers]]").join(",")).toBe("$:/TiddlerFive,Tiddler8,TiddlerSeventh,TiddlerSix,$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[all[tiddlers+shadows]]").join(",")).toBe("$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,one,Tiddler Three,TiddlerOne,$:/TiddlerFive,Tiddler8,TiddlerSeventh,TiddlerSix"); expect(wiki.filterTiddlers("[all[tiddlers]tag[two]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three"); expect(wiki.filterTiddlers("[all[orphans+tiddlers+tags]]").join(",")).toBe("$:/ShadowPlugin,$:/TiddlerTwo,a fourth tiddler,filter regexp test,has filter,hasList,Tiddler Three,TiddlerOne,two,one"); }); @@ -420,10 +420,10 @@ Tests the filtering mechanism. it("should handle the tagging operator", function() { expect(wiki.filterTiddlers("[[one]tagging[]sort[title]]").join(",")).toBe("Tiddler Three,Tiddler8,TiddlerOne,TiddlerSeventh"); - expect(wiki.filterTiddlers("[[one]tagging[]]").join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8"); + expect(wiki.filterTiddlers("[[one]tagging[]]").join(",")).toBe("Tiddler Three,TiddlerOne,Tiddler8,TiddlerSeventh"); expect(wiki.filterTiddlers("[[two]tagging[]sort[title]]").join(",")).toBe("$:/TiddlerFive,$:/TiddlerTwo,Tiddler Three"); var fakeWidget = {wiki: wiki, getVariable: function(name) {return name === "currentTiddler" ? "one": undefined;}}; - expect(wiki.filterTiddlers("[all[current]tagging[]]",fakeWidget).join(",")).toBe("Tiddler Three,TiddlerOne,TiddlerSeventh,Tiddler8"); + expect(wiki.filterTiddlers("[all[current]tagging[]]",fakeWidget).join(",")).toBe("Tiddler Three,TiddlerOne,Tiddler8,TiddlerSeventh"); }); it("should handle the untagged operator", function() { @@ -990,10 +990,10 @@ Tests the filtering mechanism. expect(wiki.filterTiddlers("[!sortsub:number]",anchorWidget).join(",")).toBe("filter regexp test,a fourth tiddler,$:/ShadowPlugin,$:/TiddlerTwo,Tiddler Three,has filter,TiddlerOne,hasList,one"); expect(wiki.filterTiddlers("[sortsub:string]",anchorWidget).join(",")).toBe("has filter,TiddlerOne,$:/TiddlerTwo,Tiddler Three,$:/ShadowPlugin,a fourth tiddler,filter regexp test,one,hasList"); expect(wiki.filterTiddlers("[!sortsub:string]",anchorWidget).join(",")).toBe("hasList,one,filter regexp test,a fourth tiddler,$:/ShadowPlugin,$:/TiddlerTwo,Tiddler Three,has filter,TiddlerOne"); - expect(wiki.filterTiddlers("[sortsub:number]",anchorWidget).join(",")).toBe("one,TiddlerOne,hasList,has filter,a fourth tiddler,Tiddler Three,$:/TiddlerTwo,filter regexp test,$:/ShadowPlugin"); - expect(wiki.filterTiddlers("[!sortsub:number]",anchorWidget).join(",")).toBe("$:/ShadowPlugin,filter regexp test,$:/TiddlerTwo,Tiddler Three,a fourth tiddler,has filter,hasList,TiddlerOne,one"); - expect(wiki.filterTiddlers("[sortsub:string]",anchorWidget).join(",")).toBe("one,TiddlerOne,hasList,has filter,$:/ShadowPlugin,a fourth tiddler,Tiddler Three,$:/TiddlerTwo,filter regexp test"); - expect(wiki.filterTiddlers("[!sortsub:string]",anchorWidget).join(",")).toBe("filter regexp test,$:/TiddlerTwo,Tiddler Three,a fourth tiddler,$:/ShadowPlugin,has filter,hasList,TiddlerOne,one"); + expect(wiki.filterTiddlers("[sortsub:number]",anchorWidget).join(",")).toBe("one,TiddlerOne,hasList,has filter,a fourth tiddler,$:/TiddlerTwo,Tiddler Three,filter regexp test,$:/ShadowPlugin"); + expect(wiki.filterTiddlers("[!sortsub:number]",anchorWidget).join(",")).toBe("$:/ShadowPlugin,filter regexp test,Tiddler Three,$:/TiddlerTwo,a fourth tiddler,has filter,hasList,TiddlerOne,one"); + expect(wiki.filterTiddlers("[sortsub:string]",anchorWidget).join(",")).toBe("one,TiddlerOne,hasList,has filter,$:/ShadowPlugin,a fourth tiddler,$:/TiddlerTwo,Tiddler Three,filter regexp test"); + expect(wiki.filterTiddlers("[!sortsub:string]",anchorWidget).join(",")).toBe("filter regexp test,Tiddler Three,$:/TiddlerTwo,a fourth tiddler,$:/ShadowPlugin,has filter,hasList,TiddlerOne,one"); expect(wiki.filterTiddlers("[[TiddlerOne]] [[$:/TiddlerTwo]] [[Tiddler Three]] [[a fourth tiddler]] +[!sortsub:number]",anchorWidget).join(",")).toBe("$:/TiddlerTwo,Tiddler Three,TiddlerOne,a fourth tiddler"); expect(wiki.filterTiddlers("a1 a10 a2 a3 b10 b3 b1 c9 c11 c1 +[sortsub:alphanumeric]",anchorWidget).join(",")).toBe("a1,a2,a3,a10,b1,b3,b10,c1,c9,c11"); // #7155. The order of the output is the same as the input when an undefined variable is used in the subfitler @@ -1066,7 +1066,11 @@ Tests the filtering mechanism. }); it("should handle the deserializers operator", function() { - expect(wiki.filterTiddlers("[deserializers[]]").join(",")).toBe("application/javascript,application/json,application/x-tiddler,application/x-tiddler-html-div,application/x-tiddlers,text/css,text/html,text/plain"); + var expectedDeserializers = ["application/javascript","application/json","application/x-tiddler","application/x-tiddler-html-div","application/x-tiddlers","text/css","text/html","text/plain"]; + if($tw.browser) { + expectedDeserializers.unshift("(DOM)"); + } + expect(wiki.filterTiddlers("[deserializers[]]").join(",")).toBe(expectedDeserializers.join(",")); }); it("should handle the charcode operator", function() { diff --git a/editions/test/tiddlers/tests/test-html-parser.js b/editions/test/tiddlers/tests/test-html-parser.js index cdc8dee47..d2266ca5e 100644 --- a/editions/test/tiddlers/tests/test-html-parser.js +++ b/editions/test/tiddlers/tests/test-html-parser.js @@ -161,6 +161,16 @@ describe("HTML tag new parser tests", function() { expect($tw.utils.parseAttribute(" attrib1>",0)).toEqual( { type : 'string', value : 'true', start : 0, name : 'attrib1', end : 8 } ); + expect($tw.utils.parseAttribute("p=`blah` ",1)).toEqual(null); + expect($tw.utils.parseAttribute("p=`blah` ",0)).toEqual( + { start: 0, name: 'p', type: 'substituted', rawValue: 'blah', end: 8 } + ); + expect($tw.utils.parseAttribute("p=```blah``` ",0)).toEqual( + { start: 0, name: 'p', type: 'substituted', rawValue: 'blah', end: 12 } + ); + expect($tw.utils.parseAttribute("p=`Hello \"There\"`",0)).toEqual( + { start: 0, name: 'p', type: 'substituted', rawValue: 'Hello "There"', end: 17 } + ); }); it("should parse HTML tags", function() { diff --git a/editions/test/tiddlers/tests/test-prefixes-filter.js b/editions/test/tiddlers/tests/test-prefixes-filter.js index 62f329d66..a8d109d73 100644 --- a/editions/test/tiddlers/tests/test-prefixes-filter.js +++ b/editions/test/tiddlers/tests/test-prefixes-filter.js @@ -434,6 +434,15 @@ describe("'reduce' and 'intersection' filter prefix tests", function() { expect(wiki.filterTiddlers("[tag[shopping]] :map[get[title]addprefix[-]addprefixaddprefix[of]addprefix]").join(",")).toBe("0of4-Brownies,1of4-Chick Peas,2of4-Milk,3of4-Rice Pudding"); }); + it("should handle the :then prefix", function() { + expect(wiki.filterTiddlers("[[one]] :then[[two]]").join(",")).toBe("two"); + expect(wiki.filterTiddlers("[[one]] :then[tag[shopping]]").join(",")).toBe("Brownies,Chick Peas,Milk,Rice Pudding"); + expect(wiki.filterTiddlers("[[one]] [[two]] [[three]] :then[[four]]").join(",")).toBe("four"); + expect(wiki.filterTiddlers("[[one]] :then[tag[nonexistent]]").join(",")).toBe("one"); + expect(wiki.filterTiddlers(":then[[two]]").length).toBe(0); + expect(wiki.filterTiddlers("[[notatiddler]is[tiddler]] :then[[two]]").length).toBe(0); + }); + it("should handle macro parameters for filter run prefixes",function() { var widget = require("$:/core/modules/widgets/widget.js"); var rootWidget = new widget.widget({ type:"widget", children:[ {type:"widget", children:[]} ] }, diff --git a/editions/test/tiddlers/tests/test-wikitext.js b/editions/test/tiddlers/tests/test-wikitext.js index cdd729cfc..eddef73f7 100644 --- a/editions/test/tiddlers/tests/test-wikitext.js +++ b/editions/test/tiddlers/tests/test-wikitext.js @@ -45,16 +45,6 @@ describe("WikiText tests", function() { it("should support attributes specified as macro invocations", function() { expect(wiki.renderTiddler("text/html","TiddlerFour")).toBe("

        This is a link

        "); }); - it("should identify wikiwords to automatically link", function() { - expect(wiki.renderText("text/html","text/vnd-tiddlywiki","No wikilinks here").indexOf("header"); expect(wiki.renderText("text/html","text/vnd-tiddlywiki","@@.myclass\n
        \n\nContent
        \n@@")).toBe("

        Content

        "); diff --git a/editions/tw5.com/tiddlers/concepts/ShadowTiddlers.tid b/editions/tw5.com/tiddlers/concepts/ShadowTiddlers.tid index 11abdd2e9..89417bc65 100644 --- a/editions/tw5.com/tiddlers/concepts/ShadowTiddlers.tid +++ b/editions/tw5.com/tiddlers/concepts/ShadowTiddlers.tid @@ -9,9 +9,13 @@ tags: Concepts <$button actions=<>>$text$ \end -ShadowTiddlers are tiddlers that are loaded from within [[Plugins]]. Unlike ordinary tiddlers, they don't appear in most lists. +ShadowTiddlers are tiddlers that are loaded from [[Plugins]] at the wiki startup. Unlike ordinary tiddlers, they don't appear in most lists. -ShadowTiddlers can be overridden with an ordinary tiddler of the same name. If that tiddler is subsequently deleted then the original shadow tiddler is automatically restored. +!! Overriding Shadow Tiddlers to modify plugins + +A ShadowTiddler can be overridden with an ordinary tiddler of the same name. This leaves the shadow tiddler intact but the plugin will use the overriding tiddler in its place, effectively allowing users to modify the behaviour of plugins. + +Users are cautioned against overriding shadow tiddlers because if the shadow tiddler is changed in a plugin update, the overriding tiddler may no longer perform as intended. To remedy this, the overriding tiddler may be modified or deleted. If the overriding tiddler is deleted, then the plugin falls back to using the original shadow tiddler. !! Overridden Shadow Tiddlers diff --git a/editions/tw5.com/tiddlers/features/Deserializers.tid b/editions/tw5.com/tiddlers/features/Deserializers.tid new file mode 100644 index 000000000..1da4813df --- /dev/null +++ b/editions/tw5.com/tiddlers/features/Deserializers.tid @@ -0,0 +1,20 @@ +created: 20230627093650105 +modified: 20230627094356394 +tags: Features +title: Deserializers +type: text/vnd.tiddlywiki + +Deserializer [[modules|Modules]] parse text in various formats into their JSON representation as tiddlers. The deserializer modules available in a wiki can be seen using the [[deserializers operator|deserializers Operator]] and can be used with the [[deserialize Operator]]. + +The TiddlyWiki core provides the following deserializers: + +|!Deserializer |!Description | +|(DOM)|Extracts tiddlers from a DOM node, should not be used with the <<.op deserialize[]>> operator | +|application/javascript|Parses a JavaScript module as a tiddler extracting fields from the header comment| +|application/json|Parses [[JSON|JSON in TiddlyWiki]] into tiddlers| +|application/x-tiddler|Parses the [[.tid file format|TiddlerFiles]] as a tiddler| +|application/x-tiddler-html-div|Parses the [[
        .tiddler file format|TiddlerFiles]] as a tiddler| +|application/x-tiddlers|Parses the [[MultiTiddlerFile format|MultiTiddlerFiles]] as tiddlers| +|text/css|Parses CSS as a tiddler extracting fields from the header comment| +|text/html|Parses an HTML file into tiddlers. Supports ~TiddlyWiki Classic HTML files, ~TiddlyWiki5 HTML files and ordinary HTML files| +|text/plain|Parses plain text as a tiddler| \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/Conditional Operators.tid b/editions/tw5.com/tiddlers/filters/Conditional Operators.tid index 7028e6dad..6d21e0864 100644 --- a/editions/tw5.com/tiddlers/filters/Conditional Operators.tid +++ b/editions/tw5.com/tiddlers/filters/Conditional Operators.tid @@ -1,12 +1,12 @@ created: 20190802113703788 -modified: 20190802132727925 +modified: 20230501175143648 tags: Filters title: Conditional Operators type: text/vnd.tiddlywiki -<<.from-version "5.1.20">>The conditional filter operators allow ''if-then-else'' logic to be expressed within filters. +<<.from-version "5.1.20">>The conditional filter operators allow for ''if-then-else'' logic to be expressed within filters. -The foundation is the convention that an empty list can be used to represent the boolean value ''false'' and a list with at one (or more) entries to represent ''true''. +The foundation is the convention that an empty list can be used to represent the Boolean value <<.value false>> and a list with at one (or more) entries to represent <<.value true>>. The conditional operators are: @@ -19,10 +19,12 @@ The conditional operators are: These operators can be combined. For example: -<<.inline-operator-example "[[New Tiddler]is[missing]then[I am missing]else[No I am not missing]]">> +<<.operator-example 1 "[[New Tiddler]is[missing]then[I am missing]else[No I am not missing]]">> -The [[else Operator]] can be used to apply a defaults for missing values. In this example, we take advantage of the fact that the [[get Operator]] returns an empty list if the field or tiddler does not exist: +The <<.olink else>> operator can be used to apply a defaults for missing values. In this example, we take advantage of the fact that the <<.olink get>> operator returns an empty list if the field or tiddler does not exist: -<<.inline-operator-example "[[HelloThere]get[custom-field]else[default-value]]">> +<<.operator-example 2 "[[HelloThere]get[custom-field]else[default-value]]">> -<> +! Filter Run Prefixes + +The [[:then|:then Filter Run Prefix]] and [[:else|:else Filter Run Prefix]] filter run prefixes serve a similar purpose as the conditional operators. Refer to their documentation for more information. \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/deserialize Operator.tid b/editions/tw5.com/tiddlers/filters/deserialize Operator.tid new file mode 100644 index 000000000..7205d09c6 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/deserialize Operator.tid @@ -0,0 +1,17 @@ +caption: deserialize +created: 20230601195749377 +from-version: 5.3.0 +modified: 20230627094109762 +op-input: a selection of strings +op-output: JSON representations of tiddlers extracted from input titles. +op-parameter: the deserializer module to be used to extract tiddlers from the input +op-purpose: extract JSON representation of tiddlers from the input strings +tags: [[Filter Operators]] [[Special Operators]] +title: deserialize Operator +type: text/vnd.tiddlywiki + +{{Deserializers}} + + + +<<.operator-examples "deserialize">> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/deserializers Operator.tid b/editions/tw5.com/tiddlers/filters/deserializers Operator.tid index 2501553e8..7691be970 100644 --- a/editions/tw5.com/tiddlers/filters/deserializers Operator.tid +++ b/editions/tw5.com/tiddlers/filters/deserializers Operator.tid @@ -1,14 +1,14 @@ caption: deserializers created: 20210506115203172 from-version: 5.2.0 -modified: 20210506130322593 +modified: 20230627094238610 op-input: ignored op-output: the title of each available deserializer op-parameter: none -tags: [[Filter Operators]] [[Special Operators]] [[Selection Constructors]] +tags: [[Filter Operators]] [[Special Operators]] [[Selection Constructors]] title: deserializers Operator type: text/vnd.tiddlywiki -<<.tip "You can specify a specific deserializer for a DropzoneWidget to use">> +<<.tip "You can specify a specific [[deserializer|Deserializers]] for a DropzoneWidget to use">> <<.operator-examples "deserializers">> diff --git a/editions/tw5.com/tiddlers/filters/examples/deserialize Operator (Examples).tid b/editions/tw5.com/tiddlers/filters/examples/deserialize Operator (Examples).tid new file mode 100644 index 000000000..5170809b7 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/deserialize Operator (Examples).tid @@ -0,0 +1,29 @@ +created: 20230601200356736 +modified: 20230602105036887 +tags: [[Operator Examples]] [[deserialize Operator]] +title: deserialize Operator (Examples) +type: text/vnd.tiddlywiki + +\define html-data() + + + + + Test Data + + + + + + + + +\end + +This example uses the predefined variable `html-data`: +<$codeblock code=<> language="HTML"/> + +<<.operator-example 1 "[deserialize[text/html]]">> diff --git a/editions/tw5.com/tiddlers/filters/examples/substitute Operator (Examples).tid b/editions/tw5.com/tiddlers/filters/examples/substitute Operator (Examples).tid new file mode 100644 index 000000000..ece8aecf6 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/substitute Operator (Examples).tid @@ -0,0 +1,37 @@ +created: 20230614225302905 +modified: 20230614233448662 +tags: [[Operator Examples]] [[substitute Operator]] +title: substitute Operator (Examples) +type: text/vnd.tiddlywiki + +\define time() morning +\define field() modified +\procedure sentence() This tiddler was last $(field)$ on ${[{!!modified}format:date[DDth MMM YYYY]]}$ +\define name() Bugs Bunny +\define address() Rabbit Hole Hill + +!Substitute <<.op substitute[]>> operator parameters +<<.operator-example 1 "[[Hi, I'm $1$ and I live in $2$]substitute[Bugs Bunny],[Rabbit Hole Hill]]">> + +!Substitute variables +This example uses the following variables: + +* name: <$codeblock code=<>/> +* address: <$codeblock code=<
        >/> + +<<.operator-example 2 "[[Hi, I'm $(name)$ and I live in $(address)$]substitute[]]">> + +!Substitute variables and operator parameters +This example uses the following variable: + +* time: <$codeblock code=<