! function() { "use strict"; function FastClick(layer, options) { function bind(method, context) { return function() { return method.apply(context, arguments) } } var oldOnClick; if (options = options || {}, this.trackingClick = !1, this.trackingClickStart = 0, this.targetElement = null, this.touchStartX = 0, this.touchStartY = 0, this.lastTouchIdentifier = 0, this.touchBoundary = options.touchBoundary || 10, this.layer = layer, this.tapDelay = options.tapDelay || 200, this.tapTimeout = options.tapTimeout || 700, !FastClick.notNeeded(layer)) { for (var methods = ["onMouse", "onClick", "onTouchStart", "onTouchMove", "onTouchEnd", "onTouchCancel"], context = this, i = 0, l = methods.length; i < l; i++) context[methods[i]] = bind(context[methods[i]], context); deviceIsAndroid && (layer.addEventListener("mouseover", this.onMouse, !0), layer.addEventListener("mousedown", this.onMouse, !0), layer.addEventListener("mouseup", this.onMouse, !0)), layer.addEventListener("click", this.onClick, !0), layer.addEventListener("touchstart", this.onTouchStart, !1), layer.addEventListener("touchmove", this.onTouchMove, !1), layer.addEventListener("touchend", this.onTouchEnd, !1), layer.addEventListener("touchcancel", this.onTouchCancel, !1), Event.prototype.stopImmediatePropagation || (layer.removeEventListener = function(type, callback, capture) { var rmv = Node.prototype.removeEventListener; "click" === type ? rmv.call(layer, type, callback.hijacked || callback, capture) : rmv.call(layer, type, callback, capture) }, layer.addEventListener = function(type, callback, capture) { var adv = Node.prototype.addEventListener; "click" === type ? adv.call(layer, type, callback.hijacked || (callback.hijacked = function(event) { event.propagationStopped || callback(event) }), capture) : adv.call(layer, type, callback, capture) }), "function" == typeof layer.onclick && (oldOnClick = layer.onclick, layer.addEventListener("click", function(event) { oldOnClick(event) }, !1), layer.onclick = null) } } var deviceIsWindowsPhone = navigator.userAgent.indexOf("Windows Phone") >= 0, deviceIsAndroid = navigator.userAgent.indexOf("Android") > 0 && !deviceIsWindowsPhone, deviceIsIOS = /iP(ad|hone|od)/.test(navigator.userAgent) && !deviceIsWindowsPhone, deviceIsIOS4 = deviceIsIOS && /OS 4_\d(_\d)?/.test(navigator.userAgent), deviceIsIOSWithBadTarget = deviceIsIOS && /OS [6-7]_\d/.test(navigator.userAgent), deviceIsBlackBerry10 = navigator.userAgent.indexOf("BB10") > 0; FastClick.prototype.needsClick = function(target) { switch (target.nodeName.toLowerCase()) { case "button": case "select": case "textarea": if (target.disabled) return !0; break; case "input": if (deviceIsIOS && "file" === target.type || target.disabled) return !0; break; case "label": case "iframe": case "video": return !0 } return /\bneedsclick\b/.test(target.className) }, FastClick.prototype.needsFocus = function(target) { switch (target.nodeName.toLowerCase()) { case "textarea": return !0; case "select": return !deviceIsAndroid; case "input": switch (target.type) { case "button": case "checkbox": case "file": case "image": case "radio": case "submit": return !1 } return !target.disabled && !target.readOnly; default: return /\bneedsfocus\b/.test(target.className) } }, FastClick.prototype.sendClick = function(targetElement, event) { var clickEvent, touch; document.activeElement && document.activeElement !== targetElement && document.activeElement.blur(), touch = event.changedTouches[0], clickEvent = document.createEvent("MouseEvents"), clickEvent.initMouseEvent(this.determineEventType(targetElement), !0, !0, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, !1, !1, !1, !1, 0, null), clickEvent.forwardedTouchEvent = !0, targetElement.dispatchEvent(clickEvent) }, FastClick.prototype.determineEventType = function(targetElement) { return deviceIsAndroid && "select" === targetElement.tagName.toLowerCase() ? "mousedown" : "click" }, FastClick.prototype.focus = function(targetElement) { var length; deviceIsIOS && targetElement.setSelectionRange && 0 !== targetElement.type.indexOf("date") && "time" !== targetElement.type && "month" !== targetElement.type ? (length = targetElement.value.length, targetElement.setSelectionRange(length, length)) : targetElement.focus() }, FastClick.prototype.updateScrollParent = function(targetElement) { var scrollParent, parentElement; if (scrollParent = targetElement.fastClickScrollParent, !scrollParent || !scrollParent.contains(targetElement)) { parentElement = targetElement; do { if (parentElement.scrollHeight > parentElement.offsetHeight) { scrollParent = parentElement, targetElement.fastClickScrollParent = parentElement; break } parentElement = parentElement.parentElement } while (parentElement) } scrollParent && (scrollParent.fastClickLastScrollTop = scrollParent.scrollTop) }, FastClick.prototype.getTargetElementFromEventTarget = function(eventTarget) { return eventTarget.nodeType === Node.TEXT_NODE ? eventTarget.parentNode : eventTarget }, FastClick.prototype.onTouchStart = function(event) { var targetElement, touch, selection; if (event.targetTouches.length > 1) return !0; if (targetElement = this.getTargetElementFromEventTarget(event.target), touch = event.targetTouches[0], deviceIsIOS) { if (selection = window.getSelection(), selection.rangeCount && !selection.isCollapsed) return !0; if (!deviceIsIOS4) { if (touch.identifier && touch.identifier === this.lastTouchIdentifier) return event.preventDefault(), !1; this.lastTouchIdentifier = touch.identifier, this.updateScrollParent(targetElement) } } return this.trackingClick = !0, this.trackingClickStart = event.timeStamp, this.targetElement = targetElement, this.touchStartX = touch.pageX, this.touchStartY = touch.pageY, event.timeStamp - this.lastClickTime < this.tapDelay && event.preventDefault(), !0 }, FastClick.prototype.touchHasMoved = function(event) { var touch = event.changedTouches[0], boundary = this.touchBoundary; return Math.abs(touch.pageX - this.touchStartX) > boundary || Math.abs(touch.pageY - this.touchStartY) > boundary }, FastClick.prototype.onTouchMove = function(event) { return !this.trackingClick || ((this.targetElement !== this.getTargetElementFromEventTarget(event.target) || this.touchHasMoved(event)) && (this.trackingClick = !1, this.targetElement = null), !0) }, FastClick.prototype.findControl = function(labelElement) { return void 0 !== labelElement.control ? labelElement.control : labelElement.htmlFor ? document.getElementById(labelElement.htmlFor) : labelElement.querySelector("button, input:not([type=hidden]), keygen, meter, output, progress, select, textarea") }, FastClick.prototype.onTouchEnd = function(event) { var forElement, trackingClickStart, targetTagName, scrollParent, touch, targetElement = this.targetElement; if (!this.trackingClick) return !0; if (event.timeStamp - this.lastClickTime < this.tapDelay) return this.cancelNextClick = !0, !0; if (event.timeStamp - this.trackingClickStart > this.tapTimeout) return !0; if (this.cancelNextClick = !1, this.lastClickTime = event.timeStamp, trackingClickStart = this.trackingClickStart, this.trackingClick = !1, this.trackingClickStart = 0, deviceIsIOSWithBadTarget && (touch = event.changedTouches[0], targetElement = document.elementFromPoint(touch.pageX - window.pageXOffset, touch.pageY - window.pageYOffset) || targetElement, targetElement.fastClickScrollParent = this.targetElement.fastClickScrollParent), targetTagName = targetElement.tagName.toLowerCase(), "label" === targetTagName) { if (forElement = this.findControl(targetElement)) { if (this.focus(targetElement), deviceIsAndroid) return !1; targetElement = forElement } } else if (this.needsFocus(targetElement)) return event.timeStamp - trackingClickStart > 100 || deviceIsIOS && window.top !== window && "input" === targetTagName ? (this.targetElement = null, !1) : (this.focus(targetElement), this.sendClick(targetElement, event), deviceIsIOS && "select" === targetTagName || (this.targetElement = null, event.preventDefault()), !1); return !(!deviceIsIOS || deviceIsIOS4 || (scrollParent = targetElement.fastClickScrollParent, !scrollParent || scrollParent.fastClickLastScrollTop === scrollParent.scrollTop)) || (this.needsClick(targetElement) || (event.preventDefault(), this.sendClick(targetElement, event)), !1) }, FastClick.prototype.onTouchCancel = function() { this.trackingClick = !1, this.targetElement = null }, FastClick.prototype.onMouse = function(event) { return !this.targetElement || (!!event.forwardedTouchEvent || (!event.cancelable || (!(!this.needsClick(this.targetElement) || this.cancelNextClick) || (event.stopImmediatePropagation ? event.stopImmediatePropagation() : event.propagationStopped = !0, event.stopPropagation(), event.preventDefault(), !1)))) }, FastClick.prototype.onClick = function(event) { var permitted; return this.trackingClick ? (this.targetElement = null, this.trackingClick = !1, !0) : "submit" === event.target.type && 0 === event.detail || (permitted = this.onMouse(event), permitted || (this.targetElement = null), permitted) }, FastClick.prototype.destroy = function() { var layer = this.layer; deviceIsAndroid && (layer.removeEventListener("mouseover", this.onMouse, !0), layer.removeEventListener("mousedown", this.onMouse, !0), layer.removeEventListener("mouseup", this.onMouse, !0)), layer.removeEventListener("click", this.onClick, !0), layer.removeEventListener("touchstart", this.onTouchStart, !1), layer.removeEventListener("touchmove", this.onTouchMove, !1), layer.removeEventListener("touchend", this.onTouchEnd, !1), layer.removeEventListener("touchcancel", this.onTouchCancel, !1) }, FastClick.notNeeded = function(layer) { var metaViewport, chromeVersion, blackberryVersion, firefoxVersion; if ("undefined" == typeof window.ontouchstart) return !0; if (chromeVersion = +(/Chrome\/([0-9]+)/.exec(navigator.userAgent) || [, 0])[1]) { if (!deviceIsAndroid) return !0; if (metaViewport = document.querySelector("meta[name=viewport]")) { if (metaViewport.content.indexOf("user-scalable=no") !== -1) return !0; if (chromeVersion > 31 && document.documentElement.scrollWidth <= window.outerWidth) return !0 } } if (deviceIsBlackBerry10 && (blackberryVersion = navigator.userAgent.match(/Version\/([0-9]*)\.([0-9]*)/), blackberryVersion[1] >= 10 && blackberryVersion[2] >= 3 && (metaViewport = document.querySelector("meta[name=viewport]")))) { if (metaViewport.content.indexOf("user-scalable=no") !== -1) return !0; if (document.documentElement.scrollWidth <= window.outerWidth) return !0 } return "none" === layer.style.msTouchAction || "manipulation" === layer.style.touchAction || (firefoxVersion = +(/Firefox\/([0-9]+)/.exec(navigator.userAgent) || [, 0])[1], !!(firefoxVersion >= 27 && (metaViewport = document.querySelector("meta[name=viewport]"), metaViewport && (metaViewport.content.indexOf("user-scalable=no") !== -1 || document.documentElement.scrollWidth <= window.outerWidth))) || ("none" === layer.style.touchAction || "manipulation" === layer.style.touchAction)) }, FastClick.attach = function(layer, options) { return new FastClick(layer, options) }, "function" == typeof define && "object" == typeof define.amd && define.amd ? define(function() { return FastClick }) : "undefined" != typeof module && module.exports ? (module.exports = FastClick.attach, module.exports.FastClick = FastClick) : window.FastClick = FastClick }(), function() { "use strict"; function startEngine() { _haven_start() } var haven = {}; haven.error = function(message) { var elem = document.createElement("div"), spinner = document.getElementById("spinner"), loader = document.getElementById("loader"); throw elem.id = "fatal-error", elem.innerHTML = message, document.body.appendChild(elem), spinner && spinner.parentNode.removeChild(spinner), loader && (loader.className = "stopped"), new Error(message) }, haven.start = function(opt) { haven.options.init(opt.options), haven.file.init(opt.virtualStoryfile), haven.assets.addCallback(function(cb) { haven.input.init(), cb() }), haven.prompt.init({ enginePrompt: !!opt.enginePrompt, unicode: !!opt.unicode }), haven.style.init({ engineFontFamily: !!opt.engineFontFamily }), haven.assets.addCallback(function(cb) { haven.loader.remove(), cb() }), haven.assets.finalCallback(startEngine), "addEventListener" in document && document.addEventListener("DOMContentLoaded", function() { FastClick.attach(document.body) }, !1) }, window.haven = haven }(), function() { function done() { var i = 0, metaCallback = function() { i++, i < callbacks.length ? callbacks[i](metaCallback) : lastCallback && lastCallback() }; 0 !== callbacks.length && callbacks[0](metaCallback) } var lastCallback, assets = {}, expectedAssets = ["engine", "storyfile"], callbacks = []; assets.addCallback = function(cb) { 0 === expectedAssets.length && setTimeout(cb, 0), callbacks.push(cb) }, assets.expect = function(asset) { return 0 === expectedAssets.length ? void console.warn('An expected asset "' + asset + '" was added but all previous assets have already finished loading') : void expectedAssets.push(asset) }, assets.finalCallback = function(cb) { lastCallback = cb }, assets.finished = function(asset) { var index = expectedAssets.indexOf(asset); return index !== -1 && (expectedAssets.splice(index, 1), 0 === expectedAssets.length && done(), !0) }, haven.assets = assets }(window.haven || {}), function() { "use strict"; function encodeHtml(text) { for (var encoded = "", i = 0; i < text.length; ++i) if (text.charCodeAt(i) > 127) encoded += "&#" + text.charCodeAt(i) + ";"; else switch (text[i]) { case "&": encoded += "&"; break; case "<": encoded += "<"; break; case ">": encoded += ">"; break; case "\r": encoded += "\n"; break; default: encoded += text[i] } return encoded } var buffer = {}, outputBuffer = [""]; buffer.append = function(text, targetWindow) { if (outputBuffer[targetWindow] || (outputBuffer[targetWindow] = ""), text.indexOf("\n") > -1 || text.indexOf("\r") > -1) { var nextLBR = Math.max(text.lastIndexOf("\n"), text.lastIndexOf("\r")) + 1; outputBuffer[targetWindow] += encodeHtml(text.substr(0, nextLBR)), haven.buffer.flush(targetWindow), outputBuffer[targetWindow] = encodeHtml(text.substr(nextLBR)) } else outputBuffer[targetWindow] += encodeHtml(text) }, buffer.flush = function flush(targetWindow) { if (void 0 === targetWindow) for (var i in outputBuffer) flush(+i); outputBuffer[targetWindow] && haven.window.get(targetWindow) && (haven.window.append(outputBuffer[targetWindow], targetWindow), outputBuffer[targetWindow] = "", 0 === targetWindow && (haven.isTextPrinted = !0)) }, buffer.newline = function(targetWindow) { outputBuffer[targetWindow] ? outputBuffer[targetWindow] += "\n" : outputBuffer[targetWindow] = "\n", haven.buffer.flush(targetWindow) }, haven.buffer = buffer }(), function() { "use strict"; function fnv32(a) { for (var len = a.length, fnv = 0, i = 0; i < len; i++) fnv = fnv + ((fnv << 1) + (fnv << 4) + (fnv << 7) + (fnv << 8) + (fnv << 24) >>> 0) ^ 255 & a[i]; return fnv >>> 0 } function writeGamefile(done) { interpreterLoaded && isGamefileLoaded || (interpreterLoaded ? document.getElementById("loader-message").innerHTML = "Loading game file" : document.getElementById("loader-message").innerHTML = "Loading interpreter"), document.getElementById("loader-message").innerHTML = "Starting game", FS.writeFile(storyFilename, gamefile, { encoding: "binary" }), datadir = "/gamedata_" + checksum, FS.analyzePath(datadir).exists || FS.mkdir(datadir), FS.mount(IDBFS, { root: "." }, datadir), FS.analyzePath("gamedata").exists || FS.mkdir("gamedata"), FS.mount(IDBFS, { root: "." }, "gamedata"), FS.chdir("gamedata"), FS.syncfs(!0, function() { haven.options.get("autosave") && (haven.state.autosave.setName("/gamedata_" + checksum + "/autosave"), haven.state.autosave.restore()), haven.input.keypress.init(), done() }) } var gamefile, checksum, datadir, storyFilename, file = {}, interpreterLoaded = !1, isGamefileLoaded = !1; file.init = function(virtualFilename) { var requestUrl, useProxy, gameUrl = haven.options.get("story"), proxyOption = haven.options.get("use_proxy"); storyFilename = virtualFilename, gameUrl || haven.error("No story file specified"); var xmlhttp = new XMLHttpRequest; switch ("" + proxyOption) { case "always": case "true": case "1": useProxy = !0; break; case "never": case "false": case "0": useProxy = !1; break; default: useProxy = /^https?:\/\//.test(gameUrl) && 0 !== gameUrl.indexOf(window.location.protocol + "//" + window.location.host), "auto" !== proxyOption && console.warn('Unknown use_proxy option "' + proxyOption + '", using "auto"') } requestUrl = useProxy ? haven.options.get("proxy_url").split("%s").join(encodeURIComponent(gameUrl)) : gameUrl, haven.assets.addCallback(writeGamefile), xmlhttp.responseType = "arraybuffer", xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == XMLHttpRequest.DONE) switch (xmlhttp.status) { case 200: isGamefileLoaded = !0, gamefile = new Uint8Array(xmlhttp.response), checksum = fnv32(gamefile).toString(16), haven.assets.finished("storyfile"); break; case 404: haven.error("Game file not found"); break; case 415: useProxy ? haven.error(String.fromCharCode.apply(null, new Uint8Array(xmlhttp.response))) : haven.error("Unsupported Media Type error encountered when loading game file"); break; case 0: haven.error("Unspecified error loading game file (possibly cross-origin restriction)"); break; default: haven.error("Error loading game file. Server returned status code " + xmlhttp.status + " (" + xmlhttp.statusText + ")") } }, xmlhttp.open("GET", requestUrl, !0), xmlhttp.send() }, file.prompt = function(why) { var filename = prompt("Enter filename " + why); filename && /\S/.test(filename) ? haven.prompt.input.value = datadir + "/" + filename.split("/").join("-") : haven.prompt.input.value = "", setTimeout(function() { haven.prompt.form.dispatchEvent(new Event("submit")), setTimeout(function() { FS.syncfs(!1, function() {}) }, 1e3) }, 1) }, file.readUIState = function() { try { var state = FS.readFile(autosaveFilename + "_haven_uidata", { encoding: "utf8" }); return JSON.parse(state) } catch (e) { return null } }, file.syncfs = function() { FS.syncfs(!1, function() {}) }, window.onbeforeunload = function() { FS.syncfs(!1, function() {}) }, document.getElementById("loader-message").innerHTML = "Loading interpreter and game file", haven.file = file }(), function() { var input = {}, inputMode = null, keypressBuffer = []; input.getMode = function() { return inputMode }, input.init = function() { document.addEventListener("keydown", input.keypress.send, !1), document.addEventListener("click", input.keypress.send, !1) }, input.keypress = { init: function() { inputMode || (inputMode = "buffer") }, isWaiting: function() { return haven.buffer.flush(), haven.isTextPrinted && haven.prompt.scrollOrFocus(), keypressBuffer.length > 0 }, send: function(e) { var keyCode = e.keyCode, doc = document.documentElement, scrolltop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0); if (!(e.altKey || e.ctrlKey || e.metaKey || e.shiftKey)) { switch (inputMode) { case "buffer": keypressBuffer.push(keyCode); break; case "getline": case null: return void haven.prompt.scrollOrFocus(e); case "getkey": break; case "endgame": return void(window.location = hugojs_options.exit_url); default: haven.error("Interpreter error: unknown input mode " + inputMode) } return inputMode = "buffer", scrolltop + window.innerHeight < document.body.clientHeight - 40 ? void haven.prompt.scrollOrFocus(e) : void Module.ccall("haven_getkey", "null", ["number"], [keyCode]) } }, wait: function() { inputMode = "getkey", haven.buffer.flush(), haven.prompt.setDoScroll(), keypressBuffer.length > 0 && input.keypress.send({ keyCode: keypressBuffer.shift() }) } }, input.setMode = function(mode) { inputMode = mode }, haven.input = input }(), function() { "use strict"; var loader = {}; loader.remove = function() { var loaderOverlay = document.getElementById("loader"); loaderOverlay && loaderOverlay.parentNode.removeChild(loaderOverlay) }, haven.loader = loader }(), function() { function getParameter(name, type, defaultValue) { var value, valueSearch = new RegExp("[?&]" + name + "=(.*?)(#|&|$)", "i").exec(window.location.href); if (null === valueSearch || valueSearch.length < 2) return defaultValue; switch (value = decodeURIComponent(valueSearch[1].split("+").join(" ")), type) { case "boolean": return "true" === value.toLowerCase() || "on" === value || "1" === value || "false" !== value.toLowerCase() && "off" !== value && "0" !== value && defaultValue; case "number": return parseFloat(value) + "" === value ? parseFloat(value) : NaN; default: return 0 === value.length ? defaultValue : value } } var options = {}, opt = { autosave: !0, exit_url: "", extra_opcodes: !0, proxy_url: "proxy.php", use_proxy: "auto", windowing: !0 }; options.get = function(name) { return opt[name] }, options.init = function(defaults) { var option_key; defaults = defaults || {}; for (option_key in defaults) defaults.hasOwnProperty(option_key) && void 0 !== defaults[option_key] && (opt[option_key] = defaults[option_key]); if (opt.lock_story || (opt.story = getParameter("story", "string", opt.story)), !opt.lock_options) { for (option_key in opt) "story" !== option_key && opt.hasOwnProperty(option_key) && (opt[option_key] = getParameter(option_key, typeof opt[option_key], opt[option_key])); "false" !== opt.exit_url && "0" !== opt.exit_url || (opt.exit_url = !1) } }, options.set = function(name, value) { opt[name] = value }, haven.options = options }(), function() { "use strict"; function appendPrompt(caret, inputText, targetContainer) { for (var target = haven.window.container.get(targetContainer), lastLineinput = document.createElement("div"), lastPrefix = document.createElement("span"), lastCommand = document.createElement("span"), previousLastPrompt = document.getElementsByClassName("lineinput last"), i = previousLastPrompt.length - 1; i >= 0; --i) previousLastPrompt[i].classList.remove("last"); lastLineinput.className = "lineinput last", lastPrefix.className = "prompt-prefix", lastCommand.className = "prompt-input", lastPrefix.innerHTML = caret, lastCommand.innerHTML = inputText, lastLineinput.appendChild(lastPrefix), lastLineinput.appendChild(lastCommand), target.appendChild(lastLineinput) } function getCmdFromHistory(delta) { var current = currentCmdHistory, new_current = current + delta; current === -1 && (currentCmdText = inputElem.value), new_current < cmdHistory.length && new_current >= 0 ? (inputElem.value = cmdHistory[new_current], currentCmdHistory = new_current) : new_current === -1 && (inputElem.value = currentCmdText, currentCmdHistory = new_current) } function resizeInput() { inputElem.style.width = haven.window.get(0).clientWidth - inputX - 2 + "px" } function scrollToContent() { for (var newScrollTop, output = haven.window.get(0), statusline = haven.window.get(1), nodes = textNodesUnder(output), scrolltop = (window.pageYOffset || document.documentElement.scrollTop) - (document.documentElement.clientTop || 0), nonWhitespaceRegex = /\S/, i = 0; i < nodes.length; ++i) if (nonWhitespaceRegex.test(nodes[i].textContent)) return newScrollTop = nodes[i].parentNode.getBoundingClientRect().top - window.innerHeight / 3, statusline && (newScrollTop += statusline.getBoundingClientRect().height), void(scrolltop < newScrollTop && window.scrollTo(0, newScrollTop)) } function textNodesUnder(node) { var all = []; for (node = node.firstChild; node; node = node.nextSibling) 3 == node.nodeType ? all.push(node) : all = all.concat(textNodesUnder(node)); return all } var prompt = {}, cmdHistory = [], currentCmdHistory = -1, currentCmdText = "", doScroll = !1, enginePrompt = !1, inputElem = document.getElementById("lineinput-field"), inputX = 0, isTextPrinted = !1, prefixElem = document.getElementById("lineinput-prefix"), promptElem = document.getElementById("lineinput"), lineinputReadyEvent = new CustomEvent("lineinputReady"); prompt.get = function() { return promptElem }, prompt.hide = function() { haven.input.setMode("buffer"), promptElem.parentNode && promptElem.parentNode.removeChild(promptElem) }, prompt.history = { add: function(cmd) { return !!cmd && (cmdHistory.push(cmd), !0) }, clear: function() { cmdHistory = [] }, get: function() { return cmdHistory.slice() }, remove: function(index) { return 0 !== cmdHistory.length && ("number" != typeof index ? (cmdHistory.pop(), !0) : !(index < 0 || index >= cmdHistory.length) && void cmdHistory.splice(index, 1)) }, set: function(newHistory) { cmdHistory = newHistory.slice() } }; for (var defaultDiacriticsRemovalMap = [{ base: "A", letters: "A??AAA????A????????A??A???????????" }, { base: "AA", letters: "?" }, { base: "AE", letters: "A??" }, { base: "AO", letters: "?" }, { base: "AU", letters: "?" }, { base: "AV", letters: "??" }, { base: "AY", letters: "?" }, { base: "B", letters: "B????????" }, { base: "C", letters: "C??????C????" }, { base: "D", letters: "D?????????????" }, { base: "DZ", letters: "??" }, { base: "Dz", letters: "??" }, { base: "E", letters: "E??EEE??????????E?????????????" }, { base: "F", letters: "F?????" }, { base: "G", letters: "G??????????????" }, { base: "H", letters: "H?????????????" }, { base: "I", letters: "I??III????I?????????" }, { base: "J", letters: "J????" }, { base: "K", letters: "K?????????????" }, { base: "L", letters: "L?????????????????" }, { base: "LJ", letters: "?" }, { base: "Lj", letters: "?" }, { base: "M", letters: "M???????" }, { base: "N", letters: "N????N??????????" }, { base: "NJ", letters: "?" }, { base: "Nj", letters: "?" }, { base: "O", letters: "O??OOO????O?????????O????????????????O?????" }, { base: "OI", letters: "?" }, { base: "OO", letters: "?" }, { base: "OU", letters: "?" }, { base: "OE", letters: "??" }, { base: "oe", letters: "??" }, { base: "P", letters: "P?????????" }, { base: "Q", letters: "Q?????" }, { base: "R", letters: "R????????????????" }, { base: "S", letters: "S????????????????" }, { base: "T", letters: "T??????????????" }, { base: "TZ", letters: "?" }, { base: "U", letters: "U??UUU?????U??????????????????????" }, { base: "V", letters: "V???????" }, { base: "VY", letters: "?" }, { base: "W", letters: "W?????????" }, { base: "X", letters: "X????" }, { base: "Y", letters: "Y???Y??????????" }, { base: "Z", letters: "Z?????????????" }, { base: "a", letters: "a???aaa????a????????a??a???????????" }, { base: "aa", letters: "?" }, { base: "ae", letters: "a??" }, { base: "ao", letters: "?" }, { base: "au", letters: "?" }, { base: "av", letters: "??" }, { base: "ay", letters: "?" }, { base: "b", letters: "b????????" }, { base: "c", letters: "c??????c?????" }, { base: "d", letters: "d?????????????" }, { base: "dz", letters: "??" }, { base: "e", letters: "e??eee??????????e??????????????" }, { base: "f", letters: "f?????" }, { base: "g", letters: "g??????????????" }, { base: "h", letters: "h??????????????" }, { base: "hv", letters: "?" }, { base: "i", letters: "i??iii???i??????????" }, { base: "j", letters: "j?????" }, { base: "k", letters: "k?????????????" }, { base: "l", letters: "l??????????????????" }, { base: "lj", letters: "?" }, { base: "m", letters: "m???????" }, { base: "n", letters: "n????n???????????" }, { base: "nj", letters: "?" }, { base: "o", letters: "o??ooo????o?????????o????????????????o?????" }, { base: "oi", letters: "?" }, { base: "ou", letters: "?" }, { base: "oo", letters: "?" }, { base: "p", letters: "p?????????" }, { base: "q", letters: "q?????" }, { base: "r", letters: "r????????????????" }, { base: "s", letters: "s??B??????????????" }, { base: "t", letters: "t???????????????" }, { base: "tz", letters: "?" }, { base: "u", letters: "u??uuu?????u??????????????????????" }, { base: "v", letters: "v???????" }, { base: "vy", letters: "?" }, { base: "w", letters: "w??????????" }, { base: "x", letters: "x????" }, { base: "y", letters: "y???y????y??????" }, { base: "z", letters: "z?????????????" }], diacriticsMap = {}, i = 0; i < defaultDiacriticsRemovalMap.length; i++) for (var letters = defaultDiacriticsRemovalMap[i].letters, j = 0; j < letters.length; j++) diacriticsMap[letters[j]] = defaultDiacriticsRemovalMap[i].base; prompt.init = function(opt) { if (enginePrompt = !!opt.enginePrompt, promptElem.addEventListener("submit", function(e) { e.preventDefault(), opt.unicode || (inputElem.value = inputElem.value.replace(/[^\u0000-\u007E]/g, function(a) { return diacriticsMap[a] || a })), inputElem.value !== cmdHistory[0] && /\S/.test(inputElem.value) && cmdHistory.unshift(inputElem.value), currentCmdHistory = -1, enginePrompt || e.detail && e.detail.silent || appendPrompt(prompt.prefix.get(), inputElem.value, 0), Module.ccall("haven_getline", "null", ["string"], [inputElem.value + "\n"]), inputElem.value = "", prompt.hide() }, !1), inputElem.addEventListener("keydown", function(e) { var keyCode = e.which || e.keyCode; 38 === keyCode && (getCmdFromHistory(1), e.preventDefault()), 40 === keyCode && (getCmdFromHistory(-1), e.preventDefault()) }, !1), "ontouchstart" in window) { var firstFocus = !0; inputElem.addEventListener("focus", function() { firstFocus ? firstFocus = !1 : document.body.classList.add("safarifix") }), inputElem.addEventListener("blur", function() { document.body.classList.remove("safarifix") }) } window.addEventListener("resize", resizeInput, !1), promptElem.parentNode.removeChild(promptElem) }, prompt.isReady = function() { return "getline" === haven.input.getMode() }, prompt.prefix = { get: function() { return enginePrompt ? "" : prefixElem.innerHTML }, set: function(prefix) { enginePrompt || (prefixElem.innerHTML = prefix) } }, prompt.resizeInput = resizeInput, prompt.scrollOrFocus = function(e) { var doc = document.documentElement, scrolltop = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0), selection = window.getSelection || function() { return document.selection ? document.selection.createRange().text : "" }, playAreaHeight = window.innerHeight; if (haven.window.get(1) && (playAreaHeight = window.innerHeight - haven.window.get(1).getBoundingClientRect().height), !e || !(32 === e.keycode || e.keyCode > 127 || e.altKey || e.ctrlKey || e.metaKey)) { if (!e || e.target && "INPUT" !== e.target.nodeName && "" === selection().toString()) if (scrolltop + window.innerHeight > document.body.clientHeight - 40) promptElem.parentNode && (window.scrollTo(0, 9e9), inputElem.focus(), e && e.stopPropagation && document.activeElement !== inputElem && e.stopPropagation()); else if (doScroll ? (window.scrollTo(0, scrolltop + playAreaHeight - 40), promptElem.parentNode && scrolltop + playAreaHeight + window.innerHeight - 40 >= document.body.clientHeight && inputElem.focus(), e && e.preventDefault && e.preventDefault()) : scrollToContent(), e && "keydown" === e.type && 8 === (e.which || e.keyCode)) return !1; isTextPrinted = !1 } }, prompt.setDoScroll = function(status) { doScroll = status }, prompt.show = function() { haven.input.setMode("getline"), haven.buffer.flush(), haven.window.get(0).appendChild(promptElem), inputX = enginePrompt ? inputElem.offsetLeft - haven.window.get(0).offsetLeft : prefixElem.offsetWidth, resizeInput(), prompt.scrollOrFocus(), doScroll = !0, haven.state.autosave.save(), promptElem.dispatchEvent(lineinputReadyEvent) }, haven.prompt = prompt }(), function() { "use strict"; var state = {}, autosaveFilename = "", readUIState = function() { try { var state = FS.readFile(autosaveFilename + "_uidata", { encoding: "utf8" }); return JSON.parse(state) } catch (e) { return null } }; state.autosave = { remove: function() { try { FS.unlink(autosaveFilename) } catch (e) {} }, restore: function() { try { FS.stat(autosaveFilename), Module.ccall("hugojs_set_autosave_filename", "null", ["string"], [autosaveFilename]) } catch (e) {} }, save: function() { haven.options.get("autosave") && (FS.writeFile(autosaveFilename + "_uidata", JSON.stringify(haven.window.getUIState()), { encoding: "utf8" }), Module.ccall("haven_save_autosave", "int", ["string"], [autosaveFilename])) }, setName: function(filename) { autosaveFilename = filename } }, state.restoreUI = function() { var windowCount, i, savedState = readUIState(); if (savedState) { for (windowCount = haven.options.get("windowing") ? savedState.windowContents.length : 1, haven.window.clear(), i = 0; i < windowCount; ++i) haven.window.create(i, savedState.windowDimensions[i].left, savedState.windowDimensions[i].top, savedState.windowDimensions[i].right, savedState.windowDimensions[i].bottom); for (haven.style.color.restore(savedState.currentColors), haven.style.restore(savedState.font), haven.window.position.restore(savedState.position), savedState.title && (document.title = savedState.title), i = 0; i < savedState.windowContents.length; ++i) haven.window.get(i).innerHTML = savedState.windowContents[i], haven.style.apply(haven.window.get(i), i); haven.style.apply(document.body, 0), Module.ccall("hugojs_set_font", "null", ["int"], [savedState.font[0].original]), Module.ccall("hugojs_set_colors", "null", ["int", "int"], [savedState.currentColors[0].text, savedState.currentColors[0].background]), haven.prompt.history.set(savedState.cmdHistory || []), window.scrollTo(0, 9e9), haven.prompt.setDoScroll() } }, window.haven.state = state }(), function() { "use strict"; function defaultColors(targetWindow) { return 1 === targetWindow ? { text: 15, background: 1 } : { text: 7, background: 0 } } function defaultStyles() { return { bold: !1, italic: !1, underline: !1, proportional: !0, original: 0 } } var style = {}, currentColors = [defaultColors(0)], font = [defaultStyles()], ignoreFontFamily = !1; style.apply = function(elem, targetWindow) { var newClasses = [], prompt = haven.prompt.get().getElementsByTagName("INPUT")[0], setPromptStyle = 0 === targetWindow; currentColors[targetWindow] || (currentColors[targetWindow] = defaultColors(targetWindow)), haven.options.get("engineColors") && (newClasses.push("textcolor-" + currentColors[targetWindow].text), newClasses.push("bgcolor-" + currentColors[targetWindow].background)), elem.className = elem.className.replace(/\b(text|bg)color-\d+/g, ""), elem.classList.remove("font-fixed-width"), setPromptStyle && (prompt.className = prompt.className.replace(/\b(text|bg)color-\d+/g, ""), prompt.classList.remove("font-fixed-width")), font[targetWindow] || (font[targetWindow] = defaultStyles()); for (var prop in font[targetWindow]) ignoreFontFamily && "proportional" === prop || font[targetWindow].hasOwnProperty(prop) && (elem.classList.remove("font-" + prop), setPromptStyle && prompt.classList.remove("font-" + prop), font[targetWindow][prop] && newClasses.push("font-" + prop)); ignoreFontFamily || !font[targetWindow].hasOwnProperty("proportional") || font[targetWindow].proportional || newClasses.push("font-fixed-width"); for (var i = 0; i < newClasses.length; ++i) elem.classList.add(newClasses[i]), setPromptStyle && prompt.classList.add(newClasses[i]) }, style.color = { get: function() { return currentColors }, restore: function(oldState) { currentColors = oldState }, set: function(which, color, targetWindow) { currentColors[targetWindow] || (currentColors[targetWindow] = defaultColors(targetWindow)), currentColors[targetWindow][which] !== color && (haven.buffer.flush(targetWindow), currentColors[targetWindow][which] = color) } }, style.font = { get: function() { return font } }, style.init = function(options) { ignoreFontFamily = !options.engineFontFamily }, style.restore = function(oldState) { font = oldState; }, style.set = function(type, value, targetWindow) { haven.buffer.flush(targetWindow), font[targetWindow][type] = value }, haven.style = style }(), function() { "use strict"; function createLines(amount, column, havenWindow) { for (var i = 0; i < amount; ++i) { var newlineFiller = document.createElement("span"); newlineFiller.className = "font-fixed-width", newlineFiller.innerHTML = "\n", outputWindows[havenWindow].appendChild(newlineFiller) } if (column > 0) { var spaceFiller = document.createElement("span"); spaceFiller.innerHTML = Array(column).join(" "), spaceFiller.className = "font-fixed-width", outputWindows[havenWindow].appendChild(spaceFiller) } win.position.reset(havenWindow) } function replacePart(line, col, newContent, havenWindow) { var textContent, output = outputWindows[havenWindow], nodes = textNodesUnder(output), range = document.createRange(), currentLine = 1, currentCol = 1, startFound = !1, endCounter = 0, overflow = function() { for (var i = 0; i < nodes.length; ++i) if (textContent = nodes[i].textContent, currentLine === line) for (var j = 0; j < textContent.length; ++j) { if (startFound) { if (endCounter++, endCounter === newContent.textContent.length || "\n" === textContent[j]) return range.setEnd(nodes[i], j), !1 } else if (currentCol === col) { if (range.setStart(nodes[i], j), startFound = !0, j === textContent.length - 1) return range.setEnd(nodes[i], j), !1 } else if ("\n" === textContent[j]) { var filler = document.createTextNode(Array(col - currentCol + 1).join(" ") + "\n"); return nodes[i].textContent = textContent.substr(0, j) + " ", nodes[i].parentNode.insertBefore(filler, nodes[i].nextSibling), range.setStart(filler, col - currentCol - 1), range.setEnd(filler, col - currentCol - 1), !1 } currentCol++ } else textContent.indexOf("\n") > -1 && currentLine++; return !0 }(); return overflow ? (createLines(position[havenWindow].line - currentLine, col, havenWindow), void output.appendChild(newContent)) : (newContent.textContent.indexOf("\n") > -1 ? (newContent.textContent = newContent.textContent.replace("\n", ""), position[havenWindow].line++, position[havenWindow].col = 1) : position[havenWindow].col += newContent.textContent.length, range.deleteContents(), range.insertNode(newContent), void(newContent.nextSibling || (position[havenWindow].line = null, position[havenWindow].col = null))) } function textNodesUnder(node) { var all = []; for (node = node.firstChild; node; node = node.nextSibling) 3 == node.nodeType ? all.push(node) : all = all.concat(textNodesUnder(node)); return all } var win = {}, outputWindows = [document.getElementById("window0")], mainContainer = document.getElementById("output"), currentContainers = [outputWindows[0]], position = [], windowDimensions = []; win.append = function(content, targetWindow) { var textContainer = document.createElement("span"); position[targetWindow] || (position[targetWindow] = { col: null, line: null }), haven.style.apply(textContainer, targetWindow), textContainer.innerHTML = content, null !== position[targetWindow].col && null !== position[targetWindow].line ? replacePart(position[targetWindow].line, position[targetWindow].col, textContainer, targetWindow) : currentContainers[targetWindow].appendChild(textContainer) }, win.clear = function(targetWindow) { if (void 0 === targetWindow) haven.buffer.flush(0), mainContainer.innerHTML = "", mainContainer.appendChild(outputWindows[0]), haven.style.apply(outputWindows[0], 0), haven.style.apply(document.body, 0), win.position.reset(); else { if (!outputWindows[targetWindow]) return; haven.buffer.flush(targetWindow), outputWindows[targetWindow].innerHTML = "", haven.style.apply(outputWindows[targetWindow], targetWindow), 0 === targetWindow && haven.style.apply(document.body, 0), win.position.reset(targetWindow) } }, win.create = function(outputWindow, left, top, right, bottom) { var newWindow, dimensions = win.measureDimensions(), charHeight = dimensions.char.height, mainContainer = haven.window.get(0).parentNode; return windowDimensions[outputWindow] = { left: left, top: top, right: right, bottom: bottom }, !!haven.options.get("windowing") && (0 === outputWindow ? void(haven.window.get(0).style.paddingTop = (top - 1) * dimensions.char.height + "px") : (haven.window.get(outputWindow) && mainContainer.removeChild(haven.window.get(outputWindow)), newWindow = document.createElement("div"), newWindow.id = "window" + outputWindow, newWindow.className = "havenwindow font-fixed-width", newWindow.style.height = charHeight * (bottom - top + 1) + "px", newWindow.style.top = (top - 1) * charHeight + "px", newWindow.style.marginLeft = left - 1 + "px", newWindow.style.width = (right - left + 2) * dimensions.char.width + "px", outputWindows[outputWindow] = newWindow, currentContainers[outputWindow] = newWindow, void haven.window.container.append(newWindow, mainContainer))) }, win.container = { append: function(container, target) { "number" == typeof target ? outputWindows[target].appendChild(container) : target.appendChild(container) }, get: function(targetWindow) { return currentContainers[targetWindow] }, set: function(newContainer, targetWindow) { currentContainers[targetWindow] = newContainer } }, win.get = function(targetWindow) { return outputWindows[targetWindow] }, win.getUIState = function() { var lastChild, windowContents = [], promptElem = haven.prompt.get(), promptParent = promptElem.parentNode; promptParent && promptParent.removeChild(promptElem), lastChild = outputWindows[0].lastChild, outputWindows[0].removeChild(lastChild); for (var i = 0; i < outputWindows.length; ++i) windowContents[i] = outputWindows[i].innerHTML; outputWindows[0].appendChild(lastChild), promptParent && promptParent.appendChild(promptElem); var lastLbr = windowContents[0].lastIndexOf("\n"); return windowContents[0] = windowContents[0].substring(0, lastLbr) + windowContents[0].substring(lastLbr + 1), { cmdHistory: haven.prompt.history.get(), currentColors: haven.style.color.get(), font: haven.style.font.get(), position: position, title: document.title, windowDimensions: windowDimensions, windowContents: windowContents } }, win.measureDimensions = function() { var measureElemHeight, outputContainer = haven.window.get(0).parentNode, dimensions = { window: { width: parseInt(window.getComputedStyle(outputContainer).width, 10), height: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight }, line: {}, char: {} }, measureElem = document.createElement("span"), outputDimensions = dimensions.window; return measureElem.innerHTML = "00000
00000
00000", measureElem.className = "font-fixed-width", measureElem.style.display = "inline-block", outputContainer.appendChild(measureElem), dimensions.char.width = measureElem.offsetWidth / 5, dimensions.line.width = Math.floor(outputDimensions.width / dimensions.char.width - 1), measureElem.style.display = "block", measureElemHeight = measureElem.clientHeight, measureElem.innerHTML += "
00000
00000", dimensions.char.height = (measureElem.clientHeight - measureElemHeight) / 2, dimensions.line.height = Math.floor(outputDimensions.height / dimensions.char.height), measureElem.parentNode.removeChild(measureElem), dimensions }, win.position = { reset: function(targetWindow) { void 0 === targetWindow ? position = { col: null, line: null } : win.position.set(null, null, targetWindow) }, restore: function(oldState) { position = oldState }, set: function(col, line, havenWindow) { position[havenWindow] || (position[havenWindow] = {}), position[havenWindow].col = col, position[havenWindow].line = line } }, win.setTitle = function(title) { document.title = title }, haven.window = win }(), window.Module = { arguments: [], preRun: [], postRun: [function() { haven.assets.finished("engine") }], print: function(text) { haven.error("Unexpected engine output to stdout: " + text) }, printErr: function() { console.log(arguments), haven.error(Array.prototype.slice.call(arguments).join(" ")) }, TOTAL_MEMORY: 33554432 };