This is great. I love how unapologetically "good enough" this code is—it does the absolute minimum needed to get the job done, with zero extra ceremony. A great example is the `vote` function:
Hide the up_ and down_ vote buttons, then either erase the innerHTML of the un_ vote div or construct new HTML for it from scratch. To top it all off, let's just inform the server of the vote by creating a new throwaway Image element, because it works everywhere and is less verbose than fetch.
> To top it all off, let's just inform the server of the vote by creating a new throwaway Image element, because it works everywhere and is less verbose than fetch.
I don't think it's less verbose; fetch(vurl(...)) would work perfectly fine, and I think it's clearer what the intent is. fetch is also very widely supported by now. I don't blame anyone for writing it the way it was, back when fetch probably wasn't available, but I'd definitely just use that today.
Do you mean CSRF?
I don't see upvote links for some reason so I can't debug - but does auth contains a CSRF token?
If not you could craft a page which upvote posts for the current visitor (if they're logged in HN)
You could mitigate it with server side checks (or maybe some new browser tech I don't know about?) but I think the synchroniser token pattern is still the current solution.
Needs to be completely redone using Next.js, TypeScript, and React Router for optimal efficiency. Use a React Reducer to reduce the anchor tags into discrete actions and run the code in a webworker off the main thread which maintains the vdom and does real time dom diffs based on semantic type decorators. For communication, I prefer post message and shared array buffers.
I you're gonna make this point, maybe don't use an example where the browser history is frequently busted after commenting. HN would benefit from a bit of SPA logic.
I would have used longer and more descriptive name for the functions to make the code more readable and understandable. Function names like `vis` or `upclass` don't tell much and makes the code much less understandable.
The Paul Graham school of Lisp, and of Arc, is well on display with these small pure functions with cryptic names. He used to say functions that are used often should be shortened as much as possible. Life is too short to write `getElementById` 30 times a day. In Arc (or was it Bel), even `defmacro` got shortened to `mac`.
I might not agree completely, but I see the wisdom in it.
I also agree 90%. However, when you shorten defmacro to mac, you break tooling.
Here is an example. Let's put this into a file called file.lisp:
(defmacro foo ())
(defwidget widg ()) ;; I just made up defwidget: it doesn't exist!
(mac bar ())
Now, let's run Exuberant Tags (ctags):
$ ctags file.lisp
$ cat tags
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
!_TAG_PROGRAM_NAME Exuberant Ctags //
!_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
!_TAG_PROGRAM_VERSION 5.9~svn20110310 //
foo file.lisp /^(defmacro foo ())$/;" f
widg file.lisp /^(defwidget widg ())$/;" f
Hey look, ctags recognized my defwidget invention and index widg as a tag.
Where is Graham's mac?
Always call your custom definers defsomething, and put them in the first column as
(defsomething name* ...*. Then ctags will index you custom defined
constructs.
Also, saving a couple of character in defining a macro is false economy, because a macro should be used at least several times and considerably condense your program and make it more readable.
defmacro is already abbreviated; it could be define-macro which would be bad. defmac is a nice possibility.
That corroborates our suspicion that lisps (and lispers) are "write once, read never" languages.
For most of us less insane folks, we like to be able to read our code.
Interestingly, Paul Graham seems to not have taken this approach towards writing. I mean, if u can omit bnch o chars in wrds & stll gt the meanin acrx, why bthr wrtin ful wrds or sntnces?
This might have made sense 20 years ago, but it kind of assumes you're writing code in Notepad. Literally, I type `g` + `Tab` to get an element. I would spend multitudes more time guessing what functions mean than I would typing.
Since this is here - was anything related to voting changed in the past year or so? Every now and then I pull out an old Windows phone with old-Edge or IE11, and upvoting worked as it should a while ago, but now it does a full page reload (the vote still counts). At a glance, the code looks quite tame and not full of fancy new features.
Yep, but the whole file is parsed. This is not an execution issue, this is a parsing issue. IE and Edge < 12 don't know how to read this JS file because of this syntax. The whole thing.
A good example of progressive enhancement, without useless resource hogging. HN works well without JS, but uses some lightweight JS for more convenience.
For my personal single-html projects, I prefer a straightforward coding style that avoids external dependencies. I implement what I need in a simple, quick, and efficient manner using only native browser APIs, supplemented with a few helpers like those in 'Hn.js' to make the rest of the code shorter.
They have an API now actually too. I’ve been working on a Hacker News cross platform app on the side as an excuse to try out compose multi platform and kotlin on iOS.
Anyone with the time would care to explain how voting is handled on the server? I image that the image which is generated on the client is somehow tied to an endpoint that runs the necessary SQL or something.