[SugarCube] Browser back and forward buttons

583 views
Skip to first unread message

Erik Temple

unread,
Nov 25, 2013, 4:55:14 PM11/25/13
to twee...@googlegroups.com
I want to disable the browser's back and forward buttons in favor of controlling all navigation from within the story. Is modifying the Glorious Trainwrecks code the best way to do that in SugarCube? (I'm planning to use Transition CSS.)

Alternatively, is there a way to hook an event onto the back button/forward button? I need to be able to cancel ongoing media events (sounds playing) if I do allow the back button.

Thanks!

Thomas Michael EDWARDS

unread,
Nov 25, 2013, 7:05:32 PM11/25/13
to twee...@googlegroups.com

--- Erik Temple on 11/25/2013 3:55 PM, wrote:

> I want to disable the browser's back and forward buttons in favor of
> controlling all navigation from within the story. Is modifying the
> Glorious Trainwrecks code ... the best way to do that in SugarCube?

No. Don't use that, you'll break everything, everywhere, forever.

Let me see what I can do.


> (I'm planning to use Transition CSS.)

No. Don't use that, you'll break everything, everywhere, forever.

In this case, nothing much should need to be done. SugarCube's
passage transition code is largely compatible with Transition CSS.
There are some differences, but I can't say if they'd affect you.

Best thing to do here would be to try out whatever CSS you'd like
to use and let me know if you run into any problems.


> Alternatively, is there a way to hook an event onto the back
> button/forward button? I need to be able to cancel ongoing media
> events (sounds playing) if I do allow the back button.

Using the browser's back/forward buttons, or anything which provides
the same functionality (e.g. mice with back/forward button), generates
a "popstate" event, so you can do something like this:

$(window).on("popstate", function () {
macros.getHandler("stopAllSounds")();
});

This attaches a handler to the event and calls stopAllSounds handler
whenever it fires. FYI, this is how SugarCube handles its History
mode, by attaching a handler to the "popstate" event.

Note: You, generally, do not want to be calling a macro's handler
directly like that, because it won't have a proper execution context
(or a proper set of arguments for old-style macros), however, the
stopAllSounds handler doesn't use any of that anyway, so it's okay
in this case.

--
Thomas Michael EDWARDS
Email: tmed...@motoslave.net Website: http://www.motoslave.net/thom/
"That a word like unnatural exists at all indicates a serious deficiency
in common sense...." -- Anna Puma, Dominion: Conflict 1 (No More Noise)

Erik Temple

unread,
Nov 26, 2013, 10:06:12 AM11/26/13
to twee...@googlegroups.com, tmed...@motoslave.net
On Monday, November 25, 2013 6:05:32 PM UTC-6, Thomas Michael EDWARDS wrote:
> I want to disable the browser's back and forward buttons in favor of
> controlling all navigation from within the story. Is modifying the
> Glorious Trainwrecks code ... the best way to do that in SugarCube?

No.  Don't use that, you'll break everything, everywhere, forever.

I tried it. Nothing obviously broke, but it also did nothing for the back button, so there was no point in testing further!
 

Let me see what I can do.

Awesome, thanks!

 
> (I'm planning to use Transition CSS.)

No.  Don't use that, you'll break everything, everywhere, forever.

In this case, nothing much should need to be done.  SugarCube's
passage transition code is largely compatible with Transition CSS.
There are some differences, but I can't say if they'd affect you.

Yeah, everything I've tried so far with Transition CSS seems to work fine. We'll see as the project evolves...
 
> Alternatively, is there a way to hook an event onto the back
> button/forward button? I need to be able to cancel ongoing media
> events (sounds playing) if I do allow the back button.

Using the browser's back/forward buttons, or anything which provides
the same functionality (e.g. mice with back/forward button), generates
a "popstate" event, so you can do something like this: [snip]

Great, thanks! I believe that popstate is HTML5, though, and so won't work on all browsers. That's fine for my project--I mostly need it to manage HTML5 sound playback--but I thought I'd mention it for folks who might be reading this and looking for wider compatibility. (However, folks, if you need to support older browsers, you probably don't want SugarCube anyway.)
 
Note: You, generally, do not want to be calling a macro's handler
directly like that, because it won't have a proper execution context
(or a proper set of arguments for old-style macros), however, the
stopAllSounds handler doesn't use any of that anyway, so it's okay
in this case.

Is there a more robust way to generate a macro call from javascript? I don't see any real need to do this right now, but I can imagine that it might come up in the future...

Thanks! 

Thomas Michael EDWARDS

unread,
Nov 27, 2013, 12:37:28 PM11/27/13
to twee...@googlegroups.com

--- Erik Temple on 11/25/2013 3:55 PM, wrote:

> I want to disable the browser's back and forward buttons in favor
> of controlling all navigation from within the story.

This is also possible now (landed in build 2725).

Thomas Michael EDWARDS

unread,
Nov 28, 2013, 12:23:07 AM11/28/13
to twee...@googlegroups.com

--- Erik Temple on 11/26/2013 9:06 AM, wrote:

>> Note: You, generally, do not want to be calling a macro's handler
>> directly like that, because it won't have a proper execution
>> context (or a proper set of arguments for old-style macros),
>> however, the stopAllSounds handler doesn't use any of that anyway,
>> so it's okay in this case.
>
> Is there a more robust way to generate a macro call from javascript?
> I don't see any real need to do this right now, but I can imagine
> that it might come up in the future...

A more robust way? It depends on what you mean. If we're talking
about calling the macro's handler directly, then no, not really.

The problem, as I mentioned, is that you may not be able to generate
all of the required data. Let's consider old-style macros, since
that's a simpler target. As an example, let's say you have a macro
whose handler requires all four of the traditional parameters:
1. The current output element. [HTML Element]
2. The name of the macro. [String]
3. The argument string parsed into discrete arguments. [Array]
4. The Wikifier instance which generated the macro call. [Wikifier]

If the calling code doesn't have suitable replacements to give to the
macro, then you have a problem. You'll have to know the macro's name
and what the contents of its arguments array should be anyway, so
those shouldn't be an issue. An output element may or may not be an
issue, depending. The Wikifier instance is pretty much a deal
breaker. Not all macros require an output element (or you may be
substitute an appropriate element) and/or a Wikifier instance, but
that's hit or miss.

All is not lost, however, as many macros that you might wish to call
directly from some JavaScript (whether that's another macro or some
other code) can be split into the code that needs the things noted
above and code that doesn't.

Let's use your stopAllSounds as an example:

macros.add("stopAllSounds", {
version: { major: 1, minor: 0, revision: 0 },
handler: function () {
// Pretend there's code here which requires some or all
// of execution environment that a macro normally has,
// so calling it manually isn't really an option.

// Calling our own context free utility method.
this.self.stop();
},
stop: function () {
// Code which really doesn't need much, or any, context
// to run. We've split it out and stored it in its own
// function (method) on the macro definition. Now,
// anyone who needs to call this code can, safely and
// easily. Even the main handler (as seen above).
var sounds = macros.get("playSound").soundtracks;
for (var i in sounds) {
if (sounds.hasOwnProperty(i)) {
sounds[i].pause();
if (sounds[i].currentTime) {
sounds[i].currentTime = 0;
}
}
}
}
});

Other code can then safely call .stop(), like so:

macros.get("stopAllSounds").stop();

Or like this, but I generally prefer not to use getHandler() on
utility methods:

macros.getHandler("stopAllSounds", "stop")();

Without having to worry about whether the .handler() has "needs".

Now, obviously, this is only necessary when you have macro specific
code that you need to be able to call elsewhere and the macro also
has code that really does need its normal execution environment.

In the case of <<stopAllSounds>>, splitting in into multiple methods
isn't necessary since the handler doesn't depend on its execution
environment at all.

Erik Temple

unread,
Nov 28, 2013, 3:46:02 PM11/28/13
to twee...@googlegroups.com, tmed...@motoslave.net
Thanks for the primer--should be very useful if I ever need to do this! I wonder, would it be possible to use the existing Sugarcube macro system by calling the wikifier from javascript and asking it to parse a macro string? Pseudocode as I'm not sure exactly how the wikifier works or what method(s) might be needed:

macros.add("redirectSoundPlay", {
    handler
: {
         macro_string
= '<<playSound ' + this.args[0] + ' >>';
         
Wikifier.parse(macro_string);
   
}
});

Maybe that's a weird workaround (assuming it's even viable), but it might be easier for some folks than reconfiguring existing macros...

