"use strict";
(() => {
  // src/ide/web/ui-helpers.ts
  function checkIsChrome() {
    const isChromium = window.chrome;
    const winNav = window.navigator;
    const vendorName = winNav.vendor;
    const isOpera = typeof window.opr !== "undefined";
    const _isFirefox = winNav.userAgent.indexOf("Firefox") > -1;
    const isIEedge = winNav.userAgent.indexOf("Edg") > -1;
    const isIOSChrome = winNav.userAgent.match("CriOS");
    const isGoogleChrome = typeof winNav.userAgentData !== "undefined" ? winNav.userAgentData.brands.some((b) => b.brand === "Google Chrome") : vendorName === "Google Inc.";
    const isChrome2 = isChromium !== null && typeof isChromium !== "undefined" && vendorName === "Google Inc." && isOpera === false && isIEedge === false && isGoogleChrome;
    if (isIOSChrome) {
      return true;
    } else if (isChrome2 || isIEedge) {
      return true;
    } else {
      return false;
    }
  }
  function confirmContinueOnNonChromeBrowser() {
    return confirm(
      `Elan is compatible with the Chrome or Edge browser. It does not work correctly in Firefox or Safari.`
    );
  }
  function readMsg(value) {
    return { type: "read", value };
  }
  function errorMsg(value) {
    return { type: "status", status: "error", error: value };
  }

  // src/compiler/standard-library/elan-runtime-error.ts
  var ElanRuntimeError = class extends Error {
    constructor(err) {
      super(err instanceof Error ? err.message : err);
      this.err = err;
    }
    useLine(token) {
      return !(token.startsWith("data") || token.startsWith("http") || token.startsWith("async") || token.startsWith("Array") || token.startsWith("System") || token.startsWith("onmessage"));
    }
    updateLine0(l0) {
      if (l0.startsWith("RangeError")) {
        return "Error: Stack Overflow";
      }
      return l0;
    }
    get elanStack() {
      const jsStack = this.err instanceof Error ? this.err.stack : this.stack;
      const elanStack = [];
      if (jsStack) {
        let lines = jsStack.split("\n").map((l) => l.trim());
        if (lines.length > 0) {
          elanStack.push(this.updateLine0(lines[0]));
          lines = lines.slice(1);
          for (const l of lines) {
            const line = l.split(" ");
            if (line.length > 1) {
              let fn = line[1];
              fn = fn === "runTests" ? "test" : fn;
              fn = fn === "System.printLine" ? "print" : fn;
              if (this.useLine(fn)) {
                elanStack.push(`at ${fn}`);
              }
            }
          }
        }
      }
      if (elanStack.length > 0) {
        return elanStack.join("\n");
      }
      return "";
    }
  };

  // src/compiler/keywords.ts
  var abstractKeyword = "abstract";
  var classKeyword = "class";
  var copyKeyword = "copy";
  var emptyKeyword = "empty";
  var functionKeyword = "function";
  var ifKeyword = "if";
  var lambdaKeyword = "lambda";
  var newKeyword = "new";
  var privateKeyword = "private";
  var procedureKeyword = "procedure";
  var propertyKeyword = "property";
  var abstractPropertyKeywords = abstractKeyword + " " + propertyKeyword;
  var abstractProcedureKeywords = abstractKeyword + " " + procedureKeyword;
  var abstractFunctionKeywords = abstractKeyword + " " + functionKeyword;
  var privatePropertyKeywords = privateKeyword + " " + propertyKeyword;
  var privateProcedureKeywords = privateKeyword + " " + procedureKeyword;
  var privateFunctionKeywords = privateKeyword + " " + functionKeyword;
  var abstractClassKeywords = abstractKeyword + " " + classKeyword;

  // src/compiler/symbols/elan-type-names.ts
  var BooleanName = "Boolean";
  var IntName = "Int";
  var FloatName = "Float";
  var StringName = "String";
  var RegExpName = "RegExp";
  var FuncName = "Func";
  var TupleName = "Tuple";

  // src/compiler/compiler-interfaces/type-options.ts
  var noTypeOptions = {
    isImmutable: false,
    isAbstract: false,
    isIndexable: false,
    isDoubleIndexable: false,
    isIterable: false
  };
  var immutableTypeOptions = {
    isImmutable: true,
    isAbstract: false,
    isIndexable: false,
    isDoubleIndexable: false,
    isIterable: false
  };

  // src/compiler/symbols/int-type.ts
  var IntType = class _IntType {
    constructor() {
    }
    initialValue = "0";
    typeOptions = immutableTypeOptions;
    static Instance = new _IntType();
    name = IntName;
    toString() {
      return this.name;
    }
    isAssignableFrom(otherType) {
      return otherType instanceof _IntType;
    }
  };

  // src/compiler/symbols/float-type.ts
  var FloatType = class _FloatType {
    constructor() {
    }
    initialValue = "0";
    typeOptions = immutableTypeOptions;
    static Instance = new _FloatType();
    name = FloatName;
    toString() {
      return this.name;
    }
    isNumber(st) {
      return st instanceof IntType || st instanceof _FloatType;
    }
    isAssignableFrom(otherType) {
      return this.isNumber(otherType);
    }
  };

  // src/compiler/symbols/unknown-type.ts
  var UnknownType = class _UnknownType {
    constructor() {
    }
    initialValue = "";
    typeOptions = immutableTypeOptions;
    static Instance = new _UnknownType();
    name = "Unknown";
    toString() {
      return this.name;
    }
    isAssignableFrom(_otherType) {
      return true;
    }
  };

  // src/compiler/syntax-nodes/empty-asn.ts
  var EmptyAsn = class _EmptyAsn {
    constructor(fieldId) {
      this.fieldId = fieldId;
    }
    indent() {
      return "";
    }
    items = [];
    id = "";
    symbolScope = 9 /* unknown */;
    compileErrors = [];
    compile() {
      return ``;
    }
    symbolType() {
      return UnknownType.Instance;
    }
    get value() {
      return _EmptyAsn.Instance;
    }
    toString() {
      return "";
    }
    static Instance = new _EmptyAsn("");
  };

  // src/compiler/compiler-interfaces/elan-type-interfaces.ts
  var elanMetadataKey = Symbol("elan-metadata");

  // src/compiler/symbols/boolean-type.ts
  var BooleanType = class _BooleanType {
    constructor() {
    }
    typeOptions = immutableTypeOptions;
    initialValue = "false";
    static Instance = new _BooleanType();
    name = BooleanName;
    toString() {
      return this.name;
    }
    isAssignableFrom(otherType) {
      return otherType instanceof _BooleanType;
    }
  };

  // src/compiler/symbols/tuple-type.ts
  var TupleType = class _TupleType {
    constructor(ofTypes) {
      this.ofTypes = ofTypes;
    }
    typeOptions = immutableTypeOptions;
    get initialValue() {
      const init = this.ofTypes.map((t) => t.initialValue).join(", ");
      return `system.emptyTuple([${init}])`;
    }
    get name() {
      return `tuple(${this.ofTypes.map((t) => t.name).join(", ")})`;
    }
    toString() {
      return `tuple(${this.ofTypes.map((t) => t.name).join(", ")})`;
    }
    isAssignableFrom(otherType) {
      if (otherType instanceof _TupleType) {
        if (this.ofTypes.length !== otherType.ofTypes.length) {
          return false;
        }
        return this.ofTypes.map((t, i) => t.isAssignableFrom(otherType.ofTypes[i])).every((b) => b);
      }
      return false;
    }
  };

  // src/compiler/symbols/function-type.ts
  var FunctionType = class _FunctionType {
    constructor(parameterNames2, parameterTypes, returnType, isExtension, isPure, isAsync, deprecated) {
      this.parameterNames = parameterNames2;
      this.parameterTypes = parameterTypes;
      this.returnType = returnType;
      this.isExtension = isExtension;
      this.isPure = isPure;
      this.isAsync = isAsync;
      this.deprecated = deprecated;
    }
    get initialValue() {
      return `system.emptyFunc(${this.returnType.initialValue})`;
    }
    // so can have mutable type parameters
    typeOptions = noTypeOptions;
    get name() {
      return `Func<of ${this.parameterTypes.map((p) => p.name).join(", ")} => ${this.returnType.name}>`;
    }
    toString() {
      return FuncName;
    }
    isAssignableFrom(otherType) {
      if (otherType instanceof _FunctionType) {
        if (this.parameterTypes.length !== otherType.parameterTypes.length) {
          return false;
        }
        const rt = this.returnType.isAssignableFrom(otherType.returnType);
        return rt && this.parameterTypes.map((t, i) => t.isAssignableFrom(otherType.parameterTypes[i])).every((b) => b);
      }
      return false;
    }
  };

  // src/compiler/symbols/unknown-symbol.ts
  var UnknownSymbol = class {
    constructor(id) {
      this.symbolId = id ?? "";
    }
    symbolId = "";
    symbolType = () => UnknownType.Instance;
    symbolScope = 9 /* unknown */;
    name = "Unknown Symbol";
  };

  // src/compiler/symbols/generic-parameter-type.ts
  var GenericParameterType = class _GenericParameterType {
    constructor(id, constraint) {
      this.id = id;
      this.constraint = constraint;
    }
    typeOptions = noTypeOptions;
    initialValue = "";
    get name() {
      return this.constraint ? `${this.constraint.name}` : `Generic Parameter ${this.id}`;
    }
    toString() {
      return this.name;
    }
    isAssignableFrom(otherType) {
      if (otherType instanceof _GenericParameterType) {
        return this.name === otherType.name;
      }
      return false;
    }
  };

  // src/compiler/symbols/null-scope.ts
  var NullScope = class _NullScope {
    resolveSymbol(_id, _scope) {
      return new UnknownSymbol();
    }
    resolveOwnSymbol(_id) {
      return new UnknownSymbol();
    }
    getParentScope() {
      return _NullScope.Instance;
    }
    symbolMatches(_id, _all, _initialScope) {
      return [];
    }
    getChildren() {
      return [];
    }
    symbolId = "";
    symbolType = () => UnknownType.Instance;
    symbolScope = 9 /* unknown */;
    static Instance = new _NullScope();
  };

  // src/compiler/symbols/regexp-type.ts
  var RegExpType = class _RegExpType {
    constructor() {
    }
    initialValue = "system.emptyRegExp()";
    typeOptions = immutableTypeOptions;
    static Instance = new _RegExpType();
    name = RegExpName;
    toString() {
      return this.name;
    }
    isAssignableFrom(otherType) {
      return otherType instanceof _RegExpType;
    }
  };

  // src/compiler/symbols/string-type.ts
  var StringType = class _StringType {
    constructor() {
    }
    get ofTypes() {
      return [_StringType.Instance];
    }
    initialValue = '""';
    get typeOptions() {
      return {
        isImmutable: true,
        isAbstract: false,
        isIndexable: true,
        isDoubleIndexable: false,
        isIterable: true
      };
    }
    static Instance = new _StringType();
    name = StringName;
    toString() {
      return this.name;
    }
    isAssignableFrom(otherType) {
      return otherType instanceof _StringType;
    }
  };

  // src/compiler/symbols/elan-symbols.ts
  var intSymbol = {
    symbolId: IntType.Instance.name,
    symbolType: function() {
      return IntType.Instance;
    },
    symbolScope: 3 /* program */
  };
  var floatSymbol = {
    symbolId: FloatType.Instance.name,
    symbolType: function() {
      return FloatType.Instance;
    },
    symbolScope: 3 /* program */
  };
  var stringSymbol = {
    symbolId: StringType.Instance.name,
    symbolType: function() {
      return StringType.Instance;
    },
    symbolScope: 3 /* program */
  };
  var booleanSymbol = {
    symbolId: BooleanType.Instance.name,
    symbolType: function() {
      return BooleanType.Instance;
    },
    symbolScope: 3 /* program */
  };
  var regExpSymbol = {
    symbolId: RegExpType.Instance.name,
    symbolType: function() {
      return RegExpType.Instance;
    },
    symbolScope: 3 /* program */
  };
  var tupleSymbol = {
    symbolId: TupleName,
    symbolType: function() {
      return new TupleType([new GenericParameterType("T1"), new GenericParameterType("T2")]);
    },
    symbolScope: 3 /* program */,
    isClass: true,
    isAbstract: true,
    isNotInheritable: true,
    ofTypes: [new GenericParameterType("T1"), new GenericParameterType("T2")]
  };
  var funcSymbol = {
    symbolId: FuncName,
    symbolType: function() {
      return new FunctionType(
        ["T"],
        [new GenericParameterType("T")],
        new GenericParameterType("T1"),
        false,
        true,
        false
      );
    },
    symbolScope: 3 /* program */,
    isClass: true,
    isAbstract: true,
    isNotInheritable: true,
    ofTypes: [new GenericParameterType("T")]
  };

  // src/compiler/syntax-nodes/ast-helpers.ts
  var opMap = /* @__PURE__ */ new Map([
    [0 /* Add */, "+"],
    [1 /* Minus */, "-"],
    [2 /* Not */, "not"],
    [3 /* Multiply */, "*"],
    [4 /* And */, "and"],
    [5 /* Equals */, "is"],
    [6 /* LT */, "<"],
    [7 /* GT */, ">"],
    [8 /* GTE */, ">="],
    [9 /* LTE */, "<="],
    [10 /* Div */, "div"],
    [11 /* Mod */, "mod"],
    [12 /* Divide */, "/"],
    [13 /* NotEquals */, "isnt"],
    [14 /* Pow */, "^"],
    [15 /* Or */, "or"]
  ]);

  // src/ide/frames/symbol-completion-helpers.ts
  var KeywordCompletion = class _KeywordCompletion {
    constructor(keyword, spaceAfter, dotAfter, openBracketAfter) {
      this.keyword = keyword;
      this.spaceAfter = spaceAfter;
      this.dotAfter = dotAfter;
      this.openBracketAfter = openBracketAfter;
    }
    static map = /* @__PURE__ */ new Map();
    static create(keyword, spaceAfter = true, dotAfter = false, openBracketAfter = false) {
      if (!this.map.has(keyword)) {
        this.map.set(keyword, new _KeywordCompletion(keyword, spaceAfter, dotAfter, openBracketAfter));
      }
      return this.map.get(keyword);
    }
    static reset() {
      this.map.clear();
    }
  };

  // src/ide/frames/parse-nodes/parse-node-helpers.ts
  var allIds = [
    5 /* id_constant */,
    0 /* id_let */,
    4 /* id_parameter_out */,
    3 /* id_parameter_regular */,
    2 /* id_property */,
    1 /* id_variable */,
    6 /* id_enumValue */
  ];
  var allMethods = [
    12 /* method_function */,
    12 /* method_function */,
    13 /* method_system */
  ];
  var allIdsAndMethods = [
    5 /* id_constant */,
    0 /* id_let */,
    4 /* id_parameter_out */,
    3 /* id_parameter_regular */,
    2 /* id_property */,
    1 /* id_variable */,
    6 /* id_enumValue */,
    12 /* method_function */,
    12 /* method_function */,
    13 /* method_system */
  ];
  var assignableIds = [
    4 /* id_parameter_out */,
    1 /* id_variable */,
    2 /* id_property */
  ];
  var concreteAndAbstractTypes = [
    8 /* type_abstract */,
    10 /* type_notInheritable */,
    7 /* type_concrete */,
    9 /* type_enum */
  ];
  var allKeywordsThatCanStartAnExpression = new Set(
    [newKeyword, copyKeyword, ifKeyword, lambdaKeyword, emptyKeyword].map(
      (kw) => KeywordCompletion.create(kw)
    )
  );

  // src/compiler/elan-type-annotations.ts
  var ElanValueTypeDescriptor = class {
    constructor(name, ofType, valueType) {
      this.name = name;
      this.ofType = ofType;
      this.valueType = valueType;
    }
    isConstant = true;
    mapType(_scope) {
      switch (this.name) {
        case FloatName:
          return FloatType.Instance;
        case StringName:
          return StringType.Instance;
        case IntName:
          return IntType.Instance;
        case BooleanName:
          return BooleanType.Instance;
        case RegExpName:
          return RegExpType.Instance;
      }
      throw new Error("NotImplemented: " + this.name);
    }
  };
  var ElanGenericTypeDescriptor = class {
    constructor(name, constraint) {
      this.name = name;
      this.constraint = constraint;
    }
    isConstant = true;
    mapType(scope) {
      return new GenericParameterType(this.name, this.constraint?.mapType(scope));
    }
  };
  var ElanInt = new ElanValueTypeDescriptor(IntName);
  var ElanFloat = new ElanValueTypeDescriptor(FloatName);
  var ElanString = new ElanValueTypeDescriptor(StringName);
  var ElanBoolean = new ElanValueTypeDescriptor(BooleanName);
  var ElanRegExp = new ElanValueTypeDescriptor(RegExpName);
  var ElanT1 = new ElanGenericTypeDescriptor("T1");
  var ElanT2 = new ElanGenericTypeDescriptor("T2");

  // src/ide/frames/file-impl.ts
  var fileErrorPrefix = `Cannot load file:`;
  var cannotLoadInvalidHashFile = `${fileErrorPrefix} Elan header does not match the code below. The file might have been created or modified outside Elan IDE.`;
  var cannotLoadInvalidFile = `${fileErrorPrefix} code does not begin with a valid Elan header, so cannot be loaded.`;
  var cannotLoadEmptyFile = `${fileErrorPrefix} no code found to load.`;
  var cannotLoadFile = `${fileErrorPrefix} it has been created or modified outside Elan IDE`;
  var cannotLoadUnparseableFile = `${fileErrorPrefix} cannot parse code`;

  // src/ide/web/web-helpers.ts
  function checkForUnclosedHtmlTag(text) {
    if (/<[A-Za-z!\/?][^>]*$/.test(text)) {
      throw new ElanRuntimeError(
        "Unclosed HTML tag in printed text '" + text.replace(/</g, "&lt;") + "'"
      );
    }
  }
  function sanitiseHtml(text) {
    return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
  }

  // src/ide/web/web-input-output.ts
  var WebInputOutput = class {
    keyBuffer = [];
    display;
    lastDirId = "elan-data";
    constructor() {
      this.display = document.getElementById("display");
      this.display.addEventListener("keydown", (k) => {
        if (k.key === "Shift" || k.key === "Control" || k.key === "Alt") {
          return;
        }
        this.keyBuffer.push(k);
      });
      this.display.focus();
    }
    breakPoint(_allScopedSymbols, _id, _singlestep) {
      throw new Error("Method not implemented.");
    }
    useChromeFileAPI() {
      return "showOpenFilePicker" in self;
    }
    chooser() {
      const f = document.createElement("input");
      const g = this.display;
      f.style.display = "none";
      f.type = "file";
      f.name = "file";
      g.appendChild(f);
      return f;
    }
    chromeChooser() {
      const f = document.createElement("input");
      const g = this.display;
      f.style.display = "none";
      g.appendChild(f);
      return f;
    }
    readFileChrome() {
      let fileHandle;
      const chooser = this.chromeChooser();
      return new Promise((rs, rj) => {
        chooser.addEventListener("click", async () => {
          try {
            [fileHandle] = await window.showOpenFilePicker({
              startIn: "documents",
              id: this.lastDirId
            });
            const file = await fileHandle.getFile();
            if (file.type !== "text/plain") {
              rj(`cannot load non text file ${file.name} of type ${file.type}`);
            } else {
              const contents = await file.text();
              rs(contents);
            }
          } catch (_e) {
            rj("read cancelled");
          }
        });
        chooser.click();
        this.display?.removeChild(chooser);
      });
    }
    readFile() {
      if (this.useChromeFileAPI()) {
        return this.readFileChrome();
      }
      const inp = this.chooser();
      return new Promise((rs, rj) => {
        inp.addEventListener("change", (event) => {
          const elanFile = event.target.files?.[0];
          if (elanFile) {
            if (elanFile.type !== "text/plain") {
              rj(`cannot load non text file ${elanFile.name} of type ${elanFile.type}`);
            } else {
              const reader = new FileReader();
              reader.addEventListener("load", (event2) => {
                const content = event2.target.result;
                rs(content);
              });
              reader.readAsText(elanFile);
            }
          }
          event.preventDefault();
        });
        inp.addEventListener("cancel", (event) => {
          rj("read cancelled");
          event.preventDefault();
        });
        inp.click();
      });
    }
    async writeFileChrome(fileName, data) {
      try {
        const fh = await self.showSaveFilePicker({
          suggestedName: fileName,
          startIn: "documents",
          id: this.lastDirId
        });
        const writeable = await fh.createWritable();
        await writeable.write(data);
        return await writeable.close();
      } catch (_e) {
        throw new Error("write cancelled");
      }
    }
    writeFile(fileName, data) {
      if (!fileName) {
        fileName = "untitled.txt";
      }
      if (!fileName.endsWith(".txt")) {
        fileName = `${fileName}.txt`;
      }
      if (this.useChromeFileAPI()) {
        return this.writeFileChrome(fileName, data);
      }
      return new Promise((rs, rj) => {
        try {
          const blob = new Blob([data], { type: "text/plain" });
          const aElement = document.createElement("a");
          aElement.setAttribute("download", fileName);
          const href = URL.createObjectURL(blob);
          aElement.href = href;
          aElement.setAttribute("target", "_blank");
          aElement.click();
          URL.revokeObjectURL(href);
          rs();
        } catch (_e) {
          rj("write cancelled");
        }
      });
    }
    drawBlockGraphics(html) {
      document.getElementById("block-graphics").innerHTML = html;
      this.display.focus();
      return Promise.resolve();
    }
    clearBlockGraphics() {
      this.clearKeyBuffer();
      return this.drawBlockGraphics("");
    }
    drawVectorGraphics(html) {
      document.getElementById("vector-graphics").innerHTML = html;
      this.display.focus();
      return Promise.resolve();
    }
    clearVectorGraphics() {
      this.clearKeyBuffer();
      return this.drawVectorGraphics("");
    }
    async clearDisplay() {
      this.clearKeyBuffer();
      await this.clearPrintedText();
      await this.clearHtml();
      await this.clearBlockGraphics();
      return this.clearVectorGraphics();
    }
    finished() {
      this.cancelWaits = true;
    }
    cancelWaits = false;
    printedText = "";
    currentInterval;
    async printLine(text) {
      await this.print(`${text}
`);
      const element = document.getElementById("printed-text");
      element.scrollTop = element.scrollHeight;
      return Promise.resolve();
    }
    async print(text) {
      this.printedText = `${this.printedText}${sanitiseHtml(text)}`;
      await this.renderPrintedText();
      return Promise.resolve();
    }
    async printTab(position, text) {
      const lastNl = this.printedText.lastIndexOf("\n");
      const spaces = "                                                                                ";
      const charsSinceNl = this.printedText.length - lastNl;
      const tab = spaces.substring(0, position - charsSinceNl + 1);
      this.printedText = `${this.printedText}${tab}${sanitiseHtml(text)}`;
      await this.renderPrintedText();
      return Promise.resolve();
    }
    async stopReading() {
      clearInterval(this.currentInterval);
      await this.clearKeyBuffer();
      this.display.focus();
      return Promise.resolve();
    }
    async readLine() {
      await this.renderPrintedText();
      const div = document.getElementById("printed-text");
      const inp = document.createElement("input");
      inp.id = "inp";
      inp.type = "text";
      inp.autofocus = true;
      inp.tabIndex = 2;
      div.appendChild(inp);
      inp.focus();
      this.cancelWaits = false;
      return new Promise((rs) => {
        let entered = false;
        inp.addEventListener("keydown", (k) => {
          entered = k.key === "Enter";
        });
        this.currentInterval = setInterval(async () => {
          if (this.cancelWaits) {
            clearInterval(this.currentInterval);
            rs("");
          } else if (entered) {
            rs(inp.value);
            await this.stopReading();
            const v = inp.value.replace(/</g, "&lt;");
            div.removeChild(inp);
            await this.printLine(v);
          }
        }, 250);
      });
    }
    waitForAnyKey() {
      this.cancelWaits = false;
      return new Promise((rs) => {
        const timeOut = setInterval(async () => {
          if (this.cancelWaits || await this.getKey() !== "") {
            clearInterval(timeOut);
            rs();
          }
        }, 250);
      });
    }
    waitForKey() {
      this.cancelWaits = false;
      return new Promise((rs) => {
        const timeOut = setInterval(async () => {
          const key = await this.getKey();
          if (this.cancelWaits || key !== "") {
            clearInterval(timeOut);
            rs(key);
          }
        }, 250);
      });
    }
    getKey() {
      this.display.focus();
      const evt = this.keyBuffer[0];
      this.keyBuffer = this.keyBuffer.slice(1);
      const ks = evt ? evt.key : "";
      return Promise.resolve(ks);
    }
    getModKey(e) {
      return e.ctrlKey ? "Control" : e.shiftKey ? "Shift" : e.altKey ? "Alt" : "";
    }
    getKeyWithModifier() {
      this.display.focus();
      const evt = this.keyBuffer.pop();
      const ks = evt ? [evt.key, this.getModKey(evt)] : ["", ""];
      return Promise.resolve(ks);
    }
    clearKeyBuffer() {
      this.keyBuffer = [];
      return Promise.resolve();
    }
    clearSystemInfo() {
      document.getElementById("system-info").innerHTML = "";
      return Promise.resolve();
    }
    async renderPrintedText() {
      const div = document.getElementById("printed-text");
      div.innerHTML = this.printedText;
      return Promise.resolve();
    }
    async clearPrintedText() {
      this.printedText = "";
      await this.renderPrintedText();
      return Promise.resolve();
    }
    wrapHtmlInSrcdoc(s) {
      return `<head><link href='styles/ide.css' rel='stylesheet'/></head><body><div id='display-html'>${s}</div></body>`;
    }
    wrapHtmlInIframe(s) {
      return `<iframe id="display-html-sandbox" sandbox seamless srcdoc="${this.wrapHtmlInSrcdoc(s)}"</iframe>`;
    }
    drawHtml(html) {
      checkForUnclosedHtmlTag(html);
      const div = document.getElementById("display-html");
      const iframe = document.getElementById("display-html-sandbox");
      if (!iframe) {
        div.innerHTML = this.wrapHtmlInIframe(html);
      } else {
        iframe.srcdoc = this.wrapHtmlInSrcdoc(html);
      }
      return new Promise((rs) => setTimeout(() => rs(), 50));
    }
    clearHtml() {
      document.getElementById("display-html").innerHTML = "";
      return Promise.resolve();
    }
    _audioCtx;
    get audioCtx() {
      if (!this._audioCtx) {
        this._audioCtx = new AudioContext();
      }
      return this._audioCtx;
    }
    tone(duration, frequency, volume) {
      return new Promise((rs) => {
        const ac = this.audioCtx;
        const oscillator = ac.createOscillator();
        const gainNode = ac.createGain();
        oscillator.connect(gainNode);
        gainNode.connect(ac.destination);
        gainNode.gain.value = volume;
        oscillator.frequency.value = frequency;
        oscillator.type = "sine";
        oscillator.onended = (_e) => rs();
        oscillator.start(ac.currentTime);
        oscillator.stop(ac.currentTime + duration / 1e3);
      });
    }
  };

  // src/ide/web/standalone.ts
  var displayDiv = document.getElementById("display");
  var elanInputOutput = new WebInputOutput();
  var runWorker;
  var jsCode = `injected_code`;
  displayDiv.addEventListener("click", () => {
    displayDiv.getElementsByTagName("input")?.[0]?.focus();
  });
  function runProgram() {
    try {
      runWorker = new Worker(jsCode, { type: "module" });
      runWorker.onmessage = async (e) => {
        const data = e.data;
        switch (data.type) {
          case "write":
            await handleWorkerIO(data);
            break;
          case "status":
            switch (data.status) {
              case "finished":
                handleRunWorkerFinished();
                break;
              case "error":
                await handleRunWorkerError(data);
                break;
            }
        }
      };
      runWorker.onerror = async (ev) => {
        console.warn(ev.message);
      };
      runWorker.postMessage({ type: "start" });
    } catch (e) {
      console.warn(e);
    }
  }
  var isChrome = checkIsChrome();
  var okToContinue = isChrome || confirmContinueOnNonChromeBrowser();
  if (okToContinue) {
    runProgram();
  }
  async function handleWorkerIO(data) {
    switch (data.function) {
      case "readLine":
        const line = await elanInputOutput.readLine();
        runWorker?.postMessage(readMsg(line));
        break;
      case "waitForAnyKey":
        await elanInputOutput.waitForAnyKey();
        runWorker?.postMessage(readMsg(""));
        break;
      case "waitForKey":
        const wkey = await elanInputOutput.waitForKey();
        runWorker?.postMessage(readMsg(wkey));
        break;
      case "getKey":
        const key = await elanInputOutput.getKey();
        runWorker?.postMessage(readMsg(key));
        break;
      case "getKeyWithModifier":
        const keyWithMod = await elanInputOutput.getKeyWithModifier();
        runWorker?.postMessage(readMsg(keyWithMod));
        break;
      case "readFile":
        try {
          const file = await elanInputOutput.readFile();
          runWorker?.postMessage(readMsg(file));
        } catch (e) {
          runWorker?.postMessage(errorMsg(e));
        }
        break;
      case "writeFile":
        try {
          await elanInputOutput.writeFile(data.parameters[0], data.parameters[1]);
          runWorker?.postMessage(readMsg(""));
        } catch (e) {
          runWorker?.postMessage(errorMsg(e));
        }
        break;
      default:
        try {
          await elanInputOutput[data.function](...data.parameters);
          runWorker?.postMessage(readMsg(""));
        } catch (e) {
          runWorker?.postMessage(errorMsg(e));
        }
        break;
    }
  }
  function handleRunWorkerFinished() {
    runWorker?.terminate();
    runWorker = void 0;
    console.info("elan program completed OK");
  }
  async function handleRunWorkerError(data) {
    runWorker?.terminate();
    runWorker = void 0;
    console.warn(data.error);
  }
})();
