<script> // Sheet! // ====== // a sub-256b full-featured JS spreadsheet // ======================================= // This app creates a spreadsheet of 4 x 6 cells supporting: // - any value: text, number or float (ex: `Hi!` or `8` or `1.234`). // - formulae (ex: `=A1+8`), visible when a cell is focused and executed on blur. // - implicit `Math` in formulae (ex: `=sqrt(A1)`). // - updates all the grid in cascade when a cell value changes. // - protection against auto or circular references. // - localStorage persistence. // We start by declaring the function `o`. This function has two uses: // - If it's called with an argument `b`, it will initialize and draw all the spreadsheet in HTML. // - It it's called with no argument, it will evaluate all the cells and update their values. o = b => { // This weird `for` statement is used to make a loop that iterates a lot of times, by using very few characters. // `{} + o` generates a 245-chars string containing the String value of `{}` ("[object Object]"), // followed by all the source code of `o`. ("b=>{for(i in{}+o) ... x-~x?+x:x)}"). // Minified, `for(i in{}+o)` is 5 chars shorter than the equivalent `for(i=0;i<245;i++)`. // `i` is the loop var going from 0 to 245. for(i in {} + o){ // All the following code is executed with `Math` implied. // This allows to call Math functions in formulae, without writing "Math.", // but the Math object (`M`) will also be used to store all the cells values before they're evaluated (especially the formulae). with(M = Math){ // The string `y` represents the current cell's name. // It is built by concatenating the (i%5)'th char of 'ABCD', // and `-~(i/5%6)`, which is equivalent to `1 + Math.floor(((i / 5) % 6))` (this is needed to name the first line "1" instead of "0"). // So in each loop, `y` gets a value from this list: "A1", "B1", "C1", "D1", NaN, "A2", "B2", ..., "C6", "D6", NaN, then it gets back to "A1" , "A2", ... // We then store data in `M[y]`, according to the value of `b`: M[y = 'ABCD'[i % 5] + -~(i / 5 % 6)] = // If `b` is set: b ? // We use the first 30 values of `y` to render the grid in HTML: document.write( ( // If `y` is truthy and lower than 30: // (the limit of 30 ensures that the grid keeps a 24 cells + 6 line breaks) y && i < 30 // write the beginning of an <input> tag, with the attributes "placeholder" and "id" equal to `y`. ? `<input placeholder=${y} id=` + y // if `y` is NaN, write the beginning of a <br> tag instead, to pass on a new line. : '<br' ), // then write the end of the current tag, including two event listeners: // // `onblur=l[id]=value;o()`: // as soon as a cell loses focus, // this event will store the value of the cell (`this.value`) in `l[this.id]` (`l` represents `localStorage`, and the two `this` are implicit), // then it will call the function `o()` with no argument to evaluate all the cells. // // `onfocus=value=[l[id]]`: // as soon as the cell is focused, // this event will put the stored cell value (`l[this.id]`) in `this.value` (so the user can see his original formula). // `l[this.id]` is surrounded by an array (`[]`) to avoid writing "undefined" when we focus an empty cell. // indeed, the array `[undefined]` coerces into an empty string, which is what we want here (keep the empty cells empty on focus). ` onfocus=value=[l[id]] onblur=l[id]=value,o()>` // You may notice that the <br>'s also get an id, a placeholder and two event listeners, but they don't have any use. // It just saves a few chars to use the same ending for both <br> and <input> tags. // The result of `document.write` (i.e. undefined) is stored in `M[y]`, which also has no effect. // It just saves a few chars to use the same assignment `M[y]=` whether `b` is set or not. ) // If `b` is not set: : // the loop is not limited to 30 iterations anymore, so the following code is executed 8 to 9 times for each cell of the spreadsheet. // These passes allow to execute deeply nested formulae and avoid browser-freezing, infinite circular references, // which could occur if we had executed each formula as soon as its cell's value changes. ( // `top[y]` is the input tag with the id `y`. We're updating its content according to the following rules: top[y].value = [ // If the stored value of the cell (called `z`) starts with a "=", then it's a formula. // We use `/^=/.test(z)` here instead of `z[0]=="="` to avoid breaking when `l[y]` is undefined. /^=/.test(z = l[y]) ? // We eval the string `'x'+z`. // Ex: if the stored formula of A1 is "=A2+2", then we eval the string 'x=A2+2". // here, `A2` represents the value of the cell A2 which has been previously stored in `M`. // Indeed, if we consider the surrounding `with(M=Math)`, the string we execute is equivalent to 'x=M.A2+2'. // So, `x` contains the the saved value of `A2`, plus 2. // If A2 contains a number, it'll work instantly. // But if A2 contains a formula (for example "=A3+8", and A3 contains "5"), A1's `x` will not get its value instantly. // For the cell A1, the computed value of `x` will get a garbage value, but for A2, it will get computed fine as 5+8 = 13. // By chance, there are many "eval" passes on the grid's cells, and at the second pass, A1 will be evaluated correctly: // This time, 'x=M.A2+2' will now equal 'x=13+2' and `x` will contain the number 15. eval('x' + z) : // If `z` is not a formula (i.e. number or text), it's put it directly in x, as a string. x = z ], // Now it's time to decide what to store in `M[y]`: // if (x-~x) is not truthy, it means that x is a string representing an integer or a float. // We use this formula instead of "+x" to also support the number "0" (because "0"-~"0" == 1) x - ~x ? // In this case, we store `+x` (similar to `parseFloat(x)`) in `M[y]`. +x // Else, if `x` contains text: : // We store that text in `M[y]`. x ) // When a parenthesis contains many statements like here, all are executed, but only the last one is returned, // That's why `M[y]=(..., x-~x?+x:x)` successfully stores `+x` or `x` in `M[y]` }; } } // Finally, let's declare `l` as a shortcut for `localStorage`, // then call `o()` a first time with `l` as argument. `localStorage` is truthy, so `o` will draw the grid. // Then call `o()` a second time with the return of the first call as argument. // But `o()` returns `undefined`, so this time, all the cells are filled with the values stored in localStorage and all the formulae are evaluated. o( o( l = localStorage ) ) // The End </script>