103 lines
3.4 KiB
JavaScript
103 lines
3.4 KiB
JavaScript
/*
|
|
* Wiki page references.
|
|
*
|
|
* This module owns the syntax used to identify root and namespaced pages.
|
|
* It has no application or DOM state.
|
|
*/
|
|
|
|
/** Split a wiki reference into its namespace and page slug. */
|
|
function splitPageReference(reference) {
|
|
const value = String(reference || "");
|
|
const separator = value.indexOf(":");
|
|
if (separator < 0) return { namespace: "", slug: value };
|
|
return { namespace: value.slice(0, separator), slug: value.slice(separator + 1) };
|
|
}
|
|
|
|
/** Build the compact external reference for a root or namespaced page. */
|
|
function pageReference(namespace, slug) {
|
|
const cleanNamespace = String(namespace || "").trim();
|
|
return cleanNamespace ? `${cleanNamespace}:${slug}` : slug;
|
|
}
|
|
|
|
/** Convert one freely entered reference part to a bounded wiki identifier. */
|
|
function slugifyReferencePart(value, maximum) {
|
|
return String(value || "")
|
|
.normalize("NFKD")
|
|
.toLocaleLowerCase()
|
|
.replace(/[\u0300-\u036f]/g, "")
|
|
.replace(/[^\p{L}\p{N}]+/gu, "-")
|
|
.replace(/^-+|-+$/g, "")
|
|
.slice(0, maximum)
|
|
.replace(/-+$/g, "");
|
|
}
|
|
|
|
/**
|
|
* goal : Convert a freely entered page title/address to a wiki reference.
|
|
* pre : input may contain a namespace separated by a colon.
|
|
* post : No page is created and no application state is changed.
|
|
* result : A normalized reference, or null when the input is empty or invalid.
|
|
*/
|
|
function newPageReference(input) {
|
|
const parts = splitPageReference(String(input || "").trim());
|
|
const namespace = parts.namespace ? slugifyReferencePart(parts.namespace, 80) : "";
|
|
const slug = slugifyReferencePart(parts.slug, 120);
|
|
if (!slug || (parts.namespace && !namespace)) return null;
|
|
return pageReference(namespace, slug);
|
|
}
|
|
|
|
/** Normalize human-readable text for ambiguity-aware WikiWord matching. */
|
|
function normalizeMentionText(text) {
|
|
return String(text || "")
|
|
.normalize("NFKD")
|
|
.replace(/[\u0300-\u036f]/g, "")
|
|
.toLocaleLowerCase()
|
|
.replace(/[^\p{L}\p{N}]+/gu, "");
|
|
}
|
|
|
|
/** Return the separate capitalized words when text is a classic WikiWord. */
|
|
function wikiWordParts(text) {
|
|
const value = String(text || "");
|
|
if (!/^(?:\p{Lu}\p{Ll}+){2,}$/u.test(value)) return [];
|
|
return value.match(/\p{Lu}\p{Ll}+/gu) || [];
|
|
}
|
|
|
|
/**
|
|
* goal : Resolve a classic WikiWord to an existing page or new page target.
|
|
* pre : aliases is an ambiguity-aware map built by wiki-markdown.js.
|
|
* post : No page is created and aliases is not changed.
|
|
* result : A slug/title pair, or null when text is not a WikiWord.
|
|
*/
|
|
function wikiWordTarget(text, aliases, namespace = "") {
|
|
const parts = wikiWordParts(text);
|
|
if (parts.length === 0) return null;
|
|
|
|
const aliasKey = `${String(namespace || "").toLocaleLowerCase()}:${normalizeMentionText(text)}`;
|
|
const existing = aliases.get(aliasKey);
|
|
if (existing) return { slug: existing.slug, title: existing.title };
|
|
|
|
const slug = parts.map((part) => part.toLocaleLowerCase()).join("-");
|
|
return {
|
|
slug: pageReference(namespace, slug),
|
|
title: parts.join(" ")
|
|
};
|
|
}
|
|
|
|
/** Derive a readable title from the page part of a wiki reference. */
|
|
function slugTitle(reference) {
|
|
const slug = splitPageReference(reference || "").slug;
|
|
return slug
|
|
.split("-")
|
|
.filter(Boolean)
|
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
.join(" ");
|
|
}
|
|
|
|
export {
|
|
newPageReference,
|
|
normalizeMentionText,
|
|
pageReference,
|
|
slugTitle,
|
|
splitPageReference,
|
|
wikiWordTarget
|
|
};
|