/* GCOVR Custom JavaScript - Tree View & Interactivity */ (function() { 'use strict'; // Wait for DOM ready document.addEventListener('DOMContentLoaded', function() { initTheme(); initSidebar(); initSidebarResize(); initMobileMenu(); initFileTree(); initNavOverride(); initBreadcrumbs(); initSearch(); initFunctionRows(); initSorting(); initToggleButtons(); initCoverageNav(); initTreeControls(); initViewToggle(); initSettingsDropdown(); initTlaNavigation(); initLineHighlight(); initColumnToggles(); initPopupResize(); initFileNavTooltips(); initFileNavKeys(); initFunctionListPersistence(); // Reveal page now that all init is done document.documentElement.classList.remove('no-transitions'); // Prefetch linked pages on hover for instant navigation initPrefetch(); }); // =========================================== // Breadcrumb Links // =========================================== // Find a node in the tree by its link (HTML filename) and return // the full ancestor path as an array of nodes from root to target. function findPathInTree(nodes, targetLink) { for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node.link === targetLink) { return [node]; } if (node.children) { var childPath = findPathInTree(node.children, targetLink); if (childPath) { return [node].concat(childPath); } } } return null; } function initBreadcrumbs() { var currentSpan = document.querySelector('.breadcrumb .current'); if (!currentSpan || !window.GCOVR_TREE_DATA) { if (currentSpan) currentSpan.classList.add('ready'); return; } // Find current page in tree by its HTML filename — this is unambiguous // since each page only appears once in the tree. var currentPage = window.location.pathname.split('/').pop() || 'index.html'; var treePath = findPathInTree(window.GCOVR_TREE_DATA, currentPage); if (!treePath || treePath.length === 0) { currentSpan.classList.add('ready'); return; } // Build breadcrumb from the tree path (ancestor nodes → current node) var fragment = document.createDocumentFragment(); var matchedSegments = []; // Fill an element with the segments of a (possibly joined) name like // "boost/url", rendering "boost", a separator, "url". Used so a joined // directory shows its segments inline yet remains one hyperlink target. function appendSegments(parentEl, name) { var segments = name.split('/'); for (var k = 0; k < segments.length; k++) { if (k > 0) { var inner = document.createElement('span'); inner.className = 'separator'; inner.textContent = '/'; parentEl.appendChild(inner); } parentEl.appendChild(document.createTextNode(segments[k])); } } for (var i = 0; i < treePath.length; i++) { var node = treePath[i]; var isLast = (i === treePath.length - 1); if (i > 0) { var sep = document.createElement('span'); sep.className = 'separator'; sep.textContent = '/'; fragment.appendChild(sep); } matchedSegments.push(node.name); if (node.link && !isLast) { var a = document.createElement('a'); a.href = node.link; appendSegments(a, node.name); fragment.appendChild(a); } else { var span = document.createElement('span'); span.className = 'current-file'; appendSegments(span, node.name); fragment.appendChild(span); } } currentSpan.innerHTML = ''; currentSpan.appendChild(fragment); currentSpan.classList.add('ready'); // Update source-filename to match breadcrumb path var sourceFilename = document.querySelector('.source-filename'); if (sourceFilename) { sourceFilename.textContent = matchedSegments.join('/'); } } // =========================================== // Theme Toggle // =========================================== function initTheme() { const toggle = document.getElementById('theme-toggle'); const iconSun = toggle ? toggle.querySelector('.icon-sun') : null; const iconMoon = toggle ? toggle.querySelector('.icon-moon') : null; // Get system preference function getSystemTheme() { return window.matchMedia('(prefers-color-scheme: light)').matches ? 'light' : 'dark'; } // Get effective theme: saved preference or OS default function getEffectiveTheme() { var saved = localStorage.getItem('gcovr-theme'); return (saved === 'light' || saved === 'dark') ? saved : getSystemTheme(); } // Apply theme to document function applyTheme(theme) { document.documentElement.setAttribute('data-theme', theme); if (iconSun) iconSun.style.display = (theme === 'dark') ? 'block' : 'none'; if (iconMoon) iconMoon.style.display = (theme === 'light') ? 'block' : 'none'; } // Apply current theme applyTheme(getEffectiveTheme()); // Listen for system theme changes — only apply if no stored preference window.matchMedia('(prefers-color-scheme: light)').addEventListener('change', function() { var saved = localStorage.getItem('gcovr-theme'); if (saved !== 'light' && saved !== 'dark') { applyTheme(getSystemTheme()); } }); // Toggle between light and dark on click if (toggle) { toggle.addEventListener('click', function() { var current = getEffectiveTheme(); var next = (current === 'dark') ? 'light' : 'dark'; localStorage.setItem('gcovr-theme', next); applyTheme(next); }); } } // =========================================== // Tree Controls (Expand/Collapse All) // =========================================== function initTreeControls() { var expandBtn = document.getElementById('expand-all'); var collapseBtn = document.getElementById('collapse-all'); if (expandBtn) { expandBtn.addEventListener('click', function() { document.querySelectorAll('.tree-item').forEach(function(item) { if (!item.classList.contains('no-children')) { item.classList.add('expanded'); var toggle = item.querySelector(':scope > .tree-item-header > .tree-folder-toggle'); if (toggle) toggle.textContent = '−'; } }); saveExpandedFolders(); }); } if (collapseBtn) { collapseBtn.addEventListener('click', function() { document.querySelectorAll('.tree-item').forEach(function(item) { item.classList.remove('expanded'); var toggle = item.querySelector(':scope > .tree-item-header > .tree-folder-toggle'); if (toggle) toggle.textContent = '+'; }); saveExpandedFolders(); }); } } // =========================================== // Sidebar Toggle // =========================================== function initSidebar() { const sidebar = document.getElementById('sidebar'); const toggle = document.getElementById('sidebar-toggle'); const header = sidebar ? sidebar.querySelector('.sidebar-header') : null; if (!sidebar) return; // Load saved state const isCollapsed = localStorage.getItem('sidebar-collapsed') === 'true'; if (isCollapsed) { sidebar.classList.add('collapsed'); } // Toggle button if (toggle) { toggle.addEventListener('click', function() { sidebar.classList.toggle('collapsed'); sidebar.classList.remove('hover-expand'); var isNowCollapsed = sidebar.classList.contains('collapsed'); localStorage.setItem('sidebar-collapsed', isNowCollapsed); // Restore custom width when un-collapsing if (!isNowCollapsed) { var savedWidth = localStorage.getItem('gcovr-sidebar-width'); if (savedWidth) { document.documentElement.style.setProperty('--sidebar-width', savedWidth + 'px'); } } }); } // Hover expand - expands when hovering sidebar content (not header or no-expand zones) var hoverTimeout = null; var HOVER_DELAY = 150; // ms delay before expanding var isOverContent = false; // Check if element is within a no-expand zone function isInNoExpandZone(el) { while (el && el !== sidebar) { if (el.classList && el.classList.contains('no-expand')) { return true; } el = el.parentElement; } return false; } function scheduleExpand() { if (hoverTimeout) return; // already scheduled if (sidebar.classList.contains('hover-expand')) return; // already expanded hoverTimeout = setTimeout(function() { if (isOverContent) { sidebar.classList.add('hover-expand'); } hoverTimeout = null; }, HOVER_DELAY); } function cancelExpand() { if (hoverTimeout) { clearTimeout(hoverTimeout); hoverTimeout = null; } sidebar.classList.remove('hover-expand'); } sidebar.addEventListener('mouseenter', function(e) { if (!sidebar.classList.contains('collapsed')) return; // Check if entering over content area (not header or no-expand zones) if (!header.contains(e.target) && !isInNoExpandZone(e.target)) { isOverContent = true; scheduleExpand(); } }); sidebar.addEventListener('mousemove', function(e) { if (!sidebar.classList.contains('collapsed')) return; var wasOverContent = isOverContent; isOverContent = !header.contains(e.target) && !isInNoExpandZone(e.target); if (isOverContent && !wasOverContent && !sidebar.classList.contains('hover-expand')) { scheduleExpand(); } }); sidebar.addEventListener('mouseleave', function() { isOverContent = false; cancelExpand(); }); } // =========================================== // Sidebar Resize // =========================================== function initSidebarResize() { var sidebar = document.getElementById('sidebar'); var handle = document.getElementById('sidebar-resize-handle'); if (!sidebar || !handle) return; var MIN_WIDTH = 200; var startX, startWidth; // Restore saved width var savedWidth = localStorage.getItem('gcovr-sidebar-width'); if (savedWidth && !sidebar.classList.contains('collapsed')) { var w = parseInt(savedWidth, 10); if (w >= MIN_WIDTH) { document.documentElement.style.setProperty('--sidebar-width', w + 'px'); } } function getMaxWidth() { return Math.floor(window.innerWidth * 0.5); } function onMouseMove(e) { var newWidth = startWidth + (e.clientX - startX); var maxW = getMaxWidth(); if (newWidth < MIN_WIDTH) newWidth = MIN_WIDTH; if (newWidth > maxW) newWidth = maxW; document.documentElement.style.setProperty('--sidebar-width', newWidth + 'px'); } function onMouseUp() { document.body.classList.remove('sidebar-resizing'); document.removeEventListener('mousemove', onMouseMove); document.removeEventListener('mouseup', onMouseUp); // Save the current width var computed = parseInt(getComputedStyle(sidebar).width, 10); localStorage.setItem('gcovr-sidebar-width', computed); } handle.addEventListener('mousedown', function(e) { if (sidebar.classList.contains('collapsed')) return; e.preventDefault(); startX = e.clientX; startWidth = parseInt(getComputedStyle(sidebar).width, 10); document.body.classList.add('sidebar-resizing'); document.addEventListener('mousemove', onMouseMove); document.addEventListener('mouseup', onMouseUp); }); // Double-click to reset to default width var DEFAULT_WIDTH = 320; handle.addEventListener('dblclick', function() { if (sidebar.classList.contains('collapsed')) return; document.documentElement.style.setProperty('--sidebar-width', DEFAULT_WIDTH + 'px'); localStorage.setItem('gcovr-sidebar-width', DEFAULT_WIDTH); }); } // =========================================== // Mobile Menu // =========================================== function initMobileMenu() { var sidebar = document.getElementById('sidebar'); var menuBtn = document.getElementById('mobile-menu-btn'); var backdrop = document.getElementById('sidebar-backdrop'); if (!menuBtn || !sidebar) return; // Open sidebar on hamburger click menuBtn.addEventListener('click', function() { sidebar.classList.add('mobile-open'); }); // Close on backdrop click if (backdrop) { backdrop.addEventListener('click', function() { sidebar.classList.remove('mobile-open'); }); } // Close when clicking a navigation link sidebar.addEventListener('click', function(e) { if (e.target.closest('a[href]')) { sidebar.classList.remove('mobile-open'); } }); // Close on escape key document.addEventListener('keydown', function(e) { if (e.key === 'Escape' && sidebar.classList.contains('mobile-open')) { sidebar.classList.remove('mobile-open'); } }); } // =========================================== // File Tree - Load from tree.json // =========================================== function initFileTree() { var treeContainer = document.getElementById('file-tree'); if (!treeContainer) return; // Tree data is produced already-normalized and already-sorted by the // upstream tooling (Python's gcovr_build_tree.py or gcovr itself), so // no normalize/sort pass is needed here. We just join single-child // dir chains for sidebar compactness before rendering. joinSingleChildDirs(window.GCOVR_TREE_DATA); sortTree(window.GCOVR_TREE_DATA); renderTree(treeContainer, window.GCOVR_TREE_DATA); } // Re-sort the tree: directories first, then files, alphabetically within // each group. Python already sorts each level, but normalizeTree creates // synthetic directory nodes from multi-segment FILE entries (e.g. a deep // chain like subdir1/subdir2/subdir3/file.hpp that gcovr itself collapsed). // Those synthetic dirs end up wherever the originating file landed in the // Python sort — i.e. in the file bucket — so without this pass they appear // mixed in with the files instead of at the top with the other directories. function sortTree(nodes) { if (!nodes || nodes.length === 0) return; nodes.sort(function(a, b) { var aIsDir = a.isDirectory || (a.children && a.children.length > 0); var bIsDir = b.isDirectory || (b.children && b.children.length > 0); if (aIsDir && !bIsDir) return -1; if (!aIsDir && bIsDir) return 1; var aName = (a.name || '').toLowerCase(); var bName = (b.name || '').toLowerCase(); return aName.localeCompare(bName); }); for (var i = 0; i < nodes.length; i++) { if (nodes[i].children) sortTree(nodes[i].children); } } // Join chains of single-child directories into one sidebar entry. // If a directory contains nothing but one child directory, the two are // merged: the name becomes "parent/child", and the link + stats are taken // from the (deepest) child. The grandchildren become the new children. // Repeats until the chain ends (multiple children, or a file appears). // Result: e.g. include > boost > url > [files] becomes include/boost/url // as a single entry whose click navigates straight to the url directory. function joinSingleChildDirs(nodes) { if (!nodes) return; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (!node.isDirectory || !node.children) continue; while (node.children.length === 1 && node.children[0].isDirectory) { var child = node.children[0]; node.name = node.name + '/' + child.name; // The joined entry represents the deepest directory for clicks // and for the coverage stats shown next to it. Statically-named // assignments avoid the dynamic [key] indexing that static // analyzers flag as a prototype-pollution risk. if (child.link) node.link = child.link; if (child.coverage) node.coverage = child.coverage; if (child.coverageClass) node.coverageClass = child.coverageClass; if (child.linesTotal) node.linesTotal = child.linesTotal; if (child.linesExec) node.linesExec = child.linesExec; if (child.linesCoverage) node.linesCoverage = child.linesCoverage; if (child.linesClass) node.linesClass = child.linesClass; if (child.functionsCoverage) node.functionsCoverage = child.functionsCoverage; if (child.functionsClass) node.functionsClass = child.functionsClass; if (child.branchesCoverage) node.branchesCoverage = child.branchesCoverage; if (child.branchesClass) node.branchesClass = child.branchesClass; node.children = child.children || []; } joinSingleChildDirs(node.children); } } // Save expanded folder paths to localStorage function saveExpandedFolders() { var paths = []; document.querySelectorAll('.tree-item.expanded[data-tree-path]').forEach(function(el) { paths.push(el.getAttribute('data-tree-path')); }); localStorage.setItem('gcovr-expanded-folders', JSON.stringify(paths)); } function renderTree(container, tree) { container.innerHTML = ''; if (!tree || tree.length === 0) { container.innerHTML = '
No files found
'; return; } tree.forEach(function(item) { container.appendChild(createTreeItem(item, '')); }); // Auto-expand to current file and highlight it expandToCurrentFile(container); } function expandToCurrentFile(container) { // Get current page filename var currentPage = window.location.pathname.split('/').pop() || 'index.html'; // Find the link matching current page var currentLink = container.querySelector('a[href="' + currentPage + '"]'); if (currentLink) { // Mark as active var treeItem = currentLink.closest('.tree-item'); if (treeItem) { treeItem.classList.add('active'); } // Expand all parent folders var parent = currentLink.closest('.tree-children'); while (parent) { var parentItem = parent.closest('.tree-item'); if (parentItem) { parentItem.classList.add('expanded'); var toggle = parentItem.querySelector(':scope > .tree-item-header > .tree-folder-toggle'); if (toggle) toggle.textContent = '−'; } parent = parentItem ? parentItem.parentElement.closest('.tree-children') : null; } } // Restore previously expanded folders from localStorage try { var saved = localStorage.getItem('gcovr-expanded-folders'); if (saved) { var paths = JSON.parse(saved); paths.forEach(function(path) { var el = container.querySelector('.tree-item[data-tree-path="' + CSS.escape(path) + '"]'); if (el && !el.classList.contains('no-children')) { el.classList.add('expanded'); var toggle = el.querySelector(':scope > .tree-item-header > .tree-folder-toggle'); if (toggle) toggle.textContent = '−'; } }); } } catch (e) { // Ignore localStorage errors } // Scroll active item into view instantly if (currentLink) { currentLink.scrollIntoView({ block: 'center', behavior: 'instant' }); } } // Clean relative path prefixes like '../../../' from names function cleanPathName(name) { if (!name) return 'unknown'; // Remove leading ./ or ../ while (name.indexOf('./') === 0 || name.indexOf('../') === 0) { if (name.indexOf('./') === 0) { name = name.substring(2); } else if (name.indexOf('../') === 0) { name = name.substring(3); } } return name || 'unknown'; } // Get just the filename from a path function getDisplayName(name) { var cleaned = cleanPathName(name); var lastSlash = cleaned.lastIndexOf('/'); return lastSlash >= 0 ? cleaned.substring(lastSlash + 1) : cleaned; } function createTreeItem(item, parentPath) { var hasChildren = item.children && item.children.length > 0; var isDirectory = item.isDirectory || hasChildren; var cleanedName = cleanPathName(item.name); var treePath = parentPath ? (parentPath + '/' + cleanedName) : cleanedName; var div = document.createElement('div'); div.className = 'tree-item' + (isDirectory ? ' is-folder' : '') + (hasChildren ? '' : ' no-children'); div.setAttribute('data-tree-path', treePath); var header = document.createElement('div'); header.className = 'tree-item-header'; var toggle = null; // Toggle button (+/-) for folders with children if (hasChildren) { toggle = document.createElement('button'); toggle.className = 'tree-folder-toggle'; toggle.textContent = '+'; toggle.setAttribute('aria-label', 'Toggle folder'); toggle.addEventListener('click', function(e) { e.stopPropagation(); e.preventDefault(); var isExpanded = div.classList.toggle('expanded'); toggle.textContent = isExpanded ? '−' : '+'; saveExpandedFolders(); }); header.appendChild(toggle); // Make entire header clickable to expand/collapse header.style.cursor = 'pointer'; header.addEventListener('click', function(e) { // If clicking directly on a link, let it navigate if (e.target.closest('a')) return; e.preventDefault(); var isExpanded = div.classList.toggle('expanded'); toggle.textContent = isExpanded ? '−' : '+'; saveExpandedFolders(); }); } else { var spacer = document.createElement('span'); spacer.className = 'tree-spacer'; header.appendChild(spacer); } // Icon - different for folders vs files var icon = document.createElement('span'); if (isDirectory) { icon.className = 'tree-icon tree-icon-folder'; icon.innerHTML = ''; } else { icon.className = 'tree-icon tree-icon-file'; icon.innerHTML = ''; } header.appendChild(icon); // Label (with link if available) // Use the full cleaned name so joined-dir entries like "boost/url" // display as a single multi-segment label in the sidebar. var displayName = cleanPathName(item.name); var tooltipText = cleanPathName(item.fullPath || item.name); var label = document.createElement('span'); label.className = 'tree-label'; label.title = tooltipText; if (item.link) { var link = document.createElement('a'); link.href = item.link; link.textContent = displayName; link.title = tooltipText; label.appendChild(link); } else { label.textContent = displayName; } header.appendChild(label); div.appendChild(header); // Children container (for expand/collapse) if (hasChildren) { var childrenWrapper = document.createElement('div'); childrenWrapper.className = 'tree-children'; var childrenInner = document.createElement('div'); childrenInner.className = 'tree-children-inner'; item.children.forEach(function(child) { childrenInner.appendChild(createTreeItem(child, treePath)); }); childrenWrapper.appendChild(childrenInner); div.appendChild(childrenWrapper); } return div; } // =========================================== // Search // =========================================== function initSearch() { const searchInput = document.getElementById('file-search'); const fileTree = document.getElementById('file-tree'); const clearBtn = document.getElementById('search-clear'); const searchContainer = searchInput ? searchInput.closest('.sidebar-search') : null; if (!searchInput || !fileTree) return; // Store pre-search expanded state so we can restore it var preSearchExpanded = null; // Create no-results message var noResults = document.createElement('div'); noResults.className = 'search-no-results'; noResults.textContent = 'No matching files'; noResults.style.display = 'none'; fileTree.appendChild(noResults); function updateClearButton() { if (searchContainer) { searchContainer.classList.toggle('has-query', searchInput.value.trim() !== ''); } } // Clear button if (clearBtn) { clearBtn.addEventListener('click', function() { searchInput.value = ''; sessionStorage.removeItem('gcovr-search'); updateClearButton(); performSearch(''); searchInput.focus(); }); } var debounceTimer = null; searchInput.addEventListener('input', function() { updateClearButton(); clearTimeout(debounceTimer); debounceTimer = setTimeout(function() { var val = searchInput.value; if (val.trim() !== '') { sessionStorage.setItem('gcovr-search', val); } else { sessionStorage.removeItem('gcovr-search'); } performSearch(val); }, 150); }); // Restore search state from sessionStorage on page load (synchronous // since initFileTree has already built the tree before initSearch runs) var savedSearch = sessionStorage.getItem('gcovr-search'); if (savedSearch) { searchInput.value = savedSearch; updateClearButton(); performSearch(savedSearch); } function performSearch(value) { var query = value.toLowerCase().trim(); var allItems = fileTree.querySelectorAll('.tree-item'); // Clear all highlights fileTree.querySelectorAll('.search-highlight').forEach(function(mark) { var parent = mark.parentNode; parent.replaceChild(document.createTextNode(mark.textContent), mark); parent.normalize(); }); // If query is empty, restore original state if (query === '') { noResults.style.display = 'none'; allItems.forEach(function(item) { item.style.display = ''; item.classList.remove('search-match'); }); // Restore pre-search expanded state if (preSearchExpanded !== null) { allItems.forEach(function(item) { var path = item.getAttribute('data-tree-path'); var toggle = item.querySelector(':scope > .tree-item-header > .tree-folder-toggle'); if (toggle) { if (preSearchExpanded.indexOf(path) >= 0) { item.classList.add('expanded'); toggle.textContent = '\u2212'; } else { item.classList.remove('expanded'); toggle.textContent = '+'; } } }); preSearchExpanded = null; } return; } // Save expanded state before first search if (preSearchExpanded === null) { preSearchExpanded = []; allItems.forEach(function(item) { if (item.classList.contains('expanded')) { preSearchExpanded.push(item.getAttribute('data-tree-path')); } }); } // Determine which items match (check full path and display name) var matchSet = new Set(); allItems.forEach(function(item) { var path = (item.getAttribute('data-tree-path') || '').toLowerCase(); var label = item.querySelector(':scope > .tree-item-header > .tree-label'); var text = label ? label.textContent.toLowerCase() : ''; if (path.includes(query) || text.includes(query)) { matchSet.add(item); } }); // Also mark all ancestor items of matches as visible var visibleSet = new Set(matchSet); matchSet.forEach(function(item) { var parent = item.parentElement; while (parent && parent !== fileTree) { if (parent.classList && parent.classList.contains('tree-item')) { visibleSet.add(parent); } parent = parent.parentElement; } }); // Apply visibility, expand parents of matches, highlight text var anyVisible = false; allItems.forEach(function(item) { var isVisible = visibleSet.has(item); item.style.display = isVisible ? '' : 'none'; item.classList.toggle('search-match', matchSet.has(item)); if (isVisible) { anyVisible = true; // Auto-expand folders that contain matches var toggle = item.querySelector(':scope > .tree-item-header > .tree-folder-toggle'); if (toggle && visibleSet.has(item) && !matchSet.has(item) || (toggle && matchSet.has(item) && item.classList.contains('is-folder'))) { item.classList.add('expanded'); toggle.textContent = '\u2212'; } } // Highlight matched text in label if (matchSet.has(item)) { var label = item.querySelector(':scope > .tree-item-header > .tree-label'); if (label) { highlightText(label, query); } } }); noResults.style.display = anyVisible ? 'none' : ''; } function highlightText(container, query) { // Walk text nodes inside the label (may be inside an tag) var walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT, null, false); var textNodes = []; while (walker.nextNode()) { textNodes.push(walker.currentNode); } textNodes.forEach(function(node) { var text = node.textContent; var lowerText = text.toLowerCase(); var idx = lowerText.indexOf(query); if (idx === -1) return; var frag = document.createDocumentFragment(); var lastIdx = 0; while (idx !== -1) { if (idx > lastIdx) { frag.appendChild(document.createTextNode(text.substring(lastIdx, idx))); } var mark = document.createElement('mark'); mark.className = 'search-highlight'; mark.textContent = text.substring(idx, idx + query.length); frag.appendChild(mark); lastIdx = idx + query.length; idx = lowerText.indexOf(query, lastIdx); } if (lastIdx < text.length) { frag.appendChild(document.createTextNode(text.substring(lastIdx))); } node.parentNode.replaceChild(frag, node); }); } } // =========================================== // Progressive Function Row Rendering // =========================================== function initFunctionRows() { var dataEl = document.getElementById('functions-data'); if (!dataEl) return; var config = window.__functionsPageConfig || {}; var data = JSON.parse(dataEl.textContent); var container = document.querySelector('.functions-body'); var loadingEl = document.getElementById('functions-loading'); var showBranches = config.showBranches; var showConditions = config.showConditions; var singlePage = config.singlePage; var currentFile = config.htmlFilename || ''; if (data.length === 0) { if (loadingEl) loadingEl.remove(); return; } // --- Virtual scrolling setup --- var ROW_HEIGHT = 52; var BUFFER = 10; var visibleCount = Math.max(30, Math.ceil(container.clientHeight / ROW_HEIGHT) + BUFFER * 2); var viewport, visibleEl; var lastStartIdx = -1; window.addEventListener('resize', function() { visibleCount = Math.max(30, Math.ceil(container.clientHeight / ROW_HEIGHT) + BUFFER * 2); lastStartIdx = -1; renderVisible(); }); function buildHref(entry) { if (singlePage) return '#' + entry.html_filename + '|l' + entry.line; if (currentFile !== entry.html_filename) return entry.html_filename + '#l' + entry.line; return '#l' + entry.line; } function entryKey(entry) { return entry.name + '|' + entry.filename + ':' + entry.line; } function el(tag, cls, text) { var node = document.createElement(tag); if (cls) node.className = cls; if (text !== undefined) node.textContent = text; return node; } function createRow(entry) { var row = el('div', 'function-row'); if (highlightKey && entryKey(entry) === highlightKey) { row.classList.add('function-row-visited'); } // col-function var colFn = el('div', 'col-function'); var a = document.createElement('a'); a.href = buildHref(entry); a.appendChild(el('span', 'function-name', entry.name)); a.appendChild(el('span', 'function-location', entry.filename + ':' + entry.line)); colFn.appendChild(a); row.appendChild(colFn); // col-calls var colCalls = el('div', 'col-calls'); var callSpan; if (entry.excluded) { callSpan = el('span', 'excluded', 'excluded'); } else if (entry.execution_count === 0) { callSpan = el('span', 'not-called', 'not called'); } else { callSpan = el('span', 'called', entry.execution_count + 'x'); } colCalls.appendChild(callSpan); row.appendChild(colCalls); // col-lines row.appendChild(el('div', 'col-lines', entry.line_coverage + '%')); // col-branches (optional) if (showBranches) { row.appendChild(el('div', 'col-branches', entry.branch_coverage + '%')); } // col-conditions (optional) if (showConditions) { row.appendChild(el('div', 'col-conditions', entry.condition_coverage + '%')); } return row; } function setupVirtualScroll() { if (loadingEl) loadingEl.remove(); viewport = document.createElement('div'); viewport.className = 'functions-viewport'; viewport.style.height = (data.length * ROW_HEIGHT) + 'px'; visibleEl = document.createElement('div'); visibleEl.className = 'functions-visible'; viewport.appendChild(visibleEl); container.appendChild(viewport); } function renderVisible() { var scrollTop = container.scrollTop; var startIdx = Math.max(0, Math.floor(scrollTop / ROW_HEIGHT) - BUFFER); var endIdx = Math.min(data.length, startIdx + visibleCount + BUFFER); // Skip re-render if the window hasn't shifted if (startIdx === lastStartIdx) return; lastStartIdx = startIdx; visibleEl.style.top = (startIdx * ROW_HEIGHT) + 'px'; var frag = document.createDocumentFragment(); for (var i = startIdx; i < endIdx; i++) { frag.appendChild(createRow(data[i])); } visibleEl.replaceChildren(frag); } // --- Scroll listener (rAF-throttled) --- var ticking = false; container.addEventListener('scroll', function() { if (!ticking) { requestAnimationFrame(function() { renderVisible(); ticking = false; }); ticking = true; } }); // --- Save state on navigation for back-button restore --- var highlightKey = null; container.addEventListener('click', function(e) { var row = e.target.closest('.function-row'); if (!row) return; var link = row.querySelector('a'); if (!link) return; var nameEl = row.querySelector('.function-name'); var locEl = row.querySelector('.function-location'); if (nameEl && locEl) { sessionStorage.setItem('gcovr-functions-clicked', nameEl.textContent + '|' + locEl.textContent); } sessionStorage.setItem('gcovr-functions-scrollTop', String(container.scrollTop)); }); // --- Data-level sorting --- function sortData(key, ascending) { data.sort(function(a, b) { var aVal, bVal; switch (key) { case 'name': aVal = a.name; bVal = b.name; break; case 'calls': aVal = a.excluded ? -1 : a.execution_count; bVal = b.excluded ? -1 : b.execution_count; break; case 'lines': aVal = parseFloat(a.line_coverage) || 0; bVal = parseFloat(b.line_coverage) || 0; break; case 'branches': aVal = parseFloat(a.branch_coverage) || 0; bVal = parseFloat(b.branch_coverage) || 0; break; case 'conditions': aVal = parseFloat(a.condition_coverage) || 0; bVal = parseFloat(b.condition_coverage) || 0; break; default: aVal = a.name; bVal = b.name; } if (typeof aVal === 'string' && typeof bVal === 'string') { return ascending ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal); } return ascending ? aVal - bVal : bVal - aVal; }); lastStartIdx = -1; // force re-render viewport.style.height = (data.length * ROW_HEIGHT) + 'px'; renderVisible(); } // Intercept sort clicks on functions-header before initSorting runs var funcHeaders = document.querySelectorAll('.functions-header .sortable'); funcHeaders.forEach(function(header) { header.addEventListener('click', function(e) { e.stopPropagation(); var sortKey = this.dataset.sort; var isAscending = this.classList.contains('sorted-ascending'); // Update header classes funcHeaders.forEach(function(h) { h.classList.remove('sorted-ascending', 'sorted-descending'); }); this.classList.add(isAscending ? 'sorted-descending' : 'sorted-ascending'); sortData(sortKey, !isAscending); }, true); // capture phase to beat initSorting }); // --- Restore saved state (scroll + highlight) --- function restoreSavedState() { var saved = sessionStorage.getItem('gcovr-functions-clicked'); if (saved !== null) { sessionStorage.removeItem('gcovr-functions-clicked'); highlightKey = saved; } var scroll = sessionStorage.getItem('gcovr-functions-scrollTop'); if (scroll !== null) { sessionStorage.removeItem('gcovr-functions-scrollTop'); container.scrollTop = parseInt(scroll, 10); } if (saved !== null || scroll !== null) { lastStartIdx = -1; renderVisible(); } } // --- Initialize --- data.sort(function(a, b) { return a.name.localeCompare(b.name); }); setupVirtualScroll(); renderVisible(); restoreSavedState(); // Also restore on bfcache navigation (browser Back button) window.addEventListener('pageshow', function(e) { if (e.persisted) restoreSavedState(); }); // Mark functions page so initSorting can skip it container.dataset.virtualScroll = 'true'; } // =========================================== // Sorting // =========================================== function initSorting() { var headerSets = [ { selector: '.file-list-header .sortable, .functions-header .sortable', getContainer: function() { return document.getElementById('file-list') || document.querySelector('.functions-body'); }, defaultSort: { key: 'filename', ascending: true } }, { selector: '.source-function-header .sortable', getContainer: function() { return document.querySelector('.source-functions-list'); }, defaultSort: null } ]; headerSets.forEach(function(set) { var headers = document.querySelectorAll(set.selector); if (!headers.length) return; headers.forEach(function(header) { header.addEventListener('click', function() { var sortKey = this.dataset.sort; var isAscending = this.classList.contains('sorted-ascending'); headers.forEach(function(h) { h.classList.remove('sorted-ascending', 'sorted-descending'); }); this.classList.add(isAscending ? 'sorted-descending' : 'sorted-ascending'); sortList(set.getContainer(), sortKey, !isAscending); }); }); if (set.defaultSort) { sortList(set.getContainer(), set.defaultSort.key, set.defaultSort.ascending); } }); } function sortList(container, key, ascending) { if (!container) return; // Virtual scroll handles its own sorting if (container.dataset.virtualScroll) return; var headerEl = container.querySelector('.source-function-header, .file-list-header, .functions-header'); var rows = Array.from(container.children).filter(function(el) { return el !== headerEl; }); rows.sort(function(a, b) { // Directories always come first var aIsDir = a.classList.contains('directory'); var bIsDir = b.classList.contains('directory'); if (aIsDir && !bIsDir) return -1; if (!aIsDir && bIsDir) return 1; var aVal = a.dataset[key] || a.querySelector('[data-sort]')?.dataset.sort || ''; var bVal = b.dataset[key] || b.querySelector('[data-sort]')?.dataset.sort || ''; // Try to parse as numbers var aNum = parseFloat(aVal); var bNum = parseFloat(bVal); if (!isNaN(aNum) && !isNaN(bNum)) { return ascending ? aNum - bNum : bNum - aNum; } // String comparison return ascending ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal); }); rows.forEach(function(row) { container.appendChild(row); }); } // =========================================== // Toggle Buttons (Coverage Lines) // =========================================== function initToggleButtons() { const buttons = document.querySelectorAll('.button_toggle_coveredLine, .button_toggle_uncoveredLine, .button_toggle_partialCoveredLine, .button_toggle_excludedLine'); buttons.forEach(function(button) { var lineClass = button.value; if (!document.querySelector('.' + lineClass)) { button.disabled = true; button.classList.remove('show_' + lineClass); return; } button.addEventListener('click', function() { const lineClass = this.value; const showClass = 'show_' + lineClass; // Toggle the button state this.classList.toggle(showClass); // Toggle visibility of lines const lines = document.querySelectorAll('.' + lineClass); lines.forEach(function(line) { line.classList.toggle(showClass); }); document.dispatchEvent(new CustomEvent('coverage-toggled')); }); }); // Also handle simpler toggle buttons const simpleToggles = document.querySelectorAll('.btn-toggle'); simpleToggles.forEach(function(button) { button.addEventListener('click', function() { // Use data attribute to get line class (persists after toggle) const lineClass = this.dataset.lineClass; if (!lineClass) return; const showClass = 'show_' + lineClass; this.classList.toggle(showClass); const lines = document.querySelectorAll('.' + lineClass); lines.forEach(function(line) { line.classList.toggle(showClass); }); document.dispatchEvent(new CustomEvent('coverage-toggled')); }); }); } // =========================================== // Coverage Navigation (prev/next uncovered) // =========================================== function initCoverageNav() { var prevBtn = document.getElementById('nav-prev'); var nextBtn = document.getElementById('nav-next'); var counter = document.getElementById('nav-counter'); if (!prevBtn || !nextBtn || !counter) return; var gapLines = []; var currentIndex = -1; function collectGapLines() { var uncovered = document.querySelectorAll('tr.uncoveredLine.show_uncoveredLine'); var partial = document.querySelectorAll('tr.partialCoveredLine.show_partialCoveredLine'); var merged = []; var i; for (i = 0; i < uncovered.length; i++) merged.push(uncovered[i]); for (i = 0; i < partial.length; i++) merged.push(partial[i]); // Sort by DOM order merged.sort(function(a, b) { var pos = a.compareDocumentPosition(b); if (pos & Node.DOCUMENT_POSITION_FOLLOWING) return -1; if (pos & Node.DOCUMENT_POSITION_PRECEDING) return 1; return 0; }); gapLines = merged; currentIndex = -1; updateCounter(); } function updateCounter() { if (gapLines.length === 0) { counter.textContent = 'All lines covered'; prevBtn.disabled = true; nextBtn.disabled = true; } else { var display = currentIndex >= 0 ? (currentIndex + 1) : 0; counter.textContent = display + ' / ' + gapLines.length; prevBtn.disabled = false; nextBtn.disabled = false; } } function navigateTo(index) { if (gapLines.length === 0) return; // Remove previous highlight var prev = document.querySelector('tr.source-line.nav-highlight'); if (prev) prev.classList.remove('nav-highlight'); currentIndex = index; var row = gapLines[currentIndex]; row.scrollIntoView({ block: 'center', behavior: 'instant' }); row.classList.add('nav-highlight'); setTimeout(function() { row.classList.remove('nav-highlight'); }, 1500); updateCounter(); } function nextGap() { if (gapLines.length === 0) return; var next = currentIndex + 1; if (next >= gapLines.length) next = 0; navigateTo(next); } function prevGap() { if (gapLines.length === 0) return; var prev = currentIndex - 1; if (prev < 0) prev = gapLines.length - 1; navigateTo(prev); } prevBtn.addEventListener('click', prevGap); nextBtn.addEventListener('click', nextGap); document.addEventListener('keydown', function(e) { var tag = (e.target.tagName || '').toLowerCase(); if (tag === 'input' || tag === 'textarea' || e.target.isContentEditable) return; if (e.key === 'n') nextGap(); if (e.key === 'p') prevGap(); }); document.addEventListener('coverage-toggled', function() { collectGapLines(); }); collectGapLines(); } // =========================================== // View Toggle (Nested / Flat) // =========================================== function initViewToggle() { var toggleContainer = document.getElementById('view-toggle'); var fileList = document.getElementById('file-list'); var appContainer = document.querySelector('.app-container'); if (!toggleContainer) return; // Always show the toggle toggleContainer.style.display = ''; var buttons = toggleContainer.querySelectorAll('.view-btn'); var savedView = localStorage.getItem('gcovr-view-mode'); function setActiveButton(view) { buttons.forEach(function(btn) { btn.classList.toggle('active', btn.dataset.view === view); }); } // On non-directory pages (file/source views), still respect flat mode for sidebar if (!fileList) { if (appContainer && savedView === 'flat') { appContainer.classList.add('flat-mode'); setActiveButton('flat'); } // Allow toggling view mode from source pages buttons.forEach(function(btn) { btn.addEventListener('click', function() { var view = this.dataset.view; localStorage.setItem('gcovr-view-mode', view); setActiveButton(view); if (appContainer) { if (view === 'flat') { appContainer.classList.add('flat-mode'); } else { appContainer.classList.remove('flat-mode'); document.documentElement.classList.remove('early-flat-mode'); } } }); }); return; } var originalNodes = null; // stash for restoring nested view function collectFlatFiles(nodes, parentPath) { var results = []; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; var cleanedName = node.name; // Remove leading ./ or ../ while (cleanedName.indexOf('./') === 0 || cleanedName.indexOf('../') === 0) { cleanedName = cleanedName.indexOf('./') === 0 ? cleanedName.substring(2) : cleanedName.substring(3); } var fullPath = parentPath ? (parentPath + '/' + cleanedName) : cleanedName; if (node.isDirectory && node.children && node.children.length > 0) { results = results.concat(collectFlatFiles(node.children, fullPath)); } else if (!node.isDirectory) { var copy = {}; for (var key in node) { if (node.hasOwnProperty(key)) copy[key] = node[key]; } copy.fullPath = fullPath; results.push(copy); } } return results; } function buildFlatRow(file) { var row = document.createElement('div'); row.className = 'file-row file'; row.setAttribute('data-filename', file.fullPath); row.setAttribute('data-coverage', file.coverage || '0'); row.setAttribute('data-lines', file.linesTotal || ''); row.setAttribute('data-functions', file.functionsCoverage || ''); row.setAttribute('data-branches', file.branchesCoverage || ''); // Col name var colName = document.createElement('div'); colName.className = 'col-name'; var icon = document.createElement('span'); icon.className = 'file-icon'; icon.innerHTML = ''; colName.appendChild(icon); if (file.link) { var a = document.createElement('a'); a.href = file.link; a.textContent = file.fullPath; a.title = file.fullPath; colName.appendChild(a); } else { var span = document.createElement('span'); span.className = 'no-link'; span.textContent = file.fullPath; span.title = file.fullPath; colName.appendChild(span); } row.appendChild(colName); // Col coverage var colCov = document.createElement('div'); colCov.className = 'col-coverage'; var barContainer = document.createElement('div'); barContainer.className = 'coverage-bar-container'; var bar = document.createElement('div'); var linesCov = file.linesCoverage || ''; var linesClass = file.linesClass || file.coverageClass || ''; bar.className = 'coverage-bar ' + linesClass; bar.style.width = (linesCov && linesCov !== '-') ? linesCov + '%' : '0%'; barContainer.appendChild(bar); colCov.appendChild(barContainer); var pct = document.createElement('span'); pct.className = 'coverage-percent ' + linesClass; pct.textContent = (linesCov && linesCov !== '-') ? linesCov + '%' : '-'; colCov.appendChild(pct); row.appendChild(colCov); // Col lines var colLines = document.createElement('div'); colLines.className = 'col-lines'; var execSpan = document.createElement('span'); execSpan.className = 'stat-value'; execSpan.textContent = file.linesExec || ''; colLines.appendChild(execSpan); var sep = document.createElement('span'); sep.className = 'stat-separator'; sep.textContent = '/'; colLines.appendChild(sep); var totalSpan = document.createElement('span'); totalSpan.className = 'stat-total'; totalSpan.textContent = file.linesTotal || ''; colLines.appendChild(totalSpan); row.appendChild(colLines); // Col functions (check if container has the column) var container = fileList.closest('.file-list-container'); var hasFunctions = !container || !container.classList.contains('no-functions'); var hasBranches = !container || !container.classList.contains('no-branches'); var hasConditions = !container || !container.classList.contains('no-conditions'); var hasDecision = !container || !container.classList.contains('no-decisions'); var hasCalls = !container || !container.classList.contains('no-calls'); if (hasFunctions) { var colFunc = document.createElement('div'); colFunc.className = 'col-functions'; var funcVal = document.createElement('span'); var funcCov = file.functionsCoverage || ''; var funcClass = file.functionsClass || ''; funcVal.className = 'stat-value ' + funcClass; funcVal.textContent = (funcCov && funcCov !== '-') ? funcCov + '%' : '-'; colFunc.appendChild(funcVal); row.appendChild(colFunc); } if (hasBranches) { var colBr = document.createElement('div'); colBr.className = 'col-branches'; var brVal = document.createElement('span'); var brCov = file.branchesCoverage || ''; var brClass = file.branchesClass || ''; brVal.className = 'stat-value ' + brClass; brVal.textContent = (brCov && brCov !== '-') ? brCov + '%' : '-'; colBr.appendChild(brVal); row.appendChild(colBr); } if (hasConditions) { var colCond = document.createElement('div'); colCond.className = 'col-conditions'; var condVal = document.createElement('span'); var condCov = file.conditionsCoverage || ''; var condClass = file.conditionsClass || ''; condVal.className = 'stat-value ' + condClass; condVal.textContent = (condCov && condCov !== '-') ? condCov + '%' : '-'; colCond.appendChild(condVal); row.appendChild(colCond); } if (hasDecision) { var colDec = document.createElement('div'); colDec.className = 'col-decision'; var decVal = document.createElement('span'); var decCov = file.decisionCoverage || ''; var decClass = file.decisionClass || ''; decVal.className = 'stat-value ' + decClass; decVal.textContent = (decCov && decCov !== '-') ? decCov + '%' : '-'; colDec.appendChild(decVal); row.appendChild(colDec); } if (hasCalls) { var colCalls = document.createElement('div'); colCalls.className = 'col-calls'; var callsVal = document.createElement('span'); var callsCov = file.callsCoverage || ''; var callsClass = file.callsClass || ''; callsVal.className = 'stat-value ' + callsClass; callsVal.textContent = (callsCov && callsCov !== '-') ? callsCov + '%' : '-'; colCalls.appendChild(callsVal); row.appendChild(colCalls); } return row; } function switchToFlat() { if (!window.GCOVR_TREE_DATA) return; // Stash original DOM nodes if (originalNodes === null) { originalNodes = document.createDocumentFragment(); while (fileList.firstChild) { originalNodes.appendChild(fileList.firstChild); } } var flatFiles = collectFlatFiles(window.GCOVR_TREE_DATA, ''); // Sort by coverage ascending (matching default) flatFiles.sort(function(a, b) { var aVal = parseFloat(a.coverage) || 0; var bVal = parseFloat(b.coverage) || 0; return aVal - bVal; }); while (fileList.firstChild) { fileList.removeChild(fileList.firstChild); } for (var i = 0; i < flatFiles.length; i++) { fileList.appendChild(buildFlatRow(flatFiles[i])); } if (appContainer) appContainer.classList.add('flat-mode'); setActiveButton('flat'); localStorage.setItem('gcovr-view-mode', 'flat'); } function switchToNested() { if (originalNodes !== null) { while (fileList.firstChild) { fileList.removeChild(fileList.firstChild); } fileList.appendChild(originalNodes); originalNodes = null; } if (appContainer) appContainer.classList.remove('flat-mode'); document.documentElement.classList.remove('early-flat-mode'); setActiveButton('nested'); localStorage.setItem('gcovr-view-mode', 'nested'); // Re-run sorting to maintain state sortList(document.getElementById('file-list') || document.querySelector('.functions-body'), 'filename', true); } buttons.forEach(function(btn) { btn.addEventListener('click', function() { var view = this.dataset.view; if (view === 'flat') { switchToFlat(); } else { switchToNested(); } }); }); // Apply saved preference on load if (savedView === 'flat') { // Defer to ensure tree data is loaded setTimeout(function() { switchToFlat(); }, 0); } } // =========================================== // Settings Dropdown (mobile gear icon) // =========================================== function initSettingsDropdown() { var btn = document.getElementById('settings-btn'); var dropdown = document.getElementById('settings-dropdown'); var header = document.querySelector('.main-header'); if (!btn || !dropdown || !header) return; var viewToggle = document.getElementById('view-toggle'); var themeToggle = document.getElementById('theme-toggle'); var isMobile = false; // Reference node: settings-btn, so we can insert before it when moving back function moveToDropdown() { if (viewToggle && viewToggle.parentNode !== dropdown) { dropdown.appendChild(viewToggle); } if (themeToggle && themeToggle.parentNode !== dropdown) { dropdown.appendChild(themeToggle); } } function moveToHeader() { // Insert before settings-btn so they appear in original order if (viewToggle && viewToggle.parentNode !== header) { header.insertBefore(viewToggle, btn); } if (themeToggle && themeToggle.parentNode !== header) { header.insertBefore(themeToggle, btn); } } function checkBreakpoint() { var nowMobile = window.innerWidth <= 1024; if (nowMobile === isMobile) return; isMobile = nowMobile; if (isMobile) { moveToDropdown(); } else { dropdown.classList.remove('open'); moveToHeader(); } } // Toggle dropdown on button click btn.addEventListener('click', function(e) { e.stopPropagation(); dropdown.classList.toggle('open'); }); // Close on outside click document.addEventListener('click', function(e) { if (!dropdown.contains(e.target) && e.target !== btn) { dropdown.classList.remove('open'); } }); // Close on Escape document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { dropdown.classList.remove('open'); } }); // Respond to resize window.addEventListener('resize', checkBreakpoint); // Initial check checkBreakpoint(); } // =========================================== // Popup Resize (only when overflowing) // =========================================== function initPopupResize() { var details = document.querySelectorAll('.branch-details, .condition-details, .decision-details, .call-details'); if (details.length === 0) return; details.forEach(function(det) { det.addEventListener('toggle', function() { if (!det.open) return; var popup = det.querySelector('.branch-popup, .condition-popup, .decision-popup, .call-popup'); if (!popup) return; // Check after render if content overflows requestAnimationFrame(function() { if (popup.scrollHeight > popup.clientHeight) { popup.classList.add('is-overflowing'); } else { popup.classList.remove('is-overflowing'); } }); }); }); } // =========================================== // Nav Override (prev/next follows tree order) // =========================================== function initNavOverride() { if (!window.GCOVR_TREE_DATA) return; var navPrev = document.querySelectorAll('.nav-prev'); var navNext = document.querySelectorAll('.nav-next'); if (navPrev.length === 0 && navNext.length === 0) return; // DFS-flatten tree to collect file links in sidebar order function collectLinks(nodes) { var links = []; for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node.isDirectory && node.children && node.children.length > 0) { links = links.concat(collectLinks(node.children)); } else if (!node.isDirectory && node.link) { links.push(node.link); } } return links; } var fileLinks = collectLinks(window.GCOVR_TREE_DATA); if (fileLinks.length === 0) return; var currentPage = window.location.pathname.split('/').pop() || 'index.html'; var idx = fileLinks.indexOf(currentPage); if (idx === -1) return; var prev = idx > 0 ? fileLinks[idx - 1] : null; var next = idx < fileLinks.length - 1 ? fileLinks[idx + 1] : null; function updateNavLinks(els, href) { for (var i = 0; i < els.length; i++) { var el = els[i]; if (href) { // Enable: ensure it's an with the correct href if (el.tagName === 'A') { el.setAttribute('href', href); } else { // Replace disabled with an var a = document.createElement('a'); a.className = el.className.replace(/\bdisabled\b/, '').trim(); a.href = href; a.title = el.title; while (el.firstChild) a.appendChild(el.firstChild); el.parentNode.replaceChild(a, el); } } else { // Disable: ensure it's a with disabled class if (el.tagName === 'A') { var span = document.createElement('span'); span.className = el.className + ' disabled'; span.title = el.title; while (el.firstChild) span.appendChild(el.firstChild); el.parentNode.replaceChild(span, el); } else { el.classList.add('disabled'); } } } } updateNavLinks(navPrev, prev); updateNavLinks(navNext, next); } // =========================================== // TLA Navigation (HIT/MIS/PAR links) // =========================================== function initTlaNavigation() { var rows = document.querySelectorAll('.source-line'); if (rows.length === 0) return; // Classify each row by coverage type var COV_CLASSES = ['coveredLine', 'uncoveredLine', 'partialCoveredLine']; var LABELS = { coveredLine: 'HIT', uncoveredLine: 'MIS', partialCoveredLine: 'PAR' }; var CSS_CLASSES = { coveredLine: 'tla-hit', uncoveredLine: 'tla-mis', partialCoveredLine: 'tla-par' }; // Build list of groups: contiguous runs of the same coverage class var groups = []; // { type, firstRow } var prevType = null; for (var i = 0; i < rows.length; i++) { var row = rows[i]; var type = null; for (var j = 0; j < COV_CLASSES.length; j++) { if (row.classList.contains(COV_CLASSES[j])) { type = COV_CLASSES[j]; break; } } if (type === null) { prevType = null; continue; } if (type !== prevType) { groups.push({ type: type, firstRow: row }); prevType = type; } } if (groups.length === 0) return; // Determine the anchor prefix used in this page var sampleAnchor = rows[0].querySelector('.col-lineno a'); var anchorPrefix = ''; if (sampleAnchor) { var id = sampleAnchor.id; var idx = id.indexOf('l'); if (idx > 0) { anchorPrefix = id.substring(0, idx); } } // For each group, find the line number from its first row function getLineNo(row) { var a = row.querySelector('.col-lineno a'); return a ? a.textContent.trim() : ''; } // Build per-type lists for wrap-around linking var byType = {}; for (var i = 0; i < groups.length; i++) { var g = groups[i]; if (!byType[g.type]) byType[g.type] = []; byType[g.type].push(i); } // For each group, compute next group index of same type var nextGroupIdx = new Array(groups.length); for (var type in byType) { var indices = byType[type]; for (var k = 0; k < indices.length; k++) { var nextK = (k + 1) % indices.length; nextGroupIdx[indices[k]] = indices[nextK]; } } // Inject TLA links for (var i = 0; i < groups.length; i++) { var g = groups[i]; var cell = g.firstRow.querySelector('.col-tla'); if (!cell) continue; var targetGroup = groups[nextGroupIdx[i]]; var targetLineNo = getLineNo(targetGroup.firstRow); var targetId = anchorPrefix + 'l' + targetLineNo; var a = document.createElement('a'); a.className = 'tla-link ' + CSS_CLASSES[g.type]; a.textContent = LABELS[g.type]; a.href = '#' + targetId; a.addEventListener('click', function(e) { var target = document.getElementById(this.getAttribute('href').substring(1)); if (target) { e.preventDefault(); // Scroll within the source-table-container var scrollBox = document.querySelector('.source-table-container'); var row = target.closest('tr'); if (scrollBox && row) { var thead = scrollBox.querySelector('thead'); var theadHeight = thead ? thead.offsetHeight : 0; scrollBox.scrollTo({ top: row.offsetTop - theadHeight - 8, behavior: 'instant' }); } history.replaceState(null, '', this.getAttribute('href')); // Highlight the target row (clear any previous highlight first) var prev = document.querySelector('.highlight-target'); if (prev) prev.classList.remove('highlight-target'); if (row) row.classList.add('highlight-target'); } }); cell.appendChild(a); } } // =========================================== // Line number click highlight // =========================================== function initLineHighlight() { var clickedFnItem = null; function highlightFromHash(scroll) { var prev = document.querySelector('.highlight-target'); if (prev) prev.classList.remove('highlight-target'); var prevFn = document.querySelector('.source-function-item.selected'); if (prevFn) prevFn.classList.remove('selected'); var id = window.location.hash.slice(1); if (!id) return; var el = document.getElementById(id); if (!el) return; var fnItem = clickedFnItem || document.querySelector('.source-function-item[href="#' + id + '"]'); clickedFnItem = null; if (fnItem) fnItem.classList.add('selected'); var row = el.closest('tr'); if (row) { row.classList.add('highlight-target'); if (scroll) { var scrollBox = document.querySelector('.source-table-container'); if (scrollBox) { var thead = scrollBox.querySelector('thead'); var theadHeight = thead ? thead.offsetHeight : 0; scrollBox.scrollTo({ top: row.offsetTop - theadHeight - 8, behavior: 'instant' }); } else { row.scrollIntoView({ block: 'center' }); } } } } // Handle clicks on function list items directly var fnList = document.querySelector('.source-functions-list'); if (fnList) { fnList.addEventListener('click', function(e) { var item = e.target.closest('.source-function-item'); if (!item) return; e.preventDefault(); clickedFnItem = item; var href = item.getAttribute('href'); if (href) history.replaceState(null, '', href); highlightFromHash(true); }); } // Event delegation: single listener on the table container var container = document.querySelector('.source-table-container'); if (container) { container.addEventListener('click', function(e) { var anchor = e.target.closest('.col-lineno a'); if (!anchor) return; e.preventDefault(); if (anchor.id) history.replaceState(null, '', '#' + anchor.id); highlightFromHash(false); }); } // Highlight + scroll on initial load and back/forward navigation highlightFromHash(true); window.addEventListener('hashchange', function() { highlightFromHash(true); }); } // =========================================== // Column Visibility Toggles // =========================================== function initColumnToggles() { var buttons = document.querySelectorAll('.col-toggle'); if (buttons.length === 0) return; var table = document.querySelector('.source-table'); if (!table) return; // Restore saved state var hidden = []; try { var saved = localStorage.getItem('gcovr-hidden-columns'); if (saved) { hidden = JSON.parse(saved); } else { hidden = ['tla']; } } catch (e) {} // Apply saved hidden columns var fnList = document.querySelector('.source-functions-list'); for (var i = 0; i < hidden.length; i++) { table.classList.add('hide-col-' + hidden[i]); if (fnList) { fnList.classList.add('hide-col-' + hidden[i]); } } // Update button appearance to match state buttons.forEach(function(btn) { var col = btn.getAttribute('data-col'); if (hidden.indexOf(col) >= 0) { btn.classList.remove('show-col'); } }); // Handle clicks buttons.forEach(function(btn) { btn.addEventListener('click', function() { var col = this.getAttribute('data-col'); var hideClass = 'hide-col-' + col; var isHidden = table.classList.toggle(hideClass); this.classList.toggle('show-col', !isHidden); // Sync with function list sidebar var fnList = document.querySelector('.source-functions-list'); if (fnList) { fnList.classList.toggle(hideClass, isHidden); } // Save state var current = []; var allBtns = document.querySelectorAll('.col-toggle'); allBtns.forEach(function(b) { if (!b.classList.contains('show-col')) { current.push(b.getAttribute('data-col')); } }); localStorage.setItem('gcovr-hidden-columns', JSON.stringify(current)); }); }); } // =========================================== // File nav keyboard shortcuts ([ and ]) // =========================================== function initFileNavKeys() { var prevLink = document.querySelector('.source-nav-links .nav-prev') || document.querySelector('.nav-links .nav-prev'); var nextLink = document.querySelector('.source-nav-links .nav-next') || document.querySelector('.nav-links .nav-next'); if (!prevLink && !nextLink) return; document.addEventListener('keydown', function(e) { var tag = (e.target.tagName || '').toLowerCase(); if (tag === 'input' || tag === 'textarea' || e.target.isContentEditable) return; // Re-query to pick up any DOM replacements by initNavOverride var prev = document.querySelector('.source-nav-links a.nav-prev') || document.querySelector('.nav-links a.nav-prev'); var next = document.querySelector('.source-nav-links a.nav-next') || document.querySelector('.nav-links a.nav-next'); if (e.key === '[' && prev) { window.location.href = prev.href; } if (e.key === ']' && next) { window.location.href = next.href; } }); } // =========================================== // Enrich file nav tooltips with actual filenames // =========================================== function initFileNavTooltips() { if (!window.GCOVR_TREE_DATA) return; var links = document.querySelectorAll('.source-nav-links .nav-prev, .source-nav-links .nav-next, .nav-links .nav-prev, .nav-links .nav-next'); for (var i = 0; i < links.length; i++) { var anchor = links[i]; var href = anchor.getAttribute('href'); if (!href || href === '#') continue; var filename = href.replace(/^.*\//, '').replace(/#.*$/, ''); var node = findNodeInTree(window.GCOVR_TREE_DATA, filename); if (node && node.name) { var direction = anchor.classList.contains('nav-prev') ? 'Previous' : 'Next'; anchor.title = direction + ': ' + node.name; } } } function findNodeInTree(nodes, targetLink) { for (var i = 0; i < nodes.length; i++) { var node = nodes[i]; if (node.link === targetLink) return node; if (node.children) { var found = findNodeInTree(node.children, targetLink); if (found) return found; } } return null; } // =========================================== // Prefetch pages on hover for instant nav // =========================================== function initPrefetch() { // Skip for file:// protocol (fetch won't work) if (location.protocol === 'file:') return; var prefetched = {}; document.addEventListener('mouseover', function(e) { var link = e.target.closest('a[href]'); if (!link) return; var href = link.getAttribute('href'); // Only prefetch local HTML pages if (!href || href.charAt(0) === '#' || href.indexOf('://') !== -1) return; if (prefetched[href]) return; prefetched[href] = true; var prefetchLink = document.createElement('link'); prefetchLink.rel = 'prefetch'; prefetchLink.href = href; document.head.appendChild(prefetchLink); }); } function initFunctionListPersistence() { var details = document.querySelector('details.source-functions'); if (!details) return; var key = 'gcovr-fn-list-open'; if (sessionStorage.getItem(key) === 'true') { details.setAttribute('open', ''); } details.addEventListener('toggle', function() { sessionStorage.setItem(key, details.open ? 'true' : 'false'); }); } })(); window.GCOVR_TREE_DATA = [{"name": "include", "coverage": "89.9", "coverageClass": "coverage-medium", "linesTotal": "7108", "linesExec": "6391", "linesCoverage": "89.9", "linesClass": "coverage-medium", "functionsCoverage": "59.2", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": true, "link": "index.include.d436eb0fd9de10b54a828ce6435f7e81.html", "children": [{"name": "boost", "coverage": "", "coverageClass": "coverage-unknown", "linesTotal": "", "linesExec": "", "linesCoverage": "", "linesClass": "", "functionsCoverage": "", "functionsClass": "", "branchesCoverage": "", "branchesClass": "", "isDirectory": true, "link": null, "children": [{"name": "regex", "coverage": "", "coverageClass": "coverage-unknown", "linesTotal": "", "linesExec": "", "linesCoverage": "", "linesClass": "", "functionsCoverage": "", "functionsClass": "", "branchesCoverage": "", "branchesClass": "", "isDirectory": true, "link": null, "children": [{"name": "v5", "coverage": "", "coverageClass": "coverage-unknown", "linesTotal": "", "linesExec": "", "linesCoverage": "", "linesClass": "", "functionsCoverage": "", "functionsClass": "", "branchesCoverage": "", "branchesClass": "", "isDirectory": true, "link": null, "children": [{"name": "basic_regex.hpp", "coverage": "97.6", "coverageClass": "coverage-high", "linesTotal": "205", "linesExec": "200", "linesCoverage": "97.6", "linesClass": "coverage-high", "functionsCoverage": "96.9", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.basic_regex.hpp.fc7ae442ee1c3ba72f0c8efe27e0d4a0.html", "children": []}, {"name": "basic_regex_creator.hpp", "coverage": "98.4", "coverageClass": "coverage-high", "linesTotal": "806", "linesExec": "793", "linesCoverage": "98.4", "linesClass": "coverage-high", "functionsCoverage": "89.2", "functionsClass": "coverage-medium", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.basic_regex_creator.hpp.ddc38b9ace9f1fce2474b7a8aa072758.html", "children": []}, {"name": "basic_regex_parser.hpp", "coverage": "88.1", "coverageClass": "coverage-medium", "linesTotal": "1796", "linesExec": "1582", "linesCoverage": "88.1", "linesClass": "coverage-medium", "functionsCoverage": "83.7", "functionsClass": "coverage-medium", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.basic_regex_parser.hpp.3d312756fa15eb91f18ce74346527498.html", "children": []}, {"name": "c_regex_traits.hpp", "coverage": "80.2", "coverageClass": "coverage-medium", "linesTotal": "167", "linesExec": "134", "linesCoverage": "80.2", "linesClass": "coverage-medium", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.c_regex_traits.hpp.4a312148ed0fde78796e7f563ab76c8c.html", "children": []}, {"name": "cpp_regex_traits.hpp", "coverage": "61.7", "coverageClass": "coverage-low", "linesTotal": "342", "linesExec": "211", "linesCoverage": "61.7", "linesClass": "coverage-low", "functionsCoverage": "88.9", "functionsClass": "coverage-medium", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.cpp_regex_traits.hpp.065ad3525ef9ac006d863057e1e02b79.html", "children": []}, {"name": "icu.hpp", "coverage": "83.6", "coverageClass": "coverage-medium", "linesTotal": "371", "linesExec": "310", "linesCoverage": "83.6", "linesClass": "coverage-medium", "functionsCoverage": "50.5", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.icu.hpp.85a0eeb0fabef296bfc4997ff2ba1c10.html", "children": []}, {"name": "match_flags.hpp", "coverage": "100.0", "coverageClass": "coverage-high", "linesTotal": "10", "linesExec": "10", "linesCoverage": "100.0", "linesClass": "coverage-high", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.match_flags.hpp.1af065f3f48dd91d0fe78746fcc481f7.html", "children": []}, {"name": "match_results.hpp", "coverage": "88.9", "coverageClass": "coverage-medium", "linesTotal": "307", "linesExec": "273", "linesCoverage": "88.9", "linesClass": "coverage-medium", "functionsCoverage": "61.1", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.match_results.hpp.6f836c49889c34cb37b399542baf9aad.html", "children": []}, {"name": "mem_block_cache.hpp", "coverage": "100.0", "coverageClass": "coverage-high", "linesTotal": "29", "linesExec": "29", "linesCoverage": "100.0", "linesClass": "coverage-high", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.mem_block_cache.hpp.774760178b61a39a431f88e157dbeb00.html", "children": []}, {"name": "object_cache.hpp", "coverage": "95.5", "coverageClass": "coverage-high", "linesTotal": "44", "linesExec": "42", "linesCoverage": "95.5", "linesClass": "coverage-high", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.object_cache.hpp.4205d672d499c4d505efbd5c4c18a6d7.html", "children": []}, {"name": "pattern_except.hpp", "coverage": "85.0", "coverageClass": "coverage-medium", "linesTotal": "20", "linesExec": "17", "linesCoverage": "85.0", "linesClass": "coverage-medium", "functionsCoverage": "42.9", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.pattern_except.hpp.b02995306146a84a816c726ffb2330d1.html", "children": []}, {"name": "perl_matcher.hpp", "coverage": "97.8", "coverageClass": "coverage-high", "linesTotal": "137", "linesExec": "134", "linesCoverage": "97.8", "linesClass": "coverage-high", "functionsCoverage": "62.1", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.perl_matcher.hpp.06d9b87b6156807b3cf2c759c3470f4c.html", "children": []}, {"name": "perl_matcher_common.hpp", "coverage": "95.9", "coverageClass": "coverage-high", "linesTotal": "416", "linesExec": "399", "linesCoverage": "95.9", "linesClass": "coverage-high", "functionsCoverage": "45.2", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.perl_matcher_common.hpp.a80be6e3c2e730ca65c745979c0f2c25.html", "children": []}, {"name": "perl_matcher_non_recursive.hpp", "coverage": "95.9", "coverageClass": "coverage-high", "linesTotal": "910", "linesExec": "873", "linesCoverage": "95.9", "linesClass": "coverage-high", "functionsCoverage": "48.7", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.perl_matcher_non_recursive.hpp.dab741f714b85b31fb79aec3a5169121.html", "children": []}, {"name": "primary_transform.hpp", "coverage": "54.8", "coverageClass": "coverage-low", "linesTotal": "31", "linesExec": "17", "linesCoverage": "54.8", "linesClass": "coverage-low", "functionsCoverage": "75.0", "functionsClass": "coverage-medium", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.primary_transform.hpp.f5a3fa617cbe44ea611e82cf685be5de.html", "children": []}, {"name": "regex_format.hpp", "coverage": "96.0", "coverageClass": "coverage-high", "linesTotal": "420", "linesExec": "403", "linesCoverage": "96.0", "linesClass": "coverage-high", "functionsCoverage": "42.8", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_format.hpp.cbfb4d559693198023d36b54c0718da8.html", "children": []}, {"name": "regex_grep.hpp", "coverage": "88.5", "coverageClass": "coverage-medium", "linesTotal": "26", "linesExec": "23", "linesCoverage": "88.5", "linesClass": "coverage-medium", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_grep.hpp.136157ec6043ed61e9e3c53cbca56207.html", "children": []}, {"name": "regex_iterator.hpp", "coverage": "100.0", "coverageClass": "coverage-high", "linesTotal": "48", "linesExec": "48", "linesCoverage": "100.0", "linesClass": "coverage-high", "functionsCoverage": "62.8", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_iterator.hpp.ec4c2b3efdca8342ebcbd87d33ac2551.html", "children": []}, {"name": "regex_match.hpp", "coverage": "100.0", "coverageClass": "coverage-high", "linesTotal": "20", "linesExec": "20", "linesCoverage": "100.0", "linesClass": "coverage-high", "functionsCoverage": "71.4", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_match.hpp.52d49d2882a598bc100172b30fdc9d28.html", "children": []}, {"name": "regex_merge.hpp", "coverage": "100.0", "coverageClass": "coverage-high", "linesTotal": "2", "linesExec": "2", "linesCoverage": "100.0", "linesClass": "coverage-high", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_merge.hpp.33d760b7953f0b4898c72299d831fc8c.html", "children": []}, {"name": "regex_raw_buffer.hpp", "coverage": "100.0", "coverageClass": "coverage-high", "linesTotal": "45", "linesExec": "45", "linesCoverage": "100.0", "linesClass": "coverage-high", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_raw_buffer.hpp.77701f68cbde88eced9e7f01d308832b.html", "children": []}, {"name": "regex_replace.hpp", "coverage": "96.0", "coverageClass": "coverage-high", "linesTotal": "25", "linesExec": "24", "linesCoverage": "96.0", "linesClass": "coverage-high", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_replace.hpp.c4b3d6cdac5c516e993e1b9cc67f4537.html", "children": []}, {"name": "regex_search.hpp", "coverage": "91.3", "coverageClass": "coverage-high", "linesTotal": "23", "linesExec": "21", "linesCoverage": "91.3", "linesClass": "coverage-high", "functionsCoverage": "76.9", "functionsClass": "coverage-medium", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_search.hpp.f27bcdbf1ed1b869adf859f2f255321f.html", "children": []}, {"name": "regex_split.hpp", "coverage": "97.4", "coverageClass": "coverage-high", "linesTotal": "38", "linesExec": "37", "linesCoverage": "97.4", "linesClass": "coverage-high", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_split.hpp.79301dd57396b64d102f9beeef6f66e1.html", "children": []}, {"name": "regex_token_iterator.hpp", "coverage": "98.0", "coverageClass": "coverage-high", "linesTotal": "98", "linesExec": "96", "linesCoverage": "98.0", "linesClass": "coverage-high", "functionsCoverage": "98.9", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_token_iterator.hpp.a853c95168815fb1dc822438deee2041.html", "children": []}, {"name": "regex_traits.hpp", "coverage": "100.0", "coverageClass": "coverage-high", "linesTotal": "18", "linesExec": "18", "linesCoverage": "100.0", "linesClass": "coverage-high", "functionsCoverage": "96.9", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_traits.hpp.d789ddd33a94513158258f47f9458fa9.html", "children": []}, {"name": "regex_traits_defaults.hpp", "coverage": "96.5", "coverageClass": "coverage-high", "linesTotal": "86", "linesExec": "83", "linesCoverage": "96.5", "linesClass": "coverage-high", "functionsCoverage": "90.5", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_traits_defaults.hpp.0e98adc4b3e9dddef47398f3d8191f7e.html", "children": []}, {"name": "regex_workaround.hpp", "coverage": "83.3", "coverageClass": "coverage-medium", "linesTotal": "6", "linesExec": "5", "linesCoverage": "83.3", "linesClass": "coverage-medium", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.regex_workaround.hpp.4f4c12e5cddef399d459e91c0f0af974.html", "children": []}, {"name": "sub_match.hpp", "coverage": "97.6", "coverageClass": "coverage-high", "linesTotal": "166", "linesExec": "162", "linesCoverage": "97.6", "linesClass": "coverage-high", "functionsCoverage": "72.5", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.sub_match.hpp.8759274a22f6d7afc27afd16b4ac0c35.html", "children": []}, {"name": "u32regex_iterator.hpp", "coverage": "89.1", "coverageClass": "coverage-medium", "linesTotal": "55", "linesExec": "49", "linesCoverage": "89.1", "linesClass": "coverage-medium", "functionsCoverage": "30.0", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.u32regex_iterator.hpp.cad511ff9f518ed5712544f4e2df17c3.html", "children": []}, {"name": "u32regex_token_iterator.hpp", "coverage": "38.1", "coverageClass": "coverage-low", "linesTotal": "113", "linesExec": "43", "linesCoverage": "38.1", "linesClass": "coverage-low", "functionsCoverage": "12.4", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.u32regex_token_iterator.hpp.b0c5226fc9ad02ba0b06216e9824edae.html", "children": []}, {"name": "unicode_iterator.hpp", "coverage": "87.8", "coverageClass": "coverage-medium", "linesTotal": "328", "linesExec": "288", "linesCoverage": "87.8", "linesClass": "coverage-medium", "functionsCoverage": "65.3", "functionsClass": "coverage-low", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.unicode_iterator.hpp.6391a069c7ef3c1dd9a38df9254be88f.html", "children": []}]}]}]}]}, {"name": "src", "coverage": "73.4", "coverageClass": "coverage-low", "linesTotal": "222", "linesExec": "163", "linesCoverage": "73.4", "linesClass": "coverage-low", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": true, "link": "index.src.25d902c24283ab8cfbac54dfa101ad31.html", "children": [{"name": "posix_api.cpp", "coverage": "72.6", "coverageClass": "coverage-low", "linesTotal": "113", "linesExec": "82", "linesCoverage": "72.6", "linesClass": "coverage-low", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.posix_api.cpp.cfc7145cfaa87ceb158ee21e8fa5193a.html", "children": []}, {"name": "wide_posix_api.cpp", "coverage": "74.3", "coverageClass": "coverage-low", "linesTotal": "109", "linesExec": "81", "linesCoverage": "74.3", "linesClass": "coverage-low", "functionsCoverage": "100.0", "functionsClass": "coverage-high", "branchesCoverage": "-", "branchesClass": "coverage-unknown", "isDirectory": false, "link": "index.wide_posix_api.cpp.5d708c8c14b8fa1710211bdab41e2b13.html", "children": []}]}];