aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--generator/listrenderer.js8
-rwxr-xr-xgenerator/main.js3
-rwxr-xr-xgenerator/make4htwrapper.js105
-rw-r--r--generator/package.json12
-rw-r--r--generator/postrenderer.js76
-rw-r--r--generator/scanner.js5
6 files changed, 183 insertions, 26 deletions
diff --git a/generator/listrenderer.js b/generator/listrenderer.js
index ce360af..e1fd797 100644
--- a/generator/listrenderer.js
+++ b/generator/listrenderer.js
@@ -6,7 +6,7 @@ module.exports={
};
const fs=require('fs');
const jsdom=require('jsdom');
-const path=require('path');
+const path=require('path');
let template_file='';
let tmplcont='';
@@ -20,7 +20,7 @@ async function _render(outf,pginfo,posts)
{
const tr=new jsdom.JSDOM(tmplcont);
const trd=tr.window.document;
-
+
const tags_list=trd.getElementById('tagslist');
const posts_list=trd.getElementById('postslist');
const atag_template=trd.getElementById('active_taglist_item_template');
@@ -64,7 +64,7 @@ async function _render(outf,pginfo,posts)
atag_template.parentNode.removeChild(atag_template);
tag_template.parentNode.removeChild(tag_template);
pitm_template.parentNode.removeChild(pitm_template);
-
- fs.writeFileSync(outf,trd.documentElement.outerHTML,'utf8');
+
+ fs.writeFileSync(outf,"<!DOCTYPE html>"+trd.documentElement.outerHTML,'utf8');
return outf;
}
diff --git a/generator/main.js b/generator/main.js
index e25d5f2..ce6c8ff 100755
--- a/generator/main.js
+++ b/generator/main.js
@@ -10,7 +10,7 @@ const fs=require('fs');
const path=require('path');
const scanner=require('./scanner');
-const list=scanner.scan(content_dir,dest_dir);
+let list=scanner.scan(content_dir,dest_dir);
const tags=scanner.build_list_index();
const taglist=Object.keys(tags).sort();
const force=process.argv.indexOf('--force')!=-1;
@@ -49,6 +49,7 @@ if(i.needsupdate||force)
const listrenderer=require('./listrenderer');
listrenderer.set_template(path.join(template_dir,'list_template'));
+list = list.filter((t) => (t.date.indexOf('UNLISTED') == -1));
const list_dir=path.join(dest_dir,'list');
const ppp=posts_per_listpage;
ensure_dir(list_dir);
diff --git a/generator/make4htwrapper.js b/generator/make4htwrapper.js
new file mode 100755
index 0000000..b8e0c3a
--- /dev/null
+++ b/generator/make4htwrapper.js
@@ -0,0 +1,105 @@
+#!/usr/bin/node
+//Copyright Chris Xiong 2024
+//License: Expat (MIT)
+
+const content_dir='../content';
+const image_dir='../content/img';
+
+const process = require('node:process');
+const path = require('node:path');
+const util = require('node:util');
+const fs = require('node:fs');
+const jsdom = require('jsdom');
+const spawn = require('child_process').spawn;
+const image_size = require('image-size');
+
+function promote_header(doc)
+{
+ for (let i = 2; i <= 6; ++i)
+ {
+ const list = doc.querySelectorAll(`h${i}`);
+ for (let j = list.length - 1; j >= 0; --j)
+ {
+ const e = list[j];
+ const newe = doc.createElement(`h${i-1}`);
+ newe.innerHTML = e.innerHTML;
+ e.parentNode.replaceChild(newe, e);
+ }
+ }
+}
+
+function image_unsquash(doc, wd)
+{
+ for (let im of doc.querySelectorAll('img'))
+ {
+ const d = image_size(path.join(wd, im.src));
+ if (d.width > d.height)
+ im.removeAttribute('width');
+ else if (d.width < d.height)
+ im.removeAttribute('height');
+ }
+}
+
+function image_relocate(doc, wd, imd)
+{
+ for (let im of doc.querySelectorAll('img'))
+ {
+ fs.copyFileSync(path.join(wd, im.src), path.join(imd, im.src));
+ im.src = `//filestorage.chrisoft.org/blog/img/${im.src}`;
+ }
+}
+
+function css_embed(doc, cssc)
+{
+ const style = doc.createElement('style');
+ style.textContent = cssc.replaceAll('font-family', 'not-font-family');
+ const body = doc.querySelector('body');
+ body.insertBefore(style, body.childNodes[0]);
+}
+
+async function main()
+{
+ process.stdin.setEncoding('utf8');
+
+ const input = await new Promise((resolv, rej) => {
+ let d = '';
+ process.stdin.on('readable', () => { const r = process.stdin.read(); if (r) d += r; });
+ process.stdin.on('end',() => { resolv(d.trim()); });
+ });
+
+ const wd = path.resolve(path.join(content_dir, path.dirname(input)));
+ const fn = path.basename(input);
+ const fnb = path.basename(input, '.tex');
+
+ const make4htp = spawn('make4ht', ['-f', 'html5', fn, '"fn-in"'], {
+ cwd: wd,
+ shell: true,
+ stdio: ['ignore', 'pipe', 'pipe']
+ });
+ make4htp.stdout.setEncoding('utf8');
+ make4htp.stderr.setEncoding('utf8');
+ make4htp.stdout.on('data', (d) => { console.error(d); });
+ make4htp.stderr.on('data', (d) => { console.error(d); });
+ try {
+ await new Promise((resolv, rej) => {
+ make4htp.on('close', (r) => r ? rej(r) : resolv())
+ });
+ } catch (e) {
+ console.error(`make4ht failed with exitcode ${e}`);
+ return;
+ }
+
+ const htmlc = fs.readFileSync(path.join(wd, `${fnb}.html`), 'utf8');
+ const cssc = fs.readFileSync(path.join(wd, `${fnb}.css`), 'utf8');
+ const domrt = new jsdom.JSDOM(htmlc);
+
+ promote_header(domrt.window.document);
+ image_unsquash(domrt.window.document, wd);
+ image_relocate(domrt.window.document, wd, path.resolve(image_dir));
+ css_embed(domrt.window.document, cssc);
+
+ process.stdout.setEncoding('utf8');
+ process.stdout.write(domrt.window.document.querySelector('body').innerHTML);
+}
+
+main()
diff --git a/generator/package.json b/generator/package.json
new file mode 100644
index 0000000..84473e2
--- /dev/null
+++ b/generator/package.json
@@ -0,0 +1,12 @@
+{
+ "dependencies": {
+ "aes-js": "^3.1.2",
+ "btoa": "^1.2.1",
+ "child_process": "^1.0.2",
+ "fs": "^0.0.1-security",
+ "image-size": "^1.1.1",
+ "jsdom": "^24.0.0",
+ "path": "^0.12.7",
+ "scrypt-js": "^3.0.1"
+ }
+}
diff --git a/generator/postrenderer.js b/generator/postrenderer.js
index f3cf46b..80991a0 100644
--- a/generator/postrenderer.js
+++ b/generator/postrenderer.js
@@ -22,10 +22,20 @@ let tmplcont='';
let tocid;
let headerlist;
+function texts(n)
+{
+ if (typeof n.wholeText == 'string')
+ return n.wholeText;
+ let ret = '';
+ for (let c of n.childNodes)
+ ret += texts(c);
+ return ret;
+}
+
function _dfs(doc,el,le,p)
{
var e=doc.createElement('li');
- e.innerHTML=`<a class="toctarg" href="#tocanch${tocid}">${el.innerHTML}</a>`;
+ e.innerHTML=`<a class="toctarg" href="#tocanch${tocid}">${texts(el)}</a>`;
le.appendChild(e);
el.id='tocanch'+(tocid++);
el.classList.add('tvis');
@@ -86,10 +96,10 @@ function gen_thumb(doc,el)
if(w<=0)w=h;
else if(h<=0)h=w;
}
- const dstfn=`ssbsthumb_${p>0?'s'+p.toFixed(2):w+'x'+h}_${srcfn}`
+ const dstfn=(p>0||w>0||h>0)?`ssbsthumb_${p>0?'s'+p.toFixed(2):w+'x'+h}_${srcfn}`:srcfn;
const srcf=path.join(THUMB_LOCAL_DIR,srcfn);
const dstf=path.join(THUMB_LOCAL_DIR,dstfn);
- if(!util.mtime_cmp(srcf,dstf))
+ if(dstfn!=srcfn&&!util.mtime_cmp(srcf,dstf))
{
const m=spawn('magick',[srcf,'-resize',p>0?(p*100).toFixed()+'%':w+'x'+h,dstf]);
m.stdout.on('data',(d)=>{console.log(`IMout:${d}`);});
@@ -110,11 +120,8 @@ async function encrypt(doc)
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 saltu8=aes.utils.utf8.toBytes('mmmdeliciousthermalpasteletmeconsumeallofit');
+ const enckey=scrypt.syncScrypt(keyu8, saltu8, 1024, 16, 2, 32);
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]);
@@ -133,6 +140,14 @@ function _set_template(tmplf)
template_file=tmplf;
tmplcont=fs.readFileSync(template_file,'utf8');
}
+function _style_to_header(doc)
+{
+ const head = doc.querySelector('head');
+ const body = doc.querySelector('body');
+ const styles = body.querySelectorAll('style');
+ for (let s of styles)
+ head.appendChild(s);
+}
async function _render(inf,outf,np,pp)
{
const postcont=fs.readFileSync(inf,'utf8');
@@ -142,6 +157,12 @@ async function _render(inf,outf,np,pp)
const contsplit=cont.split('\n');
const meta=contsplit.splice(0,4);
const preproc=meta[3].trim();
+ let unlisted = false;
+ if (meta[1].indexOf('UNLISTED') != -1)
+ {
+ unlisted = true;
+ meta[1] = meta[1].substr(0, meta[1].indexOf('UNLISTED'));
+ }
trd.getElementById('title').innerHTML=
trd.getElementById('titleh').innerHTML=meta[0].trim();
trd.getElementById('datetags').innerHTML=`${meta[1].trim()}<br>#${meta[2].split(',').join(' #')}`;
@@ -149,23 +170,28 @@ async function _render(inf,outf,np,pp)
{
const ppargs=preproc.split(' ');
const ppcmd=ppargs.splice(0,1);
- const pp=spawn(ppcmd[0],ppargs,{maxBuffer:1048576});
+ const pp=spawn(ppcmd[0],ppargs,{maxBuffer:1048576, stdio:['pipe', 'pipe', 'inherit']});
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)=>
+ const ppp = new Promise(
+ (resolv, rej)=>
{
- pp.on('close',(r)=>{if(r)rej(r);resolv(out);});
+ pp.on('error', (e) => rej(e));
+ pp.on('close',(r) => { if(r) rej(r); resolv(out); });
}
);
pp.stdin.end();
- trd.getElementById('article').innerHTML=await ppp;
+ try {
+ trd.getElementById('article').innerHTML = await ppp;
+ } catch (e) {
+ console.log(`failed to render ${inf} : ${e}`);
+ }
}
else
trd.getElementById('article').innerHTML=contsplit.join('\n');
-
+
//async images
trd.getElementById('article').querySelectorAll('img').forEach(
(i)=>{if(!i.getAttribute('decoding'))i.setAttribute('decoding','async');}
@@ -186,7 +212,7 @@ async function _render(inf,outf,np,pp)
const tocroot=trd.getElementById('tocroot');
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);
@@ -198,11 +224,21 @@ async function _render(inf,outf,np,pp)
l.innerHTML=`<a href="/blog/list/${tgs[i]}/">${tgs[i]}</a>`;
trd.getElementById('tagslist').appendChild(l);
}
-
+
+ _style_to_header(trd);
+
//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');
+ if (unlisted)
+ {
+ trd.getElementById('prevp').parentNode.remove();
+ trd.getElementById('nextp').parentNode.remove();
+ }
+ else
+ {
+ if(pp)trd.getElementById('prevp').href=`${pp}.html`;
+ if(np)trd.getElementById('nextp').href=`${np}.html`;
+ }
+
+ fs.writeFileSync(outf,"<!DOCTYPE html>"+trd.documentElement.outerHTML,'utf8');
return outf;
}
diff --git a/generator/scanner.js b/generator/scanner.js
index 52744a4..c8c1069 100644
--- a/generator/scanner.js
+++ b/generator/scanner.js
@@ -34,18 +34,21 @@ function _scan(s,dst)
mdate:fs.statSync(path.join(s,i)).mtimeMs,
needsupdate:!util.mtime_cmp(path.join(s,i),path.join(pdst,i.substring(0,i.length-4)+'.html'))
});
-
+
}
return list;
}
function _build_list_index()
{
for(let i of list)
+ {
+ if (i.date.indexOf('UNLISTED') != -1) continue;
for(let tg of i.tags.split(','))
{
if(!tags[tg])tags[tg]={posts:[],needsupdate:false};
tags[tg].posts.push(i);
tags[tg].needsupdate|=i.needsupdate;
}
+ }
return tags;
}