Vorple by Juhana Leinonen


The Vorple core extension defines the basic structure that's needed for the story file to communicate with the web browser, as well as other low-level rules and phrases that other extensions use to add more features to Vorple.

Authors who are not familiar with JavaScript or who wish to just use the basic Vorple features can read only the first three chapters (Vorple setup, compatibility with offline interpreters and the brief note about the command prompt). The rest of this documentation handles more advanced usage. For more practical usage of Vorple, see other Vorple extensions that implement specific features like multimedia support and hyperlinks.

Chapter: Vorple setup

Every Vorple story must include at least one Vorple extension and the custom web interpreter.

Include Vorple by Juhana Leinonen.
Release along with the "Vorple" interpreter.

All standard Vorple extensions already have the "Include Vorple" line, so it's not necessary to add it to the story project if at least one of the other extensions are used.

In contrast to previous versions that were only for Z-Machine, version 3 of Vorple is for Glulx only. The project's story file format must be set to Glulx in Inform's Settings pane.

At the time of release of the current, third version of Vorple, the latest Inform release 6M62 includes the older version 2 of Vorple which is not compatible with the new extensions. The latest Vorple interpreter template is in the same package as these extensions.

Also note that old Vorple extensions are not compatible with the current version of Vorple. If you get en error message about an extension being for Z-Machine only, the project is trying to include an old extension.

For more detailed instructions on how to get started see the documentation at the vorple-if.com website.

Chapter: Compatibility with offline interpreters

Even though Vorple can accomplish many things that are plain impossible to do with traditional interpreters, it's always a good idea to make the story playable text-only as well if at all possible. There are many players to whom a web interpreter or Vorple's features aren't accessible, and it's the Right Thing To Do to not exclude people if it's possible to include them.

A story file can detect if it's being run on an interpreter that supports Vorple. The same story file can therefore be run on both the Vorple web interpreter and other interpreters that have text-only features and display substitute content if necessary. We can test for Vorple's presence with "if Vorple is supported":

Instead of going north:
     if Vorple is supported:
         play sound file "marching_band.mp3";
     otherwise:
         say "A marching band crossing the street blocks your way."

(The above example uses the Vorple Multimedia extension.)

The say phrase in the above example is called a "fallback" and it's displayed only in normal non-Vorple interpreters.

All Vorple features do nothing by default if they're not supported by the interpreter, unless otherwise stated in the extension's documentation. If substitute content is not necessary, we don't need to specifically check for Vorple compatibility:

Instead of going north:
     play sound file "marching_band.mp3".

Many Vorple features can be replicated at least to some extent on standard Glulx interpreters, but in general Vorple extensions don't try to use those Glulx features as a default fallback, but opt to printing plain text where applicable or doing nothing at all. Authors can use their choice of Glulx extensions and the above mentioned "if Vorple is supported" checks to make fallbacks that are most suitable for their story.

Chapter: The command prompt

To gain more control over the command prompt, Vorple replaces the built-in prompt with its own. The process should be completely automated: changing the "command prompt" variable should change the Vorple prompt as well, apart from some fringe cases where the source text or an extension does something exotic with the Glulx prompt. The prompt and the player's command are printed on the screen with custom techniques so they will not be included in the usual story output flow. It means that Glulx extensions that capture output text will not be able to read them.

The extension Vorple Command Prompt Control offers features to manipulate the command prompt and the interpreter's line input in general.

Chapter: Embedding HTML elements

We can embed simple HTML elements into story text with some helper phrases.

place an "article" element;
place a "h1" element called "title";
place a block level element called "inventory";
place an inline element called "name";

The previous example generates elements equivalent to this HTML markup:

<article></article>
<h1 class="title"></h1>
<div class="inventory"></div>
<span class="name"></span>

The element's name should be one word only and a valid CSS class name. It's safest to only use letters, numbers, underscores and dashes.

Text content can be added on creation:

place a "h1" element called "title" reading "An exciting story";
place a "h2" element reading "Story so far:";

... or after the elements have been created:

say "You shall be known as ";
place an inline element called "name";
display text "Anonymous Adventurer" in the element called "name";

This technique can be used to modify the story output later (see example "Scrambled Eggs").

If the text is included at the same time when creating the element, the default behavior in non-Vorple interpreters is to print the text normally. Text added later will not do anything. In other words, 'place a "h1" element called "title" reading "An exciting story" ' will print "An exciting story" in all interpreters, but 'display text "Anonymous Adventurer" in the element called "name" ' will not print anything in anywhere other than the Vorple interpreter.

In the above examples element contents should be plain text only. Trying to add nested tags or text styles will lead to erratic behavior. For longer and more complex contents the tags can be opened and closed manually:

