diff --git a/HtmlRuntime/quixe/gi_load.js b/HtmlRuntime/quixe/gi_load.js
new file mode 100644
index 0000000..11eecd2
--- /dev/null
+++ b/HtmlRuntime/quixe/gi_load.js
@@ -0,0 +1,546 @@
+/* GiLoad -- a game-file loader for Quixe
+ * Designed by Andrew Plotkin
+ *
+ *
+ * This Javascript library is copyright 2010-2012 by Andrew Plotkin. You may
+ * copy and distribute it freely, by any means and under any conditions,
+ * as long as the code and documentation is not changed. You may also
+ * incorporate this code into your own program and distribute that, or
+ * modify this code and use and distribute the modified version, as long
+ * as you retain a notice in your program or documentation which mentions
+ * my name and the URL shown above.
+ *
+ * This library loads a game image (by one of several possible methods)
+ * and then starts up the display layer and game engine. It also extracts
+ * data from a Blorb image, if that's what's provided.
+ *
+ * (This code makes use of the Prototype library, which therefore must be
+ * available.)
+ *
+ * When you are putting together a Quixe installation page, you call
+ * GiLoad.load_run() to get the game started. You should do this in the
+ * document's "onload" handler, or later. (If you call it before "onload"
+ * time, it may not work.)
+ *
+ * You can do this in a couple of different ways:
+ *
+ * GiLoad.load_run(OPTIONS) -- load and run the game using the options
+ * passed as the argument. If OPTIONS is null or not provided, the
+ * global "game_options" object is considered. (The various options are
+ * described below.)
+ *
+ * GiLoad.load_run(OPTIONS, IMAGE, IMAGE_FORMAT) -- run the game with the
+ * given options. The IMAGE argument should be the game file itself
+ * (a glulx or blorb file). IMAGE_FORMAT describes how the game file
+ * is encoded:
+ * "base64": a base64-encoded binary file
+ * "raw": a binary file stored in a string
+ * "array": an array of (numeric) byte values
+ * Again, if OPTIONS is null, the global "game_options" object is
+ * considered.
+ *
+ * These are the game options. Most have default values, so you only have
+ * to declare the ones you want to change.
+ *
+ * use_query_story: If this is true, you (or the player) can use a
+ * "?story=..." URL parameter to load any game file. If it is false,
+ * this parameter is ignored. (default: true)
+ * set_page_title: If true, the loader will change the document title
+ * to describe the game being loaded. If false, the document title
+ * will be left alone. (default: true)
+ * default_story: The URL of the game file to load, if not otherwise
+ * provided.
+ * proxy_url: The URL of the web-app service which is used to convert
+ * binary data to Javascript, if the browser needs that. (default:
+ * http://zcode.appspot.com/proxy/)
+ * vm: The game engine interface object. (default: Quixe)
+ * io: The display layer interface object. (default: Glk)
+ *
+ * You can also include any of the display options used by the GlkOte
+ * library, such as gameport, windowport, spacing, ...
+ * And also the interpreter options used by the Quixe library, such as
+ * rethrow_exceptions, ...
+ *
+ * GiLoad.find_data_chunk(NUM) -- this finds the Data chunk of the
+ * given number from the blorb file. The returned object looks like
+ * { data:[...], type:"..." } (where the type is TEXT or BINA).
+ * If there was no such chunk, or if the game was loaded from a non-
+ * blorb file, this returns undefined.
+ */
+
+/* Put everything inside the GiLoad namespace. */
+GiLoad = function() {
+
+/* Start with the defaults. These can be modified later by the game_options
+ defined in the HTML file.
+
+ Note that the "vm" and "io" entries are not filled in here, because
+ we don't know whether the Quixe or Glk libraries were loaded before
+ this one. We'll fill them in at load_run() time.
+*/
+var all_options = {
+ vm: null, // default game engine (Quixe)
+ io: null, // default display layer (Glk)
+ spacing: 4, // default spacing between windows
+ use_query_story: true, // use the ?story= URL parameter (if provided)
+ default_story: null, // story URL to use if not otherwise set
+ set_page_title: true, // set the window title to the game name
+ proxy_url: 'http://zcode.appspot.com/proxy/'
+};
+
+var gameurl = null; /* The URL we are loading. */
+var metadata = {}; /* Title, author, etc -- loaded from Blorb */
+var datachunks = {}; /* Indexed by filenum -- loaded from Blorb */
+
+/* Begin the loading process. This is what you call to start a game;
+ it takes care of starting the Glk and Quixe modules, when the game
+ file is available.
+*/
+function load_run(optobj, image, image_format) {
+
+ /* Set the default entries for the interface objects that come from
+ other libraries. (If no such libraries have been loaded, then
+ these do nothing, but game_options can still supply these entries.) */
+ all_options.vm = window.Quixe;
+ all_options.io = window.Glk;
+
+ if (!optobj)
+ optobj = window.game_options;
+ if (optobj)
+ Object.extend(all_options, optobj); /* Prototype-ism */
+
+ /* The first question is, what's the game file URL? */
+
+ gameurl = null;
+
+ if (all_options.use_query_story) {
+ /* Use ?story= URL parameter, if present and accepted. */
+ var qparams = get_query_params();
+ gameurl = qparams['story'];
+ }
+
+ if (!gameurl && image) {
+ /* The story data is already loaded -- it's not an a URL at all.
+ Decode it, and then fire it off. */
+ GlkOte.log('### trying pre-loaded load (' + image_format + ')...');
+ switch (image_format) {
+ case 'base64':
+ image = decode_base64(image);
+ break;
+ case 'raw':
+ image = decode_text(image);
+ break;
+ case 'array':
+ /* Leave image alone */
+ break;
+ default:
+ all_options.io.fatal_error("Could not decode story file data: " + image_format);
+ return;
+ }
+
+ start_game(image);
+ return;
+ }
+
+ if (!gameurl) {
+ /* Go with the "default_story" option parameter, if present. */
+ gameurl = all_options.default_story;
+ }
+
+ if (!gameurl) {
+ all_options.io.fatal_error("No story file specified!");
+ return;
+ }
+
+ GlkOte.log('### gameurl: ' + gameurl); //###
+ /* The gameurl is now known. (It should not change after this point.)
+ The next question is, how do we load it in? */
+
+ /* If an image file was passed in, we didn't use it. So we might as
+ well free its memory at this point. */
+ image = null;
+ image_format = null;
+
+ /* The logic of the following code is adapted from Parchment's
+ file.js. */
+
+ var xhr = Ajax.getTransport();
+ var binary_supported = (xhr.overrideMimeType !== undefined && !Prototype.Browser.Opera);
+ /* I'm told that Opera's overrideMimeType() doesn't work. */
+ var crossorigin_supported = (xhr.withCredentials !== undefined);
+ xhr = null;
+
+ var regex_urldomain = /^(file:|runtime:|(\w+:)?\/\/[^\/?#]+)/;
+ var page_domain = regex_urldomain.exec(location)[0];
+ var data_exec = regex_urldomain.exec(gameurl);
+ var is_relative = data_exec ? false : true;
+ var data_domain = data_exec ? data_exec[0] : page_domain;
+
+ var same_origin = (page_domain == data_domain);
+ if (navigator.userAgent.match(/chrome/i) && data_domain == 'file:') {
+ /* Chrome enforces a stricter same-origin policy for file: URLs --
+ it doesn't want to trawl your hard drive for random files.
+ Other browsers may pick this up someday, but for now, it's
+ only Chrome. */
+ same_origin = false;
+ }
+ var old_js_url = gameurl.toLowerCase().endsWith('.js');
+
+ GlkOte.log('### is_relative=' + is_relative + ', same_origin=' + same_origin + ', binary_supported=' + binary_supported + ', crossorigin_supported=' + crossorigin_supported);
+
+ if (old_js_url && same_origin) {
+ /* Old-fashioned Javascript file -- the output of Parchment's
+ zcode2js tool. When loaded and eval'ed, this will call
+ a global function processBase64Zcode() with base64 data
+ as the argument. */
+ GlkOte.log('### trying old-fashioned load...');
+ window.processBase64Zcode = function(val) {
+ start_game(decode_base64(val));
+ };
+ new Ajax.Request(gameurl, {
+ method: 'get',
+ evalJS: 'force',
+ onFailure: function(resp) {
+ all_options.io.fatal_error("The story could not be loaded. (" + gameurl + "): Error " + resp.status + ": " + resp.statusText);
+ }
+ });
+ return;
+ }
+
+ if (old_js_url) {
+ /* Javascript file in a different domain. We'll insert it as a