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).