logo-web 0.1.0

Web-based IDE for Logo programming language
Documentation
<!doctype html>
<html lang="en-US" style="height: 100%;">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Logo</title>
    <link href="./assets/bootstrap.min.css" rel="stylesheet">
  </head>
  <body style="height: 100%;">
    <table style="width: 100%; height: 100%;">
      <thead></thead>
      <tbody>
        <tr>
          <td style="width: 25%; height: 100%;">
            <table style="width: 100%; height: 100%;">
              <thead></thead>
              <tbody>
                <tr><td style="width: 100%; height: 5%; text-align: center;">
                  <h3>Procedures</h3>
                </td></tr>
                <tr><td style="width: 100%; height: 95%;">
                  <textarea id="proc_input" spellcheck="false" class="form-control font-monospace" style="width: 100%; height: 100%; resize: none; font-size: 12px; line-height: 1.3;"></textarea>
                </td></tr>
              </tbody>
            </table>
          </td>
          <td style="width: 75%; height: 100%;">
            <table style="width: 100%; height: 100%;">
              <thead></thead>
              <tbody>
                <tr>
                  <td style="width: 100%; height: 70%; text-align: center; background-color: lightgray; border: 1px solid black;">
                    <img id="turtle" width="25px" height="25px" src="./assets/turtle.png" style="position: absolute; z-index: 1; visibility: hidden;">
                    <canvas id="canvas" width="800" height="450" style="image-rendering: pixelated; position: relative; border: 1px solid black;"></canvas>
                  </td>
                </tr>
                <tr style="width: 100%; height: 15%; border: 1px solid black;">
                  <td>
                    <div id="logs" style="width: 100%; height: 100%; overflow-x: hidden; overflow-y: auto; text-align: left; background-color: rgb(250, 250, 250);"></div>
                  </td>
                </tr>
                <tr>
                  <td style="width: 100%; height: 5%;">
                    <table style="width: 100%; height: 100%;">
                      <tr>
                        <td style="padding-top: 0px; padding-right: 7px; padding-bottom: 0px; padding-left: 5px;">
                          Commands:
                        </td> 
                        <td style="width: 100%;">
                          <input id="cmd_input" class="form-control" spellcheck="false" style="width: 100%;">
                        </td>
                      </tr>
                    </table>
                  </td>
                </tr>
              </tbody>
            </table>
          </td>
        </tr>
      </tbody>
    </table>
    <script type="module">
      import init, { context_create, context_render, context_get_state, context_set_show_fn } from "./pkg/logo_web.js";
      init().then(() => {
        const canvas = document.getElementById("canvas");
        const logs = document.getElementById("logs");
        const cmdInput = document.getElementById("cmd_input");
        const turtle = document.getElementById("turtle");

        let appendLog = function(msg) {
          logs.innerHTML += msg + "<br/>";
          logs.scrollTop = logs.scrollHeight;
        }

        let context = context_create(canvas.width, canvas.height);
        context_set_show_fn(context, appendLog);

        let render = function(cmd) {
          let procInput = document.getElementById("proc_input").value;
          let renderData;
          try {
            renderData = context_render(context, procInput, cmd);
          }
          catch(e) {
            appendLog(e);
            return;
          }
          const ctx = canvas.getContext("2d");
          const imageData = ctx.createImageData(canvas.width, canvas.height);
          for (let i = 0; i < imageData.data.length; i += 1) {
            imageData.data[i] = renderData[i];
          }
          ctx.putImageData(imageData, 0, 0);

          let state = context_get_state(context);
          let turtle_x = state.turtle_pos.x;
          let turtle_y = state.turtle_pos.y;
          let margin_left = canvas.width / 2 + turtle_x - turtle.width / 2;
          let margin_top = canvas.height / 2 - turtle_y - turtle.height / 2;
          turtle.style.marginLeft = margin_left + "px";
          turtle.style.marginTop = margin_top + "px";
          turtle.style.transform = "rotate(" + state.turtle_angle + "deg)";
          if (state.turtle_visible) {
            turtle.style.visibility = "visible";
          }
          else {
            turtle.style.visibility = "hidden";
          }
        }
        render("");

        let history = [];
        let history_idx = 0;
        let in_history = false;

        cmdInput.addEventListener("keyup", ({key}) => {
          if (key === "Enter") {
            history.push(cmdInput.value);
            appendLog(cmdInput.value);
            render(cmdInput.value);
            cmdInput.value = "";
          }
          
          if (key === "ArrowUp") {
            if (in_history && history_idx + 1 < history.length) {
              history_idx += 1;
            }
            cmdInput.value = history[history.length - 1 - history_idx];
            in_history = true;
          }
          else if (key === "ArrowDown") {
            if (in_history && history_idx - 1 >= 0) {
              history_idx -= 1;
            }
            cmdInput.value = history[history.length - 1 - history_idx];
            in_history = true;
          }
          else {
            history_idx = 0;
            in_history = false;
          }
        });
      });
    </script>
    <script src="./assets/bootstrap.min.js"></script>
  </body>
</html>