<script>

// 188b JS spreadsheet
// ===================

// The function `o` has three different uses:
// - If it's called without any argument, it will draw a grid of 4 x 8 labels and cells.
// - If it's called with an argument, it will evaluate all the cells' raw values and update their content with the result of this evaluation.
// - It's also used as an object, where we store all the cells' raw values.
// Each cell has two values: the raw value (a number of formula typed by the user) and the evaluated value (computed by the function `o`).
// This script declares the function `o` and calls it immediately with no argument.
(
  o = v => {
    
    // The function `o` declares a string `z` with a length of 39 chars, and loops on all its characters using a loop var called `i`.
    for(i in z = '<input onblur=o[id]=value;o`.value` id='){
      
      // In the loop, it declares a variable `y` representing the current cell's name ("A1", "B1", ..., "C8", "D8").
      // The first char ("A", "B", "C" or "D") is picked from the string "ABCD", at the position `i % 5` (i modulo 5).
      // The second char (the digit) is concatenated to the letter with the operaotr `+`. Its value is `-~(i / 5)`.
      // `-~(i / 5)` is the opposite of the one's complement of `i / 5`, floored, which equals `Math.ceil(i / 5)` in our case.
      // It helps us number our cells from 1 to 8, and not from 0 to 7, and ceils the decimal part of `i / 5` in the meantime.
      // The 39-chars-long string `z` generates "only" 4 x 8 cells because after each group of 4 cells, an extra `y` is generated with the value `NaN`.
      // Each input also has an `id` attribute equal to `y`, so we can easily get or set the value of A1 (for example) using `A1.value`.
      y = "ABCD"[i % 5] + -~(i / 5),
      
      // Let's test if the argument `v` is set (as we will see later, when `v` is set, it contains the string ".value")...
      v
      
      // If v is set, the following expression will be evaluated for each cell: `y + (v + o[y]).replace(/[A-Z]\d/g, " +$&" + v)`
      // For example, let's evaluate the cell `A1`. (i.e. `y` equals "A1" and `o["A1"]` contains the cell's raw value):
      //
      // - if the cell is empty, the raw value `o["A1"]` is either "" (if the cell has already been emptied manually) or `undefined` (if it hasn't been visited yet).
      //   So we will evaluate `A1 + (".value" + undefined).replace(/[A-Z]\d/g, " +$&" + ".value")`,
      //   which will do nothing ("A1.valueundefined" fails silently, so the cell keeps its empty value).
      //
      // - if the cell contains a number (ex: 2), the raw value `o["A1"] is 2.
      //   So we will evaluate `A1 + (".value" + 2).replace(/[A-Z]\d/g, " +$&" + ".value")`,
      //   which will do nothing ("A1.value2" fails silently, so the cell keeps its raw value: 2).
      //
      // - if the cell contains a formula (ex: "=B1+2"), the raw value of `o["A1"] is "=B1+2".
      //   So we will evaluate `A1 + (".value" + "=B1+2").replace(/[A-Z]\d/g, " +$&" + ".value")`,
      //   which is the same as "A1.value= +B1.value+2",
      //   which will compute the formula "+B1.value+2" and put the result in the cell A1. (nice: that's how we want the formulae to work).
      //   The regex is used to replace each cell's id (capital letter + digit) found in the formula with the same value plus the string ".value".
      //   The "+" ensures that B1's value is transformed into an integer.
      //   The space ensures that the values of two cells can be added without JS bugs.
      //   ex: "=B1+C1" becomes "= +B1.value+ +C1.value".If we removed the spaces, it wouldn't work anymore.
      //
      // Finally, when `y` is `NaN`,
      // we will evaluate `NaN + (".value" + o[NaN]).replace(/[A-Z]\d/g, " +$&" + ".value")`,
      // which will do nothing ("NaN.valueundefined" fails silently).
      ?
      eval(
        y + (v + o[y]).replace(/[A-Z]\d/g, " +$&" + v)
      )
      
      // If v is not set, we initialize the page, one cell at the time, based on the value of `y`:
      // - if `y` is set (ex: 'A1'), we write `y, z, y, ' onfocus=value=[o[id]]>'`
      //   which, after the concatenation of each part, becomes: "A1<input onblur=o[id]=value;o`.value` id=A1 onfocus=value=[o[id]]>"
      //   as you can see, this string represents the cell's label followed by an input that has the id "A1" and two events:
      //
      //   * onblur=o[id]=value;o`.value` :
      //     as soon as the cell loses focus,
      //     this event will store the raw value of the cell (`this.value`) in `o[this.id]` (the two `this` can be omitted),
      //     then call the function `o` with the argument `.value` (the parenthesis aren't required around template literal arguments)
      //
      //   * onfocus=value=[o[id]]
      //     as soon as the cell is focused,
      //     this event will put the raw value of the cell (`[o[this.id]]`) in `this.value` (to let the user see his original formula).
      //     `o[this.id]` is surrounded by an array (`[]`) to avoid putting the string "undefined" in a focused empty cell.
      //     the array `[undefined]` coerces into an empty string, which is what we want here (keep the empty cells empty).
      //
      // - if `y` is NaN, it will prepend "<p" instead of the label (thanks to the test `y || '<p '`)
      //   This <p> tag will have garbage attributes (one called "<input", another `id="NaN"`, plus onblur and onfocus event listeners,
      //   but none of these attributes will have any use whatsoever. The <p> tag is only used to start a new line.
      :
      document.write(
        y || '<p ', z, y, ' onfocus=value=[o[id]]>'
      )
    }
  }
)()

</script>