Sending timer length through widget arguments

Twine Version: 2.3.5
Story Format: Sugarcube 2.30.0

Related to a previous question, but enough of its own thing to warrant its own thread.

I’m trying to use Greyelf’s timed-or-click text reveal script as a widget, so that I can cleanly use it a lot in my story. I’ve set the widget up as a container, and I’m trying to make it so that the first argument will be the number of seconds passed to the timed construct, but I haven’t had any luck.

This is the code (of the timed portion, not of the whole widget) that has come the closest to working:

<<timed `(_args[0]) + "s"`>>
	<<if not _skipit>>
		<<run $("_contents").show()>>
	<</if>>
<</timed>>

But when I use the widget like this:

<<toc 2>>Test text<</toc>>

It keeps returning this error:

Error: <>: error within widget contents (Error: cannot execute macro <>: unable to parse macro argument expression “(_args[0]) + “s””: Cannot read properties of undefined (reading ‘0’)).

I’ve looked around and tried a few different formats and I can’t tell what it is I’m doing wrong. Any guidance would be appreciated!

Try removing the brackets around the _args[0]. That should fix it the timer part.

1 Like

I’m still getting the same error message :confused:

You didn’t supply the entire contents of your widget definition, nor the contents of the Passage the <<toc 2>>Test text<</toc>> example was used in, so I used the following TWEE Notation based code to test the _args[0] issue you are having…

:: Story Widgets [widget nobr]
<<widget "toc" container>>
	<<run console.log('debug: arg0: ' + _args[0] + ' contents: ' + _contents)>>
	
	<<timed `_args[0] + "s"`>>
		<<run console.log('debug: timer fired: contents: ' + _contents)>>
		
		<<if not _skipit>>
			<<run $("_contents").show()>>
		<</if>>
	<</timed>>
<</widget>>

:: Start
<<toc 2>>Test text<</toc>>

When I tested the above it ran without error, and I saw the two debug messages I expected within the web-browser’s Console.

There are two potential issues with the above code:
1: It makes reference to a _skipit Temporary variable whose source I have no knowledge about.

2: it is using the current value of the _contents variable (eg. “Test text”) as the selector for a jQuery HTML element search. As the supplied examples don’t contain a HTML structure like the following then that query will return an empty result.

<test>
	<text>
		<!-- possible content? -->
	</text>
</test>
1 Like

Fair enough. Here’s the full widget code:

Widget code
<<widget "toc" container>>
<<silently>>
	<<set _skipit to false>>
	<<script>>
		$(document).one(':passagerender', function (ev) {
			/* Hide the message. */
			$(ev.content)
				.find('_contents')
				.hide();
		});

		$(document).one(':passagedisplay', function (ev) {
			/* Associate a double-click handler with the current Passage. */
			$('.passage')
				.one("dblclick", function () {
					/* Update the temporary variable so the timer will do nothing. */
					State.temporary.skipit = true;
					/* Reveal the hidden element. */
					$("_contents").show();
				});
		});
	<</script>>
	<<timed `_args[0] + "s"`>>
		<<if not _skipit>>
			<<run $("_contents").show()>>
		<</if>>
	<</timed>>
<</silently>>
<</widget>>

And the full text of the passage:

Timed text should appear in 2 seconds.

<<toc 2>>It works!<</toc>>

Does the _contents variable indicate a class value? I was under the impression that it was plain text.

EDIT: I just copied and pasted that code into my editor and got the same error.

From the opening post:

If that’s true, then your SugarCube version is out-of-date for the features you’re attempting to use. The current version is v2.36.1.

1 Like

The _contents variable contains a String representation of whatever was placed between the widget’s open and close tags.

eg. if you call your widget like so <<toc 2>>It works!<</toc>> then the _contents variable will contain a String value of “It works!”.

Based on the examples you’ve supplied I think you are misunderstanding how jQuery selectors work, specifically when used with the $(selector) and find(selector) functions.

A selector is meant define the HTML element(s) you want the function to find, using a query made up of a combination of element-type and/or element-id and/or element-class and/or element-attributes. These selectors look very similar to the ones used when applying CSS to the contents of a page.

eg. if you have a HTML structure like the following within your page…

<div id="images">
	<img src="url-to-image1">
	<img src="url-to-image2">
	<img src="url-to-image3">
</div>

…then one of the possible selectors you could use to find all three of the <img> elements within that structure is…

$('#images').find('img')

…which works by first searching the page’s Document Object Model for an element that has an ID attribute equal to “images”, and then searching the child/descendent elements of it to find any elements that have a type of img.

It should be noted that you need to use a contains selector if you want to search the Textual content of a page for specific Text.

eg. If your Passage contained the following content…

<p>A quick brown fox jumps over the lazy dog.</p>

…and you wanted to find the <p> element that contain the text brown fox then one of the selectors you could to do that is…

$("p:contains('brown fox')")

but the result returned by the above function would contain a reference to the <p> element the text was found in, not a reference to the text itself.

So in your example you are searching the HTML element’s of the “passage” area of the page looking for It works!

$(ev.content).find(`It works!`).hide();

$(`It works!`).show();

…and even if we ignore the fact you have a exclamation character in that selector that query won’t return a result because the DOM doesn’t contain any <it> or <works> element-types in it.

note: One simple way to test your jQuery selectors is to open the Console of your web-browser while viewing the relevant Passage of your Story HTML file. And then executing the jQuery function call there to see what it returns.

eg. If I viewed the Passage that contained the <div> plus <img> element structure I mentioned earlier and then executed the related selector example within the Console it would of returned an Array containing three <img> elements.

1 Like

Thank you for this explanation–I have zero previous experience with JavaScript and suspect this may not be feasible for me to fully understand within the time limit on this project. I may return to the idea for a second release.