diff --git a/_tools/generate-pages.js b/_tools/generate-pages.js
index 09ac71e3..f3c47b6a 100644
--- a/_tools/generate-pages.js
+++ b/_tools/generate-pages.js
@@ -1,5 +1,7 @@
const fs = require("fs/promises");
const html = require('escape-html-template-tag');
+const {safe} = html;
+const {JSDOM} = require("jsdom");
const { expandCrawlResult } = require('reffy/src/lib/util');
@@ -8,6 +10,8 @@ const aliasIndex = new Map();
const linksIndex = new Map();
const letters = new Map();
+let results;
+
const typeInfo = {
"grammar": {
human: "grammar",
@@ -195,6 +199,33 @@ function sortByArea([relatedTerm1, relatedTermId1], [relatedTerm2, relatedTermId
return typeInfo[type1].area.localeCompare(typeInfo[type2].area) || type1.localeCompare(type2) || relatedTerm1.localeCompare(relatedTerm2);
}
+function findDfn(link) {
+ console.log("searching for " + link + " in " + results.length + " specs");
+ for (const spec of results) {
+ const dfn = spec.dfns?.find(d => d.href === link);
+ if (dfn) return {dfn, spec};
+ }
+}
+
+function substituteProseLinks(html) {
+ // TODO: how reliably can we assume that the html can be put inside a
?
+ const frag = JSDOM.fragment("
" + html + "
");
+ frag.querySelectorAll('a[href^="https://"]').forEach(link => {
+ const dfnData = findDfn(link.href);
+ if (dfnData) {
+ const {dfn, spec} = dfnData;
+ const term = cleanTerm(dfn.linkingText[0], dfn.type);
+ const termId = generateTermId(dfn, spec);
+ console.log(link.href, term, termId);
+ const dfnLink = getLink(term, termId);
+ if (dfnLink) {
+ link.href = dfnLink;
+ }
+ }
+ });
+ return frag.firstChild.innerHTML;
+}
+
function getScopingTermId(type, _for, displayTerm, dfns) {
function returnIfFound(candidates, matches) {
if (matches.length === 1) {
@@ -413,10 +444,23 @@ ${content}`);
}
+function generateTermId(dfn, spec) {
+ if (dfn.for.length === 0) {
+ if (typeInfo[dfn.type]?.exclusiveNamespace) {
+ return `@@${dfn.type}`;
+ } else {
+ return `${spec.series.shortname}%%${dfn.type}`;
+ }
+ } else {
+ // by convention, we use the first 'for' by alphabetical order
+ return `${dfn.for.sort()[0]}@${dfn.type}`;
+ }
+}
+
(async function() {
const jsonIndex = await fs.readFile("./webref/ed/index.json", "utf-8");
const index = JSON.parse(jsonIndex);
- const {results} = await expandCrawlResult(index, './webref/ed/', ['dfns', 'links']);
+ results = (await expandCrawlResult(index, './webref/ed/', ['dfns', 'links'])).results;
for (const spec of results) {
for (const dfn of (spec.dfns || [])) {
if (dfn.access === "private") continue;
@@ -425,22 +469,14 @@ ${content}`);
const term = cleanTerm(dfn.linkingText[0], dfn.type);
const displayTerm = dfn.linkingText[0].replace(/^"/, '').replace(/"$/, '');
const termEntry = termIndex.get(term) ?? {};
- let termId;
+ const termId = generateTermId(dfn, spec);
let prefixes = [];
- if (dfn.for.length === 0) {
- if (typeInfo[dfn.type]?.exclusiveNamespace) {
- termId = `@@${dfn.type}`;
- } else {
- termId = `${spec.series.shortname}%%${dfn.type}`;
- }
- } else {
+ if (dfn.for.length) {
if (dfn.type === "constructor" && term !== 'constructor()') {
prefixes = ['new '];
} else if (['method', 'const', 'attribute', 'dict-member'].includes(dfn.type)) {
prefixes = dfn.for.map(_for => `${_for}.`);
}
- // by convention, we use the first 'for' by alphabetical order
- termId = `${dfn.for.sort()[0]}@${dfn.type}`;
}
const subtermEntry = termEntry[termId] ?? {shortname: spec.series.shortname, type: dfn.type, _for: dfn.for, dfns: [], prefixes: [], refs: [], related: [], displayTerm, sortTerm: `${displayTerm}-${prefixes[0] ?? ''}`};
subtermEntry.dfns.push({...dfn, spec: spec.shortTitle});
@@ -510,16 +546,29 @@ ${content}`);
Color key: WebIDL
CSS
Markup
HTTP
${letters.get(entry).sort((a,b) => a.toLowerCase().localeCompare(b.toLowerCase())).map(term => {
+
return html`${Object.keys(termIndex.get(term)).sort((a,b) => termIndex.get(term)[a].sortTerm.localeCompare(termIndex.get(term)[b].sortTerm))
.map(termId => {
const {displayTerm, type, _for, dfns, prefixes, refs, related} = termIndex.get(term)[termId];
+ let definitionContent = ``;
+ const withProse = dfns.some(d => d.htmlProse && d.access === "public");
+ if (!withProse) {
+ definitionContent = html.join(dfns.map(dfn => {
+ return html`${dfn.spec} `;
+ }), ', ');
+ } else if (dfns.length === 1) {
+ const dfn = dfns[0];
+ definitionContent = html`${dfn.spec}: ${safe(substituteProseLinks(dfn.htmlProse))}`;
+ } else {
+ definitionContent = html` ${html.join(dfns.map(dfn => {
+ return html`- ${dfn.spec}${dfn.htmlProse? html`: ${safe(substituteProseLinks(dfn.htmlProse))}` : ""}
`;
+ }), '')}`;
+ }
+ const definitionList = html`- Defined in ${definitionContent}
`;
const webidlpedia = ['interface', 'dictionary', 'enum', 'typedef'].includes(type) ? html`- see also WebIDLPedia
` : '';
return html`- ${composeDisplayName(displayTerm, type, _for, prefixes[0] || '', dfns, termId)}
${prefixes.slice(1).map(p => html`- ${composeDisplayName(displayTerm, type, _for, p, dfns, termId)}
`)}
-- Defined in ${html.join(dfns.map(dfn => {
- return html`
- ${dfn.spec} `;
- }), ', ')}
+${definitionList}
${refs.length ? html`- Referenced in ${html.join(refs.map(ref => {
return html`
${ref.title}`;
diff --git a/package.json b/package.json
index f2cc4a21..cb1dc5ae 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,7 @@
{
"dependencies": {
"escape-html-template-tag": "^1.2.0",
+ "jsdom": "^24.0.0",
"node-fetch": "^2.6.1",
"reffy": "^6.0.0",
"vnu-jar": "^20.6.30"