mirror of
https://github.com/Jermolene/TiddlyWiki5
synced 2024-11-26 11:37:20 +00:00
parent
3733f8b4ae
commit
7b53f5724c
46
boot/boot.js
46
boot/boot.js
@ -409,10 +409,10 @@ $tw.utils.resolvePath = function(sourcepath,rootpath) {
|
||||
};
|
||||
|
||||
/*
|
||||
Parse a semantic version string into its constituent parts
|
||||
Parse a semantic version string into its constituent parts -- see https://semver.org
|
||||
*/
|
||||
$tw.utils.parseVersion = function(version) {
|
||||
var match = /^((\d+)\.(\d+)\.(\d+))(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?(?:\+([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?$/.exec(version);
|
||||
var match = /^v?((\d+)\.(\d+)\.(\d+))(?:-([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?(?:\+([\dA-Za-z\-]+(?:\.[\dA-Za-z\-]+)*))?$/.exec(version);
|
||||
if(match) {
|
||||
return {
|
||||
version: match[1],
|
||||
@ -427,25 +427,37 @@ $tw.utils.parseVersion = function(version) {
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Returns +1 if the version string A is greater than the version string B, 0 if they are the same, and +1 if B is greater than A.
|
||||
Missing or malformed version strings are parsed as 0.0.0
|
||||
*/
|
||||
$tw.utils.compareVersions = function(versionStringA,versionStringB) {
|
||||
var defaultVersion = {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
patch: 0
|
||||
},
|
||||
versionA = $tw.utils.parseVersion(versionStringA) || defaultVersion,
|
||||
versionB = $tw.utils.parseVersion(versionStringB) || defaultVersion,
|
||||
diff = [
|
||||
versionA.major - versionB.major,
|
||||
versionA.minor - versionB.minor,
|
||||
versionA.patch - versionB.patch
|
||||
];
|
||||
if((diff[0] > 0) || (diff[0] === 0 && diff[1] > 0) || (diff[0] === 0 & diff[1] === 0 & diff[2] > 0)) {
|
||||
return +1;
|
||||
} else if((diff[0] < 0) || (diff[0] === 0 && diff[1] < 0) || (diff[0] === 0 & diff[1] === 0 & diff[2] < 0)) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Returns true if the version string A is greater than the version string B. Returns true if the versions are the same
|
||||
*/
|
||||
$tw.utils.checkVersions = function(versionStringA,versionStringB) {
|
||||
var defaultVersion = {
|
||||
major: 0,
|
||||
minor: 0,
|
||||
patch: 0
|
||||
},
|
||||
versionA = $tw.utils.parseVersion(versionStringA) || defaultVersion,
|
||||
versionB = $tw.utils.parseVersion(versionStringB) || defaultVersion,
|
||||
diff = [
|
||||
versionA.major - versionB.major,
|
||||
versionA.minor - versionB.minor,
|
||||
versionA.patch - versionB.patch
|
||||
];
|
||||
return (diff[0] > 0) ||
|
||||
(diff[0] === 0 && diff[1] > 0) ||
|
||||
(diff[0] === 0 && diff[1] === 0 && diff[2] >= 0);
|
||||
return $tw.utils.compareVersions(versionStringA,versionStringB) !== -1;
|
||||
};
|
||||
|
||||
/*
|
||||
|
76
core/modules/filters/compare.js
Normal file
76
core/modules/filters/compare.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*\
|
||||
title: $:/core/modules/filters/compare.js
|
||||
type: application/javascript
|
||||
module-type: filteroperator
|
||||
|
||||
General purpose comparison operator
|
||||
|
||||
\*/
|
||||
(function(){
|
||||
|
||||
/*jslint node: true, browser: true */
|
||||
/*global $tw: false */
|
||||
"use strict";
|
||||
|
||||
exports.compare = function(source,operator,options) {
|
||||
var suffixes = operator.suffixes || [],
|
||||
type = (suffixes[0] || [])[0],
|
||||
mode = (suffixes[1] || [])[0],
|
||||
typeFn = types[type] || types.number,
|
||||
modeFn = modes[mode] || modes.eq,
|
||||
invert = operator.prefix === "!",
|
||||
results = [];
|
||||
source(function(tiddler,title) {
|
||||
if(modeFn(typeFn(title,operator.operand)) !== invert) {
|
||||
results.push(title);
|
||||
}
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
var types = {
|
||||
"number": function(a,b) {
|
||||
return compare($tw.utils.parseNumber(a),$tw.utils.parseNumber(b));
|
||||
},
|
||||
"integer": function(a,b) {
|
||||
return compare($tw.utils.parseInt(a),$tw.utils.parseInt(b));
|
||||
},
|
||||
"string": function(a,b) {
|
||||
return compare("" + a,"" +b);
|
||||
},
|
||||
"date": function(a,b) {
|
||||
var dateA = $tw.utils.parseDate(a),
|
||||
dateB = $tw.utils.parseDate(b);
|
||||
if(!isFinite(dateA)) {
|
||||
dateA = new Date(0);
|
||||
}
|
||||
if(!isFinite(dateB)) {
|
||||
dateB = new Date(0);
|
||||
}
|
||||
return compare(dateA,dateB);
|
||||
},
|
||||
"version": function(a,b) {
|
||||
return $tw.utils.compareVersions(a,b);
|
||||
}
|
||||
};
|
||||
|
||||
function compare(a,b) {
|
||||
if(a > b) {
|
||||
return +1;
|
||||
} else if(a < b) {
|
||||
return -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
var modes = {
|
||||
"eq": function(value) {return value === 0;},
|
||||
"ne": function(value) {return value !== 0;},
|
||||
"gteq": function(value) {return value >= 0;},
|
||||
"gt": function(value) {return value > 0;},
|
||||
"lteq": function(value) {return value <= 0;},
|
||||
"lt": function(value) {return value < 0;}
|
||||
}
|
||||
|
||||
})();
|
@ -114,9 +114,9 @@ exports.minall = makeNumericReducingOperator(
|
||||
function makeNumericBinaryOperator(fnCalc) {
|
||||
return function(source,operator,options) {
|
||||
var result = [],
|
||||
numOperand = parseNumber(operator.operand);
|
||||
numOperand = $tw.utils.parseNumber(operator.operand);
|
||||
source(function(tiddler,title) {
|
||||
result.push(stringifyNumber(fnCalc(parseNumber(title),numOperand)));
|
||||
result.push($tw.utils.stringifyNumber(fnCalc($tw.utils.parseNumber(title),numOperand)));
|
||||
});
|
||||
return result;
|
||||
};
|
||||
@ -129,18 +129,10 @@ function makeNumericReducingOperator(fnCalc,initialValue) {
|
||||
source(function(tiddler,title) {
|
||||
result.push(title);
|
||||
});
|
||||
return [stringifyNumber(result.reduce(function(accumulator,currentValue) {
|
||||
return fnCalc(accumulator,parseNumber(currentValue));
|
||||
return [$tw.utils.stringifyNumber(result.reduce(function(accumulator,currentValue) {
|
||||
return fnCalc(accumulator,$tw.utils.parseNumber(currentValue));
|
||||
},initialValue))];
|
||||
};
|
||||
}
|
||||
|
||||
function parseNumber(str) {
|
||||
return parseFloat(str) || 0;
|
||||
}
|
||||
|
||||
function stringifyNumber(num) {
|
||||
return num + "";
|
||||
}
|
||||
|
||||
})();
|
||||
|
@ -801,4 +801,16 @@ exports.getSystemInfo = function(str,ending,position) {
|
||||
return results.join("\n");
|
||||
};
|
||||
|
||||
exports.parseNumber = function(str) {
|
||||
return parseFloat(str) || 0;
|
||||
};
|
||||
|
||||
exports.parseInt = function(str) {
|
||||
return parseInt(str) || 0;
|
||||
};
|
||||
|
||||
exports.stringifyNumber = function(num) {
|
||||
return num + "";
|
||||
};
|
||||
|
||||
})();
|
||||
|
84
editions/test/tiddlers/tests/test-compare-filter.js
Normal file
84
editions/test/tiddlers/tests/test-compare-filter.js
Normal file
@ -0,0 +1,84 @@
|
||||
/*\
|
||||
title: test-compare-filters.js
|
||||
type: application/javascript
|
||||
tags: [[$:/tags/test-spec]]
|
||||
|
||||
Tests the compare filter.
|
||||
|
||||
\*/
|
||||
(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("'compare' filter tests", function() {
|
||||
|
||||
var wiki = new $tw.Wiki();
|
||||
|
||||
it("should compare numerical equality", function() {
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:eq[0003]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:ne[000003]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:eq[3]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:ne[3]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:eq[2]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:ne[2]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:eq[x]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:ne[x]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:eq[3]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:ne[3]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:eq[2]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:ne[2]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:eq[x]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:ne[x]]").join(",")).toBe("");
|
||||
});
|
||||
|
||||
it("should compare numerical magnitude", function() {
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:gt[3]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:lt[3]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:gt[2]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:lt[2]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:gt[x]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]compare:number:lt[x]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:gt[3]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:lt[3]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:gt[2]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:lt[2]]").join(",")).toBe("2");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:gt[x]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[2]!compare:number:lt[x]]").join(",")).toBe("2");
|
||||
});
|
||||
|
||||
it("should compare string", function() {
|
||||
expect(wiki.filterTiddlers("[[Monday]compare:string:lt[M]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[Monday]compare:string:lt[W]]").join(",")).toBe("Monday");
|
||||
expect(wiki.filterTiddlers("Monday Tuesday Wednesday Thursday Friday Saturday Sunday +[compare:string:gt[M]sort[]]").join(",")).toBe("Monday,Saturday,Sunday,Thursday,Tuesday,Wednesday");
|
||||
expect(wiki.filterTiddlers("Monday Tuesday Wednesday Thursday Friday Saturday Sunday +[compare:string:gt[M]compare:string:lt[W]sort[]]").join(",")).toBe("Monday,Saturday,Sunday,Thursday,Tuesday");
|
||||
});
|
||||
|
||||
it("should compare dates", function() {
|
||||
expect(wiki.filterTiddlers("[[20200101]compare:date:gt[201912311852]]").join(",")).toBe("20200101");
|
||||
});
|
||||
|
||||
it("should compare version numbers", function() {
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:eq[v1.1.0]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:eq[v1.2.2]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:eq[v1.2.3]]").join(",")).toBe("v1.2.3");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:eq[v1.2.4]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:eq[v2.0.0]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:gt[v1.1.0]]").join(",")).toBe("v1.2.3");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:gt[v1.2.2]]").join(",")).toBe("v1.2.3");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:gt[v1.2.3]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:gt[v1.2.4]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:gt[v2.0.0]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:lt[v1.1.0]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:lt[v1.2.2]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:lt[v1.2.3]]").join(",")).toBe("");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:lt[v1.2.4]]").join(",")).toBe("v1.2.3");
|
||||
expect(wiki.filterTiddlers("[[v1.2.3]compare:version:lt[v2.0.0]]").join(",")).toBe("v1.2.3");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
@ -107,6 +107,30 @@ describe("Utility tests", function() {
|
||||
|
||||
});
|
||||
|
||||
it("should compare versions", function() {
|
||||
var cv = $tw.utils.compareVersions;
|
||||
expect(cv("v0.0.0","v0.0.0")).toEqual(0);
|
||||
expect(cv("0.0.0","v0.0.0")).toEqual(0);
|
||||
expect(cv("v0.0.0","0.0.0")).toEqual(0);
|
||||
expect(cv("v0.0.0","not a version")).toEqual(0);
|
||||
expect(cv("v0.0.0",undefined)).toEqual(0);
|
||||
expect(cv("not a version","v0.0.0")).toEqual(0);
|
||||
expect(cv(undefined,"v0.0.0")).toEqual(0);
|
||||
expect(cv("v1.0.0","v1.0.0")).toEqual(0);
|
||||
expect(cv("v1.0.0","1.0.0")).toEqual(0);
|
||||
|
||||
expect(cv("v1.0.1",undefined)).toEqual(+1);
|
||||
expect(cv("v1.0.1","v1.0.0")).toEqual(+1);
|
||||
expect(cv("v1.1.1","v1.1.0")).toEqual(+1);
|
||||
expect(cv("v1.1.2","v1.1.1")).toEqual(+1);
|
||||
expect(cv("1.1.2","v1.1.1")).toEqual(+1);
|
||||
|
||||
expect(cv("v1.0.0","v1.0.1")).toEqual(-1);
|
||||
expect(cv("v1.1.0","v1.1.1")).toEqual(-1);
|
||||
expect(cv("v1.1.1","v1.1.2")).toEqual(-1);
|
||||
expect(cv("1.1.1","1.1.2")).toEqual(-1);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})();
|
||||
|
51
editions/tw5.com/tiddlers/filters/compare Operator.tid
Normal file
51
editions/tw5.com/tiddlers/filters/compare Operator.tid
Normal file
@ -0,0 +1,51 @@
|
||||
created: 20200412181551706
|
||||
modified: 20200412181551706
|
||||
tags: [[Filter Operators]] [[Mathematics Operators]] [[String Operators]] [[Negatable Operators]]
|
||||
title: compare Operator
|
||||
type: text/vnd.tiddlywiki
|
||||
caption: compare
|
||||
op-purpose: filter the input by comparing each item against the operand
|
||||
op-input: a [[selection of titles|Title Selection]]
|
||||
op-suffix: the <<.op compare>> operator uses a rich suffix, see below for details
|
||||
op-parameter: the value to compare
|
||||
op-output: those input titles matching the specified comparison
|
||||
op-neg-output: those input titles <<.em not>> matching the specified comparison
|
||||
|
||||
<<.from-version "5.1.22">>The <<.op compare>> filter allows numerical, string and date comparisons to be performed.
|
||||
|
||||
The <<.op compare>> operator uses an extended syntax to specify all the options:
|
||||
|
||||
```
|
||||
[compare:<type>:<mode>[<operand>]]
|
||||
```
|
||||
|
||||
The ''type'' can be:
|
||||
|
||||
* "number" - invalid numbers are interpreted as zero
|
||||
* "integer" - invalid integers are interpreted as zero
|
||||
* "string"
|
||||
* "date" - invalid dates are interpreted as 1st January 1970
|
||||
* "version" - invalid versions are interpreted as "v0.0.0"
|
||||
|
||||
The ''mode'' can be:
|
||||
|
||||
* "eq" - equal to
|
||||
* "ne" - not equal ot
|
||||
* "gteq" - greater than or equal to
|
||||
* "gt" - greater than
|
||||
* "lteq" - less than or equal to
|
||||
* "lt" - less than
|
||||
|
||||
The operator compares each item in the selection against the value of the parameter, retaining only those items that pass the specified condition.
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
[[2]compare:number:eq[3]] returns nothing
|
||||
[[2]compare:number:lt[3]] returns "2"
|
||||
[[2]compare:number:eq[2]] returns "2"
|
||||
```
|
||||
|
||||
Note that several of the variants of the <<.op compare>> operator are synonyms for existing operators, and are provided in the interests of consistency. For example, `compare:string:eq[x]` is a synonym for `match[x]`.
|
||||
|
||||
<<.operator-examples "compare">>
|
@ -0,0 +1,11 @@
|
||||
created: 20200412212935849
|
||||
modified: 20200412212935849
|
||||
tags: [[compare Operator]] [[Operator Examples]]
|
||||
title: compare Operator (Examples)
|
||||
type: text/vnd.tiddlywiki
|
||||
|
||||
<<.operator-example 1 "[[20200101]compare:date:gt[201912311852]]" "compares two partial dates">>
|
||||
<<.operator-example 2 "[[202001011852]compare:integer:gt[20191231]]" "compares the same two strings as integers">>
|
||||
<<.operator-example 3 "[list[Days of the Week]compare:string:gt[M]compare:string:lt[W]]">>
|
||||
<<.operator-example 4 "[[v5.1.23-prerelease]compare:version:gt[v5.1.22]]">>
|
||||
<<.operator-example 5 "[[1]compare:number:gt[2]then[yes]else[no]]">>
|
Loading…
Reference in New Issue
Block a user