Widget to skip <<timed>> macro

Hi all,

I’m wondering if it would be possible to make a widget that could skip a <> macro while it’s being executed, but I have no idea how to go about doing that. I’ve been scouring forum posts but haven’t been able to find anything and am not sure if such a widget is even possible, but wanted to ask here in case it is.

Thanks and if I left anything then I apologize and will respond with the pertinent info asap.

The simplest way to skip the triggering of a <<timed>> macro is to simply check a “flag” within that macro to see if the execution should be skipped. For example:

<<set _skipit = false>><<button "Skip Event">><<set _skipit = true>><</button>>

<<timed 5s>>
	<<if !_skipit>>
		<<run alert("Event wasn't skipped!")>>
	<</if>>
<</timed>>

The “!” means “not”, so if _skipit is set to false, then it will show the alert message. However, if you click the “Skip Event” button before the 5 seconds is up, then that will set _skipit to true, preventing the alert message from being shown when the <<timed>> event occurs.

While it doesn’t prevent the <<timed>> event from triggering, it does prevent that event from doing what it normally does.

Also, keep in mind that going to another passage actually cancels any <<timed>> events that haven’t triggered yet.

Hope that helps! :slight_smile:

P.S. If you want to display anything inside a <<...>> in this forum, you’ll need to select that text and click the “Preformatted text” button (the </> button) so the forum will show what’s inside. If you look at your post, you can see that it shows “skip a <> macro”, due to the forum software removing the contents of the macro.

Thanks @HiEv! (I remember you from the last time I posted here :smile: )

Your solution isn’t quite what I’m looking for though-- I’m not trying to skip the contents of the timed macro, but rather skip the waiting, so instead of a 3 second delay before showing a block of text, it shows immediately when some trigger is switched (in my case, double-clicking).

Thanks again!

As far as I can tell, what you’re trying to do is something that you will need JavaScript for, as it isn’t built into the SugarCube macro system.

So, let’s say you want to change the text of a paragraph depending on whether the time ran out or the user double-clicked on it. You would do something like this:

<p id="click-me">
	Click me before the time runs out!
</p>
<<script>>
	// Attach a listener to your paragraph to detect a double-click
	// This will run only one then remove itself
	$("#click-me").one("dblclick", function () {
		// Stop the timer and change the text of the paragraph
		clearTimeout(timer);
		$("#click-me").text("You did it!");
	});
	// Start up the timer
	var timer = setTimeout(function () {
		// When the timer runs out, this will change the text of
		// the paragraph
		$("#click-me").text("Too late!");
	}, 5000);
<</script>>

Keep in mind that in the setTimeout() method, the time is in milliseconds. So here, this will wait for 5,000 ms - 5 seconds.

If you’d like me to clarify anything to explain how to use this in a more specific way, feel free to ask.

background:

  1. The <<timed>> macro uses the JavaScript setTimeout() function to create the required timer event handler. This function returns an identifier that can be uses to reference the created timer.
  2. You can’t change the “delay period” of an active timer, you can only stop/cancel that timer by using the JavaScript clearTimerout() function along with the timer’s identifier.
  3. While the <<timed>> macro does track ALL the identifiers of the active timers associated with the current Passage, so those still active can be cancelled when the next Passage Transition occurs, there is no simple way to determine which identifier is associated with which <<timed>> macro call if you have multiple such calls within the current Passage.

The example supplied by @Tilea wont work as is, because the HTML element with the “click-me” identifier won’t exist at the time that the JavaScript within the <<script>> macro is executed. For their example to work you will need to delay the execution of the JavaScript until after the ID’ed element has been added to the current web-page (rendered), one simple way to do this is to wrap the <<script>> macro call within a <<timed 0s>> macro call.

<p id="click-me">
	Click me before the time runs out!
</p>
<<timed 0s>>
<<script>>
	// Attach a listener to your paragraph to detect a double-click
	// This will run only one then remove itself
	$("#click-me").one("dblclick", function () {
		// Stop the timer and change the text of the paragraph
		clearTimeout(timer);
		$("#click-me").text("You did it!");
	});
	// Start up the timer
	var timer = setTimeout(function () {
		// When the timer runs out, this will change the text of
		// the paragraph
		$("#click-me").text("Too late!");
	}, 5000);
<</script>>
<</timed>>

…although that does result in a “strange” situation of using one timer to generate another timer. :smile:

The following is a variation of @HiEv solution that uses Navigation Events & Tasks combined with jQuery to:

  1. hide an identified message before the current passage is shown to the end-user.
  2. associate a “double click” event handler with the current displayed passage, which will first update the temporary variable using the State.temporary object, and then “reveal” the hidden element.
A message will be displayed in 10 seconds, double click this text if you're impatient.

<p id="hidden-message">The hidden message!</p>
<<silently>>
	<<set _skipit to false>>
	<<script>>
		$(document).one(':passagerender', function (ev) {
			/* Hide the message. */
			$(ev.content)
				.find('#hidden-message')
				.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. */
					$("#hidden-message").show();
				});
		});
	<</script>>
	<<timed 10s>>
		<<if not _skipit>>
			<<run $("#hidden-message").show()>>
		<</if>>
	<</timed>>
<</silently>>

Hi @Greyelf and @Tilea,

Thanks so much for your responses, and apologies for not replying sooner-- I’ve been busy with holiday and family stuff and am getting back into things now.

This information is extremely helpful (especially the background info provided by @Greyelf) and exactly what I was looking for: I’ll definitely be able to implement the kind of functionality I was hoping was possible. I’ll post my final implementation when I’m finished in case anyone in the future might find it helpful.

Cheers and thanks again everyone!

an update

Since both of my previous code examples were given a <<done>> macro has been added to SugarCube.

This new macro delays the execution of code placed in its body until after the Passage has been displayed to the end-user, and it is designed to be a replacement of the <<timed 0s>> macro call I used to achieve the same outcome.

So my previous variation of @Tilea suggestion would now look like the following…

<p id="click-me">
	Click me before the time runs out!
</p>
<<done>>
<<script>>
	// Attach a listener to your paragraph to detect a double-click
	// This will run only one then remove itself
	$("#click-me").one("dblclick", function () {
		// Stop the timer and change the text of the paragraph
		clearTimeout(timer);
		$("#click-me").text("You did it!");
	});
	// Start up the timer
	var timer = setTimeout(function () {
		// When the timer runs out, this will change the text of
		// the paragraph
		$("#click-me").text("Too late!");
	}, 5000);
<</script>>
<</done>>
2 Likes

Nice!! Thank you for the update!