diff --git a/.docs/.eleventy.js b/.docs/.eleventy.js
index 3142200..cddd9b5 100644
--- a/.docs/.eleventy.js
+++ b/.docs/.eleventy.js
@@ -1,12 +1,14 @@
+"use strict";
+
const os = require("os");
const fs = require("fs");
const path = require("path");
+const debug = require("debug");
const htmlentities = require("html-entities");
const phin = require("phin");
-const Image = require("@11ty/eleventy-img");
-
+const HTMLPicture = require("./lib/HTMLPicture.js");
var nextid = 0;
@@ -16,46 +18,22 @@ const image_filename_format = (_id, src, width, format, _options) => {
return `${name}-${width}w.${format}`;
};
-function image_metadata_log(metadata, source) {
- for(let format in metadata) {
- for(let img of metadata[format]) {
- console.log(`${source.padEnd(10)} ${format.padEnd(5)} ${`${img.width}x${img.height}`.padEnd(10)} ${img.outputPath}`);
- }
- }
-}
-
-async function shortcode_image(src, alt, classes = "") {
- let metadata = await Image(src, {
- widths: [300, null],
- formats: ["avif", "jpeg"],
- outputDir: `./_site/img/`,
- filenameFormat: image_filename_format
- });
- image_metadata_log(metadata, `IMAGE`);
+async function shortcode_image(src, alt) {
- let imageAttributes = {
- class: classes,
- alt: htmlentities.encode(alt),
- sizes: Object.values(metadata)[0].map((el) => `${el.width}w`).join(" "),
- loading: "lazy",
- decoding: "async",
- };
-
- // You bet we throw an error on missing alt in `imageAttributes` (alt="" works okay)
- return Image.generateHTML(metadata, imageAttributes);
+ return HTMLPicture(
+ src, alt,
+ `./_site/img`, `/img`
+ );
}
async function shortcode_image_url(src) {
- let metadata = await Image(src, {
- widths: [ null ],
- formats: [ "jpeg" ],
- outputDir: `./_site/img/`,
- filenameFormat: image_filename_format
- });
- image_metadata_log(metadata, `IMAGE_URL`);
+ const src_parsed = path.parse(src);
+ const target = path.join(`./_site/img`, src_parsed.base);
+ if(!fs.existsSync(path.dirname(target)))
+ await fs.promises.mkdir(target_dir, { recursive: true });
+ await fs.promises.copyFile(src, target);
- let data = metadata.jpeg[metadata.jpeg.length - 1];
- return data.url;
+ return path.join(`/img`, src_parsed.base);
}
async function shortcode_image_urlpass(src) {
@@ -82,14 +60,14 @@ async function shortcode_gallerybox(content, src, idthis, idprev, idnext) {
}
async function fetch(url) {
- let package = JSON.parse(await fs.promises.readFile(
+ const pkg_obj = JSON.parse(await fs.promises.readFile(
path.join(__dirname, "package.json"), "utf8"
));
return (await phin({
url,
headers: {
- "user-agent": `WorldEditAdditionsStaticBuilder/${package.version} (Node.js/${process.version}; ${os.platform()} ${os.arch()}) eleventy/${package.devDependencies["@11ty/eleventy"].replace(/\^/, "")}`
+ "user-agent": `WorldEditAdditionsStaticBuilder/${pkg_obj.version} (Node.js/${process.version}; ${os.platform()} ${os.arch()}) eleventy/${pkg_obj.devDependencies["@11ty/eleventy"].replace(/\^/, "")}`
},
followRedirects: true,
parse: "string"
diff --git a/.docs/Reference.11tydata.js b/.docs/Reference.11tydata.js
index 1bcba38..6b68fc8 100644
--- a/.docs/Reference.11tydata.js
+++ b/.docs/Reference.11tydata.js
@@ -1,5 +1,12 @@
+"use strict";
+
const fs = require("fs");
const path = require("path");
+
+const columnify = require("columnify");
+const htmlentities = require("html-entities");
+
+const a = require("./lib/Ansi.js");
const parse_sections = require("./lib/parse_sections.js");
let { sections, categories } = parse_sections(fs.readFileSync(
@@ -15,11 +22,20 @@ sections = sections.slice(1).sort((a, b) => a.title.replace(/^\/+/g, "").localeC
console.log(`REFERENCE SECTION TITLES`)
-console.log(sections
- .map(s => [s.category, s.title].join(`\t`)).join(`\n`));
+console.log(columnify(sections.map(s => { return {
+ category: `${a.hicol}${a.fyellow}${s.category}${a.reset}`,
+ command: `${a.hicol}${a.fmagenta}${htmlentities.decode(s.title)}${a.reset}`
+} })));
+// console.log(sections
+// .map(s => `${a.fyellow}${a.hicol}${s.category}${a.reset}\t${a.fmagenta}${a.hicol}${s.title}${a.reset}`).join(`\n`));
console.log(`************************`);
-console.log(`REFERENCE SECTION COLOURS`, categories);
+console.log(`REFERENCE SECTION COLOURS`);
+console.log(columnify(Array.from(categories).map(el => { return {
+ category: el[0],
+ colour: el[1]
+} })));
+
module.exports = {
layout: "theme.njk",
title: "Reference",
diff --git a/.docs/Reference.html b/.docs/Reference.html
index bf10399..2cfefcc 100644
--- a/.docs/Reference.html
+++ b/.docs/Reference.html
@@ -14,11 +14,11 @@
@@ -128,6 +128,7 @@
function do_filter() {
let el_search = document.querySelector("#input-filter");
let el_searchall = document.querySelector("#input-searchall");
+ localStorage.setItem("commandlist-searchall", el_searchall.checked);
/* Filterable items
- Sections
- Commands in the command list
@@ -165,6 +166,22 @@
el_next.classList.remove("visible", "hidden");
el_next.classList.add(show ? "visible" : "hidden");
}
+
+ let commandlist_is_categorical = document.querySelector("button.active[data-mode=categorical]") !== null;
+ if(commandlist_is_categorical) {
+ let container = document.querySelector(".command-container");
+ for(let i = 0; i < container.children.length; i++) {
+ if(container.children[i].nodeName.toLowerCase() === "ul") {
+ let class_name = "visible";
+ if(container.children[i].querySelector("li.visible") === null)
+ class_name = "hidden";
+ if(i > 0) {
+ container.children[i-1].classList.remove("visible", "hidden");
+ container.children[i-1].classList.add(class_name);
+ }
+ }
+ }
+ }
}
window.addEventListener("load", (_event) => {
@@ -181,6 +198,9 @@
els_cats[i].addEventListener("touchend", handle_display_mode);
}
+ if(localStorage.getItem("commandlist-searchall") !== null)
+ el_searchall.checked = localStorage.getItem("commandlist-searchall") === "true";
+
if(localStorage.getItem("commandlist-displaymode") !== null)
set_display_mode(localStorage.getItem("commandlist-displaymode"))
});
diff --git a/.docs/lib/Ansi.js b/.docs/lib/Ansi.js
new file mode 100644
index 0000000..4bb7493
--- /dev/null
+++ b/.docs/lib/Ansi.js
@@ -0,0 +1,88 @@
+"use strict";
+
+/**
+ * Generates various VT100 ANSI escape sequences.
+ * Ported from C#.
+ * @licence MPL-2.0
+ * @source https://gist.github.com/a4edd3204a03f4eedb79785751efb0f3#file-ansi-cs
+ * @author Starbeamrainbowlabs
+ * GitHub: @sbrl | Twitter: @SBRLabs | Reddit: u/Starbeamrainbowlabs
+ ***** Changelog *****
+ * 27th March 2019:
+ * - Initial public release
+ * 9th March 2020:
+ * - Add Italics (\u001b[3m])
+ * - Export a new instance of it by default (makes it so that there's 1 global instance)
+ * 5th September 2020:
+ * - Add support for NO_COLOR environment variable
+ */
+class Ansi {
+ constructor() {
+ /**
+ * Whether we should *actually* emit ANSI escape codes or not.
+ * Useful when we want to output to a log file, for example
+ * @type {Boolean}
+ */
+ this.enabled = true;
+
+ this.escape_codes();
+ }
+
+ escape_codes() {
+ if(typeof process !== "undefined" && typeof process.env.NO_COLOR == "string") {
+ this.enabled = false;
+ return;
+ }
+ // Solution on how to output ANSI escape codes in C# from here:
+ // https://www.jerriepelser.com/blog/using-ansi-color-codes-in-net-console-apps
+ this.reset = this.enabled ? "\u001b[0m" : "";
+ this.hicol = this.enabled ? "\u001b[1m" : "";
+ this.locol = this.enabled ? "\u001b[2m" : "";
+ this.italics = this.enabled ? "\u001b[3m" : "";
+ this.underline = this.enabled ? "\u001b[4m" : "";
+ this.inverse = this.enabled ? "\u001b[7m" : "";
+ this.fblack = this.enabled ? "\u001b[30m" : "";
+ this.fred = this.enabled ? "\u001b[31m" : "";
+ this.fgreen = this.enabled ? "\u001b[32m" : "";
+ this.fyellow = this.enabled ? "\u001b[33m" : "";
+ this.fblue = this.enabled ? "\u001b[34m" : "";
+ this.fmagenta = this.enabled ? "\u001b[35m" : "";
+ this.fcyan = this.enabled ? "\u001b[36m" : "";
+ this.fwhite = this.enabled ? "\u001b[37m" : "";
+ this.bblack = this.enabled ? "\u001b[40m" : "";
+ this.bred = this.enabled ? "\u001b[41m" : "";
+ this.bgreen = this.enabled ? "\u001b[42m" : "";
+ this.byellow = this.enabled ? "\u001b[43m" : "";
+ this.bblue = this.enabled ? "\u001b[44m" : "";
+ this.bmagenta = this.enabled ? "\u001b[45m" : "";
+ this.bcyan = this.enabled ? "\u001b[46m" : "";
+ this.bwhite = this.enabled ? "\u001b[47m" : "";
+ }
+
+ // Thanks to http://ascii-table.com/ansi-escape-sequences.php for the following ANSI escape sequences
+ up(lines = 1) {
+ return this.enabled ? `\u001b[${lines}A` : "";
+ }
+ down(lines = 1) {
+ return this.enabled ? `\u001b[${lines}B` : "";
+ }
+ right(lines = 1) {
+ return this.enabled ? `\u001b[${lines}C` : "";
+ }
+ left(lines = 1) {
+ return this.enabled ? `\u001b[${lines}D` : "";
+ }
+
+ jump_to(x, y) {
+ return this.enabled ? `\u001b[${y};${x}H` : "";
+ }
+
+ cursor_pos_save() {
+ return this.enabled ? `\u001b[s` : "";
+ }
+ cursor_pos_restore() {
+ return this.enabled ? `\u001b[u` : "";
+ }
+}
+
+module.exports = new Ansi();
diff --git a/.docs/lib/HTMLPicture.js b/.docs/lib/HTMLPicture.js
new file mode 100644
index 0000000..654ced8
--- /dev/null
+++ b/.docs/lib/HTMLPicture.js
@@ -0,0 +1,155 @@
+"use strict";
+
+const os = require(`os`);
+const fs = require("fs");
+const path = require("path");
+
+const pretty_ms = require("pretty-ms");
+const debug = require("debug")("image");
+const imagickal = require("imagickal");
+const htmlentities = require("html-entities");
+
+const a = require("./Ansi.js");
+
+function calculate_size(width, height, size_spec) {
+ if(size_spec.indexOf("%") > -1) {
+ // It's a percentage
+ const multiplier = parseInt(size_spec.replace(/%/, ""), 10) / 100;
+ return {
+ width: Math.ceil(width * multiplier),
+ height: Math.ceil(height * multiplier)
+ };
+ }
+ else {
+ // It's an absolute image width
+ const new_width = parseInt(size_spec, 10);
+ return {
+ width: new_width,
+ height: Math.ceil(new_width/width * height)
+ };
+ }
+}
+
+// Main task list - we make sure it completes before exiting.
+var queue = null;
+var pMemoize = null;
+
+async function make_queue() {
+ // 1: Setup task queue
+ const PQueue = (await import("p-queue")).default;
+ let concurrency = os.cpus().length;
+ if(process.env["MAX_CONCURRENT"])
+ concurrency = parseInt(process.env["MAX_CONCURRENT"], 10);
+ debug(`Image conversion queue concurrency: `, concurrency);
+ queue = new PQueue({ concurrency });
+ queue.on("idle", () => console.log(`IMAGE ${a.fcyan}all conversions complete${a.reset}`));
+ process.on("exit", async () => {
+ debug(`Waiting for image conversions to finish...`);
+ await queue.onEmpty();
+ debug(`All image conversions complete.`);
+ });
+}
+
+async function srcset(source_image, target_dir, urlpath, format = "__AUTO__", sizes = [ "25%", "50%", "100%" ], quality = 95, strip = true) {
+ if(queue === null) await make_queue();
+
+ const source_parsed = path.parse(source_image);
+ // ext contains the dot . already
+ const target_format = format == "__AUTO__" ? source_parsed.ext.replace(/\./g, "") : format;
+
+ const source_size = await imagickal.dimensions(source_image);
+
+ debug(`SOURCE_SIZE`, source_size, `TARGET_FORMAT`, target_format);
+
+ let setitems = await Promise.all(sizes.map(async (size) => {
+ let target_filename = `${source_parsed.name}_${size}.${target_format}`
+ .replace(/%/, "pcent");
+ let target_current = path.join(
+ target_dir,
+ target_filename
+ );
+ queue.add(async () => {
+ const start = new Date();
+ await imagickal.transform(source_image, target_current, {
+ resize: { width: size },
+ quality,
+ strip
+ });
+ console.log(`IMAGE\t${a.fcyan}${queue.size}/${queue.pending} tasks${a.reset}\t${a.fyellow}${pretty_ms(new Date() - start)}${a.reset}\t${a.fgreen}${target_current}${a.reset}`);
+ });
+ // const size_target = await imagickal.dimensions(target_current);
+
+ const predict = calculate_size(source_size.width, source_size.height, size);
+ // debug(`size spec:`, size, `size predicted: ${predict.width}x${predict.height} actual: ${size_target.width}x${size_target.height}`);
+ return `${path.resolve(urlpath, target_filename)} ${predict.width}w`;
+ }));
+
+ return setitems.join(", ");
+}
+
+/**
+ * Generates a string of HTML for a