block by jmuyskens 27d0440a5d783007804cb6d11b74b58a

27d0440a5d783007804c

Full Screen

DESCRIPTION

Data from the Trump Twitter Archive: https://github.com/bpb27/political_twitter_archive

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Mister Nester</title>
    <link rel="stylesheet" href="github.css">
    <link rel="stylesheet" href="converter.css">
  </head>
  <body>
    <div class="header">
      <h1>Hello, I’m Mister Nester</h1>
      <p class="description">I’m a simple console for learning and experimenting with <a href="//d3js.org">d3.js</a> data <a href="https://github.com/mbostock/d3/wiki/Arrays#wiki-d3_nest">nesting</a>.</p>
      <p class="description">Forked by John Muyskens from <a href="//bl.ocks.org/shancarter/raw/4748131/">the original</a> by Shan Carter for the NICAR 2018 intermediate d3 workshop.</p>
      <p class="description">
        Data by David Eads for ProPublica Illinois for the story <a href="https://www.propublica.org/article/gun-trafficking-charges-illinois">“Why (Almost) No One Is Charged With Gun Trafficking in Illinois”</a> published Oct. 13, 2017 <a target="_blank" href="https://github.com/propublica/northern-il-federal-gun-cases/">(link to analysis repository on GitHub)</a>.</p> <p>Documentation links: <a target="_blank" href="https://github.com/d3/d3-collection/blob/master/README.md#nests">d3.nest</a>, <a target="_blank" href="https://github.com/d3/d3-array/blob/master/README.md#statistics">statistics</a>.</p> <p>Uses the <a href="https://github.com/caldwell/renderjson">renderjson library</a> by David Caldwell.</p>
      </p>
    </div>
    <div id="converter"></div>
    <div class="details">
      <p></p>
    </div>
      <script src="//d3js.org/d3.v4.min.js"></script>
      <script src="highlight.js"></script>
      <script src="renderjson.js"></script>
      <script src="main.js"></script>

  </body>
</html>

converter.css

html {
  font-family: "Helvetica", Arial;
}

body {
  margin: 10px 20px;
  background: #f0f0f0;
  font-family: "Helvetica";
  color: #666;
  font-weight: 200;
}

a {
  color: #004276;
}

p {
  font-size: 15px;
  line-height: 1.5em;
  margin: 0 0 10px;
}

h1 {
  font-family: "ratio", sans-serif;
  font-size: 60px;
  line-height: 1em;
  font-weight: 200;
  margin-bottom: 8px;
}

button {
  background-color: #004276;
  color: white;
  border: none;
  border-radius: 2px;
  padding: 4px 8px;
  font-size: 12px;
}

/* Header */

.header {
  width: 840px;
  margin: 0 auto;
  text-align: center;
}

.description {
  font-size: 15px;
}

/* Converter */

#converter {
  position: relative;
  width: 100%;
  border-top: solid 1px #aaa;
  border-bottom: solid 1px #aaa;
  margin: 30px 0;
  padding: 0 0 20px;
  overflow: hidden;
}

.left {
  position: relative;
  float: left;
  width: 49%;
  /*border-right: solid 1px #ccc;*/
}

.right {
  position: relative;
  float: right;
  width: 49%;
  /*border-left: solid 1px #ccc;*/
}

#converter textarea, #converter pre {
  border: solid 1px #ddd;
  border-radius: 4px;
  background: #fff;
  width: 100%;
  height: 400px;
  margin: 0;
  padding: 0px;
  font-size: 12px;
  overflow-y: scroll;
  font-family: monospace;
}

#converter .input textarea {
  height: 185px;
}

#converter .code textarea {
  height: 185px;
}

#converter .output pre {
  height: 429px;
}

.snippets {
  position: absolute;
  right: 0;
}

.snippet {
  font-size: 12px;
  float: left;
  margin-left: 10px;
}

.output button {
  display: block;
  margin: 17px 0
/*  position: absolute;
  right: 0;
  top: 18px;*/
}

.controls {
  width: 100%;
  height: 40px;
}

#converter h3 {
  font-family: "Helvetica";
  color: #666;
  font-weight: 200;
  font-size: 16px;
  line-height: 1em;
  margin: 20px 0;
}

