aboutsummaryrefslogtreecommitdiff
path: root/generator/postrenderer.js
blob: 729c4dfed36167c1cd8a7ee316f0565a5dd85a69 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
module.exports={
	render:function(inf,outf,np,pp){return _render(inf,outf,np,pp);},
	set_template:function(tmplf){return _set_template(tmplf);}
};
const fs=require('fs');
const jsdom=require('jsdom');
const path=require('path');
const aes=require('aes-js');
const scrypt=require('scrypt-js');
const btoa=require('btoa');
const sha256=require('./sha256').sha256;
const spawn=require('child_process').spawn;
let template_file='';
let tmplcont='';
let tocid;
let headerlist;

function _dfs(doc,el,le,p)
{
	var e=doc.createElement('li');
	e.innerHTML=`<a class="toctarg" href="#tocanch${tocid}">${el.innerHTML}</a>`;
	le.appendChild(e);
	el.id='tocanch'+(tocid++);
	el.classList.add('tvis');
	var che=null,i;
	for(i=p+1;i<headerlist.length;)
	{
		if(headerlist[i].tagName<=el.tagName)break;
		if(headerlist[i].classList.contains('notoc'))continue;
		if(che===null)
		{
			var te=doc.createElement('li');
			che=doc.createElement('ul');
			che.classList.add('tocnode');
			te.appendChild(che);
			le.appendChild(te);
		}
		i=_dfs(doc,headerlist[i],che,i);
	}
	return i;
}
function footnoter(doc)
{
	var footnotes=doc.body.getElementsByTagName("footnote");
	let starting=doc.getElementById("notediv").children.length;
	for(var i=0;i<footnotes.length;++i)
	{
		var s=footnotes[i].innerHTML;
		footnotes[i].innerHTML="";
		var a=doc.createElement("a");
		a.setAttribute("id","n"+(starting+i+1));
		a.setAttribute("href","#note"+(starting+i+1));
		a.setAttribute("class","note");
		a.innerHTML="["+(starting+i+1)+"]";
		footnotes[i].parentNode.insertBefore(a,footnotes[i]);
		var span=doc.createElement("span");
		span.setAttribute("class","TText");
		span.innerHTML="<a id=\"note"+(starting+i+1)+"\" href=\"#n"+(starting+i+1)+"\">["+(starting+i+1)+"]</a>: "+s+"<br>";
		doc.getElementById("notediv").appendChild(span);
	}
	starting+=footnotes.length;
	while(footnotes.length)footnotes[0].remove();
}
async function encrypt(doc)
{
	let enid=0;
	for(let i of doc.querySelectorAll('encrypted'))
	{
		const key=i.getAttribute('key');
		i.removeAttribute('key');
		const contu8=aes.utils.utf8.toBytes(i.innerHTML);
		const conths=sha256.hash(aes.utils.hex.fromBytes(contu8),{msgFormat:'hex-bytes'});
		const keyu8=aes.utils.utf8.toBytes(key);
		const saltu8=aes.utils.utf8.toBytes('hellwhymustiaddsalttothiscrap');
		const enckey=await new Promise(
			(resolv,rej)=>
			{scrypt(keyu8,saltu8,1024,16,2,32,(e,p,k)=>{e?rej(e):k?resolv(k):undefined;});}
		);
		const ctr=new aes.ModeOfOperation.ctr(enckey);
		const enccont=ctr.encrypt(contu8);
		let encconts='';for(let i=0;i<enccont.length;++i)encconts+=String.fromCharCode(enccont[i]);
		encconts=btoa(encconts);
		i.setAttribute('hash',conths);
		i.setAttribute('encont',encconts);
		i.setAttribute('enid',enid);
		i.innerHTML=
`This section is encrypted. Click <a href="javascript:void(0)" onclick="decryptui(${enid})">here</a>
to decrypt. <noscript>Note that you need JavaScript enabled for decryption.</noscript>`;
		++enid;
	}
}
function _set_template(tmplf)
{
	template_file=tmplf;
	tmplcont=fs.readFileSync(template_file,'utf8');
}
async function _render(inf,outf,np,pp)
{
	const postcont=fs.readFileSync(inf,'utf8');
	const tr=new jsdom.JSDOM(tmplcont);
	const trd=tr.window.document;
	const cont=fs.readFileSync(inf,'utf8');
	const contsplit=cont.split('\n');
	const meta=contsplit.splice(0,4);
	const preproc=meta[3].trim();
	trd.getElementById('title').innerHTML=
	trd.getElementById('titleh').innerHTML=meta[0].trim();
	trd.getElementById('datetags').innerHTML=`${meta[1].trim()}<br>#${meta[2].split(',').join(' #')}`;
	if(preproc.length)
	{
		const ppargs=preproc.split(' ');
		const ppcmd=ppargs.splice(0,1);
		const pp=spawn(ppcmd[0],ppargs,{maxBuffer:1048576});
		let out='';
		pp.stdout.setEncoding('utf8');
		pp.stdout.on('data',(d)=>{out+=d;});
		pp.stdin.write(contsplit.join('\n'),'utf8');
		const ppp=new Promise(
			(resolv,rej)=>
			{
				pp.on('close',(r)=>{if(r)rej(r);resolv(out);});
			}
		);
		pp.stdin.end();
		trd.getElementById('article').innerHTML=await ppp;
	}
	else
	trd.getElementById('article').innerHTML=contsplit.join('\n');

	//Encryption
	await encrypt(trd);

	//TOC
	const l=trd.getElementById('article').querySelectorAll('h2,h3,h4,h5,h6');
	const tocroot=trd.getElementById('tocroot');
	tocid=0;
	headerlist=[];
	for(let i of l)
		if(!i.classList.contains('notoc'))headerlist.push(i);
	for(let i=0;i<headerlist.length;)i=_dfs(trd,headerlist[i],tocroot,i);
	if(!tocroot.children.length)trd.getElementById('tocouter').style.display='none';
	
	//Footnotes
	footnoter(trd);
	
	//Tag list
	const tgs=meta[2].split(',');
	for(let i=0;i<tgs.length;++i)
	{
		let l=trd.createElement('li');
		l.innerHTML=`<a href="/blog/list/${tgs[i]}/">${tgs[i]}</a>`;
		trd.getElementById('tagslist').appendChild(l);
	}
	
	//neighboring posts
	if(pp)trd.getElementById('prevp').href=`${pp}.html`;
	if(np)trd.getElementById('nextp').href=`${np}.html`;
		
	fs.writeFileSync(outf,trd.documentElement.outerHTML,'utf8');
	return outf;
}