Sortable tables in org-mode

Increasingly, when tables are involved, I use Emacs Org mode to do quick-and-dirty generation of HTML files instead of either writing the HTML directly or generating it indirectly from DocBook XML sources.

One thing I wanted to add to the Org-mode-generated HTML tables is the ability for the user to sort the contents of table columns by clicking on the column header.

I found sorttable: Make all your tables sortable and downloaded sorttable.js to my Mac as ~/javascript/sorttable.js.

Here is a table in Org mode without sortability:

#+title: Non-sortable org-mode table
#+style: <link rel="stylesheet" type="text/css" href="file:///Users/wrg/sortable.css" />

| Guy   | Height | Hair  |
|-------+--------+-------|
| Moe   |    68" | Black |
| Larry |    69" | Brown |
| Curly |    66" | N/A   |

Here is the CSS I used to style the HTML:

*
{
  background-color : white;
  font-family      : "Courier", monospace;
}

h1, title
{
  font-weight      : bold;
}

.author, .date, .creator, a[href^='http://validator.w3.org/check?uri=referer']
{
  display          : none;
}

th
{
  background       : darkgray;
  color            : white;
}

The rendered HTML code appears thus in my browser:

 

Here is the raw HTML code that Org mode generated when exporting:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>Non-sortable org-mode table</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<meta name="title" content="Non-sortable org-mode table"/>
<meta name="generator" content="Org-mode"/>
<meta name="generated" content="2012-11-18T14:24-0500"/>
<meta name="author" content="Bill Greene"/>
<meta name="description" content=""/>
<meta name="keywords" content=""/>

<link rel="stylesheet" type="text/css" href="file:///Users/wrg/sortable.css" />
<script type="text/javascript">
...
</script>

</head>
<body>

<div id="preamble">

</div>

<div id="content">
<h1 class="title">Non-sortable org-mode table</h1>

<table border="3" cellspacing="0" cellpadding="6" rules="all" frame="box">
<colgroup><col class="left" /><col class="left" /><col class="left" />
</colgroup>
<thead>
<tr><th scope="col" class="left">Guy</th><th scope="col" class="left">Height</th><th scope="col" class="left">Hair</th></tr>
</thead>
<tbody>
<tr><td class="left">Moe</td><td class="left">68"</td><td class="left">Black</td></tr>
<tr><td class="left">Larry</td><td class="left">69"</td><td class="left">Brown</td></tr>
<tr><td class="left">Curly</td><td class="left">66"</td><td class="left">N/A</td></tr>
</tbody>
</table>

</div>

<div id="postamble">
<p class="date">Date: 2012-11-18T14:24-0500</p>
<p class="author">Author: Bill Greene</p>
<p class="creator"><a href="http://orgmode.org">Org</a> version 7.9.2 with <a href="http://www.gnu.org/software/emacs/">Emacs</a> version 23</p>
<a href="http://validator.w3.org/check?uri=referer">Validate XHTML 1.0</a>

</div>
</body>
</html>

The key insight here is that we need to insert the following <script> statement into the <head> section of the generated HTML.

<script src="file:///Users/wrg/javascript/sorttable.js"></script>

The Org mode #+style keyword provides such a means.  But since that it not its intended use, what follows is definitely a hack.

To add sortability to our Org mode source, we need to add two lines to our .org file:

#+title: Sortable org-mode table
#+style: <script src="file:///Users/wrg/javascript/sorttable.js"></script>
#+style: <link rel="stylesheet" type="text/css" href="file:///Users/wrg/sortable.css" />

#+attr_html: class="sortable"
| Guy   | Height | Hair  |
|-------+--------+-------|
| Moe   |    68" | Black |
| Larry |    69" | Brown |
| Curly |    66" | N/A   |

We also need to add the following to our .css file:

th.left.sorttable_sorted
{
  background-color : darkgray;
  color            : black;
  text-decoration  : none;
}

Now the rendered HTML code looks like this:

 

This solution is not perfect.  The user has no visual cue that the columns are sortable, since the arrows only appear on the headers after they are clicked (as in the above picture).  Also, sorttable.js does not support sorting by multiple columns, as some of the jQuery-based solutions do.  For now, I’m leaving these as exercises for the reader.  After all, a perfect solution would arrive with a future release of Org mode.

New book

"The Man in the Cat-Hair Suit: And Other True Stories"

The Apple iBooks edition of my book, The Man in the Cat-Hair Suit: And Other True Stories, is now available for the iPhone, iPad, and iPod Touch.

The Kindle edition is available from Amazon.com.

A “no-frills EPUB” edition is now available at Barnes & Noble, and should soon be available on Kobo and the Sony Reader Store.

Other editions will be available in the near future.

The Big Rig

My Web site now has a new page entitled “The Big Rig: Accessible computer workstation for an unassisted quadriplegic user”, wherein I describe the computer system I put together for my wife.

Questions, suggestions and comments can be submitted to this blog.

Copyright © 2009 Possum Technologies, All Rights Reserved.

The tyranny of poor tools (1)

I recently had the experience of checking in some changes to a source code file. I had changed just a few lines, but my editor (Emacs) had, as I had instructed it to do, automatically removed the trailing whitespace from each line whenever I executed the “save-buffer” command. Trailing spaces are worse than worthless, so removing them is a Good Thing, right?

Because the “diff” tool in common use where I worked was not configured to ignore differences consisting only of whitespace, each trailing space that Emacs had deleted was highlighted as a source code change demanding attention from the code reviewers. Instead of a few lines appearing as changed, there were dozens.

I found these spurious differences annoying, but thought the right thing for code reviewers to do was to ignore them, since these were not real changes.

Imagine my surprise when I was asked to put the deleted whitespace back!