.output .controls select {
  width: 150px;
}

.output .controls h3 {
}

/* Details */

.details {
  width: 720px;
  margin: 0 auto 20px;
}

github.css

/*

github.com style (c) Vasily Polovnyov <vast@whiteants.net>

*/

pre code {
  display: block; padding: 0.5em;
  color: #333;
  background: #f8f8ff
}

pre .comment,
pre .template_comment,
pre .diff .header,
pre .javadoc {
  color: #998;
  font-style: italic
}

pre .keyword,
pre .css .rule .keyword,
pre .winutils,
pre .javascript .title,
pre .nginx .title,
pre .subst,
pre .request,
pre .status {
  color: #333;
  font-weight: bold
}

pre .number,
pre .hexcolor,
pre .ruby .constant {
  color: #099;
}

pre .string,
pre .tag .value,
pre .phpdoc,
pre .tex .formula {
  color: #d14
}

pre .title,
pre .id {
  color: #900;
  font-weight: bold
}

pre .javascript .title,
pre .lisp .title,
pre .clojure .title,
pre .subst {
  font-weight: normal
}

pre .class .title,
pre .haskell .type,
pre .vhdl .literal,
pre .tex .command {
  color: #458;
  font-weight: bold
}

pre .tag,
pre .tag .title,
pre .rules .property,
pre .django .tag .keyword {
  color: #000080;
  font-weight: normal
}

pre .attribute,
pre .variable,
pre .lisp .body {
  color: #008080
}

pre .regexp {
  color: #009926
}

pre .class {
  color: #458;
  font-weight: bold
}

pre .symbol,
pre .ruby .symbol .string,
pre .lisp .keyword,
pre .tex .special,
pre .prompt {
  color: #990073
}

pre .built_in,
pre .lisp .title,
pre .clojure .built_in {
  color: #0086b3
}

pre .preprocessor,
pre .pi,
pre .doctype,
pre .shebang,
pre .cdata {
  color: #999;
  font-weight: bold
}

pre .deletion {
  background: #fdd
}

pre .addition {
  background: #dfd
}

pre .diff .change {
  background: #0086b3
}

pre .chunk {
  color: #aaa
}

highlight.js

