Skip to content

Instantly share code, notes, and snippets.

@prurigro
Last active August 29, 2015 14:16
Show Gist options
  • Save prurigro/e45d70c3cc44b8f57194 to your computer and use it in GitHub Desktop.
Save prurigro/e45d70c3cc44b8f57194 to your computer and use it in GitHub Desktop.
Converts json to tidy html
#!/usr/bin/env node
/*
* json2weblist: convert a heading in json to an HTML list, then tidy it up
* Version: 1.0.1
*
* Requirements:
* Node.js: http://nodejs.org/
* Tidy-HTML: http://tidy.sourceforge.net/
* * Experimental version with HTML5 support : https://github.com/w3c/tidy-html5
* * Less official, more maintained fork : https://github.com/bbatsche/tidy-html5
*
* Script by:
* Kevin MacMartin
* [email protected]
* https://github.com/prurigro
*
* Libraries included:
* node-json2html (convert json to html) : https://github.com/moappi/node-json2html
* htmltidy (tidy-html5 frontend) : https://github.com/vavere/htmltidy
*
* Released under the MIT license : http://opensource.org/licenses/MIT
*/
// library:json2html
var json2html={'transform':function(json,transform,_options){var out={'events':[],'html':''};var options={'events':false};options=json2html._extend(options,_options);if(transform!==undefined||json!==undefined){var obj=typeof json==='string'?JSON.parse(json):json;out=json2html._transform(obj,transform,options);} if(options.events)return(out);else return(out.html);},'_extend':function(obj1,obj2){var obj3={};for(var attrname in obj1){obj3[attrname]=obj1[attrname];} for(var attrname in obj2){obj3[attrname]=obj2[attrname];} return obj3;},'_append':function(obj1,obj2){var out={'html':'','event':[]};if(typeof obj1!=='undefined'&&typeof obj2!=='undefined'){out.html=obj1.html+obj2.html;out.events=obj1.events.concat(obj2.events);} return(out);},'_transform':function(json,transform,options){var elements={'events':[],'html':''};if(Array.isArray(json)){var len=json.length;for(var j=0;j<len;++j){elements=json2html._append(elements,json2html._apply(json[j],transform,j,options));}}else if(typeof json==='object'){elements=json2html._append(elements,json2html._apply(json,transform,undefined,options));} return(elements);},'_apply':function(obj,transform,index,options){var element={'events':[],'html':''};if(Array.isArray(transform)){var t_len=transform.length;for(var t=0;t<t_len;++t){element=json2html._append(element,json2html._apply(obj,transform[t],index,options));}}else if(typeof transform==='object'){if(transform.tag!==undefined){element.html+='<'+transform.tag;var children={'events':[],'html':''};var html;for(var key in transform){switch(key){case'tag':break;case'children':if(Array.isArray(transform.children)){children=json2html._append(children,json2html._apply(obj,transform.children,index,options));}else if(typeof transform.children==='function'){var temp=transform.children.call(obj,obj,index);if(typeof temp==='object'){if(temp.html!==undefined&&temp.events!==undefined)children=json2html._append(children,temp);}else if(typeof temp==='string'){children.html+=temp;}} break;case'html':html=json2html._getValue(obj,transform,'html',index);break;default:var isEvent=false;if(key.length>2) if(key.substring(0,2).toLowerCase()=='on'){if(options.events){var data={'action':transform[key],'obj':obj,'data':options.eventData,'index':index};var id=json2html._guid();element.events[element.events.length]={'id':id,'type':key.substring(2),'data':data};element.html+=" json2html-event-id='"+id+"'";} isEvent=true;} if(!isEvent){var val=json2html._getValue(obj,transform,key,index);if(val!==undefined)element.html+=" "+key+"='"+val+"'";} break;}} element.html+='>';if(html)element.html+=html;element=json2html._append(element,children);element.html+='</'+transform.tag+'>';}} return(element);},'_guid':function(){var S4=function(){return(((1+Math.random())*0x10000)|0).toString(16).substring(1);};return(S4()+S4()+"-"+S4()+S4()+"-"+S4()+S4());},'_getValue':function(obj,transform,key,index){var out='';var val=transform[key];var type=typeof val;if(type==='function'){return(val.call(obj,obj,index));}else if(type==='string'){var _tokenizer=new json2html._tokenizer([/\$\{([^\}\{]+)\}/],function(src,real,re){return real?src.replace(re,function(all,name){var components=name.split('.');var useObj=obj;var outVal='';var c_len=components.length;for(var i=0;i<c_len;++i){if(components[i].length>0){var newObj=useObj[components[i]];useObj=newObj;if(useObj===null||useObj===undefined)break;}} if(useObj!==null&&useObj!==undefined)outVal=useObj;return(outVal);}):src;});out=_tokenizer.parse(val).join('');} return(out);},'_tokenizer':function(tokenizers,doBuild){if(!(this instanceof json2html._tokenizer)) return new json2html._tokenizer(tokenizers,doBuild);this.tokenizers=tokenizers.splice?tokenizers:[tokenizers];if(doBuild) this.doBuild=doBuild;this.parse=function(src){this.src=src;this.ended=false;this.tokens=[];do{this.next();}while(!this.ended);return this.tokens;};this.build=function(src,real){if(src) this.tokens.push(!this.doBuild?src:this.doBuild(src,real,this.tkn));};this.next=function(){var self=this,plain;self.findMin();plain=self.src.slice(0,self.min);self.build(plain,false);self.src=self.src.slice(self.min).replace(self.tkn,function(all){self.build(all,true);return'';});if(!self.src) self.ended=true;};this.findMin=function(){var self=this,i=0,tkn,idx;self.min=-1;self.tkn='';while((tkn=self.tokenizers[i++])!==undefined){idx=self.src[tkn.test?'search':'indexOf'](tkn);if(idx!=-1&&(self.min==-1||idx<self.min)){self.tkn=tkn;self.min=idx;}} if(self.min==-1) self.min=self.src.length;};}};
// library:htmltidy
var Stream=require('stream').Stream;var inherits=require('util').inherits;var fs=require('fs');var path=require('path');var spawn=require('child_process').spawn;var TIDY_OK=0;var TIDY_WARN=1;var TIDY_ERR=2;var DEFAULT_OPTS={showWarnings:false,tidyMark:false,forceOutput:true,quiet:false};var tidyExec=chooseExec();function TidyWorker(opts){Stream.call(this);var mergedOpts=merge(opts,DEFAULT_OPTS);this.writable=true;this.readable=true;this._worker=spawn(tidyExec,parseOpts(mergedOpts));var self=this;var errors='';this._worker.stdin.on('drain',function(){self.emit('drain');});this._worker.stdin.on('error',function(){self.emit('error');});this._worker.stdout.on('data',function(data){self.emit('data',data);});this._worker.stdout.on('close',function(data){self.emit('close');});this._worker.stderr.on('data',function(data){errors+=data;});this._worker.on('exit',function(code){switch(code){case TIDY_WARN:if(mergedOpts.showWarnings){self.emit('error',errors);} break;case TIDY_ERR:if(mergedOpts.showErrors){self.emit('error',errors);} break;} self.emit('end');});} inherits(TidyWorker,Stream);TidyWorker.prototype.write=function(data){if(!this._worker) throw new Error('worker has been destroyed');return this._worker.stdin.write(data);};TidyWorker.prototype.end=function(data){if(!this._worker) throw new Error('worker has been destroyed');this._worker.stdin.end(data);};TidyWorker.prototype.pause=function(){if(!this._worker) throw new Error('worker has been destroyed');if(this._worker.stdout.pause) this._worker.stdout.pause();};TidyWorker.prototype.resume=function(){if(!this._worker) throw new Error('worker has been destroyed');if(this._worker.stdout.resume) this._worker.stdout.resume();};TidyWorker.prototype.destroy=function(){if(this._worker) return;this._worker.kill();this._worker=null;this.emit('close');};function createWorker(opts){return new TidyWorker(opts);} function tidy(text,opts,cb){if(typeof opts=='function'){cb=opts;opts={};} if(typeof cb!='function') throw new Error('no callback provided for tidy');var worker=new TidyWorker(opts);var result='';var error='';worker.on('data',function(data){result+=data;});worker.on('error',function(data){error+=data;});worker.on('end',function(code){cb(error,result);});worker.end(text);} function chooseExec(){var tidyExe;switch(process.platform){case'win32':tidyExe=path.join('win32','tidy.exe');break;case'linux':tidyExe='tidy';break;case'darwin':tidyExe=path.join('darwin','tidy');break;default:throw new Error('unsupported execution platform');} if(process.platform=='linux'){__dirname='/usr';} tidyExe=path.join(__dirname,'bin',tidyExe);var existsSync=fs.existsSync||path.existsSync;if(!existsSync(tidyExe)) throw new Error('missing tidy executable: '+tidyExe);return tidyExe;} function parseOpts(opts){opts=opts||{};var args=[];for(var n in opts){args.push('--'+toHyphens(n));switch(typeof opts[n]){case'string':case'number':args.push(opts[n]);break;case'boolean':args.push(opts[n]?'yes':'no');break;default:throw new Error('unknown option type');}} return args;} function toHyphens(str){return str.replace(/([A-Z])/g,function(m,w){return'-'+w.toLowerCase();});} function merge(obj1,obj2){obj1=obj1||{};obj2=obj2||{};var obj3={};for(var attrname in obj2)obj3[attrname]=obj2[attrname];for(var attrname2 in obj1)obj3[attrname2]=obj1[attrname2];return obj3;} exports.createWorker=createWorker;exports.tidy=tidy;
// library:htmltidy:config
var opts = {
language : 'en',
charEncoding : 'utf8',
indent : 'auto',
doctype : 'html5',
tidyMark : false,
outputXhtml : true,
clean : true,
breakBeforeBr : true,
quiet : true,
tabSize : 4,
indentSpaces : 4
}
/* ----------------------------- */
/* JSON to HTML List Converter */
/* ----------------------------- */
// Get the JSON variable name to generate a list from or exit
if (process.argv[2] != null) {
var jsonDataName=process.argv[2];
} else {
var appNameRegex = /[^\/]*$/;
var appName = appNameRegex.exec(process.argv[1]);
process.stdout.write('USAGE\n');
process.stdout.write(' Generate HTML list from json data with matching names: '+appName+' [jsonHeading]\n');
process.stdout.write(' Include a custom title and heading in the HTML output: '+appName+' [jsonHeading] [docTitle]\n');
process.exit(0);
}
// Generate the HTML above the list and include a title if one was specified
var websiteTitle='';
var websiteH1='';
if (process.argv[3] != null) {
websiteTitle=process.argv[3];
websiteH1='<h1>'+websiteTitle+'</h1>';
}
var htmlHead='<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>'+websiteTitle+'</title></head><body>'+websiteH1;
// Read JSON from stdin
var stdin = process.stdin;
stdin.resume();
stdin.setEncoding('utf8');
stdin.on('data', function(json_string) {
if (json_string === '\u0003') process.exit(1);
// Transform JSON into HTML
json_string = '['+json_string+']'
var transform = {'tag':'li','html':'${'+jsonDataName+'}'}
var messyHTML = json2html.transform(json_string,transform);
// Clean HTML with tidy and write to stdout
tidy(htmlHead+'<ol>'+messyHTML+'</ol></body></html>',
function(err, tidyHTML) {
process.stdout.write(tidyHTML);
});
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment