"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("<", "<") .replaceAll(">", ">") .replaceAll('"', """) .replaceAll("'", "'"); } 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); }