Skip to content

Commit 82defbe

Browse files
committed
Minify the HTML too, and externalize the css. node-cssmin is borked, though; I should use a real minifier that does not nuke generated content.
1 parent 3e99fda commit 82defbe

File tree

5 files changed

+112
-16
lines changed

5 files changed

+112
-16
lines changed

QuickJSON/quicklookjson.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include "quicklookjson.h"
22

3-
#define HTML_HEADER "<!DOCTYPE html SYSTEM>\n<html>\n <head>\n <meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" />\n <script>var JSONFormatter=(function(){var toString=Object.prototype.toString,re=/^[\\s\\u200B\\uFEFF]*([\\w$\\[\\]\\.]+)[\\s\\u200B\\uFEFF]*\\([\\s\\u200B\\uFEFF]*([\\[{][\\s\\S]*[\\]}])[\\s\\u200B\\uFEFF]*\\)([\\s\\u200B\\uFEFF;]*)$/m;function detectJSONP(s){var js=s,cb='',se='',match;if('string'!==typeof s)return wrapJSONP(s,cb,se);if((match=re.exec(s))&&4===match.length){cb=match[1];js=match[2];se=match[3].replace(/[^;]+/g,'');}try{return wrapJSONP(JSON.parse(js),cb,se);}catch(e){return error(e,s);}}function wrapJSONP(val,callback,semicolon){var output=span(value(val,callback?'':null,callback&&'<br\\n/>'),'json');if(callback)output=span(callback+'(','callback')+output+span(')'+semicolon,'callback');return output;}function isArray(obj){return'[object Array]'===toString.call(obj);}function span(html,className){return'<span class=\"\\''+className+'\\\">'+html+'</span>';}function error(e,data){return span('Error parsing JSON: '+e,'error')+'<h1>Content:</h1>'+span(html(data),'json');}function html(s,isAttribute){if(s==null)return'';s=(s+'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');return isAttribute?s.replace(/'/g,'&apos;'):s;}var js=JSON.stringify('\\b\\f\\n\\r\\t').length===12?function saneJSEscaper(s,noQuotes){s=html(JSON.stringify(s).slice(1,-1));return noQuotes?s:'&quot;'+s+'&quot;';}:function insaneEscaper(s,noQuotes){var had={'\\b':'b','\\f':'f','\\r':'r','\\n':'n','\\t':'t'},ws;for(ws in had)if(-1===s.indexOf(ws))delete had[ws];s=JSON.stringify(s).slice(1,-1);for(ws in had)s=s.replace(new RegExp('\\\\\\\\u000'+(ws.charCodeAt().toString(16)),'ig'),'\\\\'+had[ws]);s=html(s);return noQuotes?s:'&quot;'+s+'&quot;';};function value(v,indent,nl){var output;switch(typeof v){case'boolean':output=span(html(v),'bool');break;case'number':output=span(html(v),'num');break;case'string':if(/^(\\w+):\\/\\/[^\\s]+$/i.test(v)){output='&quot;<a href=\"\\''+html(v,!!'attribute')+'\\\">'+js(v,1)+'</a>&quot;';}else{output=span(js(v),'string');}break;case'object':if(null===v){output=span('null','null');}else{indent=indent==null?'':indent+'&nbsp; ';if(isArray(v)){output=array(v,indent,nl);}else{output=object(v,indent,nl);}}break;}return output;}function object(obj,indent,nl){var output='';for(var key in obj){if(output)output+='<br\\n/>'+indent+', ';output+=span(js(key),'prop')+': '+value(obj[key],indent,'<br\\n/>');}if(!output)return'{}';return'<span class=\\'unfolded obj\\'><span class=\"content\">'+(nl?nl+indent:'')+'{ '+output+'<br\\n/>'+indent+'}</span>';}function array(a,indent,nl){for(var i=0,output='';i<a.length;i++){if(output)output+='<br\\n/>'+indent+', ';output+=value(a[i],indent,'');}if(!output)return'[]';return'<span class=\\'unfolded array\\'><span class=\"content\">'+(nl?nl+indent:'')+'[ '+output+'<br\\n/>'+indent+']</span>';}function JSONFormatter(s){return detectJSONP(s);}JSONFormatter.init=function init(doc){doc=doc||document;doc.addEventListener('click',function folding(e){var elem=e.target,is,is_json=elem;while(is_json&&is_json.className!='json')is_json=is_json.parentNode;if(!is_json)return;do{if(/^a$/i.test(elem.nodeName))return;is=elem.className||'';}while(!/\\b(un)?folded /.test(is)&&(elem=elem.parentNode));if(elem){elem.className=/unfolded /.test(is)?is.replace('unfolded ','folded '):is.replace('folded ','unfolded ');}},false);};return JSONFormatter;})();</script>\n <script>\n function init() {\n JSONFormatter.init();\n var json = document.getElementById('json').textContent;\n document.body.innerHTML = JSONFormatter(json);\n }\n </script>\n <style>.prop{font-weight:700;}.null{color:red;}.bool,.num{color:blue;}.string{color:green;white-space:pre-wrap;}.error{-moz-border-radius:8px;border:1px solid #970000;background-color:#F7E8E8;margin:.5em;padding:.5em;}.json{white-space:pre-wrap;font-family:monospace;font-size:1.1em;}h1{font-size:1.2em;}.callback{font-family:monospace;color:#A52A2A;}.folded *{position:absolute;color:transparent;height:0;width:0;outline:5px solid red;white-space:normal;top:-100000cm;left:-100000cm;}*.folded.array:before{content:\"[\\002026 ]\";/* [...] */}.folded.obj:before{content:\"{\\002026 }\";/* {...} */}.callback+.json>.folded:after{content:\"\";}.folded:after{content:\" \";}.folded{background:#FFF;}.folded:hover{background:rgba(255,192,203,0.5);}.folded{cursor:se-resize;}.unfolded.hovered{background:rgba(255,192,203,0.5);}.unfolded{cursor:nw-resize;}</style>\n </head>\n <body onload=\"init()\">\n <script id=\"json\" type=\"application/json\">\n "
4-
#define HTML_FOOTER "\n </script>\n </body>\n</html>\n"
3+
#define HTML_HEADER "<!DOCTYPE html SYSTEM><html><head><meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\" /><style>.prop{font-weight:bold}.null {color:red}.bool,.num {color:blue}.string {color:green;white-space:pre-wrap}h1 {font-size:1.2em}.error {-webkit-border-radius:8px;-moz-border-radius:8px;border-radius:8px;border:1px solid #970000;background:#F7E8E8;margin:0.5em;padding:0.5em}.json {white-space:pre-wrap;font-family:monospace;font-size:1.1em}.callback {font-family:monospace;color:#A52A2A}.folded * {position:absolute;color:transparent;height:0;width:0;outline:5px solid red;white-space:normal;top:-100000cm;left:-100000cm}*.folded.array:before {content:\"[\\002026 ]\"}.folded.obj:before {content:\"{\\002026 }\"}.callback +.json >.folded:after {content:\"\"}.folded:after {content:\" \"}.folded {background:#FFF}.folded:hover {background:rgba(255,192,203,0.5)}.folded {cursor:se-resize}.unfolded {cursor:nw-resize}.unfolded.hovered {background:rgba(255,192,203,0.5)}</style><script>var JSONFormatter=(function(){var toString=Object.prototype.toString,re=/^[\\s\\u200B\\uFEFF]*([\\w$\\[\\]\\.]+)[\\s\\u200B\\uFEFF]*\\([\\s\\u200B\\uFEFF]*([\\[{][\\s\\S]*[\\]}])[\\s\\u200B\\uFEFF]*\\)([\\s\\u200B\\uFEFF;]*)$/m;function detectJSONP(s){var js=s,cb='',se='',match;if('string'!==typeof s)return wrapJSONP(s,cb,se);if((match=re.exec(s))&&4===match.length){cb=match[1];js=match[2];se=match[3].replace(/[^;]+/g,'');}try{return wrapJSONP(JSON.parse(js),cb,se);}catch(e){return error(e,s);}}function wrapJSONP(val,callback,semicolon){var output=span(value(val,callback?'':null,callback&&'<br\\n/>'),'json');if(callback)output=span(callback+'(','callback')+output+span(')'+semicolon,'callback');return output;}function isArray(obj){return'[object Array]'===toString.call(obj);}function span(html,className){return'<span class=\"\\''+className+'\\\">'+html+'</span>';}function error(e,data){return span('Error parsing JSON: '+e,'error')+'<h1>Content:</h1>'+span(html(data),'json');}function html(s,isAttribute){if(s==null)return'';s=(s+'').replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');return isAttribute?s.replace(/'/g,'&apos;'):s;}var js=JSON.stringify('\\b\\f\\n\\r\\t').length===12?function saneJSEscaper(s,noQuotes){s=html(JSON.stringify(s).slice(1,-1));return noQuotes?s:'&quot;'+s+'&quot;';}:function insaneEscaper(s,noQuotes){var had={'\\b':'b','\\f':'f','\\r':'r','\\n':'n','\\t':'t'},ws;for(ws in had)if(-1===s.indexOf(ws))delete had[ws];s=JSON.stringify(s).slice(1,-1);for(ws in had)s=s.replace(new RegExp('\\\\\\\\u000'+(ws.charCodeAt().toString(16)),'ig'),'\\\\'+had[ws]);s=html(s);return noQuotes?s:'&quot;'+s+'&quot;';};function value(v,indent,nl){var output;switch(typeof v){case'boolean':output=span(html(v),'bool');break;case'number':output=span(html(v),'num');break;case'string':if(/^(\\w+):\\/\\/[^\\s]+$/i.test(v)){output='&quot;<a href=\"\\''+html(v,!!'attribute')+'\\\">'+js(v,1)+'</a>&quot;';}else{output=span(js(v),'string');}break;case'object':if(null===v){output=span('null','null');}else{indent=indent==null?'':indent+'&nbsp; ';if(isArray(v)){output=array(v,indent,nl);}else{output=object(v,indent,nl);}}break;}return output;}function object(obj,indent,nl){var output='';for(var key in obj){if(output)output+='<br\\n/>'+indent+', ';output+=span(js(key),'prop')+': '+value(obj[key],indent,'<br\\n/>');}if(!output)return'{}';return'<span class=\\'unfolded obj\\'><span class=\"content\">'+(nl?nl+indent:'')+'{ '+output+'<br\\n/>'+indent+'}</span>';}function array(a,indent,nl){for(var i=0,output='';i<a.length;i++){if(output)output+='<br\\n/>'+indent+', ';output+=value(a[i],indent,'');}if(!output)return'[]';return'<span class=\\'unfolded array\\'><span class=\"content\">'+(nl?nl+indent:'')+'[ '+output+'<br\\n/>'+indent+']</span>';}function JSONFormatter(s){return detectJSONP(s);}JSONFormatter.init=function init(doc){doc=doc||document;doc.addEventListener('click',function folding(e){var elem=e.target,is,is_json=elem;while(is_json&&is_json.className!='json')is_json=is_json.parentNode;if(!is_json)return;do{if(/^a$/i.test(elem.nodeName))return;is=elem.className||'';}while(!/\\b(un)?folded /.test(is)&&(elem=elem.parentNode));if(elem){elem.className=/unfolded /.test(is)?is.replace('unfolded ','folded '):is.replace('folded ','unfolded ');}},false);};return JSONFormatter;})();</script><script>function init() { JSONFormatter.init(); var json = document.getElementById('json').textContent; document.body.innerHTML = JSONFormatter(json); }</script></head><body onload=\"init()\"><script id=\"json\" type=\"application/json\">"
4+
#define HTML_FOOTER "</script></body></html>"
55

