big cmap refactoring

This commit is contained in:
2026-09-03 08:40:15 +02:00
parent f0562a06cc
commit 2c141b6d3f
32 changed files with 5616 additions and 4216 deletions
+93
View File
@@ -0,0 +1,93 @@
"use strict";
export const debugPrefix = "[racket-wiki:cmap 0.2.122]";
export function debug(message, details) {
if (details === undefined) {
console.info(debugPrefix, message);
return;
}
console.info(debugPrefix, message, details);
}
export function elementDescription(element) {
if (!(element instanceof Element)) return String(element);
return {
tag: element.tagName,
id: element.id || null,
classes: Array.from(element.classList),
itemId: element.dataset.rwCmapItemId || null
};
}
export function selectionStyle(element) {
if (!(element instanceof Element) || typeof window.getComputedStyle !== "function") return null;
const style = window.getComputedStyle(element);
return {
pointerEvents: style.pointerEvents,
outline: style.outline,
outlineOffset: style.outlineOffset,
boxShadow: style.boxShadow,
overflow: style.overflow,
zIndex: style.zIndex
};
}
export function escapeHtml(value) {
return String(value || "")
.replaceAll("&", "&")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
.replaceAll("'", "&#039;");
}
export function numberOr(value, fallback) {
return Number.isFinite(value) ? value : fallback;
}
export function conceptDescriptionReference(label, id) {
const slug = String(label || "")
.normalize("NFKD")
.toLocaleLowerCase()
.replace(/[\u0300-\u036f]/g, "")
.replace(/[^\p{L}\p{N}]+/gu, "-")
.replace(/^-+|-+$/g, "")
.slice(0, 120)
.replace(/-+$/g, "");
return `cmap:${slug || `concept-${id}`}`;
}
export function newConceptId() {
if (window.crypto && typeof window.crypto.randomUUID === "function") {
try {
return window.crypto.randomUUID().toLowerCase();
} catch (_error) {
// randomUUID can be exposed but forbidden in an insecure/file context.
}
}
const bytes = new Uint8Array(16);
if (window.crypto && typeof window.crypto.getRandomValues === "function") {
window.crypto.getRandomValues(bytes);
} else {
for (let index = 0; index < bytes.length; index += 1) {
bytes[index] = Math.floor(Math.random() * 256);
}
}
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0"));
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
}
export function normalizeConceptTags(tags) {
if (!Array.isArray(tags)) return [];
return tags.map((tag) => {
if (typeof tag === "string") return { type: "label", value: tag.trim() };
if (!tag || typeof tag !== "object") return null;
return {
type: String(tag.type || "label").trim() || "label",
value: String(tag.value || tag.name || "").trim()
};
}).filter((tag) => tag && tag.value);
}