import Mustache from "mustache";
import { mkdirSync } from "fs";
import {
getPosts,
getTags,
BlogTags,
BlogPost,
getPostsByTagID,
getTagByName,
getPostsByCatID,
getPostsBySubCatID,
getCatByName,
getCategoryByID,
} from "../models/blog.js";
/* _ _ ___ _ __
* |_ | | |\ | / | | / \ |\ | (_
* | |_| | \| \_ | | \_/ | \| __)
*/
function tagCloud(min: number, max: number, domain: string): string {
let tags: BlogTags = getTags();
let count = new Array();
tags.forEach((tag) => {
count.push(tag.count);
});
const maxCount: number = Math.max(...count);
const minCount: number = Math.min(...count);
let spread: number = maxCount - minCount;
if (spread <= 0) {
spread = 1;
}
let step: number = (max - min) / spread;
let html: string = '
";
return html;
}
function pagination(
domain: string,
totalRows: number,
perPage: number,
currentPage: number,
linkCount: number = 5,
): string {
if (totalRows == 0 || perPage == 0) {
return "";
}
const pageCount = Math.ceil(totalRows / perPage);
if (pageCount == 1) {
return "";
}
if (currentPage > totalRows) {
currentPage = (pageCount - 1) * perPage;
}
let first = currentPage - linkCount;
let last = currentPage + linkCount;
if (first < 0) {
first = 1;
last += linkCount - first;
}
if (last > pageCount) {
last = pageCount;
first -= linkCount;
}
let DOM = "";
if (currentPage != first) {
DOM += `«`;
}
if (currentPage != 1) {
let prev = currentPage - 1;
if (prev <= 0) {
prev = currentPage;
}
DOM += `<`;
}
for (let i = first - 1; i <= last; i++) {
if (i > 0) {
if (currentPage == i) {
DOM += `${i}`;
} else {
DOM += `${i}`;
}
}
}
if (currentPage < pageCount) {
let next = currentPage + 1;
if (next >= last) {
next = last;
}
DOM += `>`;
}
if (currentPage != last) {
// last should be pageCount, but i kinda like the stepping better
DOM += `»`;
}
return ``;
}
async function getFile(file: string) {
return Bun.file(`src/views/${file}.html`, {
type: "text/html;charset=utf-8",
}).text();
}
function convertToRoman(num: number): string {
var roman: any = {
m: 1000,
cm: 900,
d: 500,
cd: 400,
c: 100,
xc: 90,
l: 50,
xl: 40,
x: 10,
ix: 9,
v: 5,
iv: 4,
i: 1,
};
let str = "";
for (var i of Object.keys(roman)) {
let q = Math.floor(num / roman[i]);
num -= q * roman[i];
str += i.repeat(q);
}
return `${str}`;
}
async function RenderSidebar(domain: string) {
let sidebar =
"tags
" +
tagCloud(80, 175, domain) +
"";
let sidebarHtml = await getFile("sidebar");
sidebar += Mustache.render(sidebarHtml, { domain: domain });
return sidebar;
}
async function RenderCatPagePosts(
domain: string,
title: string,
cat_id: number,
cat_url: string,
limit: number,
offset: number,
total: number,
current: number,
) {
const data = getPostsByCatID(cat_id, limit, offset);
const postHtml = await getFile("preview");
let DOM: string = `${title} ${cat_url}: page ${current} of ${total}`;
data.forEach((post: any) => {
const postDate = new Date(post.date * 1000);
DOM += Mustache.render(postHtml, {
domain: domain,
url: post.url,
title: post.title,
content: post.excerpt,
baseCat: post.meta.base[0].url,
baseCatUrl: post.meta.base[0].url,
mainCat: post.meta.main[0].url,
mainCatUrl: post.meta.main[0].url,
subtitle: post.subtitle,
day: postDate.getDate(),
month: postDate.toLocaleString("default", { month: "short" }),
year: postDate.getFullYear(),
tagList: () => {
let list = "";
post.meta.tags.forEach((tag: any) => {
list += `${tag.name}, `;
});
return list.slice(0, -2);
},
catList: () => {
let list = "";
post.meta.cats.forEach((cat: any) => {
if (cat.blog_cat_id.toString().includes(".")) {
const parentID: number = Math.floor(cat.blog_cat_id);
const parent = getCategoryByID(parentID);
list += `${parent[0].name}/${cat.name}, `;
} else {
list += `${cat.name}, `;
}
});
return list.slice(0, -2);
},
});
});
DOM += pagination(`${domain}/category/${cat_url}`, total, limit, current);
return DOM;
}
async function RenderSubCatPagePosts(
domain: string,
title: string,
subcat_id: number,
subcat_url: string,
limit: number,
offset: number,
total: number,
current: number,
) {
const data = getPostsBySubCatID(subcat_id, limit, offset);
const postHtml = await getFile("preview");
let DOM: string = `${title} ${subcat_url}: page ${current} of ${total}`;
data.forEach((post: any) => {
const postDate = new Date(post.date * 1000);
DOM += Mustache.render(postHtml, {
domain: domain,
url: post.url,
title: post.title,
content: post.excerpt,
baseCat: post.meta.base[0].url,
baseCatUrl: post.meta.base[0].url,
mainCat: post.meta.main[0].url,
mainCatUrl: post.meta.main[0].url,
subtitle: post.subtitle,
day: postDate.getDate(),
month: postDate.toLocaleString("default", { month: "short" }),
year: postDate.getFullYear(),
tagList: () => {
let list = "";
post.meta.tags.forEach((tag: any) => {
list += `${tag.name}, `;
});
return list.slice(0, -2);
},
catList: () => {
let list = "";
post.meta.cats.forEach((cat: any) => {
if (cat.blog_cat_id.toString().includes(".")) {
const parentID: number = Math.floor(cat.blog_cat_id);
const parent = getCategoryByID(parentID);
list += `${parent[0].name}/${cat.name}, `;
} else {
list += `${cat.name}, `;
}
});
return list.slice(0, -2);
},
});
});
const parentID: number = Math.floor(subcat_id);
const parent = getCategoryByID(parentID);
DOM += pagination(
`${domain}/category/${parent[0].url}/${subcat_url}`,
total,
limit,
current,
);
return DOM;
}
async function RenderTagPagePosts(
domain: string,
title: string,
tag_id: number,
tag_url: string,
limit: number,
offset: number,
total: number,
current: number,
) {
const data = getPostsByTagID(tag_id, limit, offset);
const postHtml = await getFile("preview");
let DOM: string = `${title} ${tag_url}: page ${current} of ${total}`;
data.forEach((post: any) => {
const postDate = new Date(post.date * 1000);
DOM += Mustache.render(postHtml, {
domain: domain,
url: post.url,
title: post.title,
content: post.excerpt,
baseCat: post.meta.base[0].url,
baseCatUrl: post.meta.base[0].url,
mainCat: post.meta.main[0].url,
mainCatUrl: post.meta.main[0].url,
subtitle: post.subtitle,
day: postDate.getDate(),
month: postDate.toLocaleString("default", { month: "short" }),
year: postDate.getFullYear(),
tagList: () => {
let list = "";
post.meta.tags.forEach((tag: any) => {
list += `${tag.name}, `;
});
return list.slice(0, -2);
},
catList: () => {
let list = "";
post.meta.cats.forEach((cat: any) => {
if (cat.blog_cat_id.toString().includes(".")) {
const parentID: number = Math.floor(cat.blog_cat_id);
const parent = getCategoryByID(parentID);
list += `${parent[0].name}/${cat.name}, `;
} else {
list += `${cat.name}, `;
}
});
return list.slice(0, -2);
},
});
});
DOM += pagination(`${domain}/tag/${tag_url}`, total, limit, current);
return DOM;
}
async function RenderPagePosts(
domain: string,
title: string,
limit: number,
offset: number,
total: number,
current: number,
) {
let data = getPosts(limit, offset);
const postHtml = await getFile("post");
const prevHtml = await getFile("preview");
let DOM: string = `${title} page ${current} of ${total}`;
data.forEach((post) => {
const postDate = new Date(post.date * 1000);
DOM += Mustache.render(post.excerpt == post.content ? postHtml : prevHtml, {
domain: domain,
url: post.url,
title: post.title,
content: post.excerpt,
baseCat: post.meta.base[0].url,
baseCatUrl: post.meta.base[0].url,
mainCat: post.meta.main[0].url,
mainCatUrl: post.meta.main[0].url,
subtitle: post.subtitle,
day: postDate.getDate(),
month: postDate.toLocaleString("default", { month: "short" }),
year: postDate.getFullYear(),
tagList: () => {
let list = "";
post.meta.tags.forEach((tag) => {
list += `${tag.name}, `;
});
return list.slice(0, -2);
},
catList: () => {
let list = "";
post.meta.cats.forEach((cat) => {
if (cat.blog_cat_id.toString().includes(".")) {
const parentID: number = Math.floor(cat.blog_cat_id);
const parent = getCategoryByID(parentID);
list += `${parent[0].name}/${cat.name}, `;
} else {
list += `${cat.name}, `;
}
});
return list.slice(0, -2);
},
});
});
DOM += pagination(domain, total, limit, current);
return DOM;
}
async function RenderPost(domain: string, title: string, data: BlogPost) {
const postHtml = await getFile("post");
const post = data[0];
const postDate = new Date(post.date * 1000);
return Mustache.render(postHtml, {
domain: domain,
pageTitle: title,
url: post.url,
title: post.title,
content: post.content,
baseCat: post.meta.base[0].url,
baseCatUrl: post.meta.base[0].url,
mainCat: post.meta.main[0].url,
mainCatUrl: post.meta.main[0].url,
subtitle: post.subtitle,
day: postDate.getDate(),
month: postDate.toLocaleString("default", { month: "short" }),
year: postDate.getFullYear(),
tagList: () => {
let list = "";
post.meta.tags.forEach((tag) => {
list += `${tag.name}, `;
});
return list.slice(0, -2);
},
catList: () => {
let list = "";
post.meta.cats.forEach((cat) => {
if (cat.blog_cat_id.toString().includes(".")) {
const parentID: number = Math.floor(cat.blog_cat_id);
const parent = getCategoryByID(parentID);
list += `${parent[0].name}/${cat.name}, `;
} else {
list += `${cat.name}, `;
}
});
return list.slice(0, -2);
},
});
}
export async function generateErrorPages(domain: string, title: string) {
const sidebar = await RenderSidebar(domain);
let errorHtml = await getFile("error");
const renderedError = Mustache.render(errorHtml, {
domain: domain,
title: title,
});
let mainHtml = await getFile("main");
const renderedHtml = Mustache.render(mainHtml, {
ripcache: Date.now(),
domain: domain,
description: `${title} the error page`,
content: renderedError,
sidebar: sidebar,
footer: () => {
const year = convertToRoman(new Date().getFullYear());
return `${year} xero harrison`;
},
});
mkdirSync(`dist/htmx/`, { recursive: true });
Bun.write(`dist/htmx/error.html`, renderedError);
Bun.write(`dist/error.html`, renderedHtml);
}
export async function generateCatPage(
domain: string,
title: string,
cat_url: string,
limit: number,
offset: number,
total: number,
current: number,
) {
const cat_id: number = getCatByName(cat_url);
const sidebar = await RenderSidebar(domain);
const content = await RenderCatPagePosts(
domain,
title,
cat_id,
cat_url,
limit,
offset,
total,
current,
);
let mainHtml = await getFile("main");
const renderedHtml = Mustache.render(mainHtml, {
ripcache: Date.now(),
domain: domain,
description: `${title} posts categorized: ${cat_url}, page ${current} of ${total}`,
content: content,
sidebar: sidebar,
footer: () => {
const year = convertToRoman(new Date().getFullYear());
return `${year} xero harrison`;
},
});
mkdirSync(`dist/htmx/category/${cat_url}/page`, { recursive: true });
mkdirSync(`dist/category/${cat_url}/page`, { recursive: true });
if (current == 1) {
Bun.write(`dist/htmx/category/${cat_url}/index.html`, content);
Bun.write(`dist/category/${cat_url}/index.html`, renderedHtml);
/* @todo: is this cheaper than nginix? */
Bun.write(`dist/category/${cat_url}/page/index.html`, renderedHtml);
}
Bun.write(`dist/htmx/category/${cat_url}/page/${current}.html`, content);
Bun.write(`dist/category/${cat_url}/page/${current}.html`, renderedHtml);
}
export async function generateSubCatPage(
domain: string,
title: string,
subcat_url: string,
subcat_id: number,
limit: number,
offset: number,
total: number,
current: number,
) {
const sidebar = await RenderSidebar(domain);
const content = await RenderSubCatPagePosts(
domain,
title,
subcat_id,
subcat_url,
limit,
offset,
total,
current,
);
let mainHtml = await getFile("main");
const renderedHtml = Mustache.render(mainHtml, {
ripcache: Date.now(),
domain: domain,
description: `${title} posts categorized: ${subcat_url}, page ${current} of ${total}`,
content: content,
sidebar: sidebar,
footer: () => {
const year = convertToRoman(new Date().getFullYear());
return `${year} xero harrison`;
},
});
const parentID: number = Math.floor(subcat_id);
const parent = getCategoryByID(parentID);
const cat_url = parent[0].url;
mkdirSync(`dist/htmx/category/${cat_url}/${subcat_url}/page`, {
recursive: true,
});
mkdirSync(`dist/category/${cat_url}/${subcat_url}/page`, { recursive: true });
if (current == 1) {
Bun.write(
`dist/htmx/category/${cat_url}/${subcat_url}/index.html`,
content,
);
Bun.write(
`dist/category/${cat_url}/${subcat_url}/index.html`,
renderedHtml,
);
/* @todo: is this cheaper than nginix? */
Bun.write(
`dist/category/${cat_url}/${subcat_url}/page/index.html`,
renderedHtml,
);
}
Bun.write(
`dist/htmx/category/${cat_url}/${subcat_url}/page/${current}.html`,
content,
);
Bun.write(
`dist/category/${cat_url}/${subcat_url}/page/${current}.html`,
renderedHtml,
);
}
export async function generateTagPage(
domain: string,
title: string,
tag_url: string,
limit: number,
offset: number,
total: number,
current: number,
) {
const tag_id: number = getTagByName(tag_url);
const sidebar = await RenderSidebar(domain);
const content = await RenderTagPagePosts(
domain,
title,
tag_id,
tag_url,
limit,
offset,
total,
current,
);
let mainHtml = await getFile("main");
const renderedHtml = Mustache.render(mainHtml, {
ripcache: Date.now(),
domain: domain,
description: `${title} posts tagged: ${tag_url}, page ${current} of ${total}`,
content: content,
sidebar: sidebar,
footer: () => {
const year = convertToRoman(new Date().getFullYear());
return `${year} xero harrison`;
},
});
mkdirSync(`dist/htmx/tag/${tag_url}/page`, { recursive: true });
mkdirSync(`dist/tag/${tag_url}/page`, { recursive: true });
if (current == 1) {
Bun.write(`dist/htmx/tag/${tag_url}/index.html`, content);
Bun.write(`dist/tag/${tag_url}/index.html`, renderedHtml);
/* @todo: is this cheaper than nginix? */
Bun.write(`dist/tag/${tag_url}/page/index.html`, renderedHtml);
}
Bun.write(`dist/htmx/tag/${tag_url}/page/${current}.html`, content);
Bun.write(`dist/tag/${tag_url}/page/${current}.html`, renderedHtml);
}
export async function generatePage(
domain: string,
title: string,
limit: number,
offset: number,
total: number,
current: number,
) {
const sidebar = await RenderSidebar(domain);
const content = await RenderPagePosts(
domain,
title,
limit,
offset,
total,
current,
);
let mainHtml = await getFile("main");
const renderedHtml = Mustache.render(mainHtml, {
ripcache: Date.now(),
domain: domain,
description: (current == 1) ? `${title} xero's blog` : `${title} page ${current} of ${total}`,
content: content,
sidebar: sidebar,
footer: () => {
const year = convertToRoman(new Date().getFullYear());
return `${year} xero harrison`;
},
});
mkdirSync(`dist/page`, { recursive: true });
mkdirSync(`dist/htmx/page`, { recursive: true });
if (current == 1) {
Bun.write(`dist/htmx/index.html`, content);
Bun.write(`dist/index.html`, renderedHtml);
}
Bun.write(`dist/htmx/page/${current}.html`, content);
Bun.write(`dist/page/${current}.html`, renderedHtml);
}
export async function generatePost(
domain: string,
title: string,
data: BlogPost,
) {
const sidebar = await RenderSidebar(domain);
const content = await RenderPost(domain, title, data);
const file = data[0].url;
Bun.write(`dist/htmx/${file}.html`, content);
let mainHtml = await getFile("main");
Bun.write(
`dist/${file}.html`,
Mustache.render(mainHtml, {
ripcache: Date.now(),
domain: domain,
description: `${title} ${data[0].title} : ${data[0].subtitle}`,
content: content,
sidebar: sidebar,
footer: () => {
const year = convertToRoman(new Date().getFullYear());
return `${year} xero harrison`;
},
}),
);
}