Report reading the letter:
     open HTML tag "div" called "letter";
     place "h2" element reading "Dear Esther,";
     say "I'm writing to tell you...";
     close HTML tag.

When there are multiple elements with the same name, only the last element will be updated. This is to accommodate repeat actions and specifically UNDO which can easily generate duplicate content. To modify all elements, use the following phrase:

display "Hello World!" in all elements called "greeting";

We can also test whether an element exists or not, or count the number of elements:

let n be the number of elements called "greeting";
if an element called "greeting" exists:...
if an element called "greeting" doesn't exist:...

The extension Vorple Element Manipulation contains more tools for working with the HTML document.

Finally, the Vorple interpreter uses a concept called "output focus" to decide where it should print the story text. Any HTML element* can have the output focus, and any text coming from the story will be appended to the end of that element. For example we can have a separate element where the player's inventory is printed:

(* The element that has output focus must be able contain child elements, so void elements, for example <img> or <hr>, can receive output focus but any output is ignored.)

Before taking inventory:
     set output focus to the element called "inventory".

After taking inventory:
     set output focus to the main window;
     continue the action.

"Set output focus to the main window" returns the focus back to the default location.

Chapter: Scrolling to elements

The page can be scrolled to bring a named element into view:

scroll to the element called "target";

The page then scrolls so that the target element's top is near the browser window's top. If the element is already fully visible on the page and its top position is on the top half of the window, the phrase does nothing.

A similar phrase can be used to scroll to the bottom of the page:

scroll to the end of the page;

Chapter: Executing JavaScript code

Vorple breaks out of the Glulx sandbox by letting the story file send JavaScript code for the web browser to evaluate. An "execute JavaScript command" phrase is provided to do just this:

execute JavaScript command "alert('Hello World!')";

There are no safeguards against invalid or potentially malicious JavaScript. If an illegal JavaScript expression is evaluated, the browser will show an error message in the console and the interpreter will halt. (Although this might sound ominous, there's no real danger unless you're doing some very complex things that involve evaluating JavaScript from unknown or untrusted sources, and the web browser itself has its own safeguards. Using any of the official Vorple extensions is safe.)

Any value the JavaScript code returns can be retrieved with "the value returned by the JavaScript command" which gives the return value as text. If we know the type of the return value, we can use specific phrases to retrieve values of those types:

the text returned by the JavaScript command
the number returned by the JavaScript command

There's also a shorthand phrase for testing for boolean return values:

if the JavaScript command returned true:
     say "Yup."

The type of the return value can be retrieved with "the type of the value returned by the JavaScript command". The list of all possible return value types is in the technical documentation (link below).

The return value gets overwritten by the next JavaScript command's return value (or "undefined" if it doesn't return anything), so it's best to save the value immediately to a variable after executing the command. Otherwise another JavaScript between executing the command and reading the value might cause a hard to detect bug. The other code call might not be obvious, for example changing the font style with the Vorple Screen Effects extension involves a JavaScript call.

execute JavaScript command "return [bracket]'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'[close bracket][bracket]new Date().getDay()[close bracket]";
let weekday be the text returned by the JavaScript command;
say "It's [weekday]!";

Note: Since version 3.1, the command must explicitly return a value with the "return" keyword for the expression's value to be readable in Inform. If the command returns nothing or undefined, the value of the last command that explicitly returned a value is still what you get when you query it with a "...returned by the JavaScript command" phrase. In other words:

execute JavaScript command "return 'foo'";
execute JavaScript command "'bar'";
say the text returned by the JavaScript command;

The above code will print "foo", because the second line doesn't explicitly return anything. In versions 3.0 and before the same code would have printed "bar".

See the documentation at https://vorple-if.com for more details about JavaScript evaluation.

Chapter: Escaping strings

When evaluating JavaScript expressions, quotation marks must often be exactly right. Inform formats quotes according to literary standards which doesn't necessarily work together with JavaScript. Consider the following example:

To greet (name - text):
     execute JavaScript command "alert( 'Hello [name]!' )".

When play begins:
     greet "William 'Bill' O'Malley".

This leads to a JavaScript error because all single quotes (except the one in "O'Malley") are converted to double quotes, which in turn leads to a JavaScript syntax error. Changing the string delimiters to single quotes wouldn't help because there's an unescaped single quote as well inside the string.

To escape text we can prefix it with "escaped", which adds backslashes before characters that might cause problems inside strings:

To greet (name - text):
     execute JavaScript command "alert( 'Hello [escaped name]!' )".

Now the string can be safely used in the JavaScript expression.

By default escaping removes newlines. If we want to turn them into, for example, HTML line breaks, or just preserve them:

