Show commit details

Now let’s add an ability to show a specific commit.

In HTML, we add a single `div` for this mode, and inside it a heading
for the commit message summary, a `pre` for the body, and a `section`
for all the diffs.

We style the `commits` list and the `single-commit` div to be visible
only depending on the class of the root `body` element, which we toggle
in specific command handlers.

The `showCommit` function takes a `sha` argument, fetches the commit
details and populates the `single-commit` section.

For now, we are treating everything as plain text.
Render markdown

GitHub has a Markdown API but it suffers from a fundamental flaw: It
takes a single Markdown string, returns an HTML string, and is
Rate-limited. This has two consequences:

* We would make a lot of requests, which would be fulfilled
asynchronously and cause much redisplay.
* The default rate limit for unauthenticated requests is 60 requests per
hour. By browsing the list of commits of a mature repository, we can
exhaust that instantly.

So let’s do markdown rendering on the client, using the [marked][]
library. It is distributed under the MIT license.

[marked]: https://github.com/chjj/marked
Fix commit list link formatting

Originally, I just wrapped an `<a href="…">` element around the commit
summary. This was all good when the commit summary was just plain
text. Won’t do when it’s rendered markdown, because <a> is an inline
element while markdown typically produces a block element at the top
level.

While it’s possible to style `<a>` as a block element, it makes
for bad user experience: the link takes all horizontal space, so
careless clicks take the user to the linked page.

So we need a function that will take a DOM node and somehow turn “it”
into a link. How would it work?

* The function is initially invoked on the `<li>` element that wraps
  the whole markdown content.
* We want to wrap as long spans of child nodes as possible.
* We never wrap a block element.
* Neither do we wrap another hyperlink.

So:

* Start with the first child of the context node.
* If it is a _restricted_ element (either a hyperlink or a block
  element), or if it contains a restricted element directly or
  indirectly, then stop before it. Wrap the collected span of child
  nodes in a link. (Notice how `nextSibling` of each child must be taken
  before reparenting it.)
* For a block element or an inline element containing a link, apply
  recursively.
* An inline element containing a block element is technically a
  violation of HTML, but apply recursively anyway.
* For a link, just skip it.
* Continue until there are no more children.
Format diffs nicely

We’d like to color our diffs. Fortunately, the unified diff format is
easy to parse.

* Split the diff text into lines.
* A line that starts with `@@` is a hunk header. Wrap with `<heading>`.
* Lines that start with a space are context lines. Find the maximum
  sequence and emit it as a single text node.
* Lines that start with `-` or `+` are deleted and added lines. Find
  the maximum sequence and wrap it in an `<del>` or `<ins>`,
  respectively.

Notice also that a hunk header is always followed by a different kind
of line. To simplify code, we can apply the maximum sequence algorithm
to them, too.
Show commit author and date

The commit data contains these, just put them after the commit summary.
Reverse the commit order

GitHub API returns commits in blog order (most recent first). In a
Literate setting, I feel it is more natural to present them in book
order (most recent last).
Accept repository name in query string

Looking at just one repository is boring. Let’s take the owner,
repository and optional commit hash from the query string, in the
following format:

* [`?` [_owner_ `/` _repo_ [`/commits/` [_id_]]]]

  List the commits starting from _id_ or `master` if unspecified. If
  _owner_ and _repo_ are omitted, default to this repository.

* `?` _owner_ `/` _repo_ `/commit/` [_id_]

  Show a single commit _id_ or the last commit on `master`.
Fix display of short commit messages

Now that we can see other repositories, we stumble on a bug. When a
commit message consists of only the summary line, or is empty
altogether, `marked` breaks down trying to render the empty string.

So we go over the script and whenever input to `toHTML` might be an
empty string, add a sensible default.

Mostly, the sensible default is an empty string as well, except in the
commit list: we need something to click on, so we put the commit ID
there. (By default, `git` does not accept empty commit messages, so this
should be a rare occasion.)
Add pagination

Up to now, the commit list only shows the last 30 commits. This might be
enough for a small repository like this one, but not for real
repositories.

When there are more commits in the selected branch, GitHub sends a
`Link:` header in the response. We’re supposed to use it to fetch more
commits.

* We add two static items at the beginning and end of the commit list
  and style them to be hidden at first.
* We add an optional `/pages/` _page_ part to our query string. If
  specified, we add the `&page=` _page_ parameter to the API query.
* If the API responds with a `Link:`, parse out the actual link URLs and
  the page numbers from them; set suitable `href`s on the `next` and
  `prev` links so that the user can bookmark those or open in another tab.
* At the same time, hook an `onclick` function on that same link, which
  will fetch the next or previous page without reloading. Note that we
  have to pass a reference position where the newly fetched commits need
  to be inserted. We pass the element _after_ which new items go, since
  we want them to end up in reverse direction.
Add code block syntax highlighting

GitHub-Flavored Markdown has a nifty feature where you can specify a
language on a code block and it will be syntax-highlighted accordingly:

```c
#include <iostream>

int main(int argc, char* argv[])
{
	std::cout << "Hello World\n";
}
```

We’d like to have this feature as well. Fortunately, there’s quite a few
javascript-based syntax highlighter libraries around. I, for one, like
[highlight.js][] by Ivan Sagalaev.

[highlight.js]: http://highlightjs.org/

It’s easy to integrate, it includes a lot of language definitions and a
GitHub color theme.