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) {
|
$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) {
|
if(match) {
|
||||||
return {
|
return {
|
||||||
version: match[1],
|
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
|
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) {
|
$tw.utils.checkVersions = function(versionStringA,versionStringB) {
|
||||||
var defaultVersion = {
|
return $tw.utils.compareVersions(versionStringA,versionStringB) !== -1;
|
||||||
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);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
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) {
|
function makeNumericBinaryOperator(fnCalc) {
|
||||||
return function(source,operator,options) {
|
return function(source,operator,options) {
|
||||||
var result = [],
|
var result = [],
|
||||||
numOperand = parseNumber(operator.operand);
|
numOperand = $tw.utils.parseNumber(operator.operand);
|
||||||
source(function(tiddler,title) {
|
source(function(tiddler,title) {
|
||||||
result.push(stringifyNumber(fnCalc(parseNumber(title),numOperand)));
|
result.push($tw.utils.stringifyNumber(fnCalc($tw.utils.parseNumber(title),numOperand)));
|
||||||
});
|
});
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
@ -129,18 +129,10 @@ function makeNumericReducingOperator(fnCalc,initialValue) {
|
|||||||
source(function(tiddler,title) {
|
source(function(tiddler,title) {
|
||||||
result.push(title);
|
result.push(title);
|
||||||
});
|
});
|
||||||
return [stringifyNumber(result.reduce(function(accumulator,currentValue) {
|
return [$tw.utils.stringifyNumber(result.reduce(function(accumulator,currentValue) {
|
||||||
return fnCalc(accumulator,parseNumber(currentValue));
|
return fnCalc(accumulator,$tw.utils.parseNumber(currentValue));
|
||||||
},initialValue))];
|
},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");
|
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