SlideWin error on game load

tweego, version 2.1.1+81d1d71 (2020-02-25T07:09:26Z) [darwin/amd64]
“name”:“SugarCube”,“version”:“2.36.1”

Hi!

I’ve been trying to use HiEv’s SlideWin script from his Sample Code bundle. I copied the JS and placed it all in a dedicated js file named slidewin.js. I then added that to the :: StoryScript [script] importScripts list of js files to import.

However, when loading the game (not calling the SlideWin function yet), I get the following error:
Uncaught ReferenceError: setup is not defined
at slidewin.js:18:1
This is the statement at that line:
setup.slideWinHandler = [];

I tried adding setup = setup || {} but that didn’t work either. I’m expecting this to work out of the box. Am I doing something wrong?

Thanks!

When asking questions about a third-party addon or library it helps if you include a link to it, so those not familiar with it can find it. I will assume you mean the SlideWin Overlay addon

JavaScript files loaded via the importScripts() function are executed within a Private Scoped context, which means they don’t have access to any of SugarCube’s runtime engine’s features like the special setup object.

As instructed by that addon’s documentation (emphasis mine)…

To add SlideWin overlays to your own project, add this to your JavaScript section:

…the JavaScript code of that addon is designed to be directly executed from a project’s Story > JavaScript area, as that will allow it access to the setup object.

Ah, I’ve somehow missed that part despite reading it multiple times! Thank you for clarifying.

I’ve managed to sort out the issue in the separate js file anyway by changing the setup object initialisation line to window.setup = window.setup || {} as I’ve done in other places.

Thanks again!

SugarCube’s special setup object is not defined on the web-browser’s global-like window interface, and adding your own Custom Namespace named setup to that interface interferes with SugarCube’s ability to access its setup object.

A better way to do what you want (eg. load that addon via an external JS file) would be to give your Custom Namespace an unique name other than setup (eg. atawf) and then change the addon’s JavaScript to use that namespace instead.
eg, Use code like the following in your project’s Story > JavaScript area (or the TWEE Notation project based equivalent)…

window.atawf = window.atawf || {};

importScripts('slidewin.js');

…and change the contents of the JS file to be something like…

/* SlideWin v1.0 - Start */
// Add the SlideWin window to the page.
var el = document.createElement("div");
el.id = "slideWin";
el.setAttribute("role", "main");
el.setAttribute("aria-labelledby", "slideTitle");
document.body.appendChild(el);
$(el).css({ transform: "translateX(-101vw)", "stroke-width": "101px" });
// Allow the ESC key to hide the SlideWin window.
$(document).on("keyup", function (event) {
	if (($("#slideWin").css("stroke-width") !== "101px") && ((event.key === "Escape") || (event.key === "Esc"))) {
		window.slideWin("hide");
		return false;
	}
});
// The slideWin() function.
atawf.slideWinHandler = [];
window.slideWin = function (dir) {
	if ($("#slideWin").css("stroke-width") !== "101px") {  // Hide slide window.
		atawf.slideWinPassage = undefined;
		$("#slideWin").attr("tabindex", null);
		$("#slideWin").animate(
			{ "stroke-width": "101px" },  // Hack to get animation to work.
			{	step: function (now, fx) { $(this).css("transform", "translateX(" + (-now) + "vw)"); },
				complete: function () {
					$("#slideWin").empty();
					if (dir !== "hide") {
						window.slideWin(dir);
					}
				},
				duration: 500
			},
			"swing"
		);
		var handler;  // Remove event handlers.
		while (atawf.slideWinHandler.length) {
			handler = atawf.slideWinHandler.shift();
			$(handler.selector).off(handler.event, "#slideWin", handler.function);
		}
		$("body").removeClass("slideWin");
	} else {  // Show slide window.
		atawf.slideWinPassage = dir;
		$("#slideWin").empty().attr("tabindex", 0).wiki('<button class="ur" onclick="slideWin(\'hide\')" tabindex="0" aria-label="Close Save/Load Window">X</button><div id="slideWinContent" tabindex="0"><<include "' + dir + '">></div>');
		$("#slideWin p").each(function () {  // Remove any empty <p> elements.
			if ($(this).text().trim() === "") {
				$(this).remove();
			} else {  // Strip <p> elements from around their contents.
				$(this).children().unwrap();
			}
		});
		$("#slideWin").delay(1).animate(  // Delay prevents animation stutter.
			{ "stroke-width": "0" },  // Hack to get animation to work.
			{	step: function (now, fx) { $(this).css("transform", "translateX(" + (-now) + "vw)"); },
				complete: function () { $("body").addClass("slideWin"); },
				duration: 500
			},
			"swing"
		).focus();
	}
};
/* SlideWin - End */

Thank you Greyelf! I’d like to learn more about what you’re referring to as I’m quite curious. If SG’s special setup object is not defined in the global-like window interface, then why would it interfere? Wouldn’t it alias?

FYI, I’ve set up an ‘init.js’ meant to be imported before all other js files:

/* Controller. Provides the link between the Twee and logic and ui scripts */
window.c = window.c || {}

/* Aliases for access to SugarCube functions/variables, as they aren't available normally in pure js */
window.c.alias = window.c.alias || {}
window.c.alias.setup = function (engine, state) {
    window.c.alias.Engine = engine;
    window.c.alias.State = state;
}

/* Game data */
window.game = window.game || {}

As you can see, I’ve created a function to be called from the Story Javascript passage to pass the Engine and State, so that it is accessible by my js-file-based scripts. Does this also interfere with SG’s scripts at all?

Web-browsers generally default to execute JavaScript in a Privately Scoped context…
eg.

<!-- the doit() function will be execute in a Private scoped context  -->
<button onclick="doit()">Click me</button>

<!-- this function is defined in a Private scoped context, so it's not available outside that context -->
<script>
function somesuch() {
    return "value";
}
</script>

When a Story Format’s engine starts up it processes the meta-data, like that representing the Passages and the Story JavaScript/Stylesheet areas of the project, that is embedded within the Story HTML file.

In the case of SugarCube the content of the Story JavaScript area is executed in a Private Scope context in a way that allows that context to have access to specific components of the engine.
eg. it basically passes references to those engine components as arguments to that Private context.

SugarCube does the same with any Private Scope context its engine creates to execute JavaScript, said practice is known as “being executed in a SugarCube context”. Which is why engine related components like setup and the APIs are available when doing things like the following…

<<set setup.thing to "value">>

<<link "Restart">>
   <<run UI.restart()>>
<</link>>

…but those engine components are not available when you do something like…

<button onclick="UI.restart()">Restart</button>

When the web-browser, and thus the SugarCube engine, executes JavaScript it follows a set process for replacing (variable) references with their actually values…
eg. the setup reference in the following needs to be replace with the Generic Object value it represents, so that its thing property can be assigned a value.

<<set setup.thing to "value">>

If you have defined a custom “global-like” property on the web-browser’s window interface that has the same name…

window.setup = window.setup || {};

…then it is possible that the value of your setup Generic Object will be returned by the set process instead of that of SugarCube’s engine.

1 Like

Thanks.