Hello,
Is there a way to load external JavaScript libraries that will be used throughout the story in SugarCube 2? As a specific example, I'd like to make use of the Moment.js library (
http://momentjs.com/), though I'm interested in the process of adding libraries in general.
I have tried throwing the contents of their non-minified JS file into a [script] passage, but this yields nonspecific startup errors that change depending on how the story is compiled (TweeGo or Twee2, I know the latter is unsupported.) and which browser runs the resulting HTML (Chromium or Firefox). So I'm assuming that this not the correct way to do it.
Thank you for your time.
Comments
In Twine 1, you can do it two ways:
1. Go into header.html in the sugarcube/targets directory and put the javascript code to be loaded inside <script></script> tags in the head section (above the body). If using it directly (ie putting all the code in between script tags, and not simply referencing an external js file), make sure to use the min.js file, not the unminified one. Putting unminified js code into your heaader.html is bad.
2. Create a userlib.js file in the targets directory and put the code in there.
However, this functionality was removed from Twine 2 so you do it in one of two ways:
1. The first way is to build the story in Twine 2, then open the built html file containing your game with a text editor, and place the <script></script> tags in the header section as described above. This will import the moment.js code and all your js in the game that relies on moment.js will work.
2. Fork the SugarCube 2 story format from GitHub and edit the header.html file to create your own custom version with the <script></script> tags in the head section of the header.html. Then install your custom version of SugarCube as normal.
For workflow reasons, number 2 is probably the best approach, but you'll need to create new custom versions of sugarcube for each new release TME releases.
As a comment, you don't put js libraries into the script passages because this places the code inside the body section, not the head.
1. Bundling a 3rd-party library into your project via a scripting passage
If you're talking about bundling a 3rd-party library into your project, then that's easy enough, if not perfectly simple. The core issue is that many libraries make assumptions about the environment they're going to be evaluated within and the standard method used by most story formats for running scripts is not immediately compatible with those assumptions—for one reason or another. That said, it is fairly trivial to write a small wrapper to give those libraries a compatible environment. For example, the follwing wrapper should work in most cases: Pros: Wrapping the library and pasting it into a scripting passage works with any compiler, story format, and usage scenario.
Cons: None, really.
2. Bundling a 3rd-party library with your project as part of the compiled story format itself
If you're talking about including a 3rd-party library with your project as part of the story format, either bundled in or as a separate local file, then that's possible. How difficult it is in practice depends on which compiler, and story format, you use.
Compilers that use Twine 2 style story formats (Twine 2, Twee2, grunt-entwine, grunt-entwine-quickstart) require that you edit the story format. Depending on your skill level, that might be trivial or out of reach.
Compilers that use Twine 1 style story formats (Twine 1, Twee, TweeGo) also, generally, require that you edit the story format. In SugarCube's case, however, you have another option in the userlib.js file. If you place a file named userlib.js into your local SugarCube install directory—the same directory which contains the header.html file—then the contents of that file will be compiled into SugarCube itself when you build your project.
Pros (editing): Editing the story format works with any compiler, story format, and usage scenario.
Cons (editing): You have to edit the story format each time you want to add/remove libraries and/or update the story format.
Pros (SugarCube's userlib.js): Fits any usage scenario and should be fairly simple to use.
Cons (SugarCube's userlib.js): Only works in Twine 1 style compilers—maybe only the listed ones—and only with SugarCube.
3. Including a 3rd-party library with your project by loading it locally or over the network
If you're talking about including a 3rd-party library with your project by loading it locally or over the network, then that's possible as well. Obviously, in the case of network loading, the player has to have an active network and you have to allow time for the library to load. In both cases, you also have to get the source path correct. For example, the follwing module should work in most cases: Pros (local): Loading the library locally should work with any compiler, story format, and usage scenario.
Cons (local): Happens after document load, so if you need to use the library immediately after requesting it be loaded, then you must use the onload callback.
Pros (network): Loading the library over the network should work with any compiler, story format, and any usage scenario that mandates network access.
Cons (network): Happens after document load, so if you need to use the library soon after requesting it be loaded, then you must use the onload callback.
Unless you plan on editing the library—which, frankly, seems unlikely given your demonstrated skill set—why are you using the unminified version? You're bloating your project's size for no good reason.
Similarly, there is no pre-build header.html file for the Twine 1 compatible build.
If someone is forking the repo, then I'd suggested editing the source templates instead, src/templates/{twine1,twine2}/html.tpl. Afterwards, build and install as normal.
What? SugarCube doesn't do that. Beyond that, it wouldn't matter even if it did, since scripting passages are not set up until after the story format itself is executing, by which point it's pretty much moot where they go.
That's how I do it in SugarCube 1 - there's a header.html in the targets directory I use and changes to that carry through the build process. I suppose it might have changed in SugarCube 2, so I'll have to think about that when I switch over.
Which is good to know, because I wouldn't have been able to change if not for finding that out.
Additionally, I noted in my previous post where the templates for each style exist within the v2 codebase—specifically: src/templates/{twine1,twine2}/html.tpl. You'd edit one or both of those to make pre-build changes.
Thank you very much for the extremely thorough answer. Option 2 looks to be the best choice in my case, particularly with TweeGo's support for userlib.js. I apologize for not being more specific about what I was trying to accomplish.
Also, I'm here answering questions so that people don't need to get frustrated. If you do eventually pull the trigger on switching your project to v2, don't start chewing on the furniture if something isn't working. Come here and
invoke the summoning ritualstell me of your problems—related to SugarCube, keep the Dr. Phil stuff to yourself, seriously.If they just need to go into the <body> someplace, which is what I'm assuming, then the templates I mentioned before should work as they contain SugarCube 2's basic structure—UI elements are added during startup by their modules. I'd probably suggest just above the <div id="store-area"> element.
Ideally, however, you'd use JavaScript. For someone doing nontrivial web development, "I don't like {doing something} via JavaScript" should not be a serious answer. At least, not when it's probably the best answer available and I know for a fact that you're already heavily leaning upon JS via your use of animation libraries. I have a hard time understanding schizophrenia like that.
But I can just use jquery, as that's less complex than js to add divs. I know it sounds downright silly when you look at how much javascript my animation uses.
As always, thanks for the advice. I don't always get the correct information across, but I usually benefit from the discussions.
In that case i suppose i gave a correct answer with the userlib.js as well.