var hljs=new function(){function l(o){return o.replace(/&/gm,"&amp;").replace(/</gm,"&lt;").replace(/>/gm,"&gt;")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o<p.length;o++){if(e[p[o]]||p[o]=="no-highlight"){return p[o]}}}function c(q){var o=[];(function p(r,s){for(var t=r.firstChild;t;t=t.nextSibling){if(t.nodeType==3){s+=t.nodeValue.length}else{if(t.nodeName=="BR"){s+=1}else{if(t.nodeType==1){o.push({event:"start",offset:s,node:t});s=p(t,s);o.push({event:"stop",offset:s,node:t})}}}}return s})(q,0);return o}function j(x,v,w){var p=0;var y="";var r=[];function t(){if(x.length&&v.length){if(x[0].offset!=v[0].offset){return(x[0].offset<v[0].offset)?x:v}else{return v[0].event=="start"?x:v}}else{return x.length?x:v}}function s(A){function z(B){return" "+B.nodeName+'="'+l(B.value)+'"'}return"<"+A.nodeName+Array.prototype.map.call(A.attributes,z).join("")+">"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("</"+o.nodeName.toLowerCase()+">")}while(o!=u.node);r.splice(q,1);while(q<r.length){y+=s(r[q]);q++}}}}return y+l(w.substr(p))}function f(q){function o(s,r){return RegExp(s,"m"+(q.cI?"i":"")+(r?"g":""))}function p(y,w){if(y.compiled){return}y.compiled=true;var s=[];if(y.k){var r={};function z(A,t){t.split(" ").forEach(function(B){var C=B.split("|");r[C[0]]=[A,C[1]?Number(C[1]):1];s.push(C[0])})}y.lR=o(y.l||hljs.IR,true);if(typeof y.k=="string"){z("keyword",y.k)}else{for(var x in y.k){if(!y.k.hasOwnProperty(x)){continue}z(x,y.k[x])}}y.k=r}if(w){if(y.bWK){y.b="\\b("+s.join("|")+")\\s"}y.bR=o(y.b?y.b:"\\B|\\b");if(!y.e&&!y.eW){y.e="\\B|\\b"}if(y.e){y.eR=o(y.e)}y.tE=y.e||"";if(y.eW&&w.tE){y.tE+=(y.e?"|":"")+w.tE}}if(y.i){y.iR=o(y.i)}if(y.r===undefined){y.r=1}if(!y.c){y.c=[]}for(var v=0;v<y.c.length;v++){if(y.c[v]=="self"){y.c[v]=y}p(y.c[v],y)}if(y.starts){p(y.starts,w)}var u=[];for(var v=0;v<y.c.length;v++){u.push(y.c[v].b)}if(y.tE){u.push(y.tE)}if(y.i){u.push(y.i)}y.t=u.length?o(u.join("|"),true):{exec:function(t){return null}}}p(q)}function d(D,E){function o(r,M){for(var L=0;L<M.c.length;L++){var K=M.c[L].bR.exec(r);if(K&&K.index==0){return M.c[L]}}}function s(K,r){if(K.e&&K.eR.test(r)){return K}if(K.eW){return s(K.parent,r)}}function t(r,K){return K.i&&K.iR.test(r)}function y(L,r){var K=F.cI?r[0].toLowerCase():r[0];return L.k.hasOwnProperty(K)&&L.k[K]}function G(){var K=l(w);if(!A.k){return K}var r="";var N=0;A.lR.lastIndex=0;var L=A.lR.exec(K);while(L){r+=K.substr(N,L.index-N);var M=y(A,L);if(M){v+=M[1];r+='<span class="'+M[0]+'">'+L[0]+"</span>"}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return'<span class="'+r.language+'">'+r.value+"</span>"}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'<span class="'+L.cN+'">':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+="</span>"}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"<br>")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.javascript=function(a){return{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",i:"\\n",c:[{b:"\\\\/"}]},{b:"<",e:">;",sL:"xml"}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}(hljs);hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"<!DOCTYPE",e:">",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"<!--",e:"-->",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"<style(?=\\s|>|$)",e:">",k:{title:"style"},c:[b],starts:{e:"</style>",rE:true,sL:"css"}},{cN:"tag",b:"<script(?=\\s|>|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"</?",e:"/?>",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs);

main.js

var codeSamples = [
// Default
{ label: "Identity", code: "d3.nest()\n  .map(data);"},
{ label: "By charge", code: "d3.nest()\n  .key(function(d) { return d.charges; })\n  .map(data);"},
{ label: "Cases by charge", code: "d3.nest()\n  .key(function(d) { return d.charges; })\n  .rollup(function(d) { return d.length; })\n  .map(data);"},
{ label: "Cases by year", code: "var parseTime = d3.timeParse('%d/%m/%Y');\n\nd3.nest()\n  .key(function(d) { return parseTime(d.cs_date_filed).getFullYear(); })\n  .rollup(function(d) { return d.length; })\n  .object(data);"},
{ label: "Cases by year and charge", code: "var parseTime = d3.timeParse('%d/%m/%Y');\n\nd3.nest()\n  .key(function(d) { return parseTime(d.cs_date_filed).getFullYear(); })\n  .key(function(d) { return d.charges; })\n  .rollup(function(d) { return d.length; })\n  .object(data);"},
{ label: "Cases by year (entries)", code: "var parseTime = d3.timeParse('%d/%m/%Y');\n\nd3.nest()\n  .key(function(d) { return parseTime(d.cs_date_filed).getFullYear(); })\n  .rollup(function(d) { return d.length; })\n  .entries(data)\n  .sort(function(a, b) { return a.key - b.key; });"},
];

var html = d3.select("#converter");

var left = html.append("div")
    .classed("left", true);

var input = left.append("div")
    .classed("input", true);

input.append("h3")
    .text("Input CSV or TSV");

input.append("textarea");

var code = left.append("div")
    .classed("code", true);

var snippets = code.append("div")
    .classed("snippets", true);

snippets.selectAll("snippet")
    .data(codeSamples)
  .enter().append("a")
    .classed("snippet", true)
    .text(function(d) { return d.label; })
    .attr("href", "#")
    .on("click", function(d) {
      d3.event.preventDefault();
      code.select("textarea")
          .property("value", d.code);
      update();
    });

code.append("h3")
    .text("Code");

code.append("textarea");

var right = html.append("div")
    .classed("right", true);

var output = right.append("div")
    .classed("output", true);

output.append("button")
    .text("Update Output")
    .on("click", update);

var codeOutput = output.append("pre");

codeOutput.append("code");

d3.text("https://raw.githubusercontent.com/propublica/northern-il-federal-gun-cases/master/processed/federal-gun-cases.csv", function(d) {

    input.select("textarea")
        .property("value", d);

    code.select("textarea")
        .property("value", codeSamples[0].code);

    update();
});

function update() {
  var t = input.select("textarea")
      .property("value");

  var c = code.select("textarea")
      .property("value");

  var data = (t.indexOf("\t") < 0 ? d3.csvParse : d3.tsvParse)(t);

  var out = eval(c);
  renderjson.set_show_to_level(1);
  if (codeOutput.node().hasChildNodes()) {
    codeOutput.node().removeChild(codeOutput.node().childNodes[0]);
  }
  codeOutput.node().append(renderjson(out));

  //hljs.highlightBlock(codeOutput.node());
}

renderjson.js

// Copyright © 2013-2014 David Caldwell <david@porkrind.org>
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

// Usage
// -----
// The module exports one entry point, the `renderjson()` function. It takes in
// the JSON you want to render as a single argument and returns an HTML
// element.
//
// Options
// -------
// renderjson.set_icons("+", "-")
//   This Allows you to override the disclosure icons.
//
// renderjson.set_show_to_level(level)
//   Pass the number of levels to expand when rendering. The default is 0, which
//   starts with everything collapsed. As a special case, if level is the string
//   "all" then it will start with everything expanded.
//
// renderjson.set_max_string_length(length)
//   Strings will be truncated and made expandable if they are longer than
//   `length`. As a special case, if `length` is the string "none" then
//   there will be no truncation. The default is "none".
//
// renderjson.set_sort_objects(sort_bool)
//   Sort objects by key (default: false)
//
// Theming
// -------
// The HTML output uses a number of classes so that you can theme it the way
// you'd like:
//     .disclosure    ("⊕", "⊖")
//     .syntax        (",", ":", "{", "}", "[", "]")
//     .string        (includes quotes)
//     .number
//     .boolean
//     .key           (object key)
//     .keyword       ("null", "undefined")
//     .object.syntax ("{", "}")
//     .array.syntax  ("[", "]")

var module;
(module||{}).exports = renderjson = (function() {
    var themetext = function(/* [class, text]+ */) {
        var spans = [];
        while (arguments.length)
            spans.push(append(span(Array.prototype.shift.call(arguments)),
                              text(Array.prototype.shift.call(arguments))));
        return spans;
    };
    var append = function(/* el, ... */) {
        var el = Array.prototype.shift.call(arguments);
        for (var a=0; a<arguments.length; a++)
            if (arguments[a].constructor == Array)
                append.apply(this, [el].concat(arguments[a]));
            else
                el.appendChild(arguments[a]);
        return el;
    };
    var prepend = function(el, child) {
        el.insertBefore(child, el.firstChild);
        return el;
    }
    var isempty = function(obj) { for (var k in obj) if (obj.hasOwnProperty(k)) return false;
                                  return true; }
    var text = function(txt) { return document.createTextNode(txt) };
    var div = function() { return document.createElement("div") };
    var span = function(classname) { var s = document.createElement("span");
                                     if (classname) s.className = classname;
                                     return s; };
    var A = function A(txt, classname, callback) { var a = document.createElement("a");
                                                   if (classname) a.className = classname;
                                                   a.appendChild(text(txt));
                                                   a.href = '#';
                                                   a.onclick = function() { callback(); return false; };
                                                   return a; };

    function _renderjson(json, indent, dont_indent, show_level, max_string, sort_objects) {
        var my_indent = dont_indent ? "" : indent;

        var disclosure = function(open, placeholder, close, type, builder) {
            var content;
            var empty = span(type);
            var show = function() { if (!content) append(empty.parentNode,
                                                         content = prepend(builder(),
                                                                           A(renderjson.hide, "disclosure",
                                                                             function() { content.style.display="none";
                                                                                          empty.style.display="inline"; } )));
                                    content.style.display="inline";
                                    empty.style.display="none"; };
            append(empty,
                   A(renderjson.show, "disclosure", show),
                   themetext(type+ " syntax", open),
                   A(placeholder, null, show),
                   themetext(type+ " syntax", close));

            var el = append(span(), text(my_indent.slice(0,-1)), empty);
            if (show_level > 0)
                show();
            return el;
        };

        if (json === null) return themetext(null, my_indent, "keyword", "null");
        if (json === void 0) return themetext(null, my_indent, "keyword", "undefined");

        if (typeof(json) == "string" && json.length > max_string)
            return disclosure('"', json.substr(0,max_string)+" ...", '"', "string", function () {
                return append(span("string"), themetext(null, my_indent, "string", JSON.stringify(json)));
            });

        if (typeof(json) != "object") // Strings, numbers and bools
            return themetext(null, my_indent, typeof(json), JSON.stringify(json));

        if (json.constructor == Array) {
            if (json.length == 0) return themetext(null, my_indent, "array syntax", "[]");

            return disclosure("[", " ... ", "]", "array", function () {
                var as = append(span("array"), themetext("array syntax", "[", null, "\n"));
                for (var i=0; i<json.length; i++)
                    append(as,
                           _renderjson(json[i], indent+"    ", false, show_level-1, max_string, sort_objects),
                           i != json.length-1 ? themetext("syntax", ",") : [],
                           text("\n"));
                append(as, themetext(null, indent, "array syntax", "]"));
                return as;
            });
        }

        // object
        if (isempty(json))
            return themetext(null, my_indent, "object syntax", "{}");

        return disclosure("{", "...", "}", "object", function () {
            var os = append(span("object"), themetext("object syntax", "{", null, "\n"));
            for (var k in json) var last = k;
            var keys = Object.keys(json);
            if (sort_objects)
                keys = keys.sort();
            for (var i in keys) {
                var k = keys[i];
                append(os, themetext(null, indent+"    ", "key", '"'+k+'"', "object syntax", ': '),
                       _renderjson(json[k], indent+"    ", true, show_level-1, max_string, sort_objects),
                       k != last ? themetext("syntax", ",") : [],
                       text("\n"));
            }
            append(os, themetext(null, indent, "object syntax", "}"));
            return os;
        });
    }

    var renderjson = function renderjson(json)
    {
        var pre = append(document.createElement("pre"), _renderjson(json, "", false, renderjson.show_to_level, renderjson.max_string_length, renderjson.sort_objects));
        pre.className = "renderjson";
        return pre;
    }
    renderjson.set_icons = function(show, hide) { renderjson.show = show;
                                                  renderjson.hide = hide;
                                                  return renderjson; };
    renderjson.set_show_to_level = function(level) { renderjson.show_to_level = typeof level == "string" &&
                                                                                level.toLowerCase() === "all" ? Number.MAX_VALUE
                                                                                                              : level;
                                                     return renderjson; };
    renderjson.set_max_string_length = function(length) { renderjson.max_string_length = typeof length == "string" &&
                                                                                         length.toLowerCase() === "none" ? Number.MAX_VALUE
                                                                                                                         : length;
                                                          return renderjson; };
    renderjson.set_sort_objects = function(sort_bool) { renderjson.sort_objects = sort_bool;
                                                        return renderjson; };
    // Backwards compatiblity. Use set_show_to_level() for new code.
    renderjson.set_show_by_default = function(show) { renderjson.show_to_level = show ? Number.MAX_VALUE : 0;
                                                      return renderjson; };
    renderjson.set_icons('⊕', '⊖');
    renderjson.set_show_by_default(false);
    renderjson.set_sort_objects(false);
    renderjson.set_max_string_length("none");
    return renderjson;
})();