(function (window, document) { "use strict"; function changeElementClass(element, classValue) { if (element.getAttribute("className")) { element.setAttribute("className", classValue); } else { element.setAttribute("class", classValue); } } function getClassAttribute(element) { if (element.getAttribute("className")) { return element.getAttribute("className"); } else { return element.getAttribute("class"); } } function addClass(element, classValue) { changeElementClass(element, getClassAttribute(element) + " " + classValue); } function removeClass(element, classValue) { changeElementClass(element, getClassAttribute(element).replace(classValue, "")); } function getCheckBox() { return document.getElementById("line-wrapping-toggle"); } function getLabelForCheckBox() { return document.getElementById("label-for-line-wrapping-toggle"); } function findCodeBlocks() { const codeBlocks = []; const tabContainers = getTabContainers(); for (let i = 0; i < tabContainers.length; i++) { const spans = tabContainers[i].getElementsByTagName("span"); for (let i = 0; i < spans.length; ++i) { if (spans[i].className.indexOf("code") >= 0) { codeBlocks.push(spans[i]); } } } return codeBlocks; } function forAllCodeBlocks(operation) { const codeBlocks = findCodeBlocks(); for (let i = 0; i < codeBlocks.length; ++i) { operation(codeBlocks[i], "wrapped"); } } function toggleLineWrapping() { const checkBox = getCheckBox(); if (checkBox.checked) { forAllCodeBlocks(addClass); } else { forAllCodeBlocks(removeClass); } } function initClipboardCopyButton() { document.querySelectorAll(".clipboard-copy-btn").forEach((button) => { const copyElementId = button.getAttribute("data-copy-element-id"); const elementWithCodeToSelect = document.getElementById(copyElementId); button.addEventListener("click", () => { const text = elementWithCodeToSelect.innerText.trim(); navigator.clipboard .writeText(text) .then(() => { button.textContent = "Copied!"; setTimeout(() => { button.textContent = "Copy"; }, 1500); }) .catch((err) => { alert("Failed to copy to the clipboard: '" + err.message + "'. Check JavaScript console for more details.") console.warn("Failed to copy to the clipboard", err); }); }); }); } function initControls() { if (findCodeBlocks().length > 0) { const checkBox = getCheckBox(); const label = getLabelForCheckBox(); checkBox.onclick = toggleLineWrapping; checkBox.checked = false; removeClass(label, "hidden"); } initClipboardCopyButton() } class TabManager { baseId; tabs; titles; headers; constructor(baseId, tabs, titles, headers) { this.baseId = baseId; this.tabs = tabs; this.titles = titles; this.headers = headers; this.init(); } init() { for (let i = 0; i < this.headers.length; i++) { const header = this.headers[i]; header.onclick = () => { this.select(i); return false; }; } } select(i) { this.deselectAll(); changeElementClass(this.tabs[i], "tab selected"); changeElementClass(this.headers[i], "selected"); } deselectAll() { for (let i = 0; i < this.tabs.length; i++) { changeElementClass(this.tabs[i], "tab deselected"); changeElementClass(this.headers[i], "deselected"); } } } function getTabContainers() { const tabContainers = Array.from(document.getElementsByClassName("tab-container")); // Used by existing TabbedPageRenderer users, which have not adjusted to use TabsRenderer yet. const legacyContainer = document.getElementById("tabs"); if (legacyContainer) { tabContainers.push(legacyContainer); } return tabContainers; } function initTabs() { let tabGroups = 0; function createTab(num, container) { const tabElems = findTabs(container); const tabManager = new TabManager("tabs" + num, tabElems, findTitles(tabElems), findHeaders(container)); tabManager.select(0); } const tabContainers = getTabContainers(); for (let i = 0; i < tabContainers.length; i++) { createTab(tabGroups, tabContainers[i]); tabGroups++; } return true; } function findTabs(container) { return findChildElements(container, "DIV", "tab"); } function findHeaders(container) { const owner = findChildElements(container, "UL", "tabLinks"); return findChildElements(owner[0], "LI", null); } function findTitles(tabs) { const titles = []; for (let i = 0; i < tabs.length; i++) { const tab = tabs[i]; const header = findChildElements(tab, "H2", null)[0]; header.parentNode.removeChild(header); if (header.innerText) { titles.push(header.innerText); } else { titles.push(header.textContent); } } return titles; } function findChildElements(container, name, targetClass) { const elements = []; const children = container.childNodes; for (let i = 0; i < children.length; i++) { const child = children.item(i); if (child.nodeType === 1 && child.nodeName === name) { if (targetClass && child.className.indexOf(targetClass) < 0) { continue; } elements.push(child); } } return elements; } function initTableSorting() { document.querySelectorAll("table.sortable").forEach((table) => { const thead = table.tHead; if (!thead || thead.rows.length === 0) return; const headerRow = thead.rows[0]; const headers = Array.from(headerRow.cells); const tbody = table.tBodies[0] || table; const originalRows = Array.from(tbody.querySelectorAll(":scope > tr")); headers.forEach((th, colIndex) => { if (th.classList.contains("no-sort")) return; th.classList.add("sortable-header"); th.addEventListener("click", () => { const current = th.getAttribute("data-sort-dir"); const firstDir = th.getAttribute("data-sort-default") === "desc" ? "desc" : "asc"; const secondDir = firstDir === "desc" ? "asc" : "desc"; // Cycle: none -> firstDir -> secondDir -> none let next; if (current === firstDir) next = secondDir; else if (current === secondDir) next = "none"; else next = firstDir; headers.forEach((h) => h.removeAttribute("data-sort-dir")); if (next === "none") { originalRows.forEach((r) => tbody.appendChild(r)); return; } th.setAttribute("data-sort-dir", next); const rows = Array.from(tbody.querySelectorAll(":scope > tr")); const dir = next === "asc" ? 1 : -1; rows.sort((a, b) => { const av = getSortValue(a.cells[colIndex]); const bv = getSortValue(b.cells[colIndex]); if (av === bv) return 0; // Numeric compare if both parse as numbers const an = parseFloat(av), bn = parseFloat(bv); if (!isNaN(an) && !isNaN(bn) && isFinite(an) && isFinite(bn) && av.trim() !== "" && bv.trim() !== "") { return (an - bn) * dir; } return av.localeCompare(bv) * dir; }); rows.forEach((r) => tbody.appendChild(r)); }); }); }); } function getSortValue(cell) { if (!cell) return ""; const v = cell.getAttribute("data-sort-value"); return v !== null ? v : cell.textContent; } // Entry point. window.onload = function() { initTabs(); initControls(); initTableSorting(); }; } (window, window.document));