Add sparkline charts, add more help, and add a chart gallery to help.
diff --git a/edit.html b/edit.html
index 25c1ea1..78ec492 100644
--- a/edit.html
+++ b/edit.html
@@ -4,7 +4,7 @@
   <style>
   iframe {
     width: 95%;
-    height: 450px;
+    min-height: 450px;
     margin-left: auto;
     margin-right: auto;
     margin-bottom: 2em;
@@ -29,7 +29,7 @@
 
 <div id='viewurl'><a href=''>foo</a></div>
 
-<iframe id='result'></iframe>
+<iframe id='result' seamless></iframe>
 
 <script>
 function argsToForm(args) {
diff --git a/help.md b/help.md
index 5f8eaa4..41f1dd1 100644
--- a/help.md
+++ b/help.md
@@ -18,6 +18,21 @@
   margin-right: auto;
   margin-bottom: 2em;
 }
+.gallery iframe {
+  width: 40%;
+  height: 200px;
+  margin: 1em;
+  border: 0;
+}
+.gallery iframe.spark {
+  display: block;
+  width: 100px;
+  height: 40px;
+  margin-top: 0;
+  margin-bottom: 0;
+  margin-left: auto;
+  margin-right: auto;
+}
 u {
   color: red;
 }
@@ -85,114 +100,124 @@
 
 Available transforms:
 
- - <b>&limit=<u>n</u></b>  
-   Discard all data after <u>n</u> rows.
+ -  <b>&limit=<u>n</u></b>  
+    Discard all data after <u>n</u> rows.
 
- - <b>&filter=<u>key</u>>=<u>value1</u>,<u>value2</u>,...</b>  
-   Show only rows where the column named <u>key</u> has a
-   value >= <u>value1</u> or <u>value2</u> etc.  The
-   operator (>= in this example) can be one
-   of =, <, >, <=, >=, <>, or !=.  If you specify more
-   than one value, they
-   are combined in an OR configuration (ie. a row matches
-   if any of the values match).  If you provide
-   more than one &filter= clause, they are combined
-   successively in an AND configuration (ie. a row matches
-   only if all the filters are true).
+ -  <b>&filter=<u>key</u>>=<u>value1</u>,<u>value2</u>,...</b>  
+    Show only rows where the column named <u>key</u> has a
+    value >= <u>value1</u> or <u>value2</u> etc.  The
+    operator (>= in this example) can be one
+    of =, <, >, <=, >=, <>, or !=.  If you specify more
+    than one value, they
+    are combined in an OR configuration (ie. a row matches
+    if any of the values match).  If you provide
+    more than one &filter= clause, they are combined
+    successively in an AND configuration (ie. a row matches
+    only if all the filters are true).
 
- - <b>&q=<u>value1</u>,<u>value2</u>,...</b>  
-   Show only rows where any of the columns contain the
-   substring <u>value1</u> or <u>value2</u> etc.  If more
-   than one value is specified, they are combined in an OR
-   configuration.  If you provide more than one &q= clause,
-   they are combined successively in an AND configuration.
+ -  <b>&q=<u>value1</u>,<u>value2</u>,...</b>  
+    Show only rows where any of the columns contain the
+    substring <u>value1</u> or <u>value2</u> etc.  If more
+    than one value is specified, they are combined in an OR
+    configuration.  If you provide more than one &q= clause,
+    they are combined successively in an AND configuration.
 
- - <b>&order=<u>[-]key1</u>,<u>[-]key2</u></b>  
-   Sort the table in order of <u>key1</u> and then (if key1
-   is the same) by <u>key2</u> etc.  If a key starts with
-   '-' that means to sort that key in descending order
-   (largest to smallest).
+ -  <b>&order=<u>[-]key1</u>,<u>[-]key2</u></b>  
+    Sort the table in order of <u>key1</u> and then (if key1
+    is the same) by <u>key2</u> etc.  If a key starts with
+    '-' that means to sort that key in descending order
+    (largest to smallest).
 
- - <b>&extract_regexp=<u>key</u>=<u>regexp(match)regexp</u></b>  
-   Search for the given regular expression in each row in
-   the column named <u>key</u>.  If it's found, replace the
-   column with the substring in the <u>(match)</u> part of
-   the regexp.  For example,
-   `&extract_regexp=ver=version-(.*)` would replace a
-   string `version-25.4` in column `ver` with the string `25.4`.
+ -  <b>&extract_regexp=<u>key</u>=<u>regexp(match)regexp</u></b>  
+    Search for the given regular expression in each row in
+    the column named <u>key</u>.  If it's found, replace the
+    column with the substring in the <u>(match)</u> part of
+    the regexp.  For example,
+    `&extract_regexp=ver=version-(.*)` would replace a
+    string `version-25.4` in column `ver` with the string `25.4`.
 
- - <b>&group=<u>key1</u>,<u>key2</u>,...;<u>val1</u>,<u>val2</u>,...</b>  
-   Summarize the table data by summing and counting.  This
-   operation works like a simplified version of SQL's
-   "group by" clause.  In the resulting output, the order
-   of the columns will be
-   <u>key1</u>,<u>key2</u>,...,<u>val1</u>,<u>val2</u>... 
-   and there will only be at most one row with any
-   particular combination of <u>key1</u>,<u>key2</u>,... 
-   fields.  The <u>val</u> columns will be summed (if they
-   were already numerical) or counted (if they were
-   non-numeric).
+ -  <b>&group=<u>key1</u>,<u>key2</u>,...;<u>val1</u>,<u>val2</u>,...</b>  
+    Summarize the table data by summing and counting.  This
+    operation works like a simplified version of SQL's
+    "group by" clause.  In the resulting output, the order
+    of the columns will be
+    <u>key1</u>,<u>key2</u>,...,<u>val1</u>,<u>val2</u>... 
+    and there will only be at most one row with any
+    particular combination of <u>key1</u>,<u>key2</u>,... 
+    fields.  The <u>val</u> columns will be summed (if they
+    were already numerical) or counted (if they were
+    non-numeric).
+    
+    A clause like `&group=a,b;x,y` (if x is a string and y
+    is a number) would be equivalent to this in SQL: `select
+    a, b, count(x) x, sum(y) y from MyTable group by a, b`
    
-   A clause like `&group=a,b;x,y` (if x is a string and y
-   is a number) would be equivalent to this in SQL: `select
-   a, b, count(x) x, sum(y) y from MyTable group by a, b`
+    If you apply multiple <b>&group=</b> clauses, it works like
+    using multiple nested subqueries in SQL.  (That is, the
+    output of one <b>&group=</b> clause becomes the MyTable
+    of the next one.)
    
-   If you apply multiple <b>&group=</b> clauses, it works like
-   using multiple nested subqueries in SQL.  (That is, the
-   output of one <b>&group=</b> clause becomes the MyTable
-   of the next one.)
+    If you leave out the ';' and the <u>val</u> column
+    names, the default is to automatically include all
+    columns other than the <u>key</u> columns.
    
-   If you leave out the ';' and the <u>val</u> column
-   names, the default is to automatically include all
-   columns other than the <u>key</u> columns.
-   
-   If you include the ';' but leave out the <u>val</u>
-   column names, that means you don't want any value
-   columns (so only the key fields will be included, and
-   nothing will be summed or counted at that step).  So
-   `&group=a,b;` (with a trailing semicolon) is equivalent
-   to this in SQL: `select a, b from MyTable group by a,
-   b`.
-
- - <b>&pivot=<u>rowkeys...</u>;<u>colkeys...</u>;<u>valkeys...</u></b>  
-   A <b>&pivot=</b> clause works like a <b>&group=</b>
-   clause, but creates a
-   [pivot table](http://en.wikipedia.org/wiki/Pivot_table). 
-   Pivot tables are a bit complicated; the easiest way to
-   learn about them is to play with an example.
-   [Here's one to start
-   with](/?url=example1.json&group=date,flag,ver,serial;&pivot=date,ver;flag;serial).
-   
-   The simplest way to think of a pivot table is like this:
-   the values originally in the columns named by <u>rowkeys</u>
-   end up down the left of the output table.  The values
-   originally in the columns named by <u>colkeys</u> end up
-   as headings across the top of the output table.  The values
-   originally in the columns named by <u>valkeys</u> end up
-   as values in the body section of the output table.  A
-   pivot table is very handy when you have raw data in
-   SQL-like format and you want to rearrange it to be
-   suitable for charting (where each line in a line chart,
-   say, is usually one column of the table).
-   
-   If the <u>rowkeys</u> section is empty, the output will
-   have exactly one row (with all the value fields counted
-   or summed into that one row).  If the <u>colkeys</u>
-   section is empty, the <b>&pivot=</b> operation is
-   essentially equivalent to a
-   <b>&group=<u>rowkeys...</u>;<u>valkeys...</u></b> operation. 
-   If the <u>valkeys</u> section is empty, there are no
-   values used to calculate the table body, so it is
-   equivalent to an <b>&group=<u>rowkeys...</u>;</b>
-   operation.
+    If you include the ';' but leave out the <u>val</u>
+    column names, that means you don't want any value
+    columns (so only the key fields will be included, and
+    nothing will be summed or counted at that step).  So
+    `&group=a,b;` (with a trailing semicolon) is equivalent
+    to this in SQL: `select a, b from MyTable group by a,
+    b`.
  
- - <b>&chart=<u>charttype</u></b>  
-   Instead of showing a table of values, show a chart.  The
-   available <u>charttypes</u> are currently: stacked (a
-   stacked area chart), line, column, bar, pie.
+ -  <b>&treegroup=<u>key1</u>,<u>key2</u>,...;[<u>val1</u>,[<u>val2</u>]]</b>  
+    Like <b>&group=</b>, but produces an output table arranged hierarchically
+    by each <u>key1</u>..<u>keyn</u>, so you can drill down.  There can be
+    zero, one, or two <u>val</u> columns; the first value is the size of
+    each box in the tree view (if omitted, they are all the same size), and
+    the second value is the colour of each box (if omitted, the colour varies
+    with the size).  <b>&treegroup=</b> isn't really useful unless you also
+    use <b>&chart=tree</b>.
+
+ -  <b>&pivot=<u>rowkeys...</u>;<u>colkeys...</u>;<u>valkeys...</u></b>  
+    A <b>&pivot=</b> clause works like a <b>&group=</b>
+    clause, but creates a
+    [pivot table](http://en.wikipedia.org/wiki/Pivot_table). 
+    Pivot tables are a bit complicated; the easiest way to
+    learn about them is to play with an example.
+    [Here's one to start
+    with](/?url=example1.json&group=date,flag,ver,serial;&pivot=date,ver;flag;serial).
+   
+    The simplest way to think of a pivot table is like this:
+    the values originally in the columns named by <u>rowkeys</u>
+    end up down the left of the output table.  The values
+    originally in the columns named by <u>colkeys</u> end up
+    as headings across the top of the output table.  The values
+    originally in the columns named by <u>valkeys</u> end up
+    as values in the body section of the output table.  A
+    pivot table is very handy when you have raw data in
+    SQL-like format and you want to rearrange it to be
+    suitable for charting (where each line in a line chart,
+    say, is usually one column of the table).
+   
+    If the <u>rowkeys</u> section is empty, the output will
+    have exactly one row (with all the value fields counted
+    or summed into that one row).  If the <u>colkeys</u>
+    section is empty, the <b>&pivot=</b> operation is
+    essentially equivalent to a
+    <b>&group=<u>rowkeys...</u>;<u>valkeys...</u></b> operation. 
+    If the <u>valkeys</u> section is empty, there are no
+    values used to calculate the table body, so it is
+    equivalent to an <b>&group=<u>rowkeys...</u>;</b>
+    operation.
  
- - <b>&title=<u>title</u></b>  
-   Add a title to the chart.
+ -  <b>&chart=<u>charttype</u></b>  
+    Instead of showing a table of values, show a chart.  The
+    available <u>charttypes</u> are currently: stacked (a
+    stacked area chart), line, spark, column, bar, pie,
+    tree (see <b>treegroup</b>), candle, timeline, dygraph, dygraph+errors.
+ 
+ -  <b>&title=<u>title</u></b>  
+    Add a title to the chart.
 
 
 ## Example 1
@@ -203,44 +228,41 @@
 <iframe src="example2.json">
 </iframe>
 
-Afterquery can render it as a table like this
-[(link)](/?url=example2.json):
+(Tip: in the tables and charts below, click the "Edit" link in the
+upper-right corner to see how the query works.)
+
+Afterquery can render it as a table like this:
 
 <iframe src="/?url=example2.json">
 </iframe>
 
-Or pre-sort the table for you like this
-[(link)](/?url=example2.json&order=state,-date):
+Or pre-sort the table for you like this:
 
 <iframe src="/?url=example2.json&order=state,-date">
 </iframe>
 
-Or filter it by date [(link)](/?url=example2.json&filter=date<2012-11-10&filter=date>=2012-11-01):
+Or filter it by date:
 
 <iframe src="/?url=example2.json&filter=date<2012-11-10&filter=date>=2012-11-01">
 </iframe>
 
-Or summarize the results (like a "group by" in SQL)
-[(link)](/?url=example2.json&group=date):
+Or summarize the results (like a "group by" in SQL):
 
 <iframe src="/?url=example2.json&group=date">
 </iframe>
 
-Or summarize and display only a subset of columns
-[(link)](/?url=example2.json&group=date;NumDevices):
+Or summarize and display only a subset of columns:
 
 <iframe src="/?url=example2.json&group=date;NumDevices">
 </iframe>
 
 Or do a [pivot table](http://en.wikipedia.org/wiki/Pivot_table)
-(converting rows into columns)
-[(link)](/?url=example2.json&pivot=date;state;NumDevices):
+(converting rows into columns):
 
 <iframe src="/?url=example2.json&pivot=date;state;NumDevices">
 </iframe>
 
 Or filter, then pivot, and then make a chart!
-[(link)](/?url=example2.json&order=date,state&filter=date>=2012-11-01&filter=date<2012-11-14&pivot=date;state;NumDevices&chart=stacked&title=Devices Rebooted/Upgraded by Date)
 
 <iframe
 src="/?url=example2.json&order=date,state&filter=date>=2012-11-01&filter=date<2012-11-14&pivot=date;state;NumDevices&chart=stacked&title=Devices Rebooted/Upgraded by Date">
@@ -249,7 +271,7 @@
 
 ## Example 2
 
-Here's another dataset [(link)](/?url=example1.json):
+Here's another dataset:
 
 <iframe src="/?url=example1.json">
 </iframe>
@@ -263,45 +285,63 @@
 
 Hmm, those version numbers are ugly because some of them
 have extra debug information after them.  Let's trim it
-out using a regex <a href="/?url=/example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=ver,serial;&group=ver;serial&order=ver">(link)</a>:
+out using a regex:
 
 <iframe
 src="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=ver,serial;&group=ver;serial&order=ver">
 </iframe>
 
 And make a pivot table to easily show the pattern over
-time <a href="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&pivot=date;ver;serial">(link)</a>:
+time:
 
 <iframe
 src="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&pivot=date;ver;serial">
 </iframe>
 
-Trim out some outliers <a href="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&group=date,ver;serial&filter=serial&gt;10&filter=ver&gt;&pivot=date;ver;serial">(link)</a>:
+Trim out some outliers:
 
 <iframe
 src="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&group=date,ver;serial&filter=serial&gt;10&filter=ver&gt;&pivot=date;ver;serial">
 </iframe>
 
-And graph it <a href="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&group=date,ver;serial&filter=serial&gt;10&filter=ver&gt;&pivot=date;ver;serial&chart=stacked">(link)</a>:
+And graph it:
 
 <iframe
 src="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&group=date,ver;serial&filter=serial&gt;10&filter=ver&gt;&pivot=date;ver;serial&chart=stacked">
 </iframe>
 
-Or graph a subset of the data <a href="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&q=v35,v37,v36&pivot=date;ver;serial&chart=line">(link)</a>:
+Or graph a subset of the data:
 
 <iframe
 src="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&q=v35,v37,v36&pivot=date;ver;serial&chart=line">
 </iframe>
 
-Or maybe show the top 4 versions <a
-href="/?url=example1.json&group=ver,serial;&filter=ver&gt;&order=ver&group=ver;serial&order=-serial&limit=4&chart=pie">(link)</a>:
+Or maybe show the top 4 versions:
 
 <iframe
 src="/?url=example1.json&group=ver,serial;&filter=ver&gt;&order=ver&group=ver;serial&order=-serial&limit=4&chart=pie">
 </iframe>
 
 
+## Chart gallery
+
+<div class="gallery">
+<iframe src="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&group=date,ver;serial&filter=serial&gt;10&filter=ver&gt;&pivot=date;ver;serial&chart=stacked"></iframe>
+<iframe src="/?url=example1.json&extract_regexp=ver=(v)ersion-([^-.]*)&group=date,ver,serial;&order=date,ver&group=date,ver;serial&filter=serial&gt;10&filter=ver&gt;&pivot=date;ver;serial&chart=line"></iframe>
+<iframe src="/?url=example1.json&group=ver,serial;&filter=ver&gt;&order=ver&group=ver;serial&order=-serial&limit=4&chart=column"></iframe>
+<iframe src="/?url=example1.json&group=ver,serial;&filter=ver&gt;&order=ver&group=ver;serial&order=-serial&limit=4&chart=bar"></iframe>
+<iframe src="/?url=example1.json&group=ver,serial;&filter=ver&gt;&order=ver&group=ver;serial&order=-serial&limit=4&chart=pie"></iframe>
+<iframe src="/?url=example1.json&group=flag,ver,serial;&treegroup=flag,ver;serial&chart=tree"></iframe>
+<iframe src="/?url=example2.json&pivot=date;state;NumDevices&group=date,upgraded,rebooted,rebooted,idle&chart=candle"></iframe>
+<iframe src="/?url=example2.json&pivot=date;state;NumDevices&chart=timeline"></iframe>
+<iframe src="/?url=example2.json&pivot=date;state;NumDevices&chart=dygraph"></iframe>
+<iframe src="/?url=example2.json&pivot=date;state;NumDevices&group=date,idle,rebooted,rebooted,upgraded&chart=dygraph+errors"></iframe>
+<iframe class='spark' src="/?url=example1.json&filter=ver=oldversion-1&group=date,ver,serial;&pivot=date;ver;serial&chart=spark"></iframe>
+<iframe class='spark' src="/?url=example1.json&filter=ver=oldversion-17&group=date,ver,serial;&pivot=date;ver;serial&chart=spark"></iframe>
+<iframe class='spark' src="/?url=example1.json&filter=ver=oldversion-19&group=date,ver,serial;&pivot=date;ver;serial&chart=spark"></iframe>
+</div>
+
+
 ## Where can I get a jsonp data source?
 
 There are lots of them out on the web.  If your favourite
diff --git a/render.html b/render.html
index 6f63823..669ae78 100644
--- a/render.html
+++ b/render.html
@@ -10,6 +10,10 @@
     right: 0;
     z-index: 5;
     background: #eee;
+    visibility: hidden;
+  }
+  body:hover div#editmenu {
+    visibility: visible;
   }
   div#editmenu a {
     text-decoration: none;
diff --git a/render.js b/render.js
index 39a13a4..196f3c0 100644
--- a/render.js
+++ b/render.js
@@ -703,7 +703,7 @@
 
 function fillNullsWithZero(grid) {
   for (var rowi in grid.data) {
-    row = grid.data[rowi];
+    var row = grid.data[rowi];
     for (var coli in row) {
       if (grid.types[coli] === T_NUM && row[coli] == undefined) {
 	row[coli] = 0;
@@ -832,9 +832,10 @@
   
   enqueue('gentable', function() {
     if (chartops) {
-      //TODO(apenwarr): something needed this, but I no longer remember what.
-      //  At least line and dygraph charts are seemingly fine without it.
-      //grid = fillNullsWithZero(grid);
+      if (chartops == 'stacked' || chartops == 'stackedarea') {
+	// Some charts react badly to missing values, so fill them in.
+	grid = fillNullsWithZero(grid);
+      }
       var el = document.getElementById('vizchart');
       $(el).height(window.innerHeight)
 	  .width(trace ? window.innerWidth - 40 : window.innerWidth);
@@ -850,6 +851,23 @@
 	t = new google.visualization.BarChart(el);
       } else if (chartops == 'line') {
 	t = new google.visualization.LineChart(el);
+      } else if (chartops == 'spark') {
+	// sparkline chart: get rid of everything but the data series.
+	// Looks best when small.
+	options.hAxis = {};
+	options.hAxis.baselineColor = 'none';
+	options.hAxis.textPosition = 'none';
+	options.hAxis.gridlines = {};
+	options.hAxis.gridlines.color = 'none';
+	options.vAxis = {};
+	options.vAxis.baselineColor = 'none';
+	options.vAxis.textPosition = 'none';
+	options.vAxis.gridlines = {};
+	options.vAxis.gridlines.color = 'none';
+	options.theme = 'maximized';
+	options.legend = {};
+	options.legend.position = 'none';
+	t = new google.visualization.LineChart(el);
       } else if (chartops == 'pie') {
 	t = new google.visualization.PieChart(el);
       } else if (chartops == 'tree') {
@@ -939,6 +957,9 @@
     try {
       return func.apply(null, pre_args.concat([].slice.call(arguments)));
     } catch (e) {
+      $('#vizchart').hide();
+      $('#viztable').hide();
+      $('.vizstep').show();
       err(e);
       err("<p><a href='/help'>here's the documentation</a>");
       throw e;