Thomas Michael EDWARDS

unread,
Nov 28, 2013, 11:58:39 PM11/28/13
to twee...@googlegroups.com

--- Erik Temple on 11/28/2013 2:46 PM, wrote:

> I wonder, would it be possible to use the existing Sugarcube macro
> system by calling the wikifier from javascript and asking it to
> parse a macro string?

If you mean having the Wikifier run some text, which could contain
anything that you can put into a passage (incl. macros), then yes.

I'm assuming this is a follow on to the "Is there a more robust way
to generate a macro call from javascript?" question.


> Pseudocode as I'm not sure exactly how the wikifier works or what
> method(s) might be needed:

That's definitely not it. There are a few different ways to do what
I think you want. Depending on whether you wanted error handling
for the wikified text and/or if you wanted to do anything with any
of the output generated by the wikified text.

That example isn't very useful by itself though, all it would do,
assuming it was correct, would be to indirectly call <<playSound>>,
which you could do directly yourself. If there were other things
going on in there (which may be your intend and you just didn't make
note of that), then that would make more sense.

Still, a lot of situations like what I think you're shooting for
could be done with widgets.


> Maybe that's a weird workaround (assuming it's even viable), but it
> might be easier for some folks than reconfiguring existing macros...

OK. You lost me. Why would anyone need to reconfiguring existing
macros?

