From 0cf5dc699ee76ed0800698923d6b5d470378370f Mon Sep 17 00:00:00 2001 From: Jeremy Ruston Date: Sat, 25 May 2013 17:26:22 +0100 Subject: [PATCH] Refactor the filter mechanism Long overdue rewrite to make it simpler, and break the filter operators out into individual modules. --- core/modules/filters.js | 334 ++++-------------- core/modules/filters/backlinks.js | 37 ++ core/modules/filters/field.js | 46 +++ core/modules/filters/has.js | 46 +++ core/modules/filters/is.js | 39 ++ core/modules/filters/is/current.js | 49 +++ core/modules/filters/is/missing.js | 48 +++ core/modules/filters/is/orphan.js | 46 +++ core/modules/filters/is/shadow.js | 49 +++ core/modules/filters/is/system.js | 43 +++ core/modules/filters/limit.js | 38 ++ core/modules/filters/links.js | 37 ++ core/modules/filters/list.js | 47 +++ core/modules/filters/prefix.js | 46 +++ core/modules/filters/searchVia.js | 28 ++ core/modules/filters/sort.js | 32 ++ core/modules/filters/sortcs.js | 32 ++ core/modules/filters/tag.js | 46 +++ core/modules/filters/tagging.js | 37 ++ core/modules/filters/tags.js | 40 +++ core/modules/filters/title.js | 53 +++ editions/test/tiddlers/tests/test-filters.js | 181 ++++++++++ .../system/tiddlywiki2.template.html.tid | 4 +- .../tiddlers/concepts/TiddlerFilters.tid | 2 +- 24 files changed, 1089 insertions(+), 271 deletions(-) create mode 100644 core/modules/filters/backlinks.js create mode 100644 core/modules/filters/field.js create mode 100644 core/modules/filters/has.js create mode 100644 core/modules/filters/is.js create mode 100644 core/modules/filters/is/current.js create mode 100644 core/modules/filters/is/missing.js create mode 100644 core/modules/filters/is/orphan.js create mode 100644 core/modules/filters/is/shadow.js create mode 100644 core/modules/filters/is/system.js create mode 100644 core/modules/filters/limit.js create mode 100644 core/modules/filters/links.js create mode 100644 core/modules/filters/list.js create mode 100644 core/modules/filters/prefix.js create mode 100644 core/modules/filters/searchVia.js create mode 100644 core/modules/filters/sort.js create mode 100644 core/modules/filters/sortcs.js create mode 100644 core/modules/filters/tag.js create mode 100644 core/modules/filters/tagging.js create mode 100644 core/modules/filters/tags.js create mode 100644 core/modules/filters/title.js create mode 100644 editions/test/tiddlers/tests/test-filters.js diff --git a/core/modules/filters.js b/core/modules/filters.js index cc5deae90..8ed99d18c 100644 --- a/core/modules/filters.js +++ b/core/modules/filters.js @@ -12,274 +12,6 @@ Adds tiddler filtering to the $tw.Wiki object. /*global $tw: false */ "use strict"; -exports.filterTiddlers = function(filterString,currTiddlerTitle,tiddlerList) { - var fn = this.compileFilter(filterString); - return fn.call(this,tiddlerList || this.tiddlers,currTiddlerTitle); -}; - -/* -Compiling a filter gives a JavaScript function that is invoked as `this.filter(source)`, where `source` is a hashmap of source tiddler titles (the values don't matter, so it is possible to use a store or a changes object). It returns an array of tiddler titles that satisfy the filter -*/ -exports.compileFilter = function(filterString) { - var filter = this.parseFilter(filterString), - output = [], - t,operation,operationInfo,type,p,operator,operatorInfo,fn; - output.push(this.filterFragments.prologue); - for(t=0; t=0; r--) {if(title.substr(0," + operator.operand.length + ")" + op + "==\"" + $tw.utils.stringify(operator.operand) + "\") {subResults.splice(r,1);}}"; - } - }, - "is": { // Filter by status - selector: function(operator) { - var op = operator.prefix === "!" ? "!" : ""; - switch(operator.operand) { - case "current": - if(operator.prefix === "!") { - return "for(title in source) {if(title !== currTiddlerTitle) {$tw.utils.pushTop(subResults,title);}}"; - } else { - return "$tw.utils.pushTop(subResults,currTiddlerTitle);"; - } - break; - case "system": - return "for(title in source) {if(" + op + "this.isSystemTiddler(title)) {$tw.utils.pushTop(subResults,title);}}"; - case "shadow": - if(operator.prefix === "!") { - return "for(title in source) {if(!this.isShadowTiddler(title)) {$tw.utils.pushTop(subResults,title);}}"; - } else { - return "for(title in this.shadowTiddlers) {$tw.utils.pushTop(subResults,title);}"; - } - case "missing": - if(operator.prefix === "!") { - return "for(title in source) {$tw.utils.pushTop(subResults,title);}"; - } else { - return "var m = this.getMissingTitles(); for(t=0; t=0; r--) {if(subResults[r] === currTiddlerTitle) {subResults.splice(r,1);}}"; - } else { - return "r = subResults.indexOf(currTiddlerTitle);\nif(r !== -1) {subResults = [currTiddlerTitle];} else {subResults = [];}"; - } - break; - case "system": - return "for(r=subResults.length-1; r>=0; r--) {if(" + op + "this.isSystemTiddler(subResults[r])) {subResults.splice(r,1);}}"; - case "shadow": - return "for(r=subResults.length-1; r>=0; r--) {if(" + op + "this.isShadowTiddler(subResults[r])) {subResults.splice(r,1);}}"; - case "missing": - return "t = this.getMissingTitles(); for(r=subResults.length-1; r>=0; r--) {if(" + op + "!$tw.utils.hop(t,subResults[r])) {subResults.splice(r,1);}}"; - case "orphan": - if(operator.prefix === "!") { - return "t = this.getOrphanTitles(); for(r=subResults.length-1; r>=0; r--) {if(t.indexOf(subResults[r]) === -1) {subResults.splice(r,1);}}"; - } else { - return "t = this.getOrphanTitles(); for(r=subResults.length-1; r>=0; r--) {if(t.indexOf(subResults[r]) !== -1) {subResults.splice(r,1);}}"; - } - default: - throw "Unknown operand for 'is' filter operator"; - } - } - }, - "tag": { // Filter by tag - selector: function(operator) { - var op = operator.prefix === "!" ? "!" : ""; - return "for(title in source) {if(" + op + "this.getTiddler(title).hasTag(\"" + $tw.utils.stringify(operator.operand) + "\")) {$tw.utils.pushTop(subResults,title);}}"; - }, - filter: function(operator) { - var op = operator.prefix === "!" ? "" : "!"; - return "for(r=subResults.length-1; r>=0; r--) {if(" + op + "this.getTiddler(subResults[r]).hasTag(\"" + $tw.utils.stringify(operator.operand) + "\")) {subResults.splice(r,1);}}"; - } - }, - "tags": { // Return all tags used on selected tiddlers - selector: function(operator) { - return "for(title in source) {r = this.getTiddler(title); if(r && r.fields.tags) {$tw.utils.pushTop(subResults,r.fields.tags);}}"; - }, - filter: function(operator) { - return "subResultsTemp = subResults;\nsubResults = [];for(t=subResultsTemp.length-1; t>=0; t--) {r = this.getTiddler(subResultsTemp[t]); if(r && r.fields.tags) {$tw.utils.pushTop(subResults,r.fields.tags);}}"; - } - }, - "tagging": { // Return all tiddlers tagged with any of the selected tags - selector: function(operator) { - return "for(title in source) {$tw.utils.pushTop(subResults,this.getTiddlersWithTag(title));}"; - }, - filter: function(operator) { - return "subResultsTemp = subResults;\nsubResults = [];for(t=subResultsTemp.length-1; t>=0; t--) {$tw.utils.pushTop(subResults,this.getTiddlersWithTag(subResultsTemp[t]));}"; - } - }, - "links": { // Return outgoing links on selected tiddlers - selector: function(operator) { - return "for(title in source) {r = this.getTiddlerLinks(title); $tw.utils.pushTop(subResults,r);}"; - }, - filter: function(operator) { - return "subResultsTemp = subResults;\nsubResults = [];for(t=subResultsTemp.length-1; t>=0; t--) {r = this.getTiddlerLinks(subResultsTemp[t]); $tw.utils.pushTop(subResults,r);}"; - } - }, - "backlinks": { // Return incoming links on selected tiddlers - selector: function(operator) { - return "for(title in source) {r = this.getTiddlerBacklinks(title); $tw.utils.pushTop(subResults,r);}"; - }, - filter: function(operator) { - return "subResultsTemp = subResults;\nsubResults = [];for(t=subResultsTemp.length-1; t>=0; t--) {r = this.getTiddlerBacklinks(subResultsTemp[t]); $tw.utils.pushTop(subResults,r);}"; - } - }, - "has": { // Filter by presence of a particular field - selector: function(operator) { - var op = operator.prefix === "!" ? "=" : "!"; - return "for(title in source) {if(this.getTiddler(title).fields[\"" + $tw.utils.stringify(operator.operand) + "\"] " + op + "== undefined) {$tw.utils.pushTop(subResults,title);}}"; - }, - filter: function(operator) { - var op = operator.prefix === "!" ? "!" : "="; - return "for(r=subResults.length-1; r>=0; r--) {if(this.getTiddler(subResults[r]).fields[\"" + $tw.utils.stringify(operator.operand) + "\"] " + op + "== undefined) {subResults.splice(r,1);}}"; - } - }, - "sort": { // Sort selected tiddlers - selector: function(operator) { - throw "Cannot use sort operator at the start of a filter operation"; - }, - filter: function(operator) { - var desc = operator.prefix === "!" ? "true" : "false"; - return "this.sortTiddlers(subResults,\"" + $tw.utils.stringify(operator.operand) + "\"," + desc + ");"; - } - }, // Case insensitive sort of selected tiddlers - "sort-case-sensitive": { - selector: function(operator) { - throw "Cannot use sort operator at the start of a filter operation"; - }, - filter: function(operator) { - var desc = operator.prefix === "!" ? "true" : "false"; - return "this.sortTiddlers(subResults,\"" + $tw.utils.stringify(operator.operand) + "\"," + desc + ",true);"; - } - }, - "limit": { // Limit number of members of selection - selector: function(operator) { - throw "Cannot use limit operator at the start of a filter operation"; - }, - filter: function(operator) { - var limit = parseInt(operator.operand,10), - base = operator.prefix === "!" ? 0 : limit; - return "if(subResults.length > " + limit + ") {subResults.splice(" + base + ",subResults.length-" + limit + ");}"; - } - }, - "list": { // Select all tiddlers that are listed (or not listed) in the specified tiddler - selector: function(operator) { - if(operator.prefix === "!") { - return "var list = this.getTiddlerList(\"" + $tw.utils.stringify(operator.operand) + "\");" + - "for(title in source) {if(list.indexOf(title) === -1) {$tw.utils.pushTop(subResults,title);}}"; - } else { - return "$tw.utils.pushTop(subResults,this.getTiddlerList(\"" + $tw.utils.stringify(operator.operand) + "\"));"; - } - }, - filter: function(operator) { - var op = operator.prefix === "!" ? "!==" : "==="; - return "var list = this.getTiddlerList(\"" + $tw.utils.stringify(operator.operand) + "\");" + - "for(r=subResults.length-1; r>=0; r--) {if(list.indexOf(title) " + op + " -1) {subResults.splice(r,1);}}"; - } - }, - "searchVia": { // Search for the string in the operand tiddler - selector: function(operator) { - var op = operator.prefix === "!" ? "true" : "false"; - return "var term = this.getTiddler(\"" + $tw.utils.stringify(operator.operand) + "\").fields.text;" + - "$tw.utils.pushTop(subResults,this.search(term,{titles: source, invert: " + op + ", exclude: [\"" + $tw.utils.stringify(operator.operand) + "\"]}));"; - }, - filter: function(operator) { - var op = operator.prefix === "!" ? "true" : "false"; - return "var term = this.getTiddler(\"" + $tw.utils.stringify(operator.operand) + "\").fields.text;" + - "subResults = this.search(term,{titles: subResults, invert: " + op + ", exclude: [\"" + $tw.utils.stringify(operator.operand) + "\"]});"; - } - }, - "field": { // Special handler for field comparisons - selector: function(operator) { - var op = operator.prefix === "!" ? "!" : "="; - return "for(title in source) {if(this.getTiddler(title).fields[\"" + $tw.utils.stringify(operator.operator) + "\"] " + op + "== \"" + operator.operand + "\") {$tw.utils.pushTop(subResults,title);}}"; - }, - filter: function(operator) { - var op = operator.prefix === "!" ? "=" : "!"; - return "for(r=subResults.length-1; r>=0; r--) {if(this.getTiddler(subResults[r]).fields[\"" + $tw.utils.stringify(operator.operator) + "\"] " + op + "== \"" + operator.operand + "\") {subResults.splice(r,1);}}"; - } - } -}; - /* Parses an operation within a filter string results: Array of array of operator nodes into which results should be inserted @@ -376,4 +108,70 @@ exports.parseFilter = function(filterString) { return results; }; +exports.getFilterOperators = function() { + if(!this.filterOperators) { + $tw.Wiki.prototype.filterOperators = {}; + $tw.modules.applyMethods("filteroperator",this.filterOperators); + } + return this.filterOperators; +}; + +exports.filterTiddlers = function(filterString,currTiddlerTitle,tiddlerList) { + var fn = this.compileFilter(filterString); + return fn.call(this,tiddlerList || this.tiddlers,currTiddlerTitle); +}; + +exports.compileFilter = function(filterString) { + var filterParseTree = this.parseFilter(filterString); + // Get the hashmap of filter operator functions + var filterOperators = this.getFilterOperators(); + // Assemble array of functions, one for each operation + var operationFunctions = []; + // Step through the operations + var self = this; + $tw.utils.each(filterParseTree,function(operation) { + // Create a function for the chain of operators in the operation + var operationSubFunction = function(source,currTiddlerTitle) { + var accumulator = source, + results = []; + $tw.utils.each(operation.operators,function(operator) { + var operatorFunction = filterOperators[operator.operator] || filterOperators.field || function(source,operator,operations) { + return ["Filter Error: unknown operator '" + operator.operator + "'"]; + }; + results = operatorFunction(accumulator,operator,{wiki: self, currTiddlerTitle: currTiddlerTitle}); + accumulator = results; + }); + return results; + }; + // Wrap the operator functions in a wrapper function that depends on the prefix + operationFunctions.push((function() { + switch(operation.prefix || "") { + case "": // No prefix means that the operation is unioned into the result + return function(results,source,currTiddlerTitle) { + $tw.utils.pushTop(results,operationSubFunction(source,currTiddlerTitle)); + }; + case "-": // The results of this operation are removed from the main result + return function(results,source,currTiddlerTitle) { + $tw.utils.removeArrayEntries(results,operationSubFunction(source,currTiddlerTitle)); + }; + case "+": // This operation is applied to the main results so far + return function(results,source,currTiddlerTitle) { + // This replaces all the elements of the array, but keeps the actual array so that references to it are preserved + source = results.slice(0); + results.splice(0,results.length); + $tw.utils.pushTop(results,operationSubFunction(source,currTiddlerTitle)); + }; + } + })()); + }); + // Return a function that applies the operations to a source array/hashmap of tiddler titles + return function(source,currTiddlerTitle) { + var results = []; + $tw.utils.each(operationFunctions,function(operationFunction) { + operationFunction(results,source,currTiddlerTitle); + }); + return results; + }; +}; + })(); diff --git a/core/modules/filters/backlinks.js b/core/modules/filters/backlinks.js new file mode 100644 index 000000000..81f4ef925 --- /dev/null +++ b/core/modules/filters/backlinks.js @@ -0,0 +1,37 @@ +/*\ +title: $:/core/modules/filters/backlinks.js +type: application/javascript +module-type: filteroperator + +Filter operator for returning all the backlinks from a tiddler + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.backlinks = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + $tw.utils.pushTop(results,options.wiki.getTiddlerBacklinks(title)); + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/field.js b/core/modules/filters/field.js new file mode 100644 index 000000000..ed1089deb --- /dev/null +++ b/core/modules/filters/field.js @@ -0,0 +1,46 @@ +/*\ +title: $:/core/modules/filters/field.js +type: application/javascript +module-type: filteroperator + +Filter operator for comparing fields for equality + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.field = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + var tiddler = options.wiki.getTiddler(title); + if(tiddler) { + var match = tiddler.fields[operator.operator] === operator.operand; + if(operator.prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/has.js b/core/modules/filters/has.js new file mode 100644 index 000000000..aa14e3c3e --- /dev/null +++ b/core/modules/filters/has.js @@ -0,0 +1,46 @@ +/*\ +title: $:/core/modules/filters/has.js +type: application/javascript +module-type: filteroperator + +Filter operator for checking if a tiddler has the specified field + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.has = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + var tiddler = options.wiki.getTiddler(title); + if(tiddler) { + var match = $tw.utils.hop(tiddler.fields,operator.operand); + if(operator.prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/is.js b/core/modules/filters/is.js new file mode 100644 index 000000000..fc45a62f9 --- /dev/null +++ b/core/modules/filters/is.js @@ -0,0 +1,39 @@ +/*\ +title: $:/core/modules/filters/is.js +type: application/javascript +module-type: filteroperator + +Filter operator for checking tiddler properties + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +var isFilterOperators; + +function getIsFilterOperators() { + if(!isFilterOperators) { + isFilterOperators = {}; + $tw.modules.applyMethods("isfilteroperator",isFilterOperators); + } + return isFilterOperators; +}; + +/* +Export our filter function +*/ +exports.is = function(source,operator,options) { + // Dispatch to the correct isfilteroperator + var isFilterOperators = getIsFilterOperators(); + var isFilterOperator = isFilterOperators[operator.operand]; + if(isFilterOperator) { + return isFilterOperator(source,operator.prefix,options); + } else { + return ["Filter Error: Unknown operand for the 'is' filter operator"]; + } +}; + +})(); diff --git a/core/modules/filters/is/current.js b/core/modules/filters/is/current.js new file mode 100644 index 000000000..7e87c9285 --- /dev/null +++ b/core/modules/filters/is/current.js @@ -0,0 +1,49 @@ +/*\ +title: $:/core/modules/filters/is/current.js +type: application/javascript +module-type: isfilteroperator + +Filter function for [is[current]] + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.current = function(source,prefix,options) { + var results = []; + // Function to check a tiddler + function checkTiddler(title) { + if(title !== options.currTiddlerTitle) { + results.push(title); + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + if(prefix === "!") { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + if(source.indexOf(options.currTiddlerTitle) !== -1) { + results.push(options.currTiddlerTitle); + } + } + } else { + if(prefix === "!") { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } else { + results.push(options.currTiddlerTitle); + } + } + return results; +}; + +})(); diff --git a/core/modules/filters/is/missing.js b/core/modules/filters/is/missing.js new file mode 100644 index 000000000..b8142bd94 --- /dev/null +++ b/core/modules/filters/is/missing.js @@ -0,0 +1,48 @@ +/*\ +title: $:/core/modules/filters/is/missing.js +type: application/javascript +module-type: isfilteroperator + +Filter function for [is[missing]] + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.missing = function(source,prefix,options) { + var results = [], + missingTitles; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + missingTitles = options.wiki.getMissingTitles(); + $tw.utils.each(source,function(title) { + var match = missingTitles.indexOf(title) !== -1; + if(prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + }); + } else { + if(prefix !== "!") { + missingTitles = options.wiki.getMissingTitles(); + $tw.utils.each(missingTitles,function(title) { + results.push(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + results.push(title); + }); + } + } + return results; +}; + +})(); diff --git a/core/modules/filters/is/orphan.js b/core/modules/filters/is/orphan.js new file mode 100644 index 000000000..6d3b00670 --- /dev/null +++ b/core/modules/filters/is/orphan.js @@ -0,0 +1,46 @@ +/*\ +title: $:/core/modules/filters/is/orphan.js +type: application/javascript +module-type: isfilteroperator + +Filter function for [is[orphan]] + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.orphan = function(source,prefix,options) { + var results = [], + orphanTitles = options.wiki.getOrphanTitles(); + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + var match = orphanTitles.indexOf(title) !== -1; + if(prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + }); + } else { + $tw.utils.each(source,function(element,title) { + var match = orphanTitles.indexOf(title) !== -1; + if(prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/is/shadow.js b/core/modules/filters/is/shadow.js new file mode 100644 index 000000000..4ea734240 --- /dev/null +++ b/core/modules/filters/is/shadow.js @@ -0,0 +1,49 @@ +/*\ +title: $:/core/modules/filters/is/shadow.js +type: application/javascript +module-type: isfilteroperator + +Filter function for [is[shadow]] + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.shadow = function(source,prefix,options) { + var results = []; + // Function to check a tiddler + function checkTiddler(title) { + var match = options.wiki.isShadowTiddler(title); + if(prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + if(prefix !== "!") { + $tw.utils.each(options.wiki.shadowTiddlers,function(tiddler,title) { + results.push(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + } + return results; +}; + +})(); diff --git a/core/modules/filters/is/system.js b/core/modules/filters/is/system.js new file mode 100644 index 000000000..f7aed9610 --- /dev/null +++ b/core/modules/filters/is/system.js @@ -0,0 +1,43 @@ +/*\ +title: $:/core/modules/filters/is/system.js +type: application/javascript +module-type: isfilteroperator + +Filter function for [is[system]] + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.system = function(source,prefix,options) { + var results = []; + // Function to check a tiddler + function checkTiddler(title) { + var match = options.wiki.isSystemTiddler(title); + if(prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/limit.js b/core/modules/filters/limit.js new file mode 100644 index 000000000..8075ec695 --- /dev/null +++ b/core/modules/filters/limit.js @@ -0,0 +1,38 @@ +/*\ +title: $:/core/modules/filters/limit.js +type: application/javascript +module-type: filteroperator + +Filter operator for chopping the results to a specified maximum number of entries + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.limit = function(source,operator,options) { + var results = []; + // Convert to an array if necessary + if(!$tw.utils.isArray(source)) { + var copy = []; + $tw.utils.each(source,function(element,title) { + copy.push(title); + }); + source = copy; + } + // Slice the array if necessary + var limit = Math.min(source.length,parseInt(operator.operand,10)); + if(operator.prefix === "!") { + results = source.slice(source.length - limit,limit); + } else { + results = source.slice(0,limit); + } + return results; +}; + +})(); diff --git a/core/modules/filters/links.js b/core/modules/filters/links.js new file mode 100644 index 000000000..81ac7eea6 --- /dev/null +++ b/core/modules/filters/links.js @@ -0,0 +1,37 @@ +/*\ +title: $:/core/modules/filters/links.js +type: application/javascript +module-type: filteroperator + +Filter operator for returning all the links from a tiddler + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.links = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + $tw.utils.pushTop(results,options.wiki.getTiddlerLinks(title)); + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/list.js b/core/modules/filters/list.js new file mode 100644 index 000000000..36c553bd8 --- /dev/null +++ b/core/modules/filters/list.js @@ -0,0 +1,47 @@ +/*\ +title: $:/core/modules/filters/list.js +type: application/javascript +module-type: filteroperator + +Filter operator returning the tiddlers whose title is listed in the operand tiddler + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.list = function(source,operator,options) { + var results = [], + list = options.wiki.getTiddlerList(operator.operand); + function checkTiddler(title) { + var match = list.indexOf(title) !== -1; + if(operator.prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + if(operator.prefix !== "!") { + results = list; + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + } + return results; +}; + +})(); diff --git a/core/modules/filters/prefix.js b/core/modules/filters/prefix.js new file mode 100644 index 000000000..249f6cba9 --- /dev/null +++ b/core/modules/filters/prefix.js @@ -0,0 +1,46 @@ +/*\ +title: $:/core/modules/filters/prefix.js +type: application/javascript +module-type: filteroperator + +Filter operator for checking if a title starts with a prefix + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.prefix = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + var tiddler = options.wiki.getTiddler(title); + if(tiddler) { + var match = tiddler.fields.title.substr(0,operator.operand.length) === operator.operand; + if(operator.prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/searchVia.js b/core/modules/filters/searchVia.js new file mode 100644 index 000000000..13dbc12bb --- /dev/null +++ b/core/modules/filters/searchVia.js @@ -0,0 +1,28 @@ +/*\ +title: $:/core/modules/filters/searchVia.js +type: application/javascript +module-type: filteroperator + +Filter operator for searching for the text in the operand tiddler + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.searchVia = function(source,operator,options) { + var term = options.wiki.getTiddlerText(operator.operand,""), + invert = operator.prefix === "!"; + return options.wiki.search(term,{ + titles: source, + invert: invert, + exclude: [operator.operand] + }); +}; + +})(); diff --git a/core/modules/filters/sort.js b/core/modules/filters/sort.js new file mode 100644 index 000000000..538ac0ae2 --- /dev/null +++ b/core/modules/filters/sort.js @@ -0,0 +1,32 @@ +/*\ +title: $:/core/modules/filters/sort.js +type: application/javascript +module-type: filteroperator + +Filter operator for sorting + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.sort = function(source,operator,options) { + var results; + if($tw.utils.isArray(source)) { + results = source; + } else { + results = []; + $tw.utils.each(source,function(element,title) { + results.push(title); + }); + } + options.wiki.sortTiddlers(results,operator.operand,operator.prefix === "!"); + return results; +}; + +})(); diff --git a/core/modules/filters/sortcs.js b/core/modules/filters/sortcs.js new file mode 100644 index 000000000..74117aa42 --- /dev/null +++ b/core/modules/filters/sortcs.js @@ -0,0 +1,32 @@ +/*\ +title: $:/core/modules/filters/sortcs.js +type: application/javascript +module-type: filteroperator + +Filter operator for case-sensitive sorting + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.sortcs = function(source,operator,options) { + var results; + if($tw.utils.isArray(source)) { + results = source; + } else { + results = []; + $tw.utils.each(source,function(element,title) { + results.push(title); + }); + } + options.wiki.sortTiddlers(results,operator.operand,operator.prefix === "!",true); + return results; +}; + +})(); diff --git a/core/modules/filters/tag.js b/core/modules/filters/tag.js new file mode 100644 index 000000000..291052986 --- /dev/null +++ b/core/modules/filters/tag.js @@ -0,0 +1,46 @@ +/*\ +title: $:/core/modules/filters/tag.js +type: application/javascript +module-type: filteroperator + +Filter operator for checking for the presence of a tag + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.tag = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + var tiddler = options.wiki.getTiddler(title); + if(tiddler) { + var match = tiddler.hasTag(operator.operand); + if(operator.prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/tagging.js b/core/modules/filters/tagging.js new file mode 100644 index 000000000..c999c45fd --- /dev/null +++ b/core/modules/filters/tagging.js @@ -0,0 +1,37 @@ +/*\ +title: $:/core/modules/filters/tagging.js +type: application/javascript +module-type: filteroperator + +Filter operator returning all tiddlers that are tagged with the selected tiddlers + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.tagging = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + $tw.utils.pushTop(results,options.wiki.getTiddlersWithTag(title)); + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/tags.js b/core/modules/filters/tags.js new file mode 100644 index 000000000..97e6f0d28 --- /dev/null +++ b/core/modules/filters/tags.js @@ -0,0 +1,40 @@ +/*\ +title: $:/core/modules/filters/tags.js +type: application/javascript +module-type: filteroperator + +Filter operator returning all tiddlers tagging the selected tiddlers + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.tags = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + var tiddler = options.wiki.getTiddler(title); + if(tiddler && tiddler.fields.tags) { + $tw.utils.pushTop(results,tiddler.fields.tags); + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + $tw.utils.each(source,function(element,title) { + checkTiddler(title); + }); + } + return results; +}; + +})(); diff --git a/core/modules/filters/title.js b/core/modules/filters/title.js new file mode 100644 index 000000000..1df939083 --- /dev/null +++ b/core/modules/filters/title.js @@ -0,0 +1,53 @@ +/*\ +title: $:/core/modules/filters/title.js +type: application/javascript +module-type: filteroperator + +Filter operator for comparing title fields for equality + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +/* +Export our filter function +*/ +exports.title = function(source,operator,options) { + var results = []; + // Function to check an individual title + function checkTiddler(title) { + var tiddler = options.wiki.getTiddler(title); + if(tiddler) { + var match = tiddler.fields[operator.operator] === operator.operand; + if(operator.prefix === "!") { + match = !match; + } + if(match) { + results.push(title); + } + } + }; + // Iterate through the source tiddlers + if($tw.utils.isArray(source)) { + $tw.utils.each(source,function(title) { + checkTiddler(title); + }); + } else { + // If we're filtering a hashmap we change the behaviour to pass through missing tiddlers + if(operator.prefix !== "!") { + results.push(operator.operand); + } else { + $tw.utils.each(source,function(element,title) { + if(title !== operator.operand) { + checkTiddler(title); + } + }); + } + } + return results; +}; + +})(); diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js new file mode 100644 index 000000000..ff42a3083 --- /dev/null +++ b/editions/test/tiddlers/tests/test-filters.js @@ -0,0 +1,181 @@ +/*\ +title: test-filters.js +type: application/javascript +tags: [[$:/tags/test-spec]] + +Tests the filtering mechanism. + +\*/ +(function(){ + +/*jslint node: true, browser: true */ +/*global $tw: false */ +"use strict"; + +describe("Filter tests", function() { + + // Create a wiki + var wiki = new $tw.Wiki(); + + // Some helpers + var addShadowTiddler = function(fields) { + var tiddler = new $tw.Tiddler(fields); + wiki.shadowTiddlers[tiddler.fields.title] = {tiddler: tiddler}; + }; + + // Add a few tiddlers + wiki.addTiddler({ + title: "TiddlerOne", + text: "The quick brown fox in $:/TiddlerTwo", + tags: ["one"], + modifier: "JoeBloggs"}); + wiki.addTiddler({ + title: "$:/TiddlerTwo", + text: "The rain in Spain\nfalls mainly on the plain and [[a fourth tiddler]]", + tags: ["two"]}); + wiki.addTiddler({ + title: "Tiddler Three", + text: "The speed of sound in light\n\nThere is no TiddlerZero but TiddlerSix", + tags: ["one","two"]}); + wiki.addTiddler({ + title: "a fourth tiddler", + text: "The quality of mercy is not drained by [[Tiddler Three]]", + tags: []}); + // And some shadows + addShadowTiddler({ + title: "$:/TiddlerFive", + text: "Everything in federation", + tags: ["two"]}); + addShadowTiddler({ + title: "TiddlerSix", + text: "Missing inaction from TiddlerOne", + tags: []}); + addShadowTiddler({ + title: "TiddlerSeventh", + text: "TiddlerOne\nTiddler Three\na fourth tiddler\nMissingTiddler", + tags: []}); + addShadowTiddler({ + title: "Tiddler8", + text: "Tidd", + tags: []}); + + // Our tests + + it("should handle the title operator", function() { + expect(wiki.filterTiddlers("TiddlerOne [title[$:/TiddlerTwo]] [[Tiddler Three]]").join(",")).toBe("TiddlerOne,$:/TiddlerTwo,Tiddler Three"); + expect(wiki.filterTiddlers("[!title[Tiddler Three]]").join(",")).toBe("TiddlerOne,$:/TiddlerTwo,a fourth tiddler"); + expect(wiki.filterTiddlers("TiddlerOne [title[$:/TiddlerTwo]] [[Tiddler Three]] [[A Missing Tiddler]]").join(",")).toBe("TiddlerOne,$:/TiddlerTwo,Tiddler Three,A Missing Tiddler"); + }); + + it("should handle the field operator", function() { + expect(wiki.filterTiddlers("[modifier[JoeBloggs]]").join(",")).toBe("TiddlerOne"); + expect(wiki.filterTiddlers("[!modifier[JoeBloggs]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three,a fourth tiddler"); + expect(wiki.filterTiddlers("[!is[system]!modifier[JoeBloggs]]").join(",")).toBe("Tiddler Three,a fourth tiddler"); + }); + + it("should handle the prefix operator", function() { + expect(wiki.filterTiddlers("[prefix[Tiddler]]").join(",")).toBe("TiddlerOne,Tiddler Three"); + expect(wiki.filterTiddlers("[prefix[nothing]]").join(",")).toBe(""); + }); + + it("should handle the sort and sortcs operators", function() { + expect(wiki.filterTiddlers("[sort[title]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[!sort[title]]").join(",")).toBe("TiddlerOne,Tiddler Three,a fourth tiddler,$:/TiddlerTwo"); + expect(wiki.filterTiddlers("[sortcs[title]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three,TiddlerOne,a fourth tiddler"); + expect(wiki.filterTiddlers("[!sortcs[title]]").join(",")).toBe("a fourth tiddler,TiddlerOne,Tiddler Three,$:/TiddlerTwo"); + }); + + it("should handle the tag operator", function() { + expect(wiki.filterTiddlers("[tag[one]sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[!tag[one]sort[title]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler"); + expect(wiki.filterTiddlers("[prefix[Tidd]tag[one]sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[!is[shadow]tag[two]sort[title]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three"); + expect(wiki.filterTiddlers("[is[shadow]tag[two]sort[title]]").join(",")).toBe("$:/TiddlerFive"); + }); + + it("should handle the tags operator", function() { + expect(wiki.filterTiddlers("[tags[]sort[title]]").join(",")).toBe("one,two"); + expect(wiki.filterTiddlers("[[TiddlerOne]tags[]sort[title]]").join(",")).toBe("one"); + }); + + it("should handle the tagging operator", function() { + expect(wiki.filterTiddlers("[[one]tagging[]sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[[two]tagging[]sort[title]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three"); + expect(wiki.filterTiddlers("[is[current]tagging[]sort[title]]","one").join(",")).toBe("Tiddler Three,TiddlerOne"); + }); + + it("should handle the links operator", function() { + expect(wiki.filterTiddlers("[!is[shadow]links[]sort[title]]").join(",")).toBe("a fourth tiddler,Tiddler Three,TiddlerSix,TiddlerTwo,TiddlerZero"); + expect(wiki.filterTiddlers("[is[shadow]links[]sort[title]]").join(",")).toBe("MissingTiddler,TiddlerOne"); + }); + + it("should handle the backlinks operator", function() { + expect(wiki.filterTiddlers("[!is[shadow]backlinks[]sort[title]]").join(",")).toBe("a fourth tiddler"); + expect(wiki.filterTiddlers("[is[shadow]backlinks[]sort[title]]").join(",")).toBe("Tiddler Three"); + }); + + it("should handle the has operator", function() { + expect(wiki.filterTiddlers("[has[modifier]sort[title]]").join(",")).toBe("TiddlerOne"); + expect(wiki.filterTiddlers("[!has[modifier]sort[title]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,Tiddler Three"); + }); + + it("should handle the limit operator", function() { + expect(wiki.filterTiddlers("[!is[system]sort[title]limit[2]]").join(",")).toBe("a fourth tiddler,Tiddler Three"); + expect(wiki.filterTiddlers("[prefix[Tid]sort[title]limit[1]]").join(",")).toBe("Tiddler Three"); + }); + + it("should handle the list operator", function() { + expect(wiki.filterTiddlers("[list[TiddlerSeventh]sort[title]]").join(",")).toBe("a fourth tiddler,MissingTiddler,Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[tag[one]list[TiddlerSeventh]sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne"); + }); + + it("should handle the searchVia operator", function() { + expect(wiki.filterTiddlers("[searchVia[Tiddler8]sort[title]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,Tiddler Three,TiddlerOne"); + }); + + describe("testing the is operator",function() { + + it("should handle the '[is[current]]' operator", function() { + expect(wiki.filterTiddlers("[is[current]]","Tiddler Three").join(",")).toBe("Tiddler Three"); + expect(wiki.filterTiddlers("[[Tiddler Three]is[current]]","Tiddler Three").join(",")).toBe("Tiddler Three"); + expect(wiki.filterTiddlers("[[$:/TiddlerTwo]is[current]]","Tiddler Three").join(",")).toBe(""); + expect(wiki.filterTiddlers("[!is[current]sort[title]]","Tiddler Three").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,TiddlerOne"); + }); + + it("should handle the '[is[system]]' operator", function() { + expect(wiki.filterTiddlers("[is[system]]").join(",")).toBe("$:/TiddlerTwo"); + expect(wiki.filterTiddlers("[!is[system]sort[title]]").join(",")).toBe("a fourth tiddler,Tiddler Three,TiddlerOne"); + }); + + it("should handle the '[is[shadow]]' operator", function() { + expect(wiki.filterTiddlers("[is[shadow]sort[title]]").join(",")).toBe("$:/TiddlerFive,Tiddler8,TiddlerSeventh,TiddlerSix"); + expect(wiki.filterTiddlers("[!is[shadow]sort[title]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,Tiddler Three,TiddlerOne"); + }); + + it("should handle the '[is[missing]]' operator", function() { + expect(wiki.filterTiddlers("[is[missing]]").join(",")).toBe("TiddlerZero,TiddlerSix,TiddlerTwo"); + expect(wiki.filterTiddlers("[!is[missing]sort[title]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,Tiddler Three,TiddlerOne"); + expect(wiki.filterTiddlers("[[TiddlerOne]is[missing]]").join(",")).toBe(""); + expect(wiki.filterTiddlers("[[TiddlerZero]is[missing]]").join(",")).toBe("TiddlerZero"); + expect(wiki.filterTiddlers("[!title[Tiddler Three]is[missing]]").join(",")).toBe(""); + expect(wiki.filterTiddlers("[!title[Tiddler Three]!is[missing]sort[title]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,TiddlerOne"); + }); + + it("should handle the '[is[orphan]]' operator", function() { + expect(wiki.filterTiddlers("[is[orphan]sort[title]]").join(",")).toBe("a fourth tiddler,TiddlerOne"); + expect(wiki.filterTiddlers("[!is[orphan]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three"); + expect(wiki.filterTiddlers("[[TiddlerOne]is[orphan]]").join(",")).toBe("TiddlerOne"); + expect(wiki.filterTiddlers("[[TiddlerOne]!is[orphan]]").join(",")).toBe(""); + expect(wiki.filterTiddlers("[!title[Tiddler Three]is[orphan]sort[title]]").join(",")).toBe("a fourth tiddler,TiddlerOne"); + expect(wiki.filterTiddlers("[!title[Tiddler Three]!is[orphan]]").join(",")).toBe("$:/TiddlerTwo"); + }); + + }); + + it("should handle the operand prefixes", function() { + expect(wiki.filterTiddlers("[prefix[Tiddler]] +[sort[title]]").join(",")).toBe("Tiddler Three,TiddlerOne"); + }); + +}); + +})(); diff --git a/editions/tw2/tiddlers/system/tiddlywiki2.template.html.tid b/editions/tw2/tiddlers/system/tiddlywiki2.template.html.tid index 3471b59dd..d2dcc1f0b 100644 --- a/editions/tw2/tiddlers/system/tiddlywiki2.template.html.tid +++ b/editions/tw2/tiddlers/system/tiddlywiki2.template.html.tid @@ -46,11 +46,11 @@ Welcome to TiddlyWiki created by Jeremy Ruston; Copyright © 2004-2007 Jerem
-{{{ [prefix[{shadow}]] +[sort-case-sensitive[title]] ||$:/core/templates/html-div-tiddler-remove-prefix}}} +{{{ [prefix[{shadow}]] +[sortcs[title]] ||$:/core/templates/html-div-tiddler-remove-prefix}}}
-{{{ [prefix[{tiddler}]] +[sort-case-sensitive[title]] ||$:/core/templates/html-div-tiddler-remove-prefix}}} +{{{ [prefix[{tiddler}]] +[sortcs[title]] ||$:/core/templates/html-div-tiddler-remove-prefix}}} {{{ [prefix[{plugin}]] ||$:/core/templates/plain-text-tiddler}}} {{{ [prefix[{posttiddlers}]] ||$:/core/templates/plain-text-tiddler}}}
diff --git a/editions/tw5.com/tiddlers/concepts/TiddlerFilters.tid b/editions/tw5.com/tiddlers/concepts/TiddlerFilters.tid index 6a9c7098c..4c59da908 100644 --- a/editions/tw5.com/tiddlers/concepts/TiddlerFilters.tid +++ b/editions/tw5.com/tiddlers/concepts/TiddlerFilters.tid @@ -33,7 +33,7 @@ A filter string consists of one or more runs of filter operators that each look * ''is'': tests whether a tiddler is a member of the system defined set named in the operand (see below) * ''has'': tests whether a tiddler has the field specified in the operand * ''sort'': sorts the tiddlers by the field specified in the operand -* ''sort-case-sensitive'': a case sensitive sort of the current tiddlers by the field specified in the operand +* ''sortcs'': a case sensitive sort of the current tiddlers by the field specified in the operand * ''prefix'': tests whether a tiddlers title starts with the prefix specified in the operand * ''limit'': limits the number of subresults to the integer specified in the operand * ''tag'': tests whether a given tag is (`[tag[mytag]]`) or is not (`[!tag[mytag]]`) present on the tiddler