document.addEventListener('DOMContentLoaded', () => {
fetch('functions-list.json')
.then(r => r.json())
.then(async (fileList) => {
console.log('List of function JSON files:', fileList);
const allDocs = [];
for (const fname of fileList) {
const url = 'functions/' + fname;
try {
const resp = await fetch(url);
if (!resp.ok) {
console.warn(`Failed to fetch ${url}: HTTP ${resp.status}`);
continue;
}
const docObj = await resp.json();
allDocs.push(docObj);
} catch (err) {
console.error(`Error fetching doc ${url}:`, err);
}
}
buildDocsPage(allDocs);
})
.catch(err => {
console.error('Error loading functions-list.json:', err);
});
});
function buildDocsPage(docs) {
const articleDocs = [];
const functionDocs = [];
for (const d of docs) {
const badges = Array.isArray(d.heading?.badges) ? d.heading.badges : [];
if (badges.includes('ARTICLE')) {
articleDocs.push(d);
} else {
functionDocs.push(d);
}
}
articleDocs.sort((a, b) => {
const at = a.heading?.title || '';
const bt = b.heading?.title || '';
return at.localeCompare(bt);
});
functionDocs.sort((a, b) => {
const at = a.heading?.title || '';
const bt = b.heading?.title || '';
return at.localeCompare(bt);
});
buildSidebarNav(functionDocs);
let htmlOutput = '';
for (const art of articleDocs) {
htmlOutput += buildDocBlockHtml(art);
}
for (const fnDoc of functionDocs) {
htmlOutput += buildDocBlockHtml(fnDoc);
}
const container = document.getElementById('docs-container');
if (!container) {
console.warn('No #docs-container to insert docs!');
return;
}
container.innerHTML = htmlOutput;
enableEditabilityAllDocs();
attachJsonButtons();
highlightAllCodeBlocks();
setupFiltersAndScrolling();
applyFilters();
}
function buildSidebarNav(functionDocs) {
const compSet = new Set();
const groupSet = new Set();
for (const doc of functionDocs) {
if (doc.dataComponent) compSet.add(doc.dataComponent);
if (doc.group) groupSet.add(doc.group);
}
const listEl = document.querySelector('.function-list');
if (!listEl) {
console.warn('No .function-list element found.');
return;
}
listEl.innerHTML = '';
for (const doc of functionDocs) {
const comp = doc.dataComponent || '';
const title = doc.heading?.title || '';
const finalId = comp && title ? `${comp}:${title}` : 'unknown';
const li = document.createElement('li');
li.setAttribute('data-component', comp);
li.setAttribute('data-groupUuid', doc.group || '');
const a = document.createElement('a');
a.href = '#' + finalId;
a.setAttribute('data-component', comp);
a.textContent = finalId;
li.appendChild(a);
listEl.appendChild(li);
}
const compSelect = document.getElementById('component-filter-select');
if (compSelect) {
for (let i = compSelect.options.length - 1; i >= 1; i--) {
compSelect.remove(i);
}
[...compSet].sort().forEach(c => {
const opt = document.createElement('option');
opt.value = c;
opt.textContent = c;
compSelect.appendChild(opt);
});
}
const groupSelect = document.getElementById('group-filter-select');
if (groupSelect) {
for (let i = groupSelect.options.length - 1; i >= 1; i--) {
groupSelect.remove(i);
}
[...groupSet].sort().forEach(g => {
const opt = document.createElement('option');
opt.value = g;
opt.textContent = g;
groupSelect.appendChild(opt);
});
}
}
function buildSectionHtml(section) {
if (!section || !section.type) return '';
switch (section.type) {
case 'heading':
if (!section.level || !section.text) return '';
return `<h${section.level} class="${section.level > 2 ? 'section-heading' : 'article-title'}">${escapeHTML(section.text)}</h${section.level}>`;
case 'paragraph':
return `<p>${escapeHTML(section.text || '')}</p>`;
case 'html':
return section.html || '';
case 'list':
if (!Array.isArray(section.items)) return '';
return `<ul>${section.items.map(item => `<li>${escapeHTML(item)}</li>`).join('')}</ul>`;
case 'codeBlock':
return `<div class="code-block">${escapeHTML(section.code || '')}</div>`;
case 'image':
return `<img src="${escapeAttr(section.src)}" alt="${escapeAttr(section.alt || '')}" style="max-width:100%;display:block;margin:1em auto;" />`;
default:
return '';
}
}
function buildDocBlockHtml(doc) {
const comp = doc.dataComponent || '';
const title = doc.heading?.title || '(No title)';
const finalId = comp && title ? `${comp}:${title}` : 'unknown';
const badges = Array.isArray(doc.heading?.badges) ? doc.heading.badges : [];
const badgeHtml = badges.map(b => `<span class="category-badge">${escapeHTML(b)}</span>`).join(' ');
const compBadge = comp ? `<span class="component-badge">:${escapeHTML(comp)}</span>` : '';
const headingHtml = `<h1>${escapeHTML(title)} ${compBadge} ${badgeHtml}</h1>`;
let synopsisHtml = '';
if (doc.synopsis && doc.synopsis.trim()) {
synopsisHtml = `
<div class="description-block bg-orange">
${escapeHTML(doc.synopsis)}
</div>`;
}
let mainHtml = '';
if (doc.sections && Array.isArray(doc.sections)) {
mainHtml = doc.sections.map(buildSectionHtml).join('');
} else if (doc.htmlBody && doc.htmlBody.trim()) {
mainHtml = doc.htmlBody;
} else if (Array.isArray(doc.codeBlocks) && doc.codeBlocks.length > 0) {
let blocks = '';
doc.codeBlocks.forEach(block => {
const replaced = block.replace(/\\n/g, '\n');
blocks += `<div class="code-block">${escapeHTML(replaced)}</div>`;
});
mainHtml = blocks;
}
let notesHtml = '';
if (Array.isArray(doc.notes) && doc.notes.length > 0) {
const lines = doc.notes.map(n => `<p>${escapeHTML(n)}</p>`).join('');
notesHtml = `<div class="notes-block">${lines}</div>`;
}
const docStr = JSON.stringify(doc);
const dataAttr = ` data-doc='${escapeAttr(docStr)}'`;
const jsonButton = `<button type="button" class="json-button">JSON</button>`;
const groupVal = doc.group || '';
return `
<div class="doc-block"
id="${escapeHTML(finalId)}"
data-component="${escapeHTML(comp)}"
data-groupUuid="${escapeHTML(groupVal)}"
${dataAttr}>
${headingHtml}
${synopsisHtml}
${mainHtml}
${notesHtml}
${jsonButton}
</div>
`;
}
function enableEditabilityAllDocs() { }
function attachJsonButtons() { }
function setupFiltersAndScrolling() { }
function applyFilters() { }
function scrollMainContentTo(idVal) { }
function highlightAllCodeBlocks() { }
function highlightMuMuSyntax(code) { }
function escapeHTML(str) { }
function escapeAttr(str) { }
function getCaretCharacterOffsetWithin(el) { }
function setCaretCharacterOffsetWithin(el, offset) { }
function isOrContainsNode(ancestor, descendant) { }