Erik Temple

unread,
Nov 29, 2013, 12:23:55 AM11/29/13
to twee...@googlegroups.com

On Nov 28, 2013 10:58 PM, "Thomas Michael EDWARDS" <tmed...@motoslave.net> wrote:

> I'm assuming this is a follow on to the "Is there a more robust way
> to generate a macro call from javascript?" question.

Yes, and you're reading my intent correctly throughout. With that pseudocode, I was trying to give a much simplified example of how you might call a macro from the using the wikifier,  without trying to come up with context for an example that showed why you might want to do that.

> > Maybe that's a weird workaround (assuming it's even viable), but it
> > might be easier for some folks than reconfiguring existing macros...
>
> OK.  You lost me.  Why would anyone need to reconfiguring existing
> macros?

I thought the thrust of your previous post was that it is possible to call macros with parameters from javascript, but only if their handlers have been set up to not require the execution context object. I was merely suggesting that the wikifier might be an alternate way to approach things.

Anyway, I agree that you can do most of what you'd ever need to do with widgets. As I said in my previous, this was mostly just a "what if I needed to do this" type thought experiment. Thanks for looking at it with me!

Thomas Michael EDWARDS

unread,
Nov 29, 2013, 1:17:39 AM11/29/13
to twee...@googlegroups.com

--- Erik Temple on 11/28/2013 11:23 PM, wrote:

> I thought the thrust of your previous post was that it is possible
> to call macros with parameters from javascript, but only if their
> handlers have been set up to not require the execution context
> object. I was merely suggesting that the wikifier might be an
> alternate way to approach things.

Not quite, or maybe yes. Let me restate just in case.

What I meant was that it's possible to create non-handler methods
on macros that can hold/offer some of the functionality that would
normally be in the macro's handler, but doesn't require anything
special to use. That way it could be called both by the macro's
handler and external parties without fuss.

If this was an actual need, the macro in question shouldn't need to
be reconfigured at all, because it would have been written that way
in the first place.

This shouldn't be a common practice, I only mentioned it with the
"popstate"+stopAllSounds example because I did it there (which I
only did because the way that stopAllSounds' handler is written
makes it possible). I just didn't want you to think that it was
a normal thing that you could do at any time.

I could have written this:
$(window).on("popstate", function () {
macros.getHandler("stopAllSounds")();
});

Like this (no error reporting):
$(window).on("popstate", function () {
// Normal Wikifier call with discarded output, usable anywhere
new Wikifier(
document.createElement("div"),
"<<stopAllSounds>>"
);
});

Or this (with error reporting that we're ignoring):
$(window).on("popstate", function () {
try {
// Wrapped Wikifier call that also discards output, but
// checks it for errors and throws them before returning,
// SugarCube specific
Wikifier.wikifyEval("<<stopAllSounds>>");
} catch (e) {
/* noop, don't care about errors here */
}
});

It would be crazy to start up a new Wikifier instance if you didn't
need to though, which is why I didn't.

Finally, if stopAllSounds' handler had been written so that I couldn't
have called it directly, I could have just done the audio shutdowns
directly, like so:

$(window).on("popstate", function () {
var sounds = macros.get("playSound").soundtracks;
for (var i in sounds) {
if (sounds.hasOwnProperty(i)) {
sounds[i].pause();
if (sounds[i].currentTime) {
sounds[i].currentTime = 0;
}
}
}
});

I would have done that before I'd ever use one of the Wikifer ones
from above.


TL;DR: I think I might have made you think that there is this huge
problem when there really isn't. My warning was mostly meant to
point out that I was doing something unusual (because I could) that
you wouldn't normally do. It's not common (shouldn't be anyway).

Erik Temple

unread,
Dec 1, 2013, 5:34:12 PM12/1/13
to twee...@googlegroups.com, tmed...@motoslave.net
Thanks for taking the time to clarify!
Reply all
Reply to author
Forward
0 new messages