1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2025-08-07 22:33:50 +00:00

Merge 9627f2b6b2681496b95ba015c9cfaf146b891905 into 06adaf3331d983be822a40c10677fa05f6ea4536

This commit is contained in:
Jeremy Ruston 2025-03-14 17:00:47 +01:00 committed by GitHub
commit 4626ebb8bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 185 additions and 17 deletions

View File

@ -200,12 +200,20 @@ exports.parseFilter = function(filterString) {
exports.getFilterOperators = function() { exports.getFilterOperators = function() {
if(!this.filterOperators) { if(!this.filterOperators) {
$tw.Wiki.prototype.filterOperators = {}; $tw.Wiki.prototype.oldFilterOperators = {};
$tw.modules.applyMethods("filteroperator",this.filterOperators); $tw.modules.applyMethods("filteroperator",this.oldFilterOperators);
$tw.Wiki.prototype.newFilterOperators = {};
$tw.modules.applyMethods("newfilteroperator",this.newFilterOperators);
$tw.Wiki.prototype.filterOperators = $tw.utils.extend({},$tw.Wiki.prototype.oldFilterOperators,$tw.Wiki.prototype.newFilterOperators);
} }
return this.filterOperators; return this.filterOperators;
}; };
exports.isFilterOperatorNew = function(name) {
this.getFilterOperators();
return name in $tw.Wiki.prototype.newFilterOperators;
};
exports.getFilterRunPrefixes = function() { exports.getFilterRunPrefixes = function() {
if(!this.filterRunPrefixes) { if(!this.filterRunPrefixes) {
$tw.Wiki.prototype.filterRunPrefixes = {}; $tw.Wiki.prototype.filterRunPrefixes = {};
@ -214,11 +222,21 @@ exports.getFilterRunPrefixes = function() {
return this.filterRunPrefixes; return this.filterRunPrefixes;
} }
exports.filterTiddlers = function(filterString,widget,source) { exports.filterTiddlersPolymorphic = function(filterString,widget,source) {
var fn = this.compileFilter(filterString); var fn = this.compileFilter(filterString);
return fn.call(this,source,widget); return fn.call(this,source,widget);
}; };
exports.filterTiddlers = function(filterString,widget,source) {
var results = this.filterTiddlersPolymorphic(filterString,widget,source);
for(var t=0; t<results.length; t++) {
if(typeof results[t] !== "string") {
results[t] = $tw.utils.filterItemToString(results[t]);
}
}
return results;
};
/* /*
Compile a filter into a function with the signature fn(source,widget) where: Compile a filter into a function with the signature fn(source,widget) where:
source: an iterator function for the source tiddlers, called source(iterator), where iterator is called as iterator(tiddler,title) source: an iterator function for the source tiddlers, called source(iterator), where iterator is called as iterator(tiddler,title)
@ -255,16 +273,19 @@ exports.compileFilter = function(filterString) {
currTiddlerTitle = widget && widget.getVariable("currentTiddler"); currTiddlerTitle = widget && widget.getVariable("currentTiddler");
$tw.utils.each(operation.operators,function(operator) { $tw.utils.each(operation.operators,function(operator) {
var operands = [], var operands = [],
operatorFunction; operatorFunction,
operatorName;
if(!operator.operator) { if(!operator.operator) {
// Use the "title" operator if no operator is specified // Use the "title" operator if no operator is specified
operatorFunction = filterOperators.title; operatorFunction = filterOperators.title;
operatorName = "title";
} else if(!filterOperators[operator.operator]) { } else if(!filterOperators[operator.operator]) {
// Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator // Unknown operators treated as "[unknown]" - at run time we can distinguish between a custom operator and falling back to the default "field" operator
operatorFunction = filterOperators["[unknown]"]; operatorFunction = filterOperators["[unknown]"];
} else { } else {
// Use the operator function // Use the operator function
operatorFunction = filterOperators[operator.operator]; operatorFunction = filterOperators[operator.operator];
operatorName = operator.operator;
} }
$tw.utils.each(operator.operands,function(operand) { $tw.utils.each(operator.operands,function(operand) {
if(operand.indirect) { if(operand.indirect) {
@ -277,7 +298,17 @@ exports.compileFilter = function(filterString) {
} }
operands.push(operand.value); operands.push(operand.value);
}); });
// If this is a legacy monomorphic operator then wrap the accumulator source in a wrapper that converts each item to a string
if(!self.isFilterOperatorNew(operatorName)) {
var innerAccumulator = accumulator;
accumulator = function(iterator) {
innerAccumulator(function(ignored,item) {
var title = $tw.utils.filterItemToString(item),
tiddler = self.getTiddler(title);
iterator(tiddler,title);
});
};
}
// Invoke the appropriate filteroperator module // Invoke the appropriate filteroperator module
results = operatorFunction(accumulator,{ results = operatorFunction(accumulator,{
operator: operator.operator, operator: operator.operator,

View File

@ -1,7 +1,7 @@
/*\ /*\
title: $:/core/modules/filters/count.js title: $:/core/modules/filters/count.js
type: application/javascript type: application/javascript
module-type: filteroperator module-type: newfilteroperator
Filter operator returning the number of entries in the current list. Filter operator returning the number of entries in the current list.
@ -20,7 +20,7 @@ exports.count = function(source,operator,options) {
source(function(tiddler,title) { source(function(tiddler,title) {
count++; count++;
}); });
return [count + ""]; return [count];
}; };
})(); })();

View File

@ -1,7 +1,7 @@
/*\ /*\
title: $:/core/modules/filters/json-ops.js title: $:/core/modules/filters/json-ops.js
type: application/javascript type: application/javascript
module-type: filteroperator module-type: newfilteroperator
Filter operators for JSON operations Filter operators for JSON operations
@ -57,9 +57,9 @@ exports["jsonindexes"] = function(source,operator,options) {
exports["jsontype"] = function(source,operator,options) { exports["jsontype"] = function(source,operator,options) {
var results = []; var results = [];
source(function(tiddler,title) { source(function(tiddler,title) {
var data = $tw.utils.parseJSONSafe(title,title); var data = $tw.utils.filterItemToObject(title,{defaultValue: title,parseStringsAsJson: true});
if(data) { if(data !== undefined) {
var item = getDataItemType(data,operator.operands); var item = getFilterItemType(data,operator.operands);
if(item !== undefined) { if(item !== undefined) {
results.push(item); results.push(item);
} }
@ -196,7 +196,7 @@ function convertDataItemKeysToStrings(item) {
return []; return [];
} }
function getDataItemType(data,indexes) { function getFilterItemType(data,indexes) {
// Get the item // Get the item
var item = getDataItem(data,indexes); var item = getDataItem(data,indexes);
// Return the item type // Return the item type
@ -281,4 +281,3 @@ function setDataItem(data,indexes,value) {
} }
})(); })();

View File

@ -1,7 +1,7 @@
/*\ /*\
title: $:/core/modules/filters/math.js title: $:/core/modules/filters/math.js
type: application/javascript type: application/javascript
module-type: filteroperator module-type: newfilteroperator
Filter operators for math. Unary/binary operators work on each item in turn, and return a new item list. Filter operators for math. Unary/binary operators work on each item in turn, and return a new item list.
@ -207,7 +207,7 @@ function makeNumericBinaryOperator(fnCalc) {
var result = [], var result = [],
numOperand = $tw.utils.parseNumber(operator.operand); numOperand = $tw.utils.parseNumber(operator.operand);
source(function(tiddler,title) { source(function(tiddler,title) {
result.push($tw.utils.stringifyNumber(fnCalc($tw.utils.parseNumber(title),numOperand))); result.push(fnCalc($tw.utils.parseNumber(title),numOperand));
}); });
return result; return result;
}; };
@ -226,7 +226,7 @@ function makeNumericReducingOperator(fnCalc,initialValue,fnFinal) {
if(fnFinal) { if(fnFinal) {
value = fnFinal(value,result.length,result); value = fnFinal(value,result.length,result);
} }
return [$tw.utils.stringifyNumber(value)]; return [value];
}; };
}; };
@ -238,7 +238,7 @@ function makeNumericArrayOperator(fnCalc) {
}); });
results = fnCalc(results); results = fnCalc(results);
$tw.utils.each(results,function(value,index) { $tw.utils.each(results,function(value,index) {
results[index] = $tw.utils.stringifyNumber(value); results[index] = value;
}); });
return results; return results;
}; };

View File

@ -0,0 +1,86 @@
/*\
module-type: utils
title: $:/core/modules/utils/simple-list.js
type: application/javascript
Switched from a linked list to simplify things
\*/
(function(){
function SimpleList() {
this.clear();
};
Object.defineProperty(SimpleList.prototype,"length", {
get: function() {
return this.list.length;
}
});
SimpleList.prototype.clear = function() {
this.list = [];
};
SimpleList.prototype.remove = function(value) {
if($tw.utils.isArray(value)) {
for(var t=0; t<value.length; t++) {
this._remove(value[t]);
}
} else {
this._remove(value);
}
};
/*
Push behaves like array.push and accepts multiple string arguments. But it also
accepts a single array argument too, to be consistent with its other methods.
*/
SimpleList.prototype.push = function(/* values */) {
var values = arguments;
if(arguments.length === 1 && $tw.utils.isArray(values[0])) {
values = values[0];
}
for(var i = 0; i < values.length; i++) {
this._push(values[i]);
}
return this.list.length;
};
SimpleList.prototype.pushTop = function(value) {
// this.push(value);
// -or-
$tw.utils.pushTop(this.list,value);
};
SimpleList.prototype.each = function(callback) {
$tw.utils.each(this.list,callback);
};
SimpleList.prototype.toArray = function() {
return this.list.slice(0);
};
SimpleList.prototype.makeTiddlerIterator = function(wiki) {
var self = this;
return function(callback) {
self.each(function(title) {
callback(wiki.getTiddler(title),title);
});
};
};
SimpleList.prototype._remove = function(value) {
var p = this.list.indexOf(value);
if(p !== -1) {
this.list.splice(p,1);
}
};
SimpleList.prototype._push = function(value) {
this.list.push(value);
};
exports.SimpleList = SimpleList;
})();

View File

@ -1039,4 +1039,52 @@ exports.makeCompareFunction = function(type,options) {
return (types[type] || types[options.defaultType] || types.number); return (types[type] || types[options.defaultType] || types.number);
}; };
exports.filterItemToString = function(value) {
switch(typeof value) {
case "undefined":
return "undefined"
case "object":
return JSON.stringify(value);
case "boolean":
return value ? "true" : "false";
case "number":
return value.toString();
case "bigint":
return value.toString();
case "string":
return value;
case "symbol":
throw "Filter operators cannot return Symbols";
case "function":
throw "Filter operators cannot return Functions";
}
};
exports.filterItemToObject = function(value,options) {
options = options || {};
var defaultValue = options.defaultValue || {};
switch(typeof value) {
case "undefined":
return defaultValue;
case "object":
return value;
case "boolean":
return value;
case "number":
return value;
case "bigint":
return value;
case "string":
if(options.parseStringsAsJson) {
return $tw.utils.parseJSONSafe(value,defaultValue);
} else {
return value;
}
case "symbol":
throw "Filter operators cannot return Symbols";
case "function":
throw "Filter operators cannot return Functions";
}
};
})(); })();

View File

@ -708,6 +708,10 @@ Tests the filtering mechanism.
expect(wiki.filterTiddlers("1 2 3 4 +[min[2]]").join(",")).toBe("1,2,2,2"); expect(wiki.filterTiddlers("1 2 3 4 +[min[2]]").join(",")).toBe("1,2,2,2");
}); });
it("should handle type conversions", function() {
expect(wiki.filterTiddlers("[[2]add[2]addprefix[donkey]]").join(",")).toBe("donkey4");
});
/* listops filters */ /* listops filters */
it("should handle the allafter operator", function() { it("should handle the allafter operator", function() {