One query, your ‘let’ statements wherein you include an ‘else’ alternative, is it possible to do something similar to an $image statement, trying to load an image from, say, local file store and if Not there, load from an http url?
If would be nice if the $image widget had support for “fallback” content when an image fails to load, similar to the way the $transclude widget permits fallback content if the specified tiddler doesn’t exist. For example, something like this:
If such a fallback mechanism existed, it would also be possible show an alternative remote image if the local image doesn’t exist, by writing something like this:
Unfortunately, the current implementation of the $image widget does NOT support the use of fallback content. The documentation for the $image widget (see https://tiddlywiki.com/#ImageWidget) specifically notes that “Any content of the <$image> widget is ignored.”
However, the $image widget DOES currently have the ability to detect when an image loads (or fails to load) and in response, adds either a tc-image-loaded or tc-image-error CSS class to the img DOM element. So, in theory, it should be possible to extend the underlying TWCore javascript code to add handling for fallback content and then only display that content if the tc-image-error class is added to the img DOM element.
I will try to dig into the code a bit and see what can be achieved. If I can make it work, I’ll open a ticket to propose adding it to the TWCore.
OK! I’ve got it working… and it was only 4 additional lines of code in $:/core/modules/widgets/image.js:
in the definition of ImageWidget.prototype.render, find these lines (near the end of the function):
domNode.addEventListener("error",function() {
$tw.utils.removeClass(domNode,"tc-image-loading");
$tw.utils.addClass(domNode,"tc-image-error");
},false);
// Insert element
parent.insertBefore(domNode,nextSibling);
this.domNodes.push(domNode);
and change to these (adding the lines marked with /* ELS */ comments):
domNode.addEventListener("error",function() {
$tw.utils.removeClass(domNode,"tc-image-loading");
$tw.utils.addClass(domNode,"tc-image-error");
domNode.self.makeChildWidgets(); /* ELS */
domNode.self.renderChildren(domNode.parentNode,domNode.nextSibling); /* ELS */
if(domNode.self.children.length!=0) domNode.style.display="none"; /* ELS */
},false);
// Insert element
parent.insertBefore(domNode,nextSibling);
this.domNodes.push(domNode);
domNode.self=this; /* ELS */
Notes:
in the event listener “error” function, there are three new lines at the end:
domNode.self.makeChildWidgets(); builds the “widget tree” for the fallback content
domNode.self.renderChildren(...); renders the child widgets as DOM elements (HTML)
If there are rendered child DOM elements, then hide the missing “broken image” display
at the very end of the render(), save the image widget object (this) so it can be referenced in the event listener as domNode.self
Results:
If the outer $image widget is loaded successfully, then the error handler is never triggered, the image renders normally, and no fallback content is processed.
If the outer $image widget fails to load, the error handler is triggered:
any enclosed fallback content is rendered.
If there is rendered fallback content, the browser’s usual “broken image” is hidden, otherwise the “broken image” remains visible.
Here’s a JSON containing the modified $image widget code, as well as a test image (“jeremy.jpg”) and some wikitext to test the $image widget handling (“Test Image Fallback”).
@EricShulman , what can I say that I’ve not said before?? Oh I know, its the King’s Birthday here so a proclamation might be in order or maybe even an Copanion Order of Australia for services to Tiddlywiki
As you can see, there is a </$let> statement after the image. If I remove that statement from the code, the statement also disappears from the rendered tiddler.
Maybe, though, it’s a hint to slightly change things a bit. The problem is the widget within a widget and the internal one having the expected /> tag which confused me.
Might it not be more explicit to have a fallback parameter in the first widget tag? That way, only a single close tag.
I’m not being picky but I’m sure others will make the same mistake.
Using a “fallback parameter” would only permit specifying an alternative image source (local file or URL). In contrast, by using the $image widget’s inner content, you can render ANY kind of wikitext as the fallback output (e.g. a formatted text message, a table, a form to select a different image, etc.).
In addition, while it might be a little bit confusing at first, the “widget within a widget” code pattern (<$outerwidget><$innerwidget/></$outerwidget>) is already well-established in TiddlyWiki for use with other kinds of widgets.
For example, rendering a tiddler caption with fallback to the tiddler title, like this:
or, providing a remote URL link as a fallback when transcluding a missing tiddler title, like this:
<$transclude tiddler="TiddlyTools/SaveChanges/Info" mode=block>
For documentation, see https://TiddlyTools.com/#TiddlyTools/SaveChanges/Info
</$transclude>
(I’ve used the above technique to directly show the …/Info tiddler contents if present, or a fallback message and link to a remote URL if the …/Info tiddler is missing).
Please ignore this code. It is a garbled mess that could not possibly work.
We’ve discussed elsewhere that as well as clearly marking LLM generated code, it is incumbent on the poster to verify that the code does perform as expected.
@EricShulman , I asked ChatGPT how we might accomplish a similar fallback facility for PDF files. It came back with
In TiddlyWiki, you can use a combination of JavaScript and `img` or `embed` elements to handle a fallback for loading a PDF. Unfortunately, there is no direct built-in support for this, but you can create a custom solution using the HTML and JavaScript within TiddlyWiki.
Here’s an approach using JavaScript and an embedded `<iframe>` to display the PDF. If the first PDF fails to load, it will fall back to the second PDF:
### TiddlyWiki Code:
```html
<$wikify bind="$pdfUrl">
<div class="pdf-container">
<iframe id="pdfViewer" width="100%" height="600px" src="{{pdfUrl}}"></iframe>
</div>
<script>
(function() {
var pdfUrl = $pdfUrl;
var fallbackUrl = "https://example.com/alternate.pdf"; // Put your fallback PDF URL here
var iframe = document.getElementById("pdfViewer");
function checkPdfLoading() {
var pdfIframe = iframe.contentWindow || iframe;
// Try to catch if the PDF doesn't load after some time
setTimeout(function() {
if (!pdfIframe.location || pdfIframe.location.href === "about:blank") {
iframe.src = fallbackUrl; // Set the fallback URL if the PDF didn't load
}
}, 3000); // 3 seconds to check (you can adjust this)
}
// Initial check when the page loads
iframe.onload = checkPdfLoading;
// Check after setting the source of the iframe
checkPdfLoading();
})();
</script>
</$wikify>
### Explanation:
1. **Initial PDF URL**: The main PDF is loaded using an iframe that links to a PDF URL.
2. **Fallback Logic**: A simple JavaScript function `checkPdfLoading` tries to detect whether the PDF is loaded correctly. If it’s not loaded within a few seconds, the `iframe.src` will be switched to a fallback PDF URL.
3. **Timeout for Checking**: The timeout (`setTimeout`) checks if the iframe content fails to load (i.e., stays blank for a few seconds), triggering the fallback.
### How to Use:
* Replace `{{pdfUrl}}` with the URL or reference to your primary PDF.
* Set your fallback PDF URL in the `fallbackUrl` variable.
* Adjust the timeout value or logic as needed to suit your use case.
This method relies on the fact that the iframe will fail to load a PDF if the source is incorrect or inaccessible. If the PDF fails to load, it switches the source to the fallback URL.
I haven’t tried it yet but thought you’d be interested anyway.
Please ignore this code. It is a garbled mess that could not possibly work.
We’ve discussed elsewhere that as well as clearly marking LLM generated code, it is incumbent on the poster to verify that the code does perform as expected.
Apologies to this list. I did state it was the result of a ChatGPT query and I was going to give it a go. As @jeremyruston indicated, it didn’t work at all.
Still looking for a solution to pdf file display fallback if anyone can advise