From 84056bb9a2506f104ab2d21e4b576ded1b299432 Mon Sep 17 00:00:00 2001 From: Joe Zhu Date: Sun, 20 Sep 2026 13:54:31 +0800 Subject: [PATCH 1/2] adding validation reports --- _pkgdown.yml | 2 + pkgdown/extra.js | 110 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 pkgdown/extra.js diff --git a/_pkgdown.yml b/_pkgdown.yml index 2bf041f517..1001d5849f 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -36,6 +36,8 @@ navbar: href: unit-test-report/ - text: Non-CRAN unit test report href: unit-test-report-non-cran/ + - text: Validation report + href: https://pharmar.github.io/pharmapkgs/src/contrib/Meta/validation_report_rtables_v0.6.16.html repo: url: diff --git a/pkgdown/extra.js b/pkgdown/extra.js new file mode 100644 index 0000000000..2f8e02c1b1 --- /dev/null +++ b/pkgdown/extra.js @@ -0,0 +1,110 @@ +// Resolve the "Validation report" navbar link to the correct report for the +// documentation version currently being viewed. +// +// pkgdown builds one independent static site per package version, and this file +// is auto-included on every page of each build. It reads the version shown in +// the navbar and points the link at the newest validation report whose version +// is <= the current version. Versions older than the first available report do +// not get a link at all. +// +// To add a report for a future release, just append its version to REPORTS +// (keep the list sorted ascending) -- no other change is needed. +(function () { + "use strict"; + + // Available validation reports, ascending. Only these versions have a report. + var REPORTS = ["0.6.16"]; + var REPORT_BASE = + "https://pharmar.github.io/pharmapkgs/src/contrib/Meta/validation_report_rtables_v"; + + // Parse a version string like "0.2.13" or "0.2.13.9004" into a numeric array. + function parseVersion(v) { + return v.split(/[.-]/).map(function (x) { + var n = parseInt(x, 10); + return isNaN(n) ? 0 : n; + }); + } + + // Compare two parsed versions: returns <0, 0, or >0. + function compareVersions(a, b) { + var len = Math.max(a.length, b.length); + for (var i = 0; i < len; i++) { + var d = (a[i] || 0) - (b[i] || 0); + if (d !== 0) return d; + } + return 0; + } + + // Newest report version that is <= the current version, or null if none. + function pickReport(current) { + var cur = parseVersion(current); + var chosen = null; + for (var i = 0; i < REPORTS.length; i++) { + if (compareVersions(cur, parseVersion(REPORTS[i])) >= 0) { + chosen = REPORTS[i]; + } + } + return chosen; + } + + // Read the package version from the navbar of the current page. + function detectVersion() { + var selectors = [ + ".navbar small", + "small.nav-text", + ".navbar .version", + ".navbar-brand + small", + "header .version" + ]; + var re = /\d+\.\d+\.\d+(?:\.\d+)*/; + for (var i = 0; i < selectors.length; i++) { + var el = document.querySelector(selectors[i]); + if (el) { + var m = el.textContent.match(re); + if (m) return m[0]; + } + } + // Fallback: scan the whole navbar/header text. + var header = document.querySelector(".navbar") || document.querySelector("header"); + if (header) { + var hm = header.textContent.match(re); + if (hm) return hm[0]; + } + return null; + } + + // Find the "Validation report" anchor by its (case-insensitive) link text. + function findLink() { + var anchors = document.querySelectorAll("a"); + for (var i = 0; i < anchors.length; i++) { + if (anchors[i].textContent.trim().toLowerCase() === "validation report") { + return anchors[i]; + } + } + return null; + } + + function run() { + var link = findLink(); + if (!link) return; + + var version = detectVersion(); + // If we cannot detect the version, leave the static fallback href in place. + if (!version) return; + + var report = pickReport(version); + if (report) { + link.setAttribute("href", REPORT_BASE + report + ".html"); + } else { + // No report for versions older than the first available one: drop the item. + var li = link.closest("li"); + (li || link).remove(); + } + } + + if (document.readyState === "loading") { + document.addEventListener("DOMContentLoaded", run); + } else { + run(); + } +})(); From 2b3e3810b7d1cc2387bdcc2209d81828c7f6a698 Mon Sep 17 00:00:00 2001 From: Melkiades <11279768+Melkiades@users.noreply.github.com> Date: Mon, 21 Sep 2026 22:38:33 +0000 Subject: [PATCH 2/2] Scope the validation-report link lookup and drop two ES6 DOM calls findLink() scanned every anchor on the page, so a vignette using the words "validation report" as link text would have been rewritten instead of the navbar item. Scope it to the navbar. closest() and remove() are the only ES6-era APIs in an otherwise strict ES5 file; replace them with equivalents so the target is consistent. pickReport() now takes the maximum eligible report rather than the last match, so REPORTS no longer has to be kept sorted, and the header comment notes that the static fallback in _pkgdown.yml needs updating alongside it. Mirrors the same fixes on pharmaverse/formatters#377. --- pkgdown/extra.js | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/pkgdown/extra.js b/pkgdown/extra.js index 2f8e02c1b1..2a6ae49c32 100644 --- a/pkgdown/extra.js +++ b/pkgdown/extra.js @@ -7,12 +7,13 @@ // is <= the current version. Versions older than the first available report do // not get a link at all. // -// To add a report for a future release, just append its version to REPORTS -// (keep the list sorted ascending) -- no other change is needed. +// To add a report for a future release, add its version to REPORTS. Order does +// not matter. Also update the static href in _pkgdown.yml, which is what readers +// with JavaScript disabled get. (function () { "use strict"; - // Available validation reports, ascending. Only these versions have a report. + // Versions that have a validation report. Order does not matter. var REPORTS = ["0.6.16"]; var REPORT_BASE = "https://pharmar.github.io/pharmapkgs/src/contrib/Meta/validation_report_rtables_v"; @@ -36,11 +37,13 @@ } // Newest report version that is <= the current version, or null if none. + // Takes the maximum rather than the last match, so REPORTS need not be sorted. function pickReport(current) { var cur = parseVersion(current); var chosen = null; for (var i = 0; i < REPORTS.length; i++) { - if (compareVersions(cur, parseVersion(REPORTS[i])) >= 0) { + if (compareVersions(cur, parseVersion(REPORTS[i])) < 0) continue; + if (chosen === null || compareVersions(parseVersion(REPORTS[i]), parseVersion(chosen)) > 0) { chosen = REPORTS[i]; } } @@ -74,16 +77,30 @@ } // Find the "Validation report" anchor by its (case-insensitive) link text. + // Scoped to the navbar so page content using the same wording is left alone. function findLink() { - var anchors = document.querySelectorAll("a"); - for (var i = 0; i < anchors.length; i++) { - if (anchors[i].textContent.trim().toLowerCase() === "validation report") { - return anchors[i]; + var roots = [document.querySelector(".navbar"), document.querySelector("header")]; + for (var r = 0; r < roots.length; r++) { + if (!roots[r]) continue; + var anchors = roots[r].querySelectorAll("a"); + for (var i = 0; i < anchors.length; i++) { + if (anchors[i].textContent.trim().toLowerCase() === "validation report") { + return anchors[i]; + } } } return null; } + // Nearest ancestor
  • , or null. Stands in for Element.closest(), which the + // rest of this file's ES5 target does not assume. + function closestListItem(el) { + for (var node = el; node; node = node.parentNode) { + if (node.nodeName && node.nodeName.toLowerCase() === "li") return node; + } + return null; + } + function run() { var link = findLink(); if (!link) return; @@ -97,8 +114,8 @@ link.setAttribute("href", REPORT_BASE + report + ".html"); } else { // No report for versions older than the first available one: drop the item. - var li = link.closest("li"); - (li || link).remove(); + var target = closestListItem(link) || link; + if (target.parentNode) target.parentNode.removeChild(target); } }