From 595d98f16159bdf7463adc140509ded479130b8b Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Wed, 2 Sep 2026 08:55:26 +0200 Subject: [PATCH] revert 526a2db615ffb512af5f884ea5465b3f728522a9 (#1077) Signed-off-by: Jan Kowalleck --- docgen/json/gen.sh | 2 +- .../json/templates/cyclonedx/schema_doc.css | 17 +- docgen/json/templates/cyclonedx/schema_doc.js | 347 +----------------- .../templates/cyclonedx/schema_doc.min.js | 10 +- 4 files changed, 12 insertions(+), 364 deletions(-) diff --git a/docgen/json/gen.sh b/docgen/json/gen.sh index 7bd526a6b..881693799 100755 --- a/docgen/json/gen.sh +++ b/docgen/json/gen.sh @@ -61,7 +61,7 @@ generate () { mkdir -p "$OUT_DIR" generate-schema-doc \ - --config link_to_reused_ref \ + --config no_link_to_reused_ref \ --config no_show_breadcrumbs \ --config no_collapse_long_descriptions \ --deprecated-from-description \ diff --git a/docgen/json/templates/cyclonedx/schema_doc.css b/docgen/json/templates/cyclonedx/schema_doc.css index 1367c320a..cb73114fd 100644 --- a/docgen/json/templates/cyclonedx/schema_doc.css +++ b/docgen/json/templates/cyclonedx/schema_doc.css @@ -277,19 +277,4 @@ ul .dropdown-menu li { .highlight .vg { color: #bb60d5 } /* Name.Variable.Global */ .highlight .vi { color: #bb60d5 } /* Name.Variable.Instance */ .highlight .vm { color: #bb60d5 } /* Name.Variable.Magic */ -.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */ - -/* ═══════════════════════════════════════════════════════════ - Inline expansion for reused definitions (ref-links) - ═══════════════════════════════════════════════════════════ */ - -/* Hide the "Same definition as..." link text; content is - cloned inline automatically when the parent row expands. */ -.ref-link[data-ref-expanded="true"] { - display: none; -} - -/* Container for the cloned definition content */ -.ref-expand-content { - margin-top: 0.25rem; -} \ No newline at end of file +.highlight .il { color: #40a070 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docgen/json/templates/cyclonedx/schema_doc.js b/docgen/json/templates/cyclonedx/schema_doc.js index fa9faf5be..93f1669e6 100644 --- a/docgen/json/templates/cyclonedx/schema_doc.js +++ b/docgen/json/templates/cyclonedx/schema_doc.js @@ -1,13 +1,6 @@ document.addEventListener('click', function(event) { var anchor = event.target.closest('a[href^="#"]'); if (anchor) { - // Skip ref-links; they are replaced by inline expansions - if (anchor.classList.contains('ref-link')) { - event.preventDefault(); - return; - } - // Don't interfere with Bootstrap tabs or collapse toggles - if (anchor.getAttribute('data-bs-toggle')) return; event.preventDefault(); history.pushState({}, '', anchor.href); } @@ -78,342 +71,4 @@ function anchorLink(linkTarget) { }, 500); } }, 1000); -} - - -// ═══════════════════════════════════════════════════════════ -// Fix duplicate IDs produced by link_to_reused_ref -// ═══════════════════════════════════════════════════════════ -// -// The schema doc generator reuses the same IDs when inlining -// a $ref definition at multiple schema paths. Duplicate IDs -// break Bootstrap tabs/collapses because getElementById -// always returns the first match. This pass finds duplicates -// and rewrites subsequent occurrences so every ID is unique. -// ═══════════════════════════════════════════════════════════ - -(function() { - function fixDuplicateIds() { - var seen = {}; // id -> true for first occurrence - var dupCount = 0; - - // Pass 1: rename duplicate IDs. First occurrence keeps - // its id; subsequent occurrences get a unique suffix. - var allWithId = document.querySelectorAll('[id]'); - allWithId.forEach(function(el) { - var id = el.id; - if (!id) return; - if (seen[id]) { - dupCount++; - el.setAttribute('data-orig-id', id); - el.id = id + '__d' + dupCount; - } else { - seen[id] = true; - } - }); - - if (dupCount === 0) return; - - // Build lookup: origId -> [el, el, ...] for fast scoping - var renamed = {}; - document.querySelectorAll('[data-orig-id]').forEach(function(el) { - var origId = el.getAttribute('data-orig-id'); - if (!renamed[origId]) renamed[origId] = []; - renamed[origId].push(el); - }); - - // Build full candidate list: origId -> [el, ...] including - // both the original (first-occurrence) element and all renamed - // duplicates so that scoping works for every occurrence. - var allTargets = {}; - Object.keys(renamed).forEach(function(origId) { - var orig = document.getElementById(origId); - allTargets[origId] = orig ? [orig].concat(renamed[origId]) : renamed[origId]; - }); - - // Find the target element (original or renamed) that shares - // the closest common ancestor with the referrer. - function findLocalTarget(referrer, origId) { - var candidates = allTargets[origId]; - if (!candidates) return origId; - var scope = referrer.parentElement; - while (scope) { - for (var i = 0; i < candidates.length; i++) { - if (scope.contains(candidates[i])) return candidates[i].id; - } - scope = scope.parentElement; - } - return origId; - } - - // Pass 2: fix references that point to renamed IDs. - function fixHashAttr(el, attr) { - var val = el.getAttribute(attr); - if (!val || val.charAt(0) !== '#') return; - var refId = val.substring(1); - if (!renamed[refId]) return; - var localId = findLocalTarget(el, refId); - if (localId !== refId) el.setAttribute(attr, '#' + localId); - } - - function fixPlainAttr(el, attr) { - var val = el.getAttribute(attr); - if (!val || !renamed[val]) return; - var localId = findLocalTarget(el, val); - if (localId !== val) el.setAttribute(attr, localId); - } - - document.querySelectorAll('a[href^="#"]').forEach(function(el) { - fixHashAttr(el, 'href'); - }); - document.querySelectorAll('[data-bs-target^="#"]').forEach(function(el) { - fixHashAttr(el, 'data-bs-target'); - }); - document.querySelectorAll('[data-bs-parent^="#"]').forEach(function(el) { - fixHashAttr(el, 'data-bs-parent'); - }); - document.querySelectorAll('[aria-controls]').forEach(function(el) { - fixPlainAttr(el, 'aria-controls'); - }); - document.querySelectorAll('[aria-labelledby]').forEach(function(el) { - fixPlainAttr(el, 'aria-labelledby'); - }); - document.querySelectorAll('[onclick]').forEach(function(el) { - var onclick = el.getAttribute('onclick'); - if (!onclick) return; - var changed = false; - var updated = onclick.replace( - /anchorLink\('([^']+)'\)/g, - function(match, id) { - if (!renamed[id]) return match; - var localId = findLocalTarget(el, id); - if (localId !== id) { changed = true; return "anchorLink('" + localId + "')"; } - return match; - } - ).replace( - /setAnchor\('#([^']+)'\)/g, - function(match, id) { - if (!renamed[id]) return match; - var localId = findLocalTarget(el, id); - if (localId !== id) { changed = true; return "setAnchor('#" + localId + "')"; } - return match; - } - ); - if (changed) el.setAttribute('onclick', updated); - }); - } - - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', fixDuplicateIds); - } else { - fixDuplicateIds(); - } -})(); - - -// ═══════════════════════════════════════════════════════════ -// Automatic inline expansion for reused definitions -// ═══════════════════════════════════════════════════════════ -// -// When link_to_reused_ref is enabled, repeated definitions -// render as "Same definition as X" links pointing to the -// original. This enhancement hides those links and clones -// the original definition inline automatically when the -// parent property row is expanded. No user click required. -// The full HTML stays in the DOM for SEO crawlability. -// ═══════════════════════════════════════════════════════════ - -(function() { - var expandCounter = 0; - - /** - * Rewrite IDs inside a cloned subtree so they don't collide - * with the originals. Also updates internal href="#...", - * data-bs-target, data-bs-parent, and aria attributes. - */ - function deduplicateIds(container, suffix) { - var elements = container.querySelectorAll('[id]'); - var idMap = {}; - elements.forEach(function(el) { - var oldId = el.id; - var newId = oldId + suffix; - idMap[oldId] = newId; - el.id = newId; - }); - - container.querySelectorAll('[href]').forEach(function(el) { - var href = el.getAttribute('href'); - if (href && href.charAt(0) === '#') { - var refId = href.substring(1); - if (idMap[refId]) { - el.setAttribute('href', '#' + idMap[refId]); - } - } - }); - container.querySelectorAll('[data-bs-target]').forEach(function(el) { - var val = el.getAttribute('data-bs-target'); - if (val && val.charAt(0) === '#') { - var refId = val.substring(1); - if (idMap[refId]) { - el.setAttribute('data-bs-target', '#' + idMap[refId]); - } - } - }); - container.querySelectorAll('[data-bs-parent]').forEach(function(el) { - var val = el.getAttribute('data-bs-parent'); - if (val && val.charAt(0) === '#') { - var refId = val.substring(1); - if (idMap[refId]) { - el.setAttribute('data-bs-parent', '#' + idMap[refId]); - } - } - }); - container.querySelectorAll('[aria-controls]').forEach(function(el) { - var val = el.getAttribute('aria-controls'); - if (val && idMap[val]) { - el.setAttribute('aria-controls', idMap[val]); - } - }); - container.querySelectorAll('[aria-labelledby]').forEach(function(el) { - var val = el.getAttribute('aria-labelledby'); - if (val && idMap[val]) { - el.setAttribute('aria-labelledby', idMap[val]); - } - }); - - container.querySelectorAll('[onclick]').forEach(function(el) { - var onclick = el.getAttribute('onclick'); - if (onclick) { - var updated = onclick.replace( - /anchorLink\('([^']+)'\)/g, - function(match, id) { - return idMap[id] ? "anchorLink('" + idMap[id] + "')" : match; - } - ).replace( - /setAnchor\('#([^']+)'\)/g, - function(match, id) { - return idMap[id] ? "setAnchor('#" + idMap[id] + "')" : match; - } - ); - el.setAttribute('onclick', updated); - } - }); - } - - /** - * Check whether a node is "leading metadata" that already - * appears in the ref-link's container: the type badge - * (span.badge.value-type), a
, a description span, - * or whitespace text nodes between them. - */ - function isLeadingMeta(node) { - if (node.nodeType === 3) { - // Text node: skip if whitespace-only - return node.textContent.trim() === ''; - } - if (node.nodeType !== 1) return false; - var el = node; - // Type badge, e.g. - if (el.tagName === 'SPAN' && el.classList.contains('value-type')) return true; - //
element right after the type badge - if (el.tagName === 'BR') return true; - // Description span - if (el.tagName === 'SPAN' && el.classList.contains('description')) return true; - return false; - } - - /** - * Clone a source definition into the container that holds - * the ref-link. The ref-link itself is hidden via CSS. - * Leading type badge,
, and description are skipped - * because the container already shows them. - */ - function expandRefLink(link) { - // Skip if already expanded - if (link.getAttribute('data-ref-expanded') === 'true') return; - link.setAttribute('data-ref-expanded', 'true'); - - var targetId = link.getAttribute('href').substring(1); - var source = document.getElementById(targetId); - if (!source) return; - - expandCounter++; - var suffix = '__exp' + expandCounter; - - var content = document.createElement('div'); - content.className = 'ref-expand-content'; - - // Clone child nodes, skipping leading metadata that - // duplicates what the container already displays. - var nodes = source.childNodes; - var pastLeading = false; - for (var i = 0; i < nodes.length; i++) { - if (!pastLeading && isLeadingMeta(nodes[i])) continue; - pastLeading = true; - content.appendChild(nodes[i].cloneNode(true)); - } - - deduplicateIds(content, suffix); - - // Insert the cloned content after the ref-link - link.parentNode.insertBefore(content, link.nextSibling); - } - - /** - * Check whether a ref-link is directly visible within the - * panel that was just shown. Returns false if the link sits - * inside a nested collapse that is still hidden. - */ - function isVisibleInPanel(link, panel) { - var el = link.parentElement; - while (el && el !== panel) { - if (el.classList.contains('collapse') && !el.classList.contains('show')) { - return false; - } - el = el.parentElement; - } - return true; - } - - /** - * When a collapse panel is shown, expand only the ref-links - * that are directly visible (not buried in nested collapses). - */ - function onCollapseShown(e) { - var panel = e.target; - var refLinks = panel.querySelectorAll('.ref-link'); - refLinks.forEach(function(link) { - if (isVisibleInPanel(link, panel)) { - expandRefLink(link); - } - }); - } - - /** - * Initialize: hide ref-link text, listen for collapse events. - */ - function initRefLinks() { - var refLinks = document.querySelectorAll('.ref-link'); - refLinks.forEach(function(link) { - // Remove the original onclick - link.removeAttribute('onclick'); - - // Expand ref-links that are already visible on load - // (not inside any collapsed panel) - var parentCollapse = link.closest('.collapse'); - if (!parentCollapse || parentCollapse.classList.contains('show')) { - expandRefLink(link); - } - }); - - // Listen for Bootstrap collapse show events - document.addEventListener('shown.bs.collapse', onCollapseShown); - } - - // Run on DOM ready - if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', initRefLinks); - } else { - initRefLinks(); - } -})(); +} \ No newline at end of file diff --git a/docgen/json/templates/cyclonedx/schema_doc.min.js b/docgen/json/templates/cyclonedx/schema_doc.min.js index a3934ac7c..a8756dcb1 100644 --- a/docgen/json/templates/cyclonedx/schema_doc.min.js +++ b/docgen/json/templates/cyclonedx/schema_doc.min.js @@ -1 +1,9 @@ -function flashElement(t){var e=document.getElementById(t);e&&(e.classList.add("jsfh-animated-property"),setTimeout(function(){e.classList.remove("jsfh-animated-property")},1e3))}function setAnchor(t){history.pushState({},"",t)}function anchorOnLoad(){var t=decodeURIComponent(window.location.hash.split("?")[0].split("&")[0]);"#"===t[0]&&(t=t.substr(1)),t.length>0&&anchorLink(t)}function anchorLink(t){var e=document.getElementById(t);if(e){for(var r=e;r;){if(r.classList.contains("collapse")&&!r.classList.contains("show"))new bootstrap.Collapse(r,{toggle:!0});if(r.classList.contains("tab-pane")){var n=document.querySelector('a[href="#'+r.id+'"]');if(n)new bootstrap.Tab(n).show()}if("tab"===r.getAttribute("role"))new bootstrap.Tab(r).show();r=r.parentElement}setTimeout(function(){var e=document.getElementById(t);e&&(e.scrollIntoView({block:"center",behavior:"smooth"}),setTimeout(function(){flashElement(t)},500))},1e3)}}document.addEventListener("click",function(t){var e=t.target.closest('a[href^="#"]');if(e){if(e.classList.contains("ref-link"))return void t.preventDefault();if(e.getAttribute("data-bs-toggle"))return;t.preventDefault(),history.pushState({},"",e.href)}}),function(){function t(){var t={},e=0;if(document.querySelectorAll("[id]").forEach(function(r){var n=r.id;n&&(t[n]?(e++,r.setAttribute("data-orig-id",n),r.id=n+"__d"+e):t[n]=!0)}),0!==e){var r={};document.querySelectorAll("[data-orig-id]").forEach(function(t){var e=t.getAttribute("data-orig-id");r[e]||(r[e]=[]),r[e].push(t)});var n={};Object.keys(r).forEach(function(t){var e=document.getElementById(t);n[t]=e?[e].concat(r[t]):r[t]}),document.querySelectorAll('a[href^="#"]').forEach(function(t){o(t,"href")}),document.querySelectorAll('[data-bs-target^="#"]').forEach(function(t){o(t,"data-bs-target")}),document.querySelectorAll('[data-bs-parent^="#"]').forEach(function(t){o(t,"data-bs-parent")}),document.querySelectorAll("[aria-controls]").forEach(function(t){i(t,"aria-controls")}),document.querySelectorAll("[aria-labelledby]").forEach(function(t){i(t,"aria-labelledby")}),document.querySelectorAll("[onclick]").forEach(function(t){var e=t.getAttribute("onclick");if(e){var n=!1,o=e.replace(/anchorLink\('([^']+)'\)/g,function(e,o){if(!r[o])return e;var i=a(t,o);return i!==o?(n=!0,"anchorLink('"+i+"')"):e}).replace(/setAnchor\('#([^']+)'\)/g,function(e,o){if(!r[o])return e;var i=a(t,o);return i!==o?(n=!0,"setAnchor('#"+i+"')"):e});n&&t.setAttribute("onclick",o)}})}function a(t,e){var r=n[e];if(!r)return e;for(var a=t.parentElement;a;){for(var o=0;o0){anchorLink(linkTarget);}} +function anchorLink(linkTarget){var target=document.getElementById(linkTarget);if(!target)return;var element=target;while(element){if(element.classList.contains("collapse")&&!element.classList.contains("show")){var bsCollapse=new bootstrap.Collapse(element,{toggle:true});} +if(element.classList.contains("tab-pane")){var tabTrigger=document.querySelector('a[href="#'+element.id+'"]');if(tabTrigger){var bsTab=new bootstrap.Tab(tabTrigger);bsTab.show();}} +if(element.getAttribute("role")==="tab"){var bsTab=new bootstrap.Tab(element);bsTab.show();} +element=element.parentElement;} +setTimeout(function(){var targetElement=document.getElementById(linkTarget);if(targetElement){targetElement.scrollIntoView({block:"center",behavior:"smooth"});setTimeout(function(){flashElement(linkTarget);},500);}},1000);} \ No newline at end of file