This commit is contained in:
Saq Imtiaz 2025-11-30 16:39:49 +03:00 committed by GitHub
commit d481056ded
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 156 additions and 53 deletions

View file

@ -0,0 +1,21 @@
/*\
title: $:/core/modules/filters/images.js
type: application/javascript
module-type: filteroperator
Filter operator for returning all the images used in image widgets in a tiddler
\*/
"use strict";
/*
Export our filter function
*/
exports.images = function(source,operator,options) {
const results = new $tw.utils.LinkedList();
source(function(tiddler,title) {
results.pushTop(options.wiki.getTiddlerImages(title));
});
return results.makeTiddlerIterator(options.wiki);
};

View file

@ -496,27 +496,49 @@ exports.forEachTiddler = function(/* [options,]callback */) {
};
/*
Return an array of tiddler titles that are directly linked within the given parse tree
*/
exports.extractLinks = function(parseTreeRoot) {
// Count up the links
var links = [],
checkParseTree = function(parseTree) {
for(var t=0; t<parseTree.length; t++) {
var parseTreeNode = parseTree[t];
if(parseTreeNode.type === "link" && parseTreeNode.attributes.to && parseTreeNode.attributes.to.type === "string") {
var value = parseTreeNode.attributes.to.value;
if(links.indexOf(value) === -1) {
links.push(value);
Extracts values from selected nodes of a parse tree based on a predicate and value extractor.
predicate: function(parseTreeNode, parentNode, title) => boolean
extractValue: function(parseTreeNode, parentNode, title) => value
*/
exports.extractFromParseTree = function(parseTreeRoot, predicate, extractValue, title) {
const results = new Set(),
checkParseTree = (parseTree, parentNode = null) => {
for(const parseTreeNode of parseTree) {
if(predicate(parseTreeNode, parentNode, title)) {
const value = extractValue(parseTreeNode, parentNode, title);
if(value) {
results.add(value);
}
}
if(parseTreeNode.children) {
checkParseTree(parseTreeNode.children);
checkParseTree(parseTreeNode.children, parseTreeNode);
}
}
};
checkParseTree(parseTreeRoot);
return links;
return [...results];
};
/*
Return an array of image tiddler titles that are directly referenced within the given parse tree using image widgets.
*/
exports.extractImages = function(parseTreeRoot) {
return this.extractFromParseTree(
parseTreeRoot,
(node) => node.type === "image" && node.attributes.source && node.attributes.source.type === "string",
(node) => node.attributes.source.value
);
};
/*
Return an array of tiddler titles that are directly linked within the given parse tree
*/
exports.extractLinks = function(parseTreeRoot) {
return this.extractFromParseTree(
parseTreeRoot,
(node) => node.type === "link" && node.attributes.to && node.attributes.to.type === "string",
(node) => node.attributes.to.value
);
};
/*
@ -555,54 +577,56 @@ exports.getTiddlerBacklinks = function(targetTitle) {
return backlinks;
};
/*
Return an array of tiddler titles that are directly transcluded within the given parse tree. `title` is the tiddler being parsed, we will ignore its self-referential transclusions, only return
*/
*/
exports.extractTranscludes = function(parseTreeRoot, title) {
// Count up the transcludes
var transcludes = [],
checkParseTree = function(parseTree, parentNode) {
for(var t=0; t<parseTree.length; t++) {
var parseTreeNode = parseTree[t];
if(parseTreeNode.type === "transclude") {
if(parseTreeNode.attributes.$tiddler) {
if(parseTreeNode.attributes.$tiddler.type === "string") {
var value;
// if it is Transclusion with Templates like `{{Index||$:/core/ui/TagTemplate}}`, the `$tiddler` will point to the template. We need to find the actual target tiddler from parent node
if(parentNode && parentNode.type === "tiddler" && parentNode.attributes.tiddler && parentNode.attributes.tiddler.type === "string") {
// Empty value (like `{{!!field}}`) means self-referential transclusion.
value = parentNode.attributes.tiddler.value || title;
} else {
value = parseTreeNode.attributes.$tiddler.value;
}
}
} else if(parseTreeNode.attributes.tiddler) {
if (parseTreeNode.attributes.tiddler.type === "string") {
// Old transclude widget usage
value = parseTreeNode.attributes.tiddler.value;
}
} else if(parseTreeNode.attributes.$field && parseTreeNode.attributes.$field.type === "string") {
// Empty value (like `<$transclude $field='created'/>`) means self-referential transclusion.
value = title;
} else if(parseTreeNode.attributes.field && parseTreeNode.attributes.field.type === "string") {
// Old usage with Empty value (like `<$transclude field='created'/>`)
value = title;
}
// Deduplicate the result.
if(value && transcludes.indexOf(value) === -1) {
$tw.utils.pushTop(transcludes,value);
return this.extractFromParseTree(
parseTreeRoot,
(node, parentNode) => node.type === "transclude",
(node, parentNode) => {
let value;
if(node.attributes.$tiddler) {
if(node.attributes.$tiddler.type === "string") {
// if it is Transclusion with Templates like `{{Index||$:/core/ui/TagTemplate}}`, the `$tiddler` will point to the template. We need to find the actual target tiddler from parent node
if(parentNode && parentNode.type === "tiddler" && parentNode.attributes.tiddler && parentNode.attributes.tiddler.type === "string") {
// Empty value (like `{{!!field}}`) means self-referential transclusion.
value = parentNode.attributes.tiddler.value || title;
} else {
value = node.attributes.$tiddler.value;
}
}
if(parseTreeNode.children) {
checkParseTree(parseTreeNode.children,parseTreeNode);
} else if(node.attributes.tiddler) {
// Old transclude widget usage
if(node.attributes.tiddler.type === "string") {
value = node.attributes.tiddler.value;
}
} else if(
(node.attributes.$field && node.attributes.$field.type === "string") ||
(node.attributes.field && node.attributes.field.type === "string")
) {
// Empty value (like `<$transclude $field='created'/>`) means self-referential transclusion.
value = title;
}
};
checkParseTree(parseTreeRoot);
return transcludes;
return value;
},
title
);
};
/*
Return an array of images that are used in image widgets in the specified tiddler
*/
exports.getTiddlerImages = function(title) {
const self = this;
return this.getCacheForTiddler(title,"images",() => {
const parser = self.parseTiddler(title);
if(parser) {
return self.extractImages(parser.tree);
}
return [];
});
};
/*
Return an array of tiddler titles that are transcluded from the specified tiddler

View file

@ -0,0 +1,28 @@
title: Filters/images
description: Test images operator
type: text/vnd.tiddlywiki-multiple
tags: [[$:/tags/wiki-test-spec]]
title: Tiddler1
TiddlyWiki <$image source="https://tiddlywiki.com/images/tiddlywiki-logo.png" alt="TiddlyWiki logo" width="100px" height="100px"/><$image source="Image4" alt="Image4" width="100px" height="100px"/>.
+
title: Tiddler2
This is an image <$image source="Image1"/> and this is another image [img[Image2]] but this image won't be picked up <img src="Image3" alt="Image3" width="100px" height="100px"/> nor will this one {{Image4}}
+
title: Image4
type: image/png
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=
+
title: Output
\whitespace trim
(<$text text={{{ [[Tiddler1]images[]join[,]] }}}/>)
(<$text text={{{ [[Tiddler2]images[]join[,]] }}}/>)
+
title: ExpectedResult
<p>(https://tiddlywiki.com/images/tiddlywiki-logo.png,Image4)
(Image1,Image2)</p>

View file

@ -0,0 +1,12 @@
created: 20250802195938592
modified: 20250802195948223
tags: [[Operator Examples]] [[images Operator]]
title: images Operator (Examples)
type: text/vnd.tiddlywiki
<<.operator-example 1 "[all[current]images[]]" "image tiddlers embedded in the current one using hard references">>
Here are some images using hard references:
* [img[Motovun Jack.jpg]]
* <$image source="Favicon template.svg"/>

View file

@ -0,0 +1,18 @@
caption: images
created: 20250802195117221
modified: 20250802200627718
op-input: a [[selection of titles|Title Selection]]
op-output: the image tiddlers to which the input tiddlers contain hard references using image widgets
op-parameter: none
op-purpose: find the images used by each input title
tags: [[Filter Operators]] [[Common Operators]]
title: images Operator
type: text/vnd.tiddlywiki
<<.from-version "5.4.0">>
Each input title is processed in turn. The corresponding tiddler's list of images is generated, in the order in which they appear in the tiddler's text, and [[dominantly appended|Dominant Append]] to the operator's overall output.
Images used as hard references in an image widget or the `[img[title]]` syntax are detected.
<<.operator-examples "images">>