()
| 144 | // ============================================================================ |
| 145 | |
| 146 | function buildScrollMarker() { |
| 147 | const existing = document.getElementById('scroll_marker'); |
| 148 | if (existing) existing.remove(); |
| 149 | |
| 150 | if (document.body.scrollHeight <= window.innerHeight) return; |
| 151 | |
| 152 | const allLines = document.querySelectorAll('.code-line'); |
| 153 | const lines = Array.from(allLines).filter(line => line.style.display !== 'none'); |
| 154 | const markerScale = window.innerHeight / document.body.scrollHeight; |
| 155 | const lineHeight = Math.min(Math.max(3, window.innerHeight / lines.length), 10); |
| 156 | const maxSamples = Math.max(...Array.from(lines, getSampleCount)); |
| 157 | |
| 158 | const scrollMarker = createElement('div', ''); |
| 159 | scrollMarker.id = 'scroll_marker'; |
| 160 | |
| 161 | let prevLine = -99, lastMark, lastTop; |
| 162 | |
| 163 | lines.forEach((line, index) => { |
| 164 | const samples = getSampleCount(line); |
| 165 | if (samples === 0) return; |
| 166 | |
| 167 | const lineTop = Math.floor(line.offsetTop * markerScale); |
| 168 | const lineNumber = index + 1; |
| 169 | const intensityClass = maxSamples > 0 ? (intensityToClass(samples / maxSamples) || 'cold') : 'cold'; |
| 170 | |
| 171 | if (lineNumber === prevLine + 1 && lastMark?.classList.contains(intensityClass)) { |
| 172 | lastMark.style.height = `${lineTop + lineHeight - lastTop}px`; |
| 173 | } else { |
| 174 | lastMark = createElement('div', `marker ${intensityClass}`); |
| 175 | lastMark.style.height = `${lineHeight}px`; |
| 176 | lastMark.style.top = `${lineTop}px`; |
| 177 | scrollMarker.appendChild(lastMark); |
| 178 | lastTop = lineTop; |
| 179 | } |
| 180 | |
| 181 | prevLine = lineNumber; |
| 182 | }); |
| 183 | |
| 184 | document.body.appendChild(scrollMarker); |
| 185 | } |
| 186 | |
| 187 | function applyLineColors() { |
| 188 | const lines = document.querySelectorAll('.code-line'); |
no test coverage detected
searching dependent graphs…