66
int main(int argc, char* argv[]) {
77
if (argc < 2) {

Rakefile

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,35 @@ desc 'Generates the quicklook source code from the test web page.'
3030
file 'QuickJSON/quicklookjson.c' => FileList['json-viewer/quicklook.*'] do |t|
3131
announce t
3232

33-
# fetch the html, inlining and minifying <script> tags we find
33+
# grab the leading and trailing portions and escape them to C strings
34+
quicklook_html =~ /\A(.*)__JSON__(.*)\Z/m
35+
ENV['HTML_HEADER'] = $1.to_json
36+
ENV['HTML_FOOTER'] = $2.to_json
37+
38+
# move our C file template into place and perform __MAGIC__ expansion on it
39+
cp 'json-viewer/quicklook.c', t.name
40+
write_file t.name
41+
end
42+
43+
# fetch the html, suck out whitespace, inline and minify scripts and stylesheets
44+
def quicklook_html
3445
cd 'json-viewer' do
3546
quicklook_html = IO.read('quicklook.html')
3647
dom = Hpricot.parse(quicklook_html)
48+
49+
# strip leading/trailing ws in text nodes, and consecutive mid-textnode ws
50+
dom.search("*").each do |node|
51+
node.content = node.content().strip.gsub(/\s+/, ' ') if node.text?
52+
end
53+
54+
# inline and minify linked stylesheets
55+
dom.search('link[@rel="stylesheet"][@href]').each do |link|
56+
path = link.attributes['href']
57+
css = IO.popen("../bin/cssmin #{Shellwords.escape(path)}") { |s| s.read }
58+
link.swap("<style>#{css}</style>")
59+
end
60+
61+
# inline script tags with minified versions of their content
3762
dom.search('script[@src]').each do |script|
3863
src = script.attributes['src']
3964
script.remove_attribute('src')
@@ -42,16 +67,8 @@ file 'QuickJSON/quicklookjson.c' => FileList['json-viewer/quicklook.*'] do |t|
4267
stdout.read.gsub(/\n/, ' ')
4368
end
4469
end
45-
46-
# next, grab the leading and trailing portions and escape them to C strings
47-
dom.to_s =~ /\A(.*)__JSON__(.*)\Z/m
48-
ENV['HTML_HEADER'] = $1.to_json
49-
ENV['HTML_FOOTER'] = $2.to_json
70+
return dom.to_s
5071
end
51-
52-
# move our C file template into place and perform __MAGIC__ expansion on it
53-
cp 'json-viewer/quicklook.c', t.name
54-
write_file t.name
5572
end
5673

5774
def announce(task, action = 'building')

bin/jsmin

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ if (files.length)
1515
function load(fn) {
1616
fs.readFile(fn, 'utf-8', function gotFile(e, file) {
1717
if (e) return fail(e, 'loading '+ fn);
18-
return minify(fn, file);
18+
return minify(file);
1919
});
2020
}
2121

@@ -24,7 +24,7 @@ function fail(e, when) {
2424
process.exit(1 + count - files.length);
2525
}
2626

27-
function minify(fn, js) {
27+
function minify(js) {
2828
process.stdout.write(jsmin(js, LEVEL));
2929
done();
3030
}

json-viewer/quicklook.css

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
.prop {
2+
font-weight: bold;
3+
}
4+
.null {
5+
color: red;
6+
}
7+
.bool, .num {
8+
color: blue;
9+
}
10+
.string {
11+
color: green;
12+
white-space: pre-wrap;
13+
}
14+
15+
h1 {
16+
font-size: 1.2em;
17+
}
18+
.error {
19+
-webkit-border-radius: 8px;
20+
-moz-border-radius: 8px;
21+
border-radius: 8px;
22+
border: 1px solid #970000;
23+
background: #F7E8E8;
24+
margin: 0.5em;
25+
padding: 0.5em;
26+
}
27+
28+
.json {
29+
white-space: pre-wrap;
30+
font-family: monospace;
31+
font-size: 1.1em;
32+
}
33+
34+
.callback {
35+
font-family: monospace;
36+
color: #A52A2A;
37+
}
38+
39+
.folded * {
40+
position: absolute;
41+
color: transparent;
42+
height: 0;
43+
width: 0;
44+
outline: 5px solid red;
45+
white-space: normal;
46+
top: -100000cm;
47+
left: -100000cm;
48+
}
49+
50+
*.folded.array:before {
51+
content: "[\002026 ]"; /* [...] */
52+
}
53+
.folded.obj:before {
54+
content: "{\002026 }"; /* {...} */
55+
}
56+
57+
.callback + .json > .folded:after {
58+
content: "";
59+
}
60+
.folded:after {
61+
content: " ";
62+
}
63+
64+
.folded {
65+
background: #FFF;
66+
}
67+
.folded:hover {
68+
background: rgba(255,192,203,0.5);
69+
}
70+
71+
.folded {
72+
cursor: se-resize;
73+
}
74+
.unfolded {
75+
cursor: nw-resize;
76+
}
77+
.unfolded.hovered {
78+
background: rgba(255,192,203,0.5);
79+
}

json-viewer/quicklook.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<html>
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
5+
<link href="quicklook.css" rel="stylesheet">
56
<script src="quicklook.js"></script>
67
<script>
78
function init() {
@@ -10,11 +11,10 @@
1011
document.body.innerHTML = JSONFormatter(json);
1112
}
1213
</script>
13-
<style>.prop{font-weight:700;}.null{color:red;}.bool,.num{color:blue;}.string{color:green;white-space:pre-wrap;}.error{-moz-border-radius:8px;border:1px solid #970000;background-color:#F7E8E8;margin:.5em;padding:.5em;}.json{white-space:pre-wrap;font-family:monospace;font-size:1.1em;}h1{font-size:1.2em;}.callback{font-family:monospace;color:#A52A2A;}.folded *{position:absolute;color:transparent;height:0;width:0;outline:5px solid red;white-space:normal;top:-100000cm;left:-100000cm;}*.folded.array:before{content:"[\002026 ]";/* [...] */}.folded.obj:before{content:"{\002026 }";/* {...} */}.callback+.json>.folded:after{content:"";}.folded:after{content:" ";}.folded{background:#FFF;}.folded:hover{background:rgba(255,192,203,0.5);}.folded{cursor:se-resize;}.unfolded.hovered{background:rgba(255,192,203,0.5);}.unfolded{cursor:nw-resize;}</style>
1414
</head>
1515
<body onload="init()">
1616
<script id="json" type="application/json">
17-
__JSON__
17+
__JSON__({"1":2,"3":[1,2,3]})
1818
</script>
1919
</body>
2020
</html>

0 commit comments

Comments
 (0)