From f5790f0973d8c46156cd6ad995491732cbc16b2f Mon Sep 17 00:00:00 2001 From: bimlas Date: Wed, 17 Oct 2018 22:11:11 +0200 Subject: [PATCH 01/29] Filter operator that gathering "family" of tiddler [kindred:[]] : up | down | both : name of the field --- core/modules/filters/kindred.js | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 core/modules/filters/kindred.js diff --git a/core/modules/filters/kindred.js b/core/modules/filters/kindred.js new file mode 100644 index 00000000000..6ff4da7510a --- /dev/null +++ b/core/modules/filters/kindred.js @@ -0,0 +1,68 @@ +/*\ +title: $:/core/modules/filters/kindred.js +type: application/javascript +module-type: filteroperator + +Filter operator that gathering "family" of tiddler based on + +[kindred:[]] + +\*/ +(function(){ + + /*jslint node: true, browser: true */ + /*global $tw: false */ + "use strict"; + + /* + Export our filter function + */ + exports.kindred= function(source,operator,options) { + var results = [], + base_title = '', + direction = (operator.suffix || 'both').toLowerCase(), + fieldname = (operator.operand || 'tags').toLowerCase(); + source(function(tiddler, title) { + base_title = title; + if ((direction === 'up') || (direction === 'both')) { + findRecursivelyUp(tiddler, title); + } + if ((direction === 'down') || (direction === 'both')) { + findRecursivelyDown(tiddler, title); + } + }); + + function addToResultsIfNotFoundAlready(title) { + // Parse, but do not add the base tiddler + if (title === base_title) { + return true; + } + if (results.includes(title)) { + return false; + } + results.push(title); + return true + } + + function findRecursivelyUp(tiddler, title) { + if (addToResultsIfNotFoundAlready(title)) { + if (tiddler) { + tiddler.getFieldList(fieldname).forEach(function (target_title) { + findRecursivelyUp($tw.wiki.getTiddler(target_title), target_title); + }); + } + } + } + + function findRecursivelyDown(tiddler, title) { + if (addToResultsIfNotFoundAlready(title)) { + $tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { + findRecursivelyDown($tw.wiki.getTiddler(target_title), target_title); + }); + } + } + + return results; + }; + +})(); From fff1ca0941e8c960987f773a4a4fa53e677bb5db Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 19 Oct 2018 11:41:26 +0200 Subject: [PATCH 02/29] Remove unneeded code I fetched the tiddler, but not used it, thus I removed this to speed up the things. --- core/modules/filters/kindred.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/modules/filters/kindred.js b/core/modules/filters/kindred.js index 6ff4da7510a..42a9d13b163 100644 --- a/core/modules/filters/kindred.js +++ b/core/modules/filters/kindred.js @@ -28,7 +28,7 @@ Filter operator that gathering "family" of tiddler based on findRecursivelyUp(tiddler, title); } if ((direction === 'down') || (direction === 'both')) { - findRecursivelyDown(tiddler, title); + findRecursivelyDown(title); } }); @@ -54,10 +54,10 @@ Filter operator that gathering "family" of tiddler based on } } - function findRecursivelyDown(tiddler, title) { + function findRecursivelyDown(title) { if (addToResultsIfNotFoundAlready(title)) { $tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { - findRecursivelyDown($tw.wiki.getTiddler(target_title), target_title); + findRecursivelyDown(target_title); }); } } From c8dee8355fdee991f039939ec8530c1241ecd3c5 Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 19 Oct 2018 11:56:33 +0200 Subject: [PATCH 03/29] Do not use global `$tw` In the "header" there is a line global $tw: false Since it's declared, I think I should use `options.wiki` instead of `$tw.wiki`. --- core/modules/filters/kindred.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/modules/filters/kindred.js b/core/modules/filters/kindred.js index 42a9d13b163..aa63f610615 100644 --- a/core/modules/filters/kindred.js +++ b/core/modules/filters/kindred.js @@ -48,7 +48,7 @@ Filter operator that gathering "family" of tiddler based on if (addToResultsIfNotFoundAlready(title)) { if (tiddler) { tiddler.getFieldList(fieldname).forEach(function (target_title) { - findRecursivelyUp($tw.wiki.getTiddler(target_title), target_title); + findRecursivelyUp(options.wiki.getTiddler(target_title), target_title); }); } } @@ -56,7 +56,7 @@ Filter operator that gathering "family" of tiddler based on function findRecursivelyDown(title) { if (addToResultsIfNotFoundAlready(title)) { - $tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { + options.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { findRecursivelyDown(target_title); }); } From 5890ea92cea5252003c5d122e7f408c6ce1c69de Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 19 Oct 2018 13:25:57 +0200 Subject: [PATCH 04/29] Use as a real filter I misunderstood the behaviour: I thought that I need to collect tiddlers (initialize a list) and return with them to let the `+[...]` filter expression collect the elements common with the previous expression, but instead of this I have to filter the input tiddlers by checking that it's a member of the family or not. The new syntax is: [kindred:[]] [kindredup:[]] [kindreddown:[]] Because of this filter behaviour, the "base tiddler" (passed as operand) has to be included in to the output list (I excluded it earlier). WARNING: Commit introducing TODOs --- core/modules/filters/kindred.js | 145 +++++++++++++++++++------------- 1 file changed, 88 insertions(+), 57 deletions(-) diff --git a/core/modules/filters/kindred.js b/core/modules/filters/kindred.js index aa63f610615..58d3269cff4 100644 --- a/core/modules/filters/kindred.js +++ b/core/modules/filters/kindred.js @@ -5,64 +5,95 @@ module-type: filteroperator Filter operator that gathering "family" of tiddler based on -[kindred:[]] +[kindred:[]] +[kindredup:[]] +[kindreddown:[]] \*/ -(function(){ - - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; - - /* - Export our filter function - */ - exports.kindred= function(source,operator,options) { - var results = [], - base_title = '', - direction = (operator.suffix || 'both').toLowerCase(), - fieldname = (operator.operand || 'tags').toLowerCase(); - source(function(tiddler, title) { - base_title = title; - if ((direction === 'up') || (direction === 'both')) { - findRecursivelyUp(tiddler, title); - } - if ((direction === 'down') || (direction === 'both')) { - findRecursivelyDown(title); - } - }); - - function addToResultsIfNotFoundAlready(title) { - // Parse, but do not add the base tiddler - if (title === base_title) { - return true; - } - if (results.includes(title)) { - return false; - } - results.push(title); - return true - } - - function findRecursivelyUp(tiddler, title) { - if (addToResultsIfNotFoundAlready(title)) { - if (tiddler) { - tiddler.getFieldList(fieldname).forEach(function (target_title) { - findRecursivelyUp(options.wiki.getTiddler(target_title), target_title); - }); - } - } - } - - function findRecursivelyDown(title) { - if (addToResultsIfNotFoundAlready(title)) { - options.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { - findRecursivelyDown(target_title); - }); - } - } - - return results; - }; +(function () { + + /*jslint node: true, browser: true */ + /*global $tw: false */ + "use strict"; + + // TODO: Should I set global tw to true? + + function collectFamilyMembers(tiddler, title, fieldname, direction) { + var family_members = []; + + function addToResultsIfNotFoundAlready(title) { + if (family_members.includes(title)) { + return false; + } + family_members.push(title); + return true + } + + function findRecursivelyUp(tiddler, title) { + if (addToResultsIfNotFoundAlready(title)) { + if (tiddler) { + tiddler.getFieldList(fieldname).forEach(function (target_title) { + findRecursivelyUp($tw.wiki.getTiddler(target_title), target_title); + }); + } + } + } + + function findRecursivelyDown(title) { + if (addToResultsIfNotFoundAlready(title)) { + $tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { + findRecursivelyDown(target_title); + }); + } + } + + if ((direction === 'up') || (direction === 'both')) { + findRecursivelyUp(tiddler, title); + } + if (direction === 'both') { + // Remove the base family member: + // If it's already in the results, it will be skipped when parsing in + // the oposite direction. + family_members.shift(); + } + if ((direction === 'down') || (direction === 'both')) { + findRecursivelyDown(title); + } + return family_members; + } + + function executeSource(source, operator, direction) { + + // TODO: System tiddlers are not shown??? + + var results = [], + fieldname = (operator.suffix || 'tags').toLowerCase(), + title_from_family = operator.operand, + tiddler_from_family = $tw.wiki.getTiddler(title_from_family), + family_members = collectFamilyMembers(tiddler_from_family, title_from_family, fieldname, direction); + + source(function (tiddler, title) { + if (family_members.includes(title)) { + results.push(title); + } + }); + + return results; + } + + /* + Export our filter function + */ + exports.kindred = function (source, operator, options) { + return executeSource(source, operator, 'both'); + }; + + exports.kindredup = function (source, operator, options) { + return executeSource(source, operator, 'up'); + }; + + exports.kindreddown = function (source, operator, options) { + return executeSource(source, operator, 'down'); + }; })(); From 304937d7d89b09dcb774774a9f2d5ad4249f009d Mon Sep 17 00:00:00 2001 From: bimlas Date: Wed, 24 Oct 2018 15:38:59 +0200 Subject: [PATCH 05/29] List the kindred of input tiddles if there is no operand For example: [[Drag and Drop]kindred[]] [[Drag and Drop]] [[DragAndDropMechanism]] +[kindred[]] WARNING: Commit introducing TODOs --- core/modules/filters/kindred.js | 48 ++++++++++++++++++++++++++------- 1 file changed, 38 insertions(+), 10 deletions(-) diff --git a/core/modules/filters/kindred.js b/core/modules/filters/kindred.js index 58d3269cff4..05b32187220 100644 --- a/core/modules/filters/kindred.js +++ b/core/modules/filters/kindred.js @@ -62,21 +62,49 @@ Filter operator that gathering "family" of tiddler based on return family_members; } + // TODO: Is there a better way for unique? + function uniqueArray(input) { + var seen = {}, + output = [], + len = input.length, + j = 0; + for (var i = 0; i < len; i++) { + var item = input[i]; + if (seen[item] !== 1) { + seen[item] = 1; + output[j++] = item; + } + } + return output; + } + function executeSource(source, operator, direction) { - // TODO: System tiddlers are not shown??? + // TODO: $:/tags/SideBar is not shown with operand, for example + // [[Drag and Drop]kindred[]] shows this tiddler, but + // [kindred[Drag and Drop]] is not, because this tiddler is not exists in + // the Advanced Filter's input list by default. Try to log to console the + // family members. var results = [], - fieldname = (operator.suffix || 'tags').toLowerCase(), - title_from_family = operator.operand, - tiddler_from_family = $tw.wiki.getTiddler(title_from_family), - family_members = collectFamilyMembers(tiddler_from_family, title_from_family, fieldname, direction); + fieldname = (operator.suffix || 'tags').toLowerCase(); - source(function (tiddler, title) { - if (family_members.includes(title)) { - results.push(title); - } - }); + if (operator.operand !== '') { + var title_from_family = operator.operand, + tiddler_from_family = $tw.wiki.getTiddler(title_from_family), + family_members = collectFamilyMembers(tiddler_from_family, title_from_family, fieldname, direction); + + source(function (tiddler, title) { + if (family_members.includes(title)) { + results.push(title); + } + }); + } else { + source(function (tiddler, title) { + results = results.concat(collectFamilyMembers(tiddler, title, fieldname, direction)); + }); + results = uniqueArray(results); + } return results; } From de6bf92cb21a01472442eb9fc2688d61f176d029 Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 26 Oct 2018 12:41:34 +0200 Subject: [PATCH 06/29] Add negation option --- core/modules/filters/kindred.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/modules/filters/kindred.js b/core/modules/filters/kindred.js index 05b32187220..a7e716eb434 100644 --- a/core/modules/filters/kindred.js +++ b/core/modules/filters/kindred.js @@ -87,7 +87,8 @@ Filter operator that gathering "family" of tiddler based on // family members. var results = [], - fieldname = (operator.suffix || 'tags').toLowerCase(); + fieldname = (operator.suffix || 'tags').toLowerCase(), + needs_exclusion = operator.prefix === '!'; if (operator.operand !== '') { var title_from_family = operator.operand, @@ -95,7 +96,7 @@ Filter operator that gathering "family" of tiddler based on family_members = collectFamilyMembers(tiddler_from_family, title_from_family, fieldname, direction); source(function (tiddler, title) { - if (family_members.includes(title)) { + if (needs_exclusion !== family_members.includes(title)) { results.push(title); } }); From 55a28ef0fb1ab8ac77927b6cf48712aeca54eebd Mon Sep 17 00:00:00 2001 From: bimlas Date: Thu, 25 Oct 2018 15:39:18 +0200 Subject: [PATCH 07/29] Use to/with/from instead of up/both/down For example the usage of `tags` and `list` fields are controversal: `tags` points to parents while `list` points to children, thus `up`/`down` has no sense. Use `field:direction` syntax for suffix (`[kindred:tags:to[Something]]`) instead of defining multiple operators (`kindred/kindredup/kindreddown`) --- core/modules/filters/kindred.js | 64 ++++++++++++--------------------- 1 file changed, 23 insertions(+), 41 deletions(-) diff --git a/core/modules/filters/kindred.js b/core/modules/filters/kindred.js index a7e716eb434..1aa9e9b8853 100644 --- a/core/modules/filters/kindred.js +++ b/core/modules/filters/kindred.js @@ -3,11 +3,7 @@ title: $:/core/modules/filters/kindred.js type: application/javascript module-type: filteroperator -Filter operator that gathering "family" of tiddler based on - -[kindred:[]] -[kindredup:[]] -[kindreddown:[]] +Filter operator that recursively finds connection between tiddlers \*/ (function () { @@ -29,35 +25,35 @@ Filter operator that gathering "family" of tiddler based on return true } - function findRecursivelyUp(tiddler, title) { + function findConnectionsFrom(tiddler, title) { if (addToResultsIfNotFoundAlready(title)) { if (tiddler) { tiddler.getFieldList(fieldname).forEach(function (target_title) { - findRecursivelyUp($tw.wiki.getTiddler(target_title), target_title); + findConnectionsFrom($tw.wiki.getTiddler(target_title), target_title); }); } } } - function findRecursivelyDown(title) { + function findConnectionsTo(title) { if (addToResultsIfNotFoundAlready(title)) { $tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { - findRecursivelyDown(target_title); + findConnectionsTo(target_title); }); } } - if ((direction === 'up') || (direction === 'both')) { - findRecursivelyUp(tiddler, title); + if ((direction === 'from') || (direction === 'with')) { + findConnectionsFrom(tiddler, title); } - if (direction === 'both') { + if (direction === 'with') { // Remove the base family member: // If it's already in the results, it will be skipped when parsing in - // the oposite direction. + // the opposite direction. family_members.shift(); } - if ((direction === 'down') || (direction === 'both')) { - findRecursivelyDown(title); + if ((direction === 'to') || (direction === 'with')) { + findConnectionsTo(title); } return family_members; } @@ -78,17 +74,19 @@ Filter operator that gathering "family" of tiddler based on return output; } - function executeSource(source, operator, direction) { - - // TODO: $:/tags/SideBar is not shown with operand, for example - // [[Drag and Drop]kindred[]] shows this tiddler, but - // [kindred[Drag and Drop]] is not, because this tiddler is not exists in - // the Advanced Filter's input list by default. Try to log to console the - // family members. - + /* + Export our filter function + */ + exports.kindred = function (source, operator, options) { var results = [], - fieldname = (operator.suffix || 'tags').toLowerCase(), - needs_exclusion = operator.prefix === '!'; + needs_exclusion = operator.prefix === '!', + suffix_list = (operator.suffix || '').split(':'), + fieldname = (suffix_list[0] || 'tags').toLowerCase(), + direction = (suffix_list[1] || 'with').toLowerCase(); + + if ((operator.operand === '') && (needs_exclusion)) { + return []; + } if (operator.operand !== '') { var title_from_family = operator.operand, @@ -109,20 +107,4 @@ Filter operator that gathering "family" of tiddler based on return results; } - - /* - Export our filter function - */ - exports.kindred = function (source, operator, options) { - return executeSource(source, operator, 'both'); - }; - - exports.kindredup = function (source, operator, options) { - return executeSource(source, operator, 'up'); - }; - - exports.kindreddown = function (source, operator, options) { - return executeSource(source, operator, 'down'); - }; - })(); From bb0aa4ed44a8b21d03c5d08a3e794bbd57f755a6 Mon Sep 17 00:00:00 2001 From: bimlas Date: Sat, 27 Oct 2018 21:56:16 +0200 Subject: [PATCH 08/29] Add documentation --- .../tiddlers/filters/examples/kindred.tid | 26 ++++++++++++++++ editions/tw5.com/tiddlers/filters/kindred.tid | 30 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 editions/tw5.com/tiddlers/filters/examples/kindred.tid create mode 100644 editions/tw5.com/tiddlers/filters/kindred.tid diff --git a/editions/tw5.com/tiddlers/filters/examples/kindred.tid b/editions/tw5.com/tiddlers/filters/examples/kindred.tid new file mode 100644 index 00000000000..0c3a7048751 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/examples/kindred.tid @@ -0,0 +1,26 @@ +created: 20181026114434874 +modified: 20181027193703795 +tags: [[Operator Examples]] [[kindred Operator]] +title: kindred Operator (Examples) +type: text/vnd.tiddlywiki + +Filter the input titles for those which connected with parameter title + +<<.operator-example 1 "[kindred[Filter Syntax]]">> + +Collect titles connected with input title (even if the title tiddler is not exists) + +<<.operator-example 2 "[[Filter Syntax]kindred[]]">> + +Get ancestors of tiddler based on <<.field tags>> and <<.field list>> (remember: <<.field tags>> points to parents while <<.field list>> points to children) + +<<.operator-example 3 "[kindred:tags:from[Filter Syntax]]">> +<<.operator-example 4 "[kindred:list:to[Filter Syntax]]">> + +Filter input titles to those which are connected to both parameter titles (find intersection, common titles) + +<<.operator-example 6 "[kindred[Variables]kindred[Filter Syntax]]">> + +Get tiddlers which are not related to the [[TableOfContents]] (by <<.field tags>>), but has tags + +<<.operator-example 7 "[!is[system]type[text/vnd.tiddlywiki]!kindred[TableOfContents]has[tags]]>> \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/filters/kindred.tid b/editions/tw5.com/tiddlers/filters/kindred.tid new file mode 100644 index 00000000000..dcd516134a4 --- /dev/null +++ b/editions/tw5.com/tiddlers/filters/kindred.tid @@ -0,0 +1,30 @@ +caption: kindred +created: 20181025133804171 +modified: 20181027193949233 +op-input: a [[selection of titles|Title Selection]] +op-neg-output: ''with parameter <<.place T>>''
» those input titles which are ''<<.em not>> connected to <<.place T>>''
''without parameter <<.place T>>''
» ignored +op-output: ''with parameter <<.place T>>''
» those input titles which are ''connected to <<.place T>>''
''without <<.place T>>''
» ''all'' tiddler titles which are ''connected to input titles'' +op-parameter: a tiddler title or nothing +op-parameter-name: T +op-purpose: select or collect tiddler titles that are connected to given tiddlers in any depth, connection is based on a field +op-suffix: [[field|TiddlerFields]] which connecting tiddlers (defaulting to <<.field tags>>) and direction (defaulting to `with`) +tags: [[Negatable Operators]] [[Tag Operators]] [[Filter Operators]] [[Field Operators]] +title: kindred Operator +type: text/vnd.tiddlywiki + +The syntax of suffix is: + +<$railroad text=""" +[: "" ] ":" [: ("to" |: "with" | "from") ] +"""/> + +The `` suffix is assumed to be a [[title list|Title List]]. + +|!Direction|!Members| +|^`to`|Ancestors of tiddler and the tiddler itslef| +|^`with`|All tiddlers connected to the tiddler and the tiddler itslef| +|^`from`|Succesors of tiddler and the tiddler itslef| + +<<.op {{!!caption}}>> is a [[modifier|Selection Constructors]], but without <<.place T>> is a [[constructor|Selection Constructors]]. + +<<.operator-examples "kindred">> From 7d3834aae809976dcc34d064923e3a2704229ade Mon Sep 17 00:00:00 2001 From: bimlas Date: Sat, 27 Oct 2018 22:29:15 +0200 Subject: [PATCH 09/29] Rename from `kindred` to `kin` People voted this name at https://goo.gl/forms/bywjS7UiuaosfRlR2 --- core/modules/filters/{kindred.js => kin.js} | 18 +++++++++--------- .../filters/examples/{kindred.tid => kin.tid} | 16 ++++++++-------- .../tiddlers/filters/{kindred.tid => kin.tid} | 16 ++++++++-------- 3 files changed, 25 insertions(+), 25 deletions(-) rename core/modules/filters/{kindred.js => kin.js} (84%) rename editions/tw5.com/tiddlers/filters/examples/{kindred.tid => kin.tid} (62%) rename editions/tw5.com/tiddlers/filters/{kindred.tid => kin.tid} (73%) diff --git a/core/modules/filters/kindred.js b/core/modules/filters/kin.js similarity index 84% rename from core/modules/filters/kindred.js rename to core/modules/filters/kin.js index 1aa9e9b8853..bb85554e429 100644 --- a/core/modules/filters/kindred.js +++ b/core/modules/filters/kin.js @@ -1,9 +1,9 @@ /*\ -title: $:/core/modules/filters/kindred.js +title: $:/core/modules/filters/kin.js type: application/javascript module-type: filteroperator -Filter operator that recursively finds connection between tiddlers +Filter operator that recursively finds kindred between tiddlers \*/ (function () { @@ -25,26 +25,26 @@ Filter operator that recursively finds connection between tiddlers return true } - function findConnectionsFrom(tiddler, title) { + function findKindredFrom(tiddler, title) { if (addToResultsIfNotFoundAlready(title)) { if (tiddler) { tiddler.getFieldList(fieldname).forEach(function (target_title) { - findConnectionsFrom($tw.wiki.getTiddler(target_title), target_title); + findKindredFrom($tw.wiki.getTiddler(target_title), target_title); }); } } } - function findConnectionsTo(title) { + function findKindredTo(title) { if (addToResultsIfNotFoundAlready(title)) { $tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { - findConnectionsTo(target_title); + findKindredTo(target_title); }); } } if ((direction === 'from') || (direction === 'with')) { - findConnectionsFrom(tiddler, title); + findKindredFrom(tiddler, title); } if (direction === 'with') { // Remove the base family member: @@ -53,7 +53,7 @@ Filter operator that recursively finds connection between tiddlers family_members.shift(); } if ((direction === 'to') || (direction === 'with')) { - findConnectionsTo(title); + findKindredTo(title); } return family_members; } @@ -77,7 +77,7 @@ Filter operator that recursively finds connection between tiddlers /* Export our filter function */ - exports.kindred = function (source, operator, options) { + exports.kin = function (source, operator, options) { var results = [], needs_exclusion = operator.prefix === '!', suffix_list = (operator.suffix || '').split(':'), diff --git a/editions/tw5.com/tiddlers/filters/examples/kindred.tid b/editions/tw5.com/tiddlers/filters/examples/kin.tid similarity index 62% rename from editions/tw5.com/tiddlers/filters/examples/kindred.tid rename to editions/tw5.com/tiddlers/filters/examples/kin.tid index 0c3a7048751..5f7666cc16c 100644 --- a/editions/tw5.com/tiddlers/filters/examples/kindred.tid +++ b/editions/tw5.com/tiddlers/filters/examples/kin.tid @@ -1,26 +1,26 @@ created: 20181026114434874 modified: 20181027193703795 -tags: [[Operator Examples]] [[kindred Operator]] -title: kindred Operator (Examples) +tags: [[Operator Examples]] [[kin Operator]] +title: kin Operator (Examples) type: text/vnd.tiddlywiki Filter the input titles for those which connected with parameter title -<<.operator-example 1 "[kindred[Filter Syntax]]">> +<<.operator-example 1 "[kin[Filter Syntax]]">> Collect titles connected with input title (even if the title tiddler is not exists) -<<.operator-example 2 "[[Filter Syntax]kindred[]]">> +<<.operator-example 2 "[[Filter Syntax]kin[]]">> Get ancestors of tiddler based on <<.field tags>> and <<.field list>> (remember: <<.field tags>> points to parents while <<.field list>> points to children) -<<.operator-example 3 "[kindred:tags:from[Filter Syntax]]">> -<<.operator-example 4 "[kindred:list:to[Filter Syntax]]">> +<<.operator-example 3 "[kin:tags:from[Filter Syntax]]">> +<<.operator-example 4 "[kin:list:to[Filter Syntax]]">> Filter input titles to those which are connected to both parameter titles (find intersection, common titles) -<<.operator-example 6 "[kindred[Variables]kindred[Filter Syntax]]">> +<<.operator-example 6 "[kin[Variables]kin[Filter Syntax]]">> Get tiddlers which are not related to the [[TableOfContents]] (by <<.field tags>>), but has tags -<<.operator-example 7 "[!is[system]type[text/vnd.tiddlywiki]!kindred[TableOfContents]has[tags]]>> \ No newline at end of file +<<.operator-example 7 "[!is[system]type[text/vnd.tiddlywiki]!kin[TableOfContents]has[tags]]>> diff --git a/editions/tw5.com/tiddlers/filters/kindred.tid b/editions/tw5.com/tiddlers/filters/kin.tid similarity index 73% rename from editions/tw5.com/tiddlers/filters/kindred.tid rename to editions/tw5.com/tiddlers/filters/kin.tid index dcd516134a4..b4d31f7dff8 100644 --- a/editions/tw5.com/tiddlers/filters/kindred.tid +++ b/editions/tw5.com/tiddlers/filters/kin.tid @@ -1,15 +1,15 @@ -caption: kindred +caption: kin created: 20181025133804171 -modified: 20181027193949233 +modified: 20181027202618759 op-input: a [[selection of titles|Title Selection]] op-neg-output: ''with parameter <<.place T>>''
» those input titles which are ''<<.em not>> connected to <<.place T>>''
''without parameter <<.place T>>''
» ignored op-output: ''with parameter <<.place T>>''
» those input titles which are ''connected to <<.place T>>''
''without <<.place T>>''
» ''all'' tiddler titles which are ''connected to input titles'' op-parameter: a tiddler title or nothing op-parameter-name: T -op-purpose: select or collect tiddler titles that are connected to given tiddlers in any depth, connection is based on a field +op-purpose: recursively finds kindred between tiddlers op-suffix: [[field|TiddlerFields]] which connecting tiddlers (defaulting to <<.field tags>>) and direction (defaulting to `with`) tags: [[Negatable Operators]] [[Tag Operators]] [[Filter Operators]] [[Field Operators]] -title: kindred Operator +title: kin Operator type: text/vnd.tiddlywiki The syntax of suffix is: @@ -21,10 +21,10 @@ The syntax of suffix is: The `` suffix is assumed to be a [[title list|Title List]]. |!Direction|!Members| -|^`to`|Ancestors of tiddler and the tiddler itslef| -|^`with`|All tiddlers connected to the tiddler and the tiddler itslef| -|^`from`|Succesors of tiddler and the tiddler itslef| +|^`to`|Find the title of <<.place T>> in the target field of input tiddlers recursively + <<.place T>> itself| +|^`with`|Union of `from` and `to`| +|^`from`|Find the input titles in the target field of <<.place T>> recursively + <<.place T>> itself| <<.op {{!!caption}}>> is a [[modifier|Selection Constructors]], but without <<.place T>> is a [[constructor|Selection Constructors]]. -<<.operator-examples "kindred">> +<<.operator-examples "kin">> From fdfe6deed73984228797faa83c932b2d816e536f Mon Sep 17 00:00:00 2001 From: bimlas Date: Wed, 31 Oct 2018 23:13:45 +0100 Subject: [PATCH 10/29] Visualization of the filter results in the examples --- .../tw5.com/tiddlers/filters/examples/kin.tid | 75 +++++++++++++------ 1 file changed, 54 insertions(+), 21 deletions(-) diff --git a/editions/tw5.com/tiddlers/filters/examples/kin.tid b/editions/tw5.com/tiddlers/filters/examples/kin.tid index 5f7666cc16c..5a2e983a0b0 100644 --- a/editions/tw5.com/tiddlers/filters/examples/kin.tid +++ b/editions/tw5.com/tiddlers/filters/examples/kin.tid @@ -1,26 +1,59 @@ created: 20181026114434874 -modified: 20181027193703795 +modified: 20181101135634331 tags: [[Operator Examples]] [[kin Operator]] title: kin Operator (Examples) type: text/vnd.tiddlywiki -Filter the input titles for those which connected with parameter title - -<<.operator-example 1 "[kin[Filter Syntax]]">> - -Collect titles connected with input title (even if the title tiddler is not exists) - -<<.operator-example 2 "[[Filter Syntax]kin[]]">> - -Get ancestors of tiddler based on <<.field tags>> and <<.field list>> (remember: <<.field tags>> points to parents while <<.field list>> points to children) - -<<.operator-example 3 "[kin:tags:from[Filter Syntax]]">> -<<.operator-example 4 "[kin:list:to[Filter Syntax]]">> - -Filter input titles to those which are connected to both parameter titles (find intersection, common titles) - -<<.operator-example 6 "[kin[Variables]kin[Filter Syntax]]">> - -Get tiddlers which are not related to the [[TableOfContents]] (by <<.field tags>>), but has tags - -<<.operator-example 7 "[!is[system]type[text/vnd.tiddlywiki]!kin[TableOfContents]has[tags]]>> +\define item-class(highlightfilter) <$list filter="""[all[current]$highlightfilter$first[]]""">highlighted-toc-item + +\define each-level(tiddlername, highlightfilter) +
  • + <$wikify name="transcluded-item-class" text=<> > + <$link class=<> >[[$(currentTiddler)$]] + +
      + <$list filter="""[all[current]tagging[]kin[$tiddlername$]]"""> + <> + +
    +
  • +\end + +\define kin-toc(highlightfilter) +
    + <$tiddler tiddler="TableOfContents"> +
      + <> +
    + +
    +\end + +\define kin-example-with-toc(number filter comment) +<<.operator-example """$number$""" """[$filter$]""" """$comment$""">> +<$reveal type="nomatch" state="""$:/state/kin-example-with-toc-$number$""" text="show"> +<$button set="""$:/state/kin-example-with-toc-$number$""" setTo="show">Show tree + +<$reveal type="match" state="""$:/state/kin-example-with-toc-$number$""" text="show"> +<$button set="""$:/state/kin-example-with-toc-$number$""" setTo="hide">Hide tree +<> + +\end + + + +Family tree of [[Filter Syntax]] tiddler (to really understand, look at the [[TableOfContents]]) + +<> + +<> +<> +<> +<[[Reference]] listing [[Concepts]], but [[Concepts]] does not listing [[Filters]] so they do not belong to a family based on `list`">> +<> +<> +<<.operator-example 7 "[!is[system]type[text/vnd.tiddlywiki]!kin[TableOfContents]first[10]]" "first 10 tiddlers which are not related to [[TableOfContents]] (by `tags`)">> From 05b9a5327bf5399bf3682d6b820e408e36fda3a6 Mon Sep 17 00:00:00 2001 From: bimlas Date: Thu, 1 Nov 2018 21:33:26 +0100 Subject: [PATCH 11/29] Solve the problem with cyclical connections If there is a cyclical family tree (the "bottom" (C) element is tagging the "top" (A)) and a branch is derived from one of the members (B), then the branch (B2) is not always found in the filter. A |\ | B |/ \ C B2 During the crawling of the tree, it first adds all the elements to the list, but does not scan them in the other direction: if it finds a that has been checked already, it will be skipped to prevent endless loops. To find all the elements, but avoid endless loops, I'll pick up the results in two separate lists and then combine them. WARNING: Commit introducing TODOs --- core/modules/filters/kin.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index bb85554e429..e69d23595db 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -15,18 +15,19 @@ Filter operator that recursively finds kindred between tiddlers // TODO: Should I set global tw to true? function collectFamilyMembers(tiddler, title, fieldname, direction) { - var family_members = []; + var family_members_from = [], + family_members_to = []; - function addToResultsIfNotFoundAlready(title) { - if (family_members.includes(title)) { + function addToResultsIfNotFoundAlready(list, title) { + if (list.includes(title)) { return false; } - family_members.push(title); + list.push(title); return true } function findKindredFrom(tiddler, title) { - if (addToResultsIfNotFoundAlready(title)) { + if (addToResultsIfNotFoundAlready(family_members_from, title)) { if (tiddler) { tiddler.getFieldList(fieldname).forEach(function (target_title) { findKindredFrom($tw.wiki.getTiddler(target_title), target_title); @@ -36,7 +37,7 @@ Filter operator that recursively finds kindred between tiddlers } function findKindredTo(title) { - if (addToResultsIfNotFoundAlready(title)) { + if (addToResultsIfNotFoundAlready(family_members_to, title)) { $tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { findKindredTo(target_title); }); @@ -46,16 +47,10 @@ Filter operator that recursively finds kindred between tiddlers if ((direction === 'from') || (direction === 'with')) { findKindredFrom(tiddler, title); } - if (direction === 'with') { - // Remove the base family member: - // If it's already in the results, it will be skipped when parsing in - // the opposite direction. - family_members.shift(); - } if ((direction === 'to') || (direction === 'with')) { findKindredTo(title); } - return family_members; + return uniqueArray(family_members_from.concat(family_members_to)); } // TODO: Is there a better way for unique? @@ -76,6 +71,8 @@ Filter operator that recursively finds kindred between tiddlers /* Export our filter function + + TODO: May I add tests? (editions/test/tiddlers/tests) */ exports.kin = function (source, operator, options) { var results = [], From f31afeaac954158708e43b85e05ccffdbc9cb460 Mon Sep 17 00:00:00 2001 From: bimlas Date: Thu, 1 Nov 2018 22:11:10 +0100 Subject: [PATCH 12/29] Modify examples, clarify documentation Examples also contains an other branch of "Filters" to make it clear. --- .../tw5.com/tiddlers/filters/examples/kin.tid | 18 +++++++++--------- editions/tw5.com/tiddlers/filters/kin.tid | 8 ++++---- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/editions/tw5.com/tiddlers/filters/examples/kin.tid b/editions/tw5.com/tiddlers/filters/examples/kin.tid index 5a2e983a0b0..c441000b9df 100644 --- a/editions/tw5.com/tiddlers/filters/examples/kin.tid +++ b/editions/tw5.com/tiddlers/filters/examples/kin.tid @@ -1,10 +1,10 @@ created: 20181026114434874 -modified: 20181101135634331 +modified: 20181102160039607 tags: [[Operator Examples]] [[kin Operator]] title: kin Operator (Examples) type: text/vnd.tiddlywiki -\define item-class(highlightfilter) <$list filter="""[all[current]$highlightfilter$first[]]""">highlighted-toc-item +\define item-class(highlightfilter) <$list filter="""[$highlightfilter$is[current]first[]]""">highlighted-toc-item \define each-level(tiddlername, highlightfilter)
  • @@ -12,7 +12,7 @@ type: text/vnd.tiddlywiki <$link class=<> >[[$(currentTiddler)$]]
      - <$list filter="""[all[current]tagging[]kin[$tiddlername$]]"""> + <$list filter="""$tiddlername$ +[kin[]tag[$(currentTiddler)$]]"""> <>
    @@ -23,7 +23,7 @@ type: text/vnd.tiddlywiki
    <$tiddler tiddler="TableOfContents">
      - <> + <>
    @@ -46,14 +46,14 @@ type: text/vnd.tiddlywiki } -Family tree of [[Filter Syntax]] tiddler (to really understand, look at the [[TableOfContents]]) +Family tree of [[Filter Syntax]] and [[kin Operator]] (to really understand, look at the [[TableOfContents]]) <> <> -<> +<> <> -<[[Reference]] listing [[Concepts]], but [[Concepts]] does not listing [[Filters]] so they do not belong to a family based on `list`">> -<> -<> +<[[Reference]] listing [[Concepts]], but [[Concepts]] does not listing [[Filters]] so they do not belong to the same family based on `list`">> +<> +<> <<.operator-example 7 "[!is[system]type[text/vnd.tiddlywiki]!kin[TableOfContents]first[10]]" "first 10 tiddlers which are not related to [[TableOfContents]] (by `tags`)">> diff --git a/editions/tw5.com/tiddlers/filters/kin.tid b/editions/tw5.com/tiddlers/filters/kin.tid index b4d31f7dff8..e748eb9ddf2 100644 --- a/editions/tw5.com/tiddlers/filters/kin.tid +++ b/editions/tw5.com/tiddlers/filters/kin.tid @@ -1,12 +1,12 @@ caption: kin created: 20181025133804171 -modified: 20181027202618759 +modified: 20181102155624126 op-input: a [[selection of titles|Title Selection]] -op-neg-output: ''with parameter <<.place T>>''
    » those input titles which are ''<<.em not>> connected to <<.place T>>''
    ''without parameter <<.place T>>''
    » ignored -op-output: ''with parameter <<.place T>>''
    » those input titles which are ''connected to <<.place T>>''
    ''without <<.place T>>''
    » ''all'' tiddler titles which are ''connected to input titles'' +op-neg-output: ''with parameter <<.place T>>''
    » those input titles which are ''<<.em not>> kin with <<.place T>>''
    ''without parameter <<.place T>>''
    » ignored +op-output: ''with parameter <<.place T>>''
    » those input titles which are ''kin with <<.place T>>''
    ''without <<.place T>>''
    » ''all'' tiddler titles which are ''kin with input titles'' op-parameter: a tiddler title or nothing op-parameter-name: T -op-purpose: recursively finds kindred between tiddlers +op-purpose: recursively looking for kinship between tiddler titles op-suffix: [[field|TiddlerFields]] which connecting tiddlers (defaulting to <<.field tags>>) and direction (defaulting to `with`) tags: [[Negatable Operators]] [[Tag Operators]] [[Filter Operators]] [[Field Operators]] title: kin Operator From ccc4d8705a158365f0367bce6a26e870225fbb83 Mon Sep 17 00:00:00 2001 From: bimlas Date: Tue, 6 Nov 2018 22:47:35 +0100 Subject: [PATCH 13/29] Change indentation, make it more readable --- core/modules/filters/kin.js | 198 ++++++++++++++++++------------------ 1 file changed, 99 insertions(+), 99 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index e69d23595db..e5be749a48b 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -3,105 +3,105 @@ title: $:/core/modules/filters/kin.js type: application/javascript module-type: filteroperator -Filter operator that recursively finds kindred between tiddlers +Finds out where a tiddler originates from and what other tiddlers originate from it \*/ -(function () { - - /*jslint node: true, browser: true */ - /*global $tw: false */ - "use strict"; - - // TODO: Should I set global tw to true? - - function collectFamilyMembers(tiddler, title, fieldname, direction) { - var family_members_from = [], - family_members_to = []; - - function addToResultsIfNotFoundAlready(list, title) { - if (list.includes(title)) { - return false; - } - list.push(title); - return true - } - - function findKindredFrom(tiddler, title) { - if (addToResultsIfNotFoundAlready(family_members_from, title)) { - if (tiddler) { - tiddler.getFieldList(fieldname).forEach(function (target_title) { - findKindredFrom($tw.wiki.getTiddler(target_title), target_title); - }); - } - } - } - - function findKindredTo(title) { - if (addToResultsIfNotFoundAlready(family_members_to, title)) { - $tw.wiki.findListingsOfTiddler(title, fieldname).forEach(function (target_title) { - findKindredTo(target_title); - }); - } - } - - if ((direction === 'from') || (direction === 'with')) { - findKindredFrom(tiddler, title); - } - if ((direction === 'to') || (direction === 'with')) { - findKindredTo(title); - } - return uniqueArray(family_members_from.concat(family_members_to)); - } - - // TODO: Is there a better way for unique? - function uniqueArray(input) { - var seen = {}, - output = [], - len = input.length, - j = 0; - for (var i = 0; i < len; i++) { - var item = input[i]; - if (seen[item] !== 1) { - seen[item] = 1; - output[j++] = item; - } - } - return output; - } - - /* - Export our filter function - - TODO: May I add tests? (editions/test/tiddlers/tests) - */ - exports.kin = function (source, operator, options) { - var results = [], - needs_exclusion = operator.prefix === '!', - suffix_list = (operator.suffix || '').split(':'), - fieldname = (suffix_list[0] || 'tags').toLowerCase(), - direction = (suffix_list[1] || 'with').toLowerCase(); - - if ((operator.operand === '') && (needs_exclusion)) { - return []; - } - - if (operator.operand !== '') { - var title_from_family = operator.operand, - tiddler_from_family = $tw.wiki.getTiddler(title_from_family), - family_members = collectFamilyMembers(tiddler_from_family, title_from_family, fieldname, direction); - - source(function (tiddler, title) { - if (needs_exclusion !== family_members.includes(title)) { - results.push(title); - } - }); - } else { - source(function (tiddler, title) { - results = results.concat(collectFamilyMembers(tiddler, title, fieldname, direction)); - }); - results = uniqueArray(results); - } - - return results; - } +(function() { + + /*jslint node: true, browser: true */ + /*global $tw: false */ + "use strict"; + + // TODO: Should I set global tw to true? + + function collectTitlesRecursively(baseTiddler,baseTitle,fieldName,direction) { + var titlesPointingFromBase = [], + titlesPointingToBase = []; + + function addToResultsIfNotFoundAlready(list,title) { + if(list.includes(title)) { + return false; + } + list.push(title); + return true + } + + function collectTitlesPointingFrom(tiddler,title) { + if(addToResultsIfNotFoundAlready(titlesPointingFromBase,title)) { + if(tiddler) { + tiddler.getFieldList(fieldName).forEach(function(targetTitle) { + collectTitlesPointingFrom($tw.wiki.getTiddler(targetTitle),targetTitle); + }); + } + } + } + + function collectTitlesPointingTo(title) { + if(addToResultsIfNotFoundAlready(titlesPointingToBase,title)) { + $tw.wiki.findListingsOfTiddler(title,fieldName).forEach(function(targetTitle) { + collectTitlesPointingTo(targetTitle); + }); + } + } + + if((direction === "from") || (direction === "with")) { + collectTitlesPointingFrom(baseTiddler,baseTitle); + } + if((direction === "to") || (direction === "with")) { + collectTitlesPointingTo(baseTitle); + } + return uniqueArray(titlesPointingFromBase.concat(titlesPointingToBase)); + } + + // TODO: Is there a better way for unique? + function uniqueArray(input) { + var seen = {}, + output = [], + len = input.length, + j = 0; + for(var i = 0; i < len; i++) { + var item = input[i]; + if(seen[item] !== 1) { + seen[item] = 1; + output[j++] = item; + } + } + return output; + } + + /* + Export our filter function + + TODO: May I add tests? (editions/test/tiddlers/tests) + */ + exports.kin = function(source,operator,options) { + var results = [], + needsExclusion = operator.prefix === "!", + suffixList = (operator.suffix || "").split(":"), + fieldName = (suffixList[0] || "tags").toLowerCase(), + direction = (suffixList[1] || "with").toLowerCase(); + + if((operator.operand === "") && (needsExclusion)) { + return []; + } + + if(operator.operand !== "") { + var baseTitle = operator.operand, + baseTiddler = $tw.wiki.getTiddler(baseTitle), + foundTitles = collectTitlesRecursively(baseTiddler,baseTitle,fieldName,direction); + + source(function(tiddler,title) { + if(needsExclusion !== foundTitles.includes(title)) { + results.push(title); + } + }); + } else { + source(function(tiddler,title) { + results = results.concat(collectTitlesRecursively(tiddler,title,fieldName,direction)); + }); + results = uniqueArray(results); + } + + return results; + } })(); From 8a840873c00d0e32043898947da5500425a05d1f Mon Sep 17 00:00:00 2001 From: bimlas Date: Tue, 6 Nov 2018 22:55:50 +0100 Subject: [PATCH 14/29] Modify to suit to coding style WARNING: Commit introducing TODOs --- core/modules/filters/kin.js | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index e5be749a48b..0dba24ab468 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -9,11 +9,9 @@ Finds out where a tiddler originates from and what other tiddlers originate from (function() { /*jslint node: true, browser: true */ - /*global $tw: false */ + /*global $tw: true */ "use strict"; - // TODO: Should I set global tw to true? - function collectTitlesRecursively(baseTiddler,baseTitle,fieldName,direction) { var titlesPointingFromBase = [], titlesPointingToBase = []; @@ -29,7 +27,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from function collectTitlesPointingFrom(tiddler,title) { if(addToResultsIfNotFoundAlready(titlesPointingFromBase,title)) { if(tiddler) { - tiddler.getFieldList(fieldName).forEach(function(targetTitle) { + $tw.utils.each(tiddler.getFieldList(fieldName),function(targetTitle) { collectTitlesPointingFrom($tw.wiki.getTiddler(targetTitle),targetTitle); }); } @@ -38,7 +36,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from function collectTitlesPointingTo(title) { if(addToResultsIfNotFoundAlready(titlesPointingToBase,title)) { - $tw.wiki.findListingsOfTiddler(title,fieldName).forEach(function(targetTitle) { + $tw.utils.each($tw.wiki.findListingsOfTiddler(title,fieldName),function(targetTitle) { collectTitlesPointingTo(targetTitle); }); } @@ -50,23 +48,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from if((direction === "to") || (direction === "with")) { collectTitlesPointingTo(baseTitle); } - return uniqueArray(titlesPointingFromBase.concat(titlesPointingToBase)); - } - - // TODO: Is there a better way for unique? - function uniqueArray(input) { - var seen = {}, - output = [], - len = input.length, - j = 0; - for(var i = 0; i < len; i++) { - var item = input[i]; - if(seen[item] !== 1) { - seen[item] = 1; - output[j++] = item; - } - } - return output; + return $tw.utils.pushTop(titlesPointingFromBase,titlesPointingToBase); } /* @@ -77,6 +59,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from exports.kin = function(source,operator,options) { var results = [], needsExclusion = operator.prefix === "!", + // TODO: Use operator.suffixes (see #3502) suffixList = (operator.suffix || "").split(":"), fieldName = (suffixList[0] || "tags").toLowerCase(), direction = (suffixList[1] || "with").toLowerCase(); @@ -97,9 +80,8 @@ Finds out where a tiddler originates from and what other tiddlers originate from }); } else { source(function(tiddler,title) { - results = results.concat(collectTitlesRecursively(tiddler,title,fieldName,direction)); + results = $tw.utils.pushTop(results,collectTitlesRecursively(tiddler,title,fieldName,direction)); }); - results = uniqueArray(results); } return results; From 0ae7bf2032d3a6f002085c69116291838ed286db Mon Sep 17 00:00:00 2001 From: bimlas Date: Wed, 7 Nov 2018 14:14:54 +0100 Subject: [PATCH 15/29] Use the new, builtin syntax for suffixes See 6dcdc2049a27f0acd0c996101504f732ed31e4ff for details of `operator.suffixes`. --- core/modules/filters/kin.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index 0dba24ab468..7860d8d03a6 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -59,10 +59,9 @@ Finds out where a tiddler originates from and what other tiddlers originate from exports.kin = function(source,operator,options) { var results = [], needsExclusion = operator.prefix === "!", - // TODO: Use operator.suffixes (see #3502) - suffixList = (operator.suffix || "").split(":"), - fieldName = (suffixList[0] || "tags").toLowerCase(), - direction = (suffixList[1] || "with").toLowerCase(); + suffixes = operator.suffixes || [], + fieldName = ((suffixes[0] || [])[0] || "tags").toLowerCase(), + direction = ((suffixes[1] || [])[0] || "with").toLowerCase(); if((operator.operand === "") && (needsExclusion)) { return []; From dbc2d754f6c09ce0493462f113719962c88d3c6e Mon Sep 17 00:00:00 2001 From: bimlas Date: Wed, 7 Nov 2018 15:17:22 +0100 Subject: [PATCH 16/29] Prepare code to allow additional suffix options --- core/modules/filters/kin.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index 7860d8d03a6..3624f3288d3 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -12,7 +12,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from /*global $tw: true */ "use strict"; - function collectTitlesRecursively(baseTiddler,baseTitle,fieldName,direction) { + function collectTitlesRecursively(baseTiddler,baseTitle,options) { var titlesPointingFromBase = [], titlesPointingToBase = []; @@ -27,7 +27,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from function collectTitlesPointingFrom(tiddler,title) { if(addToResultsIfNotFoundAlready(titlesPointingFromBase,title)) { if(tiddler) { - $tw.utils.each(tiddler.getFieldList(fieldName),function(targetTitle) { + $tw.utils.each(tiddler.getFieldList(options.fieldName),function(targetTitle) { collectTitlesPointingFrom($tw.wiki.getTiddler(targetTitle),targetTitle); }); } @@ -36,16 +36,16 @@ Finds out where a tiddler originates from and what other tiddlers originate from function collectTitlesPointingTo(title) { if(addToResultsIfNotFoundAlready(titlesPointingToBase,title)) { - $tw.utils.each($tw.wiki.findListingsOfTiddler(title,fieldName),function(targetTitle) { + $tw.utils.each($tw.wiki.findListingsOfTiddler(title,options.fieldName),function(targetTitle) { collectTitlesPointingTo(targetTitle); }); } } - if((direction === "from") || (direction === "with")) { + if((options.direction === "from") || (options.direction === "with")) { collectTitlesPointingFrom(baseTiddler,baseTitle); } - if((direction === "to") || (direction === "with")) { + if((options.direction === "to") || (options.direction === "with")) { collectTitlesPointingTo(baseTitle); } return $tw.utils.pushTop(titlesPointingFromBase,titlesPointingToBase); @@ -60,8 +60,10 @@ Finds out where a tiddler originates from and what other tiddlers originate from var results = [], needsExclusion = operator.prefix === "!", suffixes = operator.suffixes || [], - fieldName = ((suffixes[0] || [])[0] || "tags").toLowerCase(), - direction = ((suffixes[1] || [])[0] || "with").toLowerCase(); + suffixOptions = { + fieldName: ((suffixes[0] || [])[0] || "tags").toLowerCase(), + direction: ((suffixes[1] || [])[0] || "with").toLowerCase(), + }; if((operator.operand === "") && (needsExclusion)) { return []; @@ -70,7 +72,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from if(operator.operand !== "") { var baseTitle = operator.operand, baseTiddler = $tw.wiki.getTiddler(baseTitle), - foundTitles = collectTitlesRecursively(baseTiddler,baseTitle,fieldName,direction); + foundTitles = collectTitlesRecursively(baseTiddler,baseTitle,suffixOptions); source(function(tiddler,title) { if(needsExclusion !== foundTitles.includes(title)) { @@ -79,7 +81,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from }); } else { source(function(tiddler,title) { - results = $tw.utils.pushTop(results,collectTitlesRecursively(tiddler,title,fieldName,direction)); + results = $tw.utils.pushTop(results,collectTitlesRecursively(tiddler,title,suffixOptions)); }); } From 152c9039fadaa7654b585d1a1b8c61ccd8d0402e Mon Sep 17 00:00:00 2001 From: bimlas Date: Wed, 7 Nov 2018 21:45:22 +0100 Subject: [PATCH 17/29] Add `depth` option to specify max depth (0 means no limit) --- core/modules/filters/kin.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index 3624f3288d3..5851e6845c2 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -24,20 +24,26 @@ Finds out where a tiddler originates from and what other tiddlers originate from return true } - function collectTitlesPointingFrom(tiddler,title) { + function collectTitlesPointingFrom(tiddler,title,currentDepth = 0) { + if((options.depth) && (currentDepth++ > options.depth)) { + return; + } if(addToResultsIfNotFoundAlready(titlesPointingFromBase,title)) { if(tiddler) { $tw.utils.each(tiddler.getFieldList(options.fieldName),function(targetTitle) { - collectTitlesPointingFrom($tw.wiki.getTiddler(targetTitle),targetTitle); + collectTitlesPointingFrom($tw.wiki.getTiddler(targetTitle),targetTitle,currentDepth); }); } } } - function collectTitlesPointingTo(title) { + function collectTitlesPointingTo(title,currentDepth = 0) { + if((options.depth) && (currentDepth++ > options.depth)) { + return; + } if(addToResultsIfNotFoundAlready(titlesPointingToBase,title)) { $tw.utils.each($tw.wiki.findListingsOfTiddler(title,options.fieldName),function(targetTitle) { - collectTitlesPointingTo(targetTitle); + collectTitlesPointingTo(targetTitle,currentDepth); }); } } @@ -63,6 +69,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from suffixOptions = { fieldName: ((suffixes[0] || [])[0] || "tags").toLowerCase(), direction: ((suffixes[1] || [])[0] || "with").toLowerCase(), + depth: Number((suffixes[2] || [])[0]), }; if((operator.operand === "") && (needsExclusion)) { From 281419133a0dcf1f0569965f586d98c4a5f14131 Mon Sep 17 00:00:00 2001 From: bimlas Date: Wed, 7 Nov 2018 22:50:25 +0100 Subject: [PATCH 18/29] Update documentation, be more verbose --- .../tw5.com/tiddlers/filters/examples/kin.tid | 14 ++++--- editions/tw5.com/tiddlers/filters/kin.tid | 42 ++++++++++++------- .../tw5.com/tiddlers/images/kin Operator.svg | 1 + .../tiddlers/images/kin Operator.svg.meta | 3 ++ 4 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 editions/tw5.com/tiddlers/images/kin Operator.svg create mode 100644 editions/tw5.com/tiddlers/images/kin Operator.svg.meta diff --git a/editions/tw5.com/tiddlers/filters/examples/kin.tid b/editions/tw5.com/tiddlers/filters/examples/kin.tid index c441000b9df..99a5f9ca088 100644 --- a/editions/tw5.com/tiddlers/filters/examples/kin.tid +++ b/editions/tw5.com/tiddlers/filters/examples/kin.tid @@ -1,5 +1,5 @@ created: 20181026114434874 -modified: 20181102160039607 +modified: 20181107224559880 tags: [[Operator Examples]] [[kin Operator]] title: kin Operator (Examples) type: text/vnd.tiddlywiki @@ -51,9 +51,11 @@ Family tree of [[Filter Syntax]] and [[kin Operator]] (to really understand, loo <> <> -<> -<> -<[[Reference]] listing [[Concepts]], but [[Concepts]] does not listing [[Filters]] so they do not belong to the same family based on `list`">> +<> +<> +<> <> -<> -<<.operator-example 7 "[!is[system]type[text/vnd.tiddlywiki]!kin[TableOfContents]first[10]]" "first 10 tiddlers which are not related to [[TableOfContents]] (by `tags`)">> +<> +<> +<[[Reference]] listing [[Concepts]], but [[Concepts]] does not listing [[Filters]] so they do not belong to the same family based on `list`">> +<<.operator-example 9 "[!is[system]type[text/vnd.tiddlywiki]!kin[TableOfContents]first[10]]" "first 10 tiddlers which are not related to [[TableOfContents]] (by `tags`)">> diff --git a/editions/tw5.com/tiddlers/filters/kin.tid b/editions/tw5.com/tiddlers/filters/kin.tid index e748eb9ddf2..1aa8287bb60 100644 --- a/editions/tw5.com/tiddlers/filters/kin.tid +++ b/editions/tw5.com/tiddlers/filters/kin.tid @@ -1,30 +1,40 @@ caption: kin created: 20181025133804171 -modified: 20181102155624126 +modified: 20181107224931131 op-input: a [[selection of titles|Title Selection]] -op-neg-output: ''with parameter <<.place T>>''
    » those input titles which are ''<<.em not>> kin with <<.place T>>''
    ''without parameter <<.place T>>''
    » ignored -op-output: ''with parameter <<.place T>>''
    » those input titles which are ''kin with <<.place T>>''
    ''without <<.place T>>''
    » ''all'' tiddler titles which are ''kin with input titles'' -op-parameter: a tiddler title or nothing -op-parameter-name: T +op-neg-output: ''with parameter <<.place B>>''
    » those input titles which are ''<<.em not>> kin with <<.place B>>''
    ''without parameter <<.place B>>''
    » ignored +op-output: ''with parameter <<.place B>>''
    » those input titles which are ''kin with <<.place B>>''
    ''without <<.place B>>''
    » ''all'' tiddler titles which are ''kin with input titles'' (treat input titles as base tiddlers) +op-parameter: base tiddler title or nothing +op-parameter-name: B op-purpose: recursively looking for kinship between tiddler titles -op-suffix: [[field|TiddlerFields]] which connecting tiddlers (defaulting to <<.field tags>>) and direction (defaulting to `with`) +op-suffix: the <<.op kin>> operator uses a rich suffix, see below for details tags: [[Negatable Operators]] [[Tag Operators]] [[Filter Operators]] [[Field Operators]] title: kin Operator type: text/vnd.tiddlywiki -The syntax of suffix is: +The purpose of the <<.op {{!!caption}}>> operator with examples: -<$railroad text=""" -[: "" ] ":" [: ("to" |: "with" | "from") ] -"""/> +* Finds out where base tiddler originates and what other elements originate from it +* Finds the ancestors and successors of a family member +* Finds the "leaves" of the branch of the base tiddler in a tree-like structure (where the base tiddler is a leaf) +* Finds the super- and subsets / groups of a mathematical set (where the base tiddler is a set) -The `` suffix is assumed to be a [[title list|Title List]]. +[img[kin Operator.svg]] -|!Direction|!Members| -|^`to`|Find the title of <<.place T>> in the target field of input tiddlers recursively + <<.place T>> itself| -|^`with`|Union of `from` and `to`| -|^`from`|Find the input titles in the target field of <<.place T>> recursively + <<.place T>> itself| +The <<.op {{!!caption}}>> operator uses an extended syntax that permits multiple fields and flags to be passed: -<<.op {{!!caption}}>> is a [[modifier|Selection Constructors]], but without <<.place T>> is a [[constructor|Selection Constructors]]. +``` +[kin:::[]] +``` + +* ''field'': name of the [[field|TiddlerFields]] which connecting tiddlers (assumed to be a [[title list|Title List]], defaulting to <<.field tags>>) +* ''direction'': collect the tiddler titles in this direction relative to the base tiddler +** ''from'': collect kins of base tiddler pointing from it (including the base tiddler title itself) +** ''to'': collect kins of base tiddler pointing to it (including the base tiddler title itself) +** ''with'': (the default) union of the aboves +* ''depth'': maximum depth of the collected labels in the tree structure relative to the base tiddler (a positive number, not limited by default) +* ''operand'': filter operand, the base tiddler + +<<.op {{!!caption}}>> is a [[modifier|Selection Constructors]], but without <<.place B>> parameter is a [[constructor|Selection Constructors]]. <<.operator-examples "kin">> diff --git a/editions/tw5.com/tiddlers/images/kin Operator.svg b/editions/tw5.com/tiddlers/images/kin Operator.svg new file mode 100644 index 00000000000..2cce2f0b5ef --- /dev/null +++ b/editions/tw5.com/tiddlers/images/kin Operator.svg @@ -0,0 +1 @@ +DEPTH12123BASEFROMTO \ No newline at end of file diff --git a/editions/tw5.com/tiddlers/images/kin Operator.svg.meta b/editions/tw5.com/tiddlers/images/kin Operator.svg.meta new file mode 100644 index 00000000000..05f9ec4c718 --- /dev/null +++ b/editions/tw5.com/tiddlers/images/kin Operator.svg.meta @@ -0,0 +1,3 @@ +tags: picture +title: kin Operator.svg +type: image/svg+xml From 375099470815eedec684dac0237f531bc3dcfa9f Mon Sep 17 00:00:00 2001 From: bimlas Date: Thu, 8 Nov 2018 08:06:24 +0100 Subject: [PATCH 19/29] Flip arrows on the picture; purpose of trees in examples --- editions/tw5.com/tiddlers/filters/examples/kin.tid | 4 +++- editions/tw5.com/tiddlers/images/kin Operator.svg | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/editions/tw5.com/tiddlers/filters/examples/kin.tid b/editions/tw5.com/tiddlers/filters/examples/kin.tid index 99a5f9ca088..bf70a8c6467 100644 --- a/editions/tw5.com/tiddlers/filters/examples/kin.tid +++ b/editions/tw5.com/tiddlers/filters/examples/kin.tid @@ -1,5 +1,5 @@ created: 20181026114434874 -modified: 20181107224559880 +modified: 20181108070317997 tags: [[Operator Examples]] [[kin Operator]] title: kin Operator (Examples) type: text/vnd.tiddlywiki @@ -50,6 +50,8 @@ Family tree of [[Filter Syntax]] and [[kin Operator]] (to really understand, loo <> +''The "Show tree" button below the examples only helps in understanding the filter, it's not part of the output.'' + <> <> <> diff --git a/editions/tw5.com/tiddlers/images/kin Operator.svg b/editions/tw5.com/tiddlers/images/kin Operator.svg index 2cce2f0b5ef..d850019ad8d 100644 --- a/editions/tw5.com/tiddlers/images/kin Operator.svg +++ b/editions/tw5.com/tiddlers/images/kin Operator.svg @@ -1 +1 @@ -DEPTH12123BASEFROMTO \ No newline at end of file +DEPTH12123BASETOFROM \ No newline at end of file From 6437124db1832e902bf73ef2d702191b3b2b2c1b Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 9 Nov 2018 10:02:55 +0100 Subject: [PATCH 20/29] Use "older" standards; add tests - Function parameter default value introduced in ECMA 2015 - `array.includes()` not used anywhere, use `array.indexOf()` instead --- core/modules/filters/kin.js | 29 +++++++------- editions/test/tiddlers/tests/test-filters.js | 42 ++++++++++++++++++++ 2 files changed, 56 insertions(+), 15 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index 5851e6845c2..d36801768cb 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -17,56 +17,55 @@ Finds out where a tiddler originates from and what other tiddlers originate from titlesPointingToBase = []; function addToResultsIfNotFoundAlready(list,title) { - if(list.includes(title)) { + if(list.indexOf(title) !== -1) { return false; } list.push(title); return true } - function collectTitlesPointingFrom(tiddler,title,currentDepth = 0) { + function collectTitlesPointingFrom(tiddler,title,currentDepth) { if((options.depth) && (currentDepth++ > options.depth)) { return; } if(addToResultsIfNotFoundAlready(titlesPointingFromBase,title)) { if(tiddler) { $tw.utils.each(tiddler.getFieldList(options.fieldName),function(targetTitle) { - collectTitlesPointingFrom($tw.wiki.getTiddler(targetTitle),targetTitle,currentDepth); + collectTitlesPointingFrom(options.wiki.getTiddler(targetTitle),targetTitle,currentDepth); }); } } } - function collectTitlesPointingTo(title,currentDepth = 0) { + function collectTitlesPointingTo(title,currentDepth) { if((options.depth) && (currentDepth++ > options.depth)) { return; } if(addToResultsIfNotFoundAlready(titlesPointingToBase,title)) { - $tw.utils.each($tw.wiki.findListingsOfTiddler(title,options.fieldName),function(targetTitle) { + $tw.utils.each(options.wiki.findListingsOfTiddler(title,options.fieldName),function(targetTitle) { collectTitlesPointingTo(targetTitle,currentDepth); }); } } if((options.direction === "from") || (options.direction === "with")) { - collectTitlesPointingFrom(baseTiddler,baseTitle); + collectTitlesPointingFrom(baseTiddler,baseTitle,0); } if((options.direction === "to") || (options.direction === "with")) { - collectTitlesPointingTo(baseTitle); + collectTitlesPointingTo(baseTitle,0); } return $tw.utils.pushTop(titlesPointingFromBase,titlesPointingToBase); } /* - Export our filter function - - TODO: May I add tests? (editions/test/tiddlers/tests) - */ + Export our filter function + */ exports.kin = function(source,operator,options) { var results = [], needsExclusion = operator.prefix === "!", suffixes = operator.suffixes || [], - suffixOptions = { + filterOptions = { + wiki: options.wiki, fieldName: ((suffixes[0] || [])[0] || "tags").toLowerCase(), direction: ((suffixes[1] || [])[0] || "with").toLowerCase(), depth: Number((suffixes[2] || [])[0]), @@ -78,8 +77,8 @@ Finds out where a tiddler originates from and what other tiddlers originate from if(operator.operand !== "") { var baseTitle = operator.operand, - baseTiddler = $tw.wiki.getTiddler(baseTitle), - foundTitles = collectTitlesRecursively(baseTiddler,baseTitle,suffixOptions); + baseTiddler = options.wiki.getTiddler(baseTitle), + foundTitles = collectTitlesRecursively(baseTiddler,baseTitle,filterOptions); source(function(tiddler,title) { if(needsExclusion !== foundTitles.includes(title)) { @@ -88,7 +87,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from }); } else { source(function(tiddler,title) { - results = $tw.utils.pushTop(results,collectTitlesRecursively(tiddler,title,suffixOptions)); + results = $tw.utils.pushTop(results,collectTitlesRecursively(tiddler,title,filterOptions)); }); } diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index 191168ae40f..100020b1c79 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -203,6 +203,48 @@ describe("Filter tests", function() { expect(wiki.filterTiddlers("[!untagged[]sort[title]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three,TiddlerOne"); }); + describe("testing the kin operator",function() { + // It needs a tree-like wiki to test recursion. + var treeWiki = new $tw.Wiki(); + treeWiki.addTiddler({ + title: "A", + tags: ["B"], + list: "C" + }); + treeWiki.addTiddler({ + title: "B", + tags: ["C"], + }); + treeWiki.addTiddler({ + title: "C", + tags: ["A"], + list: ["E"] + }); + treeWiki.addTiddler({ + title: "D", + tags: ["B"], + }); + treeWiki.addTiddler({ + title: "E", + tags: ["D"], + }); + treeWiki.addTiddler({ + title: "F", + tags: ["E"], + }); + treeWiki.addTiddler({ + title: "G", + tags: ["D"], + }); + + it("should handle the kin operator",function() { + expect(treeWiki.filterTiddlers("[kin[A]sort[title]]").join(",")).toBe("A,B,C,D,E,F,G"); + expect(treeWiki.filterTiddlers("[kin[A]!kin::to[D]sort[title]]").join(",")).toBe("A,B,C"); + expect(treeWiki.filterTiddlers("[kin::from:2[F]sort[title]]").join(",")).toBe("D,E,F"); + expect(treeWiki.filterTiddlers("[kin:list[C]]").join(",")).toBe("A,C,E"); + }); + }); + it("should handle the links operator", function() { expect(wiki.filterTiddlers("[!is[shadow]links[]sort[title]]").join(",")).toBe("$:/TiddlerTwo,a fourth tiddler,one,Tiddler Three,TiddlerSix,TiddlerZero"); expect(wiki.filterTiddlers("[all[shadows]links[]sort[title]]").join(",")).toBe("TiddlerOne"); From d6ae19b06769ebff10b37270b5a5079eaaee050d Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 9 Nov 2018 12:32:06 +0100 Subject: [PATCH 21/29] Rename `findListingsOfTiddler` to `findTiddlersByField` --- core/modules/filters/kin.js | 2 +- core/modules/filters/listed.js | 2 +- core/modules/wiki.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index d36801768cb..8e696a71e97 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -42,7 +42,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from return; } if(addToResultsIfNotFoundAlready(titlesPointingToBase,title)) { - $tw.utils.each(options.wiki.findListingsOfTiddler(title,options.fieldName),function(targetTitle) { + $tw.utils.each(options.wiki.findTiddlersByField(title,options.fieldName),function(targetTitle) { collectTitlesPointingTo(targetTitle,currentDepth); }); } diff --git a/core/modules/filters/listed.js b/core/modules/filters/listed.js index 8cb1c2f68ea..76b17054acc 100644 --- a/core/modules/filters/listed.js +++ b/core/modules/filters/listed.js @@ -19,7 +19,7 @@ exports.listed = function(source,operator,options) { var field = operator.operand || "list", results = []; source(function(tiddler,title) { - $tw.utils.pushTop(results,options.wiki.findListingsOfTiddler(title,field)); + $tw.utils.pushTop(results,options.wiki.findTiddlersByField(title,field)); }); return results; }; diff --git a/core/modules/wiki.js b/core/modules/wiki.js index 8c51a709b8c..06c0f3effcf 100755 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -522,7 +522,7 @@ exports.getTagMap = function() { /* Lookup a given tiddler and return a list of all the tiddlers that include it in the specified list field */ -exports.findListingsOfTiddler = function(targetTitle,fieldName) { +exports.findTiddlersByField = function(targetTitle,fieldName) { fieldName = fieldName || "list"; var titles = []; this.each(function(tiddler,title) { From 1832e43b384baa77b0ca9aeacf697e88c427b24b Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 9 Nov 2018 12:33:49 +0100 Subject: [PATCH 22/29] Drop `describe` block of `kin` test, `it` is enough --- editions/test/tiddlers/tests/test-filters.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index 100020b1c79..eb3e61e2a18 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -203,7 +203,7 @@ describe("Filter tests", function() { expect(wiki.filterTiddlers("[!untagged[]sort[title]]").join(",")).toBe("$:/TiddlerTwo,Tiddler Three,TiddlerOne"); }); - describe("testing the kin operator",function() { + it("should handle the kin operator",function() { // It needs a tree-like wiki to test recursion. var treeWiki = new $tw.Wiki(); treeWiki.addTiddler({ @@ -237,12 +237,10 @@ describe("Filter tests", function() { tags: ["D"], }); - it("should handle the kin operator",function() { - expect(treeWiki.filterTiddlers("[kin[A]sort[title]]").join(",")).toBe("A,B,C,D,E,F,G"); - expect(treeWiki.filterTiddlers("[kin[A]!kin::to[D]sort[title]]").join(",")).toBe("A,B,C"); - expect(treeWiki.filterTiddlers("[kin::from:2[F]sort[title]]").join(",")).toBe("D,E,F"); - expect(treeWiki.filterTiddlers("[kin:list[C]]").join(",")).toBe("A,C,E"); - }); + expect(treeWiki.filterTiddlers("[kin[A]sort[title]]").join(",")).toBe("A,B,C,D,E,F,G"); + expect(treeWiki.filterTiddlers("[kin[A]!kin::to[D]sort[title]]").join(",")).toBe("A,B,C"); + expect(treeWiki.filterTiddlers("[kin::from:2[F]sort[title]]").join(",")).toBe("D,E,F"); + expect(treeWiki.filterTiddlers("[kin:list[C]]").join(",")).toBe("A,C,E"); }); it("should handle the links operator", function() { From ea5ab1e35b9a881a0d96e85be9f14e7ae2ce9018 Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 9 Nov 2018 12:54:57 +0100 Subject: [PATCH 23/29] Found a usage of `includes()`, changed to `indexOf()` --- core/modules/filters/kin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index 8e696a71e97..5d6332f6dca 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -81,7 +81,7 @@ Finds out where a tiddler originates from and what other tiddlers originate from foundTitles = collectTitlesRecursively(baseTiddler,baseTitle,filterOptions); source(function(tiddler,title) { - if(needsExclusion !== foundTitles.includes(title)) { + if(needsExclusion === (foundTitles.indexOf(title) === -1)) { results.push(title); } }); From 881bc3297bf3da31165176e46f76be9a4958fc5f Mon Sep 17 00:00:00 2001 From: bimlas Date: Fri, 9 Nov 2018 13:36:57 +0100 Subject: [PATCH 24/29] Additional tests --- editions/test/tiddlers/tests/test-filters.js | 2 ++ editions/tw5.com/tiddlers/system/DefaultTiddlers.tid | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/editions/test/tiddlers/tests/test-filters.js b/editions/test/tiddlers/tests/test-filters.js index eb3e61e2a18..b945c8d1358 100644 --- a/editions/test/tiddlers/tests/test-filters.js +++ b/editions/test/tiddlers/tests/test-filters.js @@ -238,6 +238,8 @@ describe("Filter tests", function() { }); expect(treeWiki.filterTiddlers("[kin[A]sort[title]]").join(",")).toBe("A,B,C,D,E,F,G"); + expect(treeWiki.filterTiddlers("[kin::from[A]sort[title]]").join(",")).toBe("A,B,C"); + expect(treeWiki.filterTiddlers("[kin::to[A]sort[title]]").join(",")).toBe("A,B,C,D,E,F,G"); expect(treeWiki.filterTiddlers("[kin[A]!kin::to[D]sort[title]]").join(",")).toBe("A,B,C"); expect(treeWiki.filterTiddlers("[kin::from:2[F]sort[title]]").join(",")).toBe("D,E,F"); expect(treeWiki.filterTiddlers("[kin:list[C]]").join(",")).toBe("A,C,E"); diff --git a/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid b/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid index e10c566b9d7..b93d4de82c6 100644 --- a/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid +++ b/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid @@ -1,8 +1,9 @@ created: 20131127215321439 -modified: 20140912135951542 +modified: 20181109113849516 title: $:/DefaultTiddlers type: text/vnd.tiddlywiki -HelloThere -GettingStarted -Community +[[Merged]] +[[kin Operator]] +[[kin Operator (Examples)]] +$:/core/modules/filters/kin.js \ No newline at end of file From e37596a2da593dc409bf1567da66400f1acaebd5 Mon Sep 17 00:00:00 2001 From: bimlas Date: Tue, 20 Nov 2018 12:48:37 +0100 Subject: [PATCH 25/29] Accidentally changed DefaultTiddlers --- editions/tw5.com/tiddlers/system/DefaultTiddlers.tid | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid b/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid index b93d4de82c6..e10c566b9d7 100644 --- a/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid +++ b/editions/tw5.com/tiddlers/system/DefaultTiddlers.tid @@ -1,9 +1,8 @@ created: 20131127215321439 -modified: 20181109113849516 +modified: 20140912135951542 title: $:/DefaultTiddlers type: text/vnd.tiddlywiki -[[Merged]] -[[kin Operator]] -[[kin Operator (Examples)]] -$:/core/modules/filters/kin.js \ No newline at end of file +HelloThere +GettingStarted +Community From 1906752989b13c7e2bbe380f6eff2ed4e2b3ee07 Mon Sep 17 00:00:00 2001 From: bimlas Date: Tue, 20 Nov 2018 12:55:38 +0100 Subject: [PATCH 26/29] Increment in a separate statement --- core/modules/filters/kin.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index 5d6332f6dca..2a30c0b5626 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -25,9 +25,10 @@ Finds out where a tiddler originates from and what other tiddlers originate from } function collectTitlesPointingFrom(tiddler,title,currentDepth) { - if((options.depth) && (currentDepth++ > options.depth)) { + if((options.depth) && (currentDepth > options.depth)) { return; } + currentDepth += 1; if(addToResultsIfNotFoundAlready(titlesPointingFromBase,title)) { if(tiddler) { $tw.utils.each(tiddler.getFieldList(options.fieldName),function(targetTitle) { @@ -38,9 +39,10 @@ Finds out where a tiddler originates from and what other tiddlers originate from } function collectTitlesPointingTo(title,currentDepth) { - if((options.depth) && (currentDepth++ > options.depth)) { + if((options.depth) && (currentDepth > options.depth)) { return; } + currentDepth += 1; if(addToResultsIfNotFoundAlready(titlesPointingToBase,title)) { $tw.utils.each(options.wiki.findTiddlersByField(title,options.fieldName),function(targetTitle) { collectTitlesPointingTo(targetTitle,currentDepth); From cbbc88d625f1f5b8c1d7a5df58347e2e2d32c331 Mon Sep 17 00:00:00 2001 From: bimlas Date: Tue, 20 Nov 2018 13:32:38 +0100 Subject: [PATCH 27/29] Keep `findListingsOfTiddler()` as alias of `findTiddlersByField()` Earlier I dropped `findListingsOfTiddler()` (d6ae19b06769ebff10b37270b5a5079eaaee050d), but it broke the backward compatibility. --- core/modules/wiki.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/core/modules/wiki.js b/core/modules/wiki.js index 06c0f3effcf..cb03469c325 100755 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -101,7 +101,7 @@ exports.deleteTextReference = function(textRef,currTiddlerTitle) { exports.addEventListener = function(type,listener) { this.eventListeners = this.eventListeners || {}; this.eventListeners[type] = this.eventListeners[type] || []; - this.eventListeners[type].push(listener); + this.eventListeners[type].push(listener); }; exports.removeEventListener = function(type,listener) { @@ -186,8 +186,8 @@ exports.generateNewTitle = function(baseTitle,options) { var c = 0, title = baseTitle; while(this.tiddlerExists(title) || this.isShadowTiddler(title) || this.findDraft(title)) { - title = baseTitle + - (options.prefix || " ") + + title = baseTitle + + (options.prefix || " ") + (++c); } return title; @@ -203,7 +203,7 @@ exports.isTemporaryTiddler = function(title) { exports.isImageTiddler = function(title) { var tiddler = this.getTiddler(title); - if(tiddler) { + if(tiddler) { var contentTypeInfo = $tw.config.contentTypeInfo[tiddler.fields.type || "text/vnd.tiddlywiki"]; return !!contentTypeInfo && contentTypeInfo.flags.indexOf("image") !== -1; } else { @@ -334,7 +334,7 @@ exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,is titles.sort(function(a,b) { var x,y, compareNumbers = function(x,y) { - var result = + var result = isNaN(x) && !isNaN(y) ? (isDescending ? -1 : 1) : !isNaN(x) && isNaN(y) ? (isDescending ? 1 : -1) : (isDescending ? y - x : x - y); @@ -534,6 +534,10 @@ exports.findTiddlersByField = function(targetTitle,fieldName) { return titles; }; +exports.findListingsOfTiddler = function(targetTitle,fieldName) { + return this.findTiddlersByField(targetTitle, fieldName) +}; + /* Sorts an array of tiddler titles according to an ordered list */ @@ -666,7 +670,7 @@ exports.getTiddlerDataCached = function(titleOrTiddler,defaultData) { var self = this, tiddler = titleOrTiddler; if(!(tiddler instanceof $tw.Tiddler)) { - tiddler = this.getTiddler(tiddler); + tiddler = this.getTiddler(tiddler); } if(tiddler) { return this.getCacheForTiddler(tiddler.fields.title,"data",function() { @@ -687,7 +691,7 @@ exports.getTiddlerData = function(titleOrTiddler,defaultData) { var tiddler = titleOrTiddler, data; if(!(tiddler instanceof $tw.Tiddler)) { - tiddler = this.getTiddler(tiddler); + tiddler = this.getTiddler(tiddler); } if(tiddler && tiddler.fields.text) { switch(tiddler.fields.type) { @@ -817,7 +821,7 @@ exports.initParsers = function(moduleType) { if(!$tw.utils.hop($tw.Wiki.parsers,type) && $tw.config.contentTypeInfo[type].encoding === "base64") { $tw.Wiki.parsers[type] = $tw.Wiki.parsers["application/octet-stream"]; } - }); + }); } }; @@ -881,7 +885,7 @@ exports.parseTextReference = function(title,field,index,options) { } if(field === "text" || (!field && !index)) { if(tiddler && tiddler.fields) { - return this.parseText(tiddler.fields.type,tiddler.fields.text,options); + return this.parseText(tiddler.fields.type,tiddler.fields.text,options); } else { return null; } @@ -1079,7 +1083,7 @@ exports.search = function(text,options) { searchTermsRegExps = [new RegExp("(" + terms.join("\\s+") + ")",flags)]; } else if(options.regexp) { try { - searchTermsRegExps = [new RegExp("(" + text + ")",flags)]; + searchTermsRegExps = [new RegExp("(" + text + ")",flags)]; } catch(e) { searchTermsRegExps = null; console.log("Regexp error parsing /(" + text + ")/" + flags + ": ",e); @@ -1101,7 +1105,7 @@ exports.search = function(text,options) { if($tw.utils.isArray(options.field)) { $tw.utils.each(options.field,function(fieldName) { if(fieldName) { - fields.push(fieldName); + fields.push(fieldName); } }); } else { From 717e6f5b584fc8a0c051f68bc86dedcf7bb23538 Mon Sep 17 00:00:00 2001 From: bimlas Date: Tue, 20 Nov 2018 23:18:07 +0100 Subject: [PATCH 28/29] Use cache mechanism To check the performance: * Enable $:/config/Performance/Instrumentation * Open only [[kin Operator (Examples)]] (close others) * Open the browser's console * Press "Show tree" button below these examples [kin::from[Filter Expression]kin::to[Filters]] Without cache: performance: mainRefresh: 1359.20ms performance: +filter: 1286.80ms With cache: performance: mainRefresh: 161.00ms performance: +filter: 97.30ms [kin:list:to[Filter Syntax]] Without cache: performance: mainRefresh: 222.40ms performance: +filter: 153.50ms With cache: performance: mainRefresh: 98.30ms performance: +filter: 38.20ms --- core/modules/filters/kin.js | 51 ++++++++++++++++++++++++------------- core/modules/utils/utils.js | 14 ++++++++++ 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/core/modules/filters/kin.js b/core/modules/filters/kin.js index 2a30c0b5626..46be107dd32 100644 --- a/core/modules/filters/kin.js +++ b/core/modules/filters/kin.js @@ -13,23 +13,23 @@ Finds out where a tiddler originates from and what other tiddlers originate from "use strict"; function collectTitlesRecursively(baseTiddler,baseTitle,options) { - var titlesPointingFromBase = [], - titlesPointingToBase = []; + var cacheName = "kin-filter-" + baseTitle + "-" + options.fieldName + "-", + titlesPointingFromBase = {}, + titlesPointingToBase = {}, + resultsFrom = [], + resultsTo = []; - function addToResultsIfNotFoundAlready(list,title) { - if(list.indexOf(title) !== -1) { + function addToResultsIfNotFoundAlready(alreadyFound,title,depth) { + if(title in alreadyFound) { return false; } - list.push(title); + alreadyFound[title] = depth; return true } function collectTitlesPointingFrom(tiddler,title,currentDepth) { - if((options.depth) && (currentDepth > options.depth)) { - return; - } - currentDepth += 1; - if(addToResultsIfNotFoundAlready(titlesPointingFromBase,title)) { + if(addToResultsIfNotFoundAlready(titlesPointingFromBase,title,currentDepth)) { + currentDepth += 1; if(tiddler) { $tw.utils.each(tiddler.getFieldList(options.fieldName),function(targetTitle) { collectTitlesPointingFrom(options.wiki.getTiddler(targetTitle),targetTitle,currentDepth); @@ -39,24 +39,39 @@ Finds out where a tiddler originates from and what other tiddlers originate from } function collectTitlesPointingTo(title,currentDepth) { - if((options.depth) && (currentDepth > options.depth)) { - return; - } - currentDepth += 1; - if(addToResultsIfNotFoundAlready(titlesPointingToBase,title)) { + if(addToResultsIfNotFoundAlready(titlesPointingToBase,title,currentDepth)) { + currentDepth += 1; $tw.utils.each(options.wiki.findTiddlersByField(title,options.fieldName),function(targetTitle) { collectTitlesPointingTo(targetTitle,currentDepth); }); } } + function getResultsInGivenDepth(cachedData) { + if(options.depth) { + return $tw.utils.getObjectKeysByExpression(cachedData,function(value) { + return value <= options.depth; + }) + } else { + return Object.keys(cachedData); + } + } + if((options.direction === "from") || (options.direction === "with")) { - collectTitlesPointingFrom(baseTiddler,baseTitle,0); + resultsFrom = $tw.wiki.getGlobalCache(cacheName + "from",function() { + collectTitlesPointingFrom(baseTiddler,baseTitle,0); + return titlesPointingFromBase; + }); + resultsFrom = getResultsInGivenDepth(resultsFrom); } if((options.direction === "to") || (options.direction === "with")) { - collectTitlesPointingTo(baseTitle,0); + resultsTo = $tw.wiki.getGlobalCache(cacheName + "to",function() { + collectTitlesPointingTo(baseTitle,0); + return titlesPointingToBase; + }); + resultsTo = getResultsInGivenDepth(resultsTo); } - return $tw.utils.pushTop(titlesPointingFromBase,titlesPointingToBase); + return $tw.utils.pushTop(resultsFrom,resultsTo); } /* diff --git a/core/modules/utils/utils.js b/core/modules/utils/utils.js index e3390f9c7ee..8e2e22dc2b4 100644 --- a/core/modules/utils/utils.js +++ b/core/modules/utils/utils.js @@ -223,6 +223,20 @@ exports.removeArrayEntries = function(array,value) { } }; +/* +Collect keys of object (probably hash) where callback yields to true + */ +exports.getObjectKeysByExpression = function(object,callback) { + var key, + results = []; + for (key in object) { + if (object.hasOwnProperty(key) && callback(object[key])) { + results.push(key); + } + } + return results; +}; + /* Check whether any members of a hashmap are present in another hashmap */ From 660b4d3d06162085b81edab7a12cf1ac045fa413 Mon Sep 17 00:00:00 2001 From: bimlas Date: Mon, 10 Dec 2018 08:02:48 +0100 Subject: [PATCH 29/29] Revert accidentally changed whitespace --- core/modules/wiki.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/modules/wiki.js b/core/modules/wiki.js index cb03469c325..9ee796e7e7d 100755 --- a/core/modules/wiki.js +++ b/core/modules/wiki.js @@ -101,7 +101,7 @@ exports.deleteTextReference = function(textRef,currTiddlerTitle) { exports.addEventListener = function(type,listener) { this.eventListeners = this.eventListeners || {}; this.eventListeners[type] = this.eventListeners[type] || []; - this.eventListeners[type].push(listener); + this.eventListeners[type].push(listener); }; exports.removeEventListener = function(type,listener) { @@ -186,8 +186,8 @@ exports.generateNewTitle = function(baseTitle,options) { var c = 0, title = baseTitle; while(this.tiddlerExists(title) || this.isShadowTiddler(title) || this.findDraft(title)) { - title = baseTitle + - (options.prefix || " ") + + title = baseTitle + + (options.prefix || " ") + (++c); } return title; @@ -203,7 +203,7 @@ exports.isTemporaryTiddler = function(title) { exports.isImageTiddler = function(title) { var tiddler = this.getTiddler(title); - if(tiddler) { + if(tiddler) { var contentTypeInfo = $tw.config.contentTypeInfo[tiddler.fields.type || "text/vnd.tiddlywiki"]; return !!contentTypeInfo && contentTypeInfo.flags.indexOf("image") !== -1; } else { @@ -334,7 +334,7 @@ exports.sortTiddlers = function(titles,sortField,isDescending,isCaseSensitive,is titles.sort(function(a,b) { var x,y, compareNumbers = function(x,y) { - var result = + var result = isNaN(x) && !isNaN(y) ? (isDescending ? -1 : 1) : !isNaN(x) && isNaN(y) ? (isDescending ? 1 : -1) : (isDescending ? y - x : x - y); @@ -670,7 +670,7 @@ exports.getTiddlerDataCached = function(titleOrTiddler,defaultData) { var self = this, tiddler = titleOrTiddler; if(!(tiddler instanceof $tw.Tiddler)) { - tiddler = this.getTiddler(tiddler); + tiddler = this.getTiddler(tiddler); } if(tiddler) { return this.getCacheForTiddler(tiddler.fields.title,"data",function() { @@ -691,7 +691,7 @@ exports.getTiddlerData = function(titleOrTiddler,defaultData) { var tiddler = titleOrTiddler, data; if(!(tiddler instanceof $tw.Tiddler)) { - tiddler = this.getTiddler(tiddler); + tiddler = this.getTiddler(tiddler); } if(tiddler && tiddler.fields.text) { switch(tiddler.fields.type) { @@ -821,7 +821,7 @@ exports.initParsers = function(moduleType) { if(!$tw.utils.hop($tw.Wiki.parsers,type) && $tw.config.contentTypeInfo[type].encoding === "base64") { $tw.Wiki.parsers[type] = $tw.Wiki.parsers["application/octet-stream"]; } - }); + }); } }; @@ -885,7 +885,7 @@ exports.parseTextReference = function(title,field,index,options) { } if(field === "text" || (!field && !index)) { if(tiddler && tiddler.fields) { - return this.parseText(tiddler.fields.type,tiddler.fields.text,options); + return this.parseText(tiddler.fields.type,tiddler.fields.text,options); } else { return null; } @@ -1083,7 +1083,7 @@ exports.search = function(text,options) { searchTermsRegExps = [new RegExp("(" + terms.join("\\s+") + ")",flags)]; } else if(options.regexp) { try { - searchTermsRegExps = [new RegExp("(" + text + ")",flags)]; + searchTermsRegExps = [new RegExp("(" + text + ")",flags)]; } catch(e) { searchTermsRegExps = null; console.log("Regexp error parsing /(" + text + ")/" + flags + ": ",e); @@ -1105,7 +1105,7 @@ exports.search = function(text,options) { if($tw.utils.isArray(options.field)) { $tw.utils.each(options.field,function(fieldName) { if(fieldName) { - fields.push(fieldName); + fields.push(fieldName); } }); } else {