The reason given was that, in addition to the burden on the code reviewer of having to ignore all these changes-that-are-not-really-changes, the source control software might have trouble handling those “differences”.

Now, the person who made the request is a smart fellow, no bozo. Yet he felt obliged to urge me to spend precious development time in order to better adapt to that broken source control software.

Of course, many organizations developing software have a filter that removes trailing whitespace as part of the check-in. But the relentless pressure to get things done, coupled with the fact that humans are more intelligent and more adaptable than either organizations or software, means that we humans do the adapting, even when the entire point of the organizational procedures or computer software is to help us do our jobs.

Copyright © 2009 Possum Technologies, All Rights Reserved.

Perverse genius

We might well consider a person who is nearly always right in his/her judgment a genius.
What about the person who is nearly always wrong?

If one were to make a series of binary decisions, where one alternative was right and the other was wrong, by flipping a coin, one should tend to be right about 50% of the time.  A person who is wrong in making binary decisions, say, 95% of the time could be valuable as an oracle, provided we always remember to reverse the profferred advice.

Such a person could help us in choosing investments, for example. One perverse genius advised me to buy Food Lion stock, just before ABC News broke the scandal about Food Lion's meat department. I have no reason to think this person had any inside knowledge, just a knack for wrongness.

I have known a few individuals with this sort of instinct for consistently choosing wrong technical approaches to problems. For example, given a file F1 consisting of a 2000-line procedure P that contained a line with the single statement S, instead of changing:

   S;

to

   if Some_New_Condition then
      S;
   else
      T;
   end if;

in procedure P in file F1, the perverse programming genius created a file F2 that was a perfect copy of F1, except that procedure P was renamed to Q and the call to S was replaced by a call to T, then visited the multiple sites whence P was called and replaced the call to P with:

   if Some_New_Condition then
      P;
   else
      Q;
   end if;

My first attempt to make sense of this approach led me to think that this person wanted to please management by increasing his number of lines of written source code, and thus his apparent productivity, but he revealed to me that his true aim was to not alter F1 in any way, thus avoiding breaking any of the code contained in it!

In this instance, the perverse genius was not presented with a single binary choice but with many possible designs. Still, if you were responsible for approving the perverse genius's code changes, you would have a binary choice: adding three lines of code to a single source file versus adding over 2000 lines of code, creating one additional source file and modifying every source file containing a call to P.

Copyright © 2008 Possum Technologies, All Rights Reserved.

Deeply nested statements

Problem: Deeply nested statements (usually “if” statements) tend toward unreadability.

Discussion: It is often best to rule out pathological cases before attempting to perform routine work. For example, one should ensure that a pointer is valid before attempting to dereference it, or that an array index is within the bounds of the array, or that one of many necessary functions has not returned a value indicating failure. Too many levels of nesting tend to obscure source code.

The structured programming dictum that a function must have exactly one entry and one exit point has led some to scrupulously avoid having more than one “return” statement in a function.

If all of the statements from some point onward in a function depend upon some condition and one is avoiding a short-circuiting “return” statement, one will naturally wrap the statements in an “if” test so as not to inappropriately execute statements. Thus the statements whose execution is conditional will inevitably get farther and farther from the test of that condition, impairing the readability of the source code.

    Hallmarks of excessive nesting:

  • There is a “then” part, but no “else”.
  • Every statement is wrapped in an “if” statement that does nothing but confirm that a particular value was or was not returned by the immediately preceding statement.

    Techniques to avoid excessive nesting:

     

  1. Instead of writing this:

    •    if (b1) {
         if (b2) {
            if (b3) {
               ...
               if (bn) {
                  S;
               }
            }
         }
       }
    • Do this:
      if (b1 && b2 && b3 && ... &&& bn) {
         S;
      }
  2. Instead of this:
    rc = f1 (x, y, z);
    if (rc == NO_PROBLEM) {
       rc = f2 (x, y, z);
       if (rc == NO_PROBLEM) {
          rc = f3 (x, y, z);
          if (rc == NO_PROBLEM) {
             ...
             rc = fn (x, y, z);
             if (rc == NO_PROBLEM) {
                S;
             }
          }
       }
    }
             

    Do this:

    rc = f1 (x, y, z);
    if (rc != NO_PROBLEM) {
       return;
    }

    rc = f2 (x, y, z);
    if (rc != NO_PROBLEM) {
       return;
    }

    rc = f3 (x, y, z);
    if (rc == NO_PROBLEM) {
       return;
    }

    ...

    rc = fn (x, y, z);
    if (rc != NO_PROBLEM) {
       return;
    }

    S;

    Better yet, if you are writing in a language like C++ or Ada that supports exceptions, have each of the called functions raise an exception on error instead of returning a value that must be checked by the caller (who may or may not do a necessary check). Then you can simply write the vastly more readable:

    // Each of the following statements may raise an exception:
    f1 (x, y, z);
    f2 (x, y, z);
    f3 (x, y, z);
    ...
    fn (x, y, z);

    S;

  3. Copyright  ©  2008 Possum Technologies, All Rights Reserved.

Legibility of sans serif typefaces

Problem: Illegibility of sans serif typefaces, especially when proportional spacing is used between characters.

Example: I have actually misinterpreted the following because the typeface used lacked serifs:

Insufficiently distinguishable text
Actual text Misreading
clark.net dark.net
modern modem
Ill 111? III?
Double Churn Double Chum
makefile.lvl makefile.M


Objection:
To be fair, legibility problems do not only afflict san-serif type. I have a file named “bsd-style.txt” that I always first misread as “bad-style.txt” when it is displayed in the Courier font.

Copyright © 2008 Possum Technologies, All Rights Reserved.