-
Notifications
You must be signed in to change notification settings - Fork 31
/
Copy pathmini-commented.html
85 lines (77 loc) · 5.58 KB
/
mini-commented.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
<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>