To greet (name - text):
     let safe name be escaped name using "\n" as line breaks;
     execute JavaScript command "alert( 'Hello [safe name]!' )".

Remember to use "[bracket]" and "[close bracket]" for square brackets in JavaScript code.

execute JavaScript command "var myArray = [bracket]1, 2, 3[close bracket]";

Escaping also turns Unicode characters (basically any characters that aren't letters a-z, numbers or common punctuation) into \uXXXX format so that they can be passed to the browser intact. We'll have to escape any strings that contain e.g. accented characters:

let destination be "Mêlée Island";
execute JavaScript command "var destination = '[escaped destination]'";

Non-escaped Unicode characters show up in the output as question marks or empty squares.

Chapter: User interface setup and updates

Vorple provides a separate rulebook called Vorple interface setup rules for setting up the user interface on the browser side. It runs during startup before the when play begins rules. The rulebook is meant for rules that build or initialize the user interface that has to be ready before the story does anything else.

The following example sets up a click handler that adds a custom CSS class to the command prompt. Depending on the CSS rule it might flash the prompt to draw attention to it.

Vorple interface setup rule:
     execute JavaScript command "$(document).on('click', function() { $('#lineinput').addClass('highlight') })".

When building any user interface elements we need to remember that through save/restore the player can continue the story potentially from any point or rewind actions with undo or restart, unless the story has disabled those commands. We can't rely on JavaScript code that has been run during previous commands because the player might have skipped them by restoring a later save, and we can't assume that turns happen only once because the player might undo and replay a turn. Therefore it's best to initialize the user interface at story start instead of along the way as the story progresses.

There's a mechanism in place that prevents the interface setup rules from running more than once during one session, even if the player restarts the story. In other words the interface setup rules run only when the web page loads. This guarantees that we can't add duplicate event handlers by mistake or otherwise run things twice that should only be run once.

For keeping the web interface up to date there's a rulebook called Vorple interface update rules that's run at the end of every turn and after undoing a turn, restoring a save and restarting the game.

Vorple interface update rule:
     execute JavaScript command "document.title = '[escaped printed name of the player][']s adventures'".

Refer to the documentation at vorple-if.com for more information.

Chapter: Blocking the user interface

If at some point we need to wait for a network request or some other asynchronous operation to finish before continuing with the story, we can prevent the player from doing anything in the meanwhile by blocking the user interface. When the user interface is blocked the player can't type or click on anything, but they can still scroll the page normally. The phrases to block and unblock the user interface are:

block the user interface;
unblock the user interface;

Usually it's the JavaScript code that will unblock the user interface when it's ready by running a "vorple.layout.unblock()" call, but the Inform 7 phrase is provided for cases where the script executes a parser command that causes the story to continue normally.

Note that manually blocking the user interface is necessary only in asynchronous operations, most notably network requests via Ajax. Normal synchronous code already blocks the user interface so the turn can't end before all code has been executed.

The example "The Sum of Human Knowledge" shows one use case where we might want to block the user interface: it takes some time for the request to Wikipedia to finish and we don't want the player to continue before the response has been shown on the screen.

Even though typing is disabled while the user interface is blocked, the command prompt is still visible by default (if the story is waiting for line input). In the extension Vorple Command Prompt Control there are phrases to hide the command prompt which can be used together with or instead of blocking the user interface.

Example: ** Convenience Store - Displaying the inventory styled as a HTML list.

We'll display the inventory listing using HTML unordered lists ("ul"). It might not be immediately obvious why one would want to do this, but if the items are displayed in a proper HTML structure it's possible to use CSS to style them further.

"Convenience Store"

Include Vorple by Juhana Leinonen.
Release along with the "Vorple" interpreter.

Carry out taking inventory (this is the print inventory using HTML lists rule):
     if Vorple is supported:
         say "[We] [are] carrying:[line break]" (A);
         open HTML tag "ul";
         repeat with item running through things carried by the player:
             place "li" element reading "[item]";
             if the item contains something:
                 open HTML tag "ul";
                 repeat with content running through things contained by the item:
                     place "li" element reading "[content]";
                 close HTML tag;
         close HTML tag;
     otherwise:
         follow the print standard inventory rule.

The print inventory using HTML lists rule is listed instead of the print standard inventory rule in the carry out taking inventory rules.

The Convenience store is a room. The eggs, the milk, the jar of pickles, the magazine, and the can opener are in the Convenience store. The paper bag is an open container in the Convenience store.

Test me with "take all / i / put all in paper bag / i".

Example: ** Scrambled Eggs - Hints that are initially shown obscured and revealed on request.

The hint system works by wrapping scrambled hints in named elements. Their contents can then be later replaced with unscrambled text.

"Scrambled Eggs"

Include Vorple by Juhana Leinonen.
Release along with the "Vorple" interpreter.

Chapter 1 - World

Kitchen is a room. "Your task is to find a frying pan!"
The table is a fixed in place supporter in the kitchen.
The frying pan is on the table.

After taking the frying pan:
     end the story finally saying "You found the pan!"

After looking for the first time:
     say "(Type HINTS to get help.)".

Chapter 2 - Hints

Table of Hints
hint     revealed (truth state)
"The table is relevant."     false
"Have you looked on the table?"     false
"The pan is on the table."     false

Requesting hints is an action out of world.
Understand "hint" and "hints" as requesting hints.

Carry out requesting hints (this is the scramble hints rule):
     let the alphabet be { "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" };
     let row number be 1;
     repeat through table of hints:
         let scrambled hint be "";
         say "[row number]) ";
         if revealed entry is true:
             now scrambled hint is hint entry;
         otherwise:
             repeat with index running from 1 to the number of characters in hint entry:
                 if character number index in hint entry is " ":
                     now scrambled hint is "[scrambled hint] ";
                 otherwise:
                     let rnd be a random number between 1 and the number of entries in the alphabet;
                     now scrambled hint is "[scrambled hint][entry rnd in the alphabet]";
         place an element called "hint-[row number]" reading "[scrambled hint]";
         say line break;
         increment row number;
     say "[line break](Type REVEAL # where # is the number of the hint you want to unscramble.)".

Revealing hint is an action out of world applying to one number.
Understand "reveal [number]" as revealing hint.

Check revealing hint (this is the check boundaries rule):
     if the number understood is less than 1 or the number understood is greater than the number of rows in the table of hints:
         say "Please choose a number between 1 and [number of rows in table of hints]." instead.

Carry out revealing hint when Vorple is not supported (this is the unscrambling fallback rule):
     choose row number understood in the table of hints;
     say "[hint entry][line break]".

Carry out revealing hint (this is the change past transcript rule):
     choose row number understood in the table of hints;
     display text hint entry in the element called "hint-[number understood]".

Test me with "hints / reveal 1 / reveal 2 / reveal 3".

Example: *** The Grandfather Clock - Setting the story time to match the real-world time.

In the "synchronize clocks" phrase the system time is retrieved by JavaScript and the story's internal "time of day" variable is changed to match the system time.

"The Grandfather Clock"

Include Vorple by Juhana Leinonen.
Release along with the "Vorple" interpreter.

The Library is a room.

The grandfather clock is in the Library. "A grandfather clock shows that the current time is [synchronized time]." The description is "The clock shows the time [synchronized time]." The grandfather clock is fixed in place.

The calendar is in the Library. "There's a calendar on the wall." The description is "Today's date has been circled: [today's date]."

To say today's date:
     if Vorple is supported:
         execute JavaScript command "var today=new Date(); return [bracket]'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'[close bracket][bracket]today.getMonth()[close bracket]+' '+today.getDate()+', '+today.getFullYear()";
         say the text returned by the JavaScript command;
     otherwise:
         say "March 9, 2017"

To synchronize clocks:
     if Vorple is supported:
         execute JavaScript command "var now=new Date(); return now.getHours()*60+now.getMinutes()";
         let real time be the number returned by the JavaScript command;
         let story time be ((the hours part of the time of day) * 60) + the minutes part of the time of day;
         let time difference be real time - story time;
         increase the time of day by time difference minutes.

To say synchronized time:
     synchronize clocks;
     say the time of day.

Example: **** The Sum of Human Knowledge - Retrieving and displaying data from a third party service.

Here we set up an encyclopedia that can be used to query articles from Wikipedia. The actual querying code is a bit longer so it's placed in an external encyclopedia.js file, which can be downloaded from http://vorple-if.com/doc/resources.zip. Put the file in the project's Resources folder to include it with the release.

Note that the pause between issuing the lookup command and the encyclopedia text appearing on the screen is caused by the time it takes to send a request to and receive a response from Wikipedia.

"The Sum of Human Knowledge"

Include Vorple by Juhana Leinonen.
Release along with the "Vorple" interpreter.
Release along with JavaScript "encyclopedia.js".

Library is a room. "The shelves are filled with volumes of an encyclopedia. You can look up any topic you want."

Looking up is an action applying to one topic.
Understand "look up [text]" as looking up.

Carry out looking up when Vorple is supported:
     place a block level element called "dictionary-entry";
     execute JavaScript command "wikipedia_query('[escaped topic understood]')";

Report looking up when Vorple is not supported:
     say "You find the correct volume and learn about [topic understood].".

Test me with "look up ducks / look up mars / look up interactive fiction".