coverage 7.11.1__cp314-cp314-musllinux_1_2_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- coverage/__init__.py +40 -0
- coverage/__main__.py +12 -0
- coverage/annotate.py +114 -0
- coverage/bytecode.py +196 -0
- coverage/cmdline.py +1184 -0
- coverage/collector.py +486 -0
- coverage/config.py +731 -0
- coverage/context.py +74 -0
- coverage/control.py +1481 -0
- coverage/core.py +139 -0
- coverage/data.py +227 -0
- coverage/debug.py +669 -0
- coverage/disposition.py +59 -0
- coverage/env.py +135 -0
- coverage/exceptions.py +91 -0
- coverage/execfile.py +329 -0
- coverage/files.py +553 -0
- coverage/html.py +856 -0
- coverage/htmlfiles/coverage_html.js +733 -0
- coverage/htmlfiles/favicon_32.png +0 -0
- coverage/htmlfiles/index.html +164 -0
- coverage/htmlfiles/keybd_closed.png +0 -0
- coverage/htmlfiles/pyfile.html +149 -0
- coverage/htmlfiles/style.css +377 -0
- coverage/htmlfiles/style.scss +824 -0
- coverage/inorout.py +614 -0
- coverage/jsonreport.py +188 -0
- coverage/lcovreport.py +219 -0
- coverage/misc.py +373 -0
- coverage/multiproc.py +120 -0
- coverage/numbits.py +146 -0
- coverage/parser.py +1213 -0
- coverage/patch.py +166 -0
- coverage/phystokens.py +197 -0
- coverage/plugin.py +617 -0
- coverage/plugin_support.py +299 -0
- coverage/py.typed +1 -0
- coverage/python.py +269 -0
- coverage/pytracer.py +369 -0
- coverage/regions.py +127 -0
- coverage/report.py +298 -0
- coverage/report_core.py +117 -0
- coverage/results.py +471 -0
- coverage/sqldata.py +1153 -0
- coverage/sqlitedb.py +239 -0
- coverage/sysmon.py +474 -0
- coverage/templite.py +306 -0
- coverage/tomlconfig.py +210 -0
- coverage/tracer.cpython-314-aarch64-linux-musl.so +0 -0
- coverage/tracer.pyi +43 -0
- coverage/types.py +206 -0
- coverage/version.py +35 -0
- coverage/xmlreport.py +264 -0
- coverage-7.11.1.dist-info/METADATA +221 -0
- coverage-7.11.1.dist-info/RECORD +59 -0
- coverage-7.11.1.dist-info/WHEEL +5 -0
- coverage-7.11.1.dist-info/entry_points.txt +4 -0
- coverage-7.11.1.dist-info/licenses/LICENSE.txt +177 -0
- coverage-7.11.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,733 @@
|
|
|
1
|
+
// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
|
2
|
+
// For details: https://github.com/nedbat/coveragepy/blob/master/NOTICE.txt
|
|
3
|
+
|
|
4
|
+
// Coverage.py HTML report browser code.
|
|
5
|
+
/*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
|
|
6
|
+
/*global coverage: true, document, window, $ */
|
|
7
|
+
|
|
8
|
+
coverage = {};
|
|
9
|
+
|
|
10
|
+
// General helpers
|
|
11
|
+
function debounce(callback, wait) {
|
|
12
|
+
let timeoutId = null;
|
|
13
|
+
return function(...args) {
|
|
14
|
+
clearTimeout(timeoutId);
|
|
15
|
+
timeoutId = setTimeout(() => {
|
|
16
|
+
callback.apply(this, args);
|
|
17
|
+
}, wait);
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function checkVisible(element) {
|
|
22
|
+
const rect = element.getBoundingClientRect();
|
|
23
|
+
const viewBottom = Math.max(document.documentElement.clientHeight, window.innerHeight);
|
|
24
|
+
const viewTop = 30;
|
|
25
|
+
return !(rect.bottom < viewTop || rect.top >= viewBottom);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function on_click(sel, fn) {
|
|
29
|
+
const elt = document.querySelector(sel);
|
|
30
|
+
if (elt) {
|
|
31
|
+
elt.addEventListener("click", fn);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Helpers for table sorting
|
|
36
|
+
function getCellValue(row, column = 0) {
|
|
37
|
+
const cell = row.cells[column] // nosemgrep: eslint.detect-object-injection
|
|
38
|
+
if (cell.childElementCount == 1) {
|
|
39
|
+
var child = cell.firstElementChild;
|
|
40
|
+
if (child.tagName === "A") {
|
|
41
|
+
child = child.firstElementChild;
|
|
42
|
+
}
|
|
43
|
+
if (child instanceof HTMLDataElement && child.value) {
|
|
44
|
+
return child.value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return cell.innerText || cell.textContent;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function rowComparator(rowA, rowB, column = 0) {
|
|
51
|
+
let valueA = getCellValue(rowA, column);
|
|
52
|
+
let valueB = getCellValue(rowB, column);
|
|
53
|
+
if (!isNaN(valueA) && !isNaN(valueB)) {
|
|
54
|
+
return valueA - valueB;
|
|
55
|
+
}
|
|
56
|
+
return valueA.localeCompare(valueB, undefined, {numeric: true});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function sortColumn(th) {
|
|
60
|
+
// Get the current sorting direction of the selected header,
|
|
61
|
+
// clear state on other headers and then set the new sorting direction.
|
|
62
|
+
const currentSortOrder = th.getAttribute("aria-sort");
|
|
63
|
+
[...th.parentElement.cells].forEach(header => header.setAttribute("aria-sort", "none"));
|
|
64
|
+
var direction;
|
|
65
|
+
if (currentSortOrder === "none") {
|
|
66
|
+
direction = th.dataset.defaultSortOrder || "ascending";
|
|
67
|
+
}
|
|
68
|
+
else if (currentSortOrder === "ascending") {
|
|
69
|
+
direction = "descending";
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
direction = "ascending";
|
|
73
|
+
}
|
|
74
|
+
th.setAttribute("aria-sort", direction);
|
|
75
|
+
|
|
76
|
+
const column = [...th.parentElement.cells].indexOf(th)
|
|
77
|
+
|
|
78
|
+
// Sort all rows and afterwards append them in order to move them in the DOM.
|
|
79
|
+
Array.from(th.closest("table").querySelectorAll("tbody tr"))
|
|
80
|
+
.sort((rowA, rowB) => rowComparator(rowA, rowB, column) * (direction === "ascending" ? 1 : -1))
|
|
81
|
+
.forEach(tr => tr.parentElement.appendChild(tr));
|
|
82
|
+
|
|
83
|
+
// Save the sort order for next time.
|
|
84
|
+
if (th.id !== "region") {
|
|
85
|
+
let th_id = "file"; // Sort by file if we don't have a column id
|
|
86
|
+
let current_direction = direction;
|
|
87
|
+
const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE);
|
|
88
|
+
if (stored_list) {
|
|
89
|
+
({th_id, direction} = JSON.parse(stored_list))
|
|
90
|
+
}
|
|
91
|
+
localStorage.setItem(coverage.INDEX_SORT_STORAGE, JSON.stringify({
|
|
92
|
+
"th_id": th.id,
|
|
93
|
+
"direction": current_direction
|
|
94
|
+
}));
|
|
95
|
+
if (th.id !== th_id || document.getElementById("region")) {
|
|
96
|
+
// Sort column has changed, unset sorting by function or class.
|
|
97
|
+
localStorage.setItem(coverage.SORTED_BY_REGION, JSON.stringify({
|
|
98
|
+
"by_region": false,
|
|
99
|
+
"region_direction": current_direction
|
|
100
|
+
}));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
// Sort column has changed to by function or class, remember that.
|
|
105
|
+
localStorage.setItem(coverage.SORTED_BY_REGION, JSON.stringify({
|
|
106
|
+
"by_region": true,
|
|
107
|
+
"region_direction": direction
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Find all the elements with data-shortcut attribute, and use them to assign a shortcut key.
|
|
113
|
+
coverage.assign_shortkeys = function () {
|
|
114
|
+
document.querySelectorAll("[data-shortcut]").forEach(element => {
|
|
115
|
+
document.addEventListener("keypress", event => {
|
|
116
|
+
if (event.target.tagName.toLowerCase() === "input") {
|
|
117
|
+
return; // ignore keypress from search filter
|
|
118
|
+
}
|
|
119
|
+
if (event.key === element.dataset.shortcut) {
|
|
120
|
+
element.click();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
// Create the events for the filter box.
|
|
127
|
+
coverage.wire_up_filter = function () {
|
|
128
|
+
// Populate the filter and hide100 inputs if there are saved values for them.
|
|
129
|
+
const saved_filter_value = localStorage.getItem(coverage.FILTER_STORAGE);
|
|
130
|
+
if (saved_filter_value) {
|
|
131
|
+
document.getElementById("filter").value = saved_filter_value;
|
|
132
|
+
}
|
|
133
|
+
const saved_hide100_value = localStorage.getItem(coverage.HIDE100_STORAGE);
|
|
134
|
+
if (saved_hide100_value) {
|
|
135
|
+
document.getElementById("hide100").checked = JSON.parse(saved_hide100_value);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Cache elements.
|
|
139
|
+
const table = document.querySelector("table.index");
|
|
140
|
+
const table_body_rows = table.querySelectorAll("tbody tr");
|
|
141
|
+
const no_rows = document.getElementById("no_rows");
|
|
142
|
+
|
|
143
|
+
// Observe filter keyevents.
|
|
144
|
+
const filter_handler = (event => {
|
|
145
|
+
// Keep running total of each metric, first index contains number of shown rows
|
|
146
|
+
const totals = new Array(table.rows[0].cells.length).fill(0);
|
|
147
|
+
// Accumulate the percentage as fraction
|
|
148
|
+
totals[totals.length - 1] = { "numer": 0, "denom": 0 }; // nosemgrep: eslint.detect-object-injection
|
|
149
|
+
|
|
150
|
+
var text = document.getElementById("filter").value;
|
|
151
|
+
// Store filter value
|
|
152
|
+
localStorage.setItem(coverage.FILTER_STORAGE, text);
|
|
153
|
+
const casefold = (text === text.toLowerCase());
|
|
154
|
+
const hide100 = document.getElementById("hide100").checked;
|
|
155
|
+
// Store hide value.
|
|
156
|
+
localStorage.setItem(coverage.HIDE100_STORAGE, JSON.stringify(hide100));
|
|
157
|
+
|
|
158
|
+
// Hide / show elements.
|
|
159
|
+
table_body_rows.forEach(row => {
|
|
160
|
+
var show = false;
|
|
161
|
+
// Check the text filter.
|
|
162
|
+
for (let column = 0; column < totals.length; column++) {
|
|
163
|
+
cell = row.cells[column];
|
|
164
|
+
if (cell.classList.contains("name")) {
|
|
165
|
+
var celltext = cell.textContent;
|
|
166
|
+
if (casefold) {
|
|
167
|
+
celltext = celltext.toLowerCase();
|
|
168
|
+
}
|
|
169
|
+
if (celltext.includes(text)) {
|
|
170
|
+
show = true;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Check the "hide covered" filter.
|
|
176
|
+
if (show && hide100) {
|
|
177
|
+
const [numer, denom] = row.cells[row.cells.length - 1].dataset.ratio.split(" ");
|
|
178
|
+
show = (numer !== denom);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (!show) {
|
|
182
|
+
// hide
|
|
183
|
+
row.classList.add("hidden");
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// show
|
|
188
|
+
row.classList.remove("hidden");
|
|
189
|
+
totals[0]++;
|
|
190
|
+
|
|
191
|
+
for (let column = 0; column < totals.length; column++) {
|
|
192
|
+
// Accumulate dynamic totals
|
|
193
|
+
cell = row.cells[column] // nosemgrep: eslint.detect-object-injection
|
|
194
|
+
if (cell.classList.contains("name")) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (column === totals.length - 1) {
|
|
198
|
+
// Last column contains percentage
|
|
199
|
+
const [numer, denom] = cell.dataset.ratio.split(" ");
|
|
200
|
+
totals[column]["numer"] += parseInt(numer, 10); // nosemgrep: eslint.detect-object-injection
|
|
201
|
+
totals[column]["denom"] += parseInt(denom, 10); // nosemgrep: eslint.detect-object-injection
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
totals[column] += parseInt(cell.textContent, 10); // nosemgrep: eslint.detect-object-injection
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Show placeholder if no rows will be displayed.
|
|
210
|
+
if (!totals[0]) {
|
|
211
|
+
// Show placeholder, hide table.
|
|
212
|
+
no_rows.style.display = "block";
|
|
213
|
+
table.style.display = "none";
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Hide placeholder, show table.
|
|
218
|
+
no_rows.style.display = null;
|
|
219
|
+
table.style.display = null;
|
|
220
|
+
|
|
221
|
+
const footer = table.tFoot.rows[0];
|
|
222
|
+
// Calculate new dynamic sum values based on visible rows.
|
|
223
|
+
for (let column = 0; column < totals.length; column++) {
|
|
224
|
+
// Get footer cell element.
|
|
225
|
+
const cell = footer.cells[column]; // nosemgrep: eslint.detect-object-injection
|
|
226
|
+
if (cell.classList.contains("name")) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Set value into dynamic footer cell element.
|
|
231
|
+
if (column === totals.length - 1) {
|
|
232
|
+
// Percentage column uses the numerator and denominator,
|
|
233
|
+
// and adapts to the number of decimal places.
|
|
234
|
+
const match = /\.([0-9]+)/.exec(cell.textContent);
|
|
235
|
+
const places = match ? match[1].length : 0;
|
|
236
|
+
const { numer, denom } = totals[column]; // nosemgrep: eslint.detect-object-injection
|
|
237
|
+
cell.dataset.ratio = `${numer} ${denom}`;
|
|
238
|
+
// Check denom to prevent NaN if filtered files contain no statements
|
|
239
|
+
cell.textContent = denom
|
|
240
|
+
? `${(numer * 100 / denom).toFixed(places)}%`
|
|
241
|
+
: `${(100).toFixed(places)}%`;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
cell.textContent = totals[column]; // nosemgrep: eslint.detect-object-injection
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
document.getElementById("filter").addEventListener("input", debounce(filter_handler));
|
|
250
|
+
document.getElementById("hide100").addEventListener("input", debounce(filter_handler));
|
|
251
|
+
|
|
252
|
+
// Trigger change event on setup, to force filter on page refresh
|
|
253
|
+
// (filter value may still be present).
|
|
254
|
+
document.getElementById("filter").dispatchEvent(new Event("input"));
|
|
255
|
+
document.getElementById("hide100").dispatchEvent(new Event("input"));
|
|
256
|
+
};
|
|
257
|
+
coverage.FILTER_STORAGE = "COVERAGE_FILTER_VALUE";
|
|
258
|
+
coverage.HIDE100_STORAGE = "COVERAGE_HIDE100_VALUE";
|
|
259
|
+
|
|
260
|
+
// Set up the click-to-sort columns.
|
|
261
|
+
coverage.wire_up_sorting = function () {
|
|
262
|
+
document.querySelectorAll("[data-sortable] th[aria-sort]").forEach(
|
|
263
|
+
th => th.addEventListener("click", e => sortColumn(e.target))
|
|
264
|
+
);
|
|
265
|
+
|
|
266
|
+
// Look for a localStorage item containing previous sort settings:
|
|
267
|
+
let th_id = "file", direction = "ascending";
|
|
268
|
+
const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE);
|
|
269
|
+
if (stored_list) {
|
|
270
|
+
({th_id, direction} = JSON.parse(stored_list));
|
|
271
|
+
}
|
|
272
|
+
let by_region = false, region_direction = "ascending";
|
|
273
|
+
const sorted_by_region = localStorage.getItem(coverage.SORTED_BY_REGION);
|
|
274
|
+
if (sorted_by_region) {
|
|
275
|
+
({
|
|
276
|
+
by_region,
|
|
277
|
+
region_direction
|
|
278
|
+
} = JSON.parse(sorted_by_region));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
const region_id = "region";
|
|
282
|
+
if (by_region && document.getElementById(region_id)) {
|
|
283
|
+
direction = region_direction;
|
|
284
|
+
}
|
|
285
|
+
// If we are in a page that has a column with id of "region", sort on
|
|
286
|
+
// it if the last sort was by function or class.
|
|
287
|
+
let th;
|
|
288
|
+
if (document.getElementById(region_id)) {
|
|
289
|
+
th = document.getElementById(by_region ? region_id : th_id);
|
|
290
|
+
}
|
|
291
|
+
else {
|
|
292
|
+
th = document.getElementById(th_id);
|
|
293
|
+
}
|
|
294
|
+
th.setAttribute("aria-sort", direction === "ascending" ? "descending" : "ascending");
|
|
295
|
+
th.click()
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
coverage.INDEX_SORT_STORAGE = "COVERAGE_INDEX_SORT_2";
|
|
299
|
+
coverage.SORTED_BY_REGION = "COVERAGE_SORT_REGION";
|
|
300
|
+
|
|
301
|
+
// Loaded on index.html
|
|
302
|
+
coverage.index_ready = function () {
|
|
303
|
+
coverage.assign_shortkeys();
|
|
304
|
+
coverage.wire_up_filter();
|
|
305
|
+
coverage.wire_up_sorting();
|
|
306
|
+
|
|
307
|
+
on_click(".button_prev_file", coverage.to_prev_file);
|
|
308
|
+
on_click(".button_next_file", coverage.to_next_file);
|
|
309
|
+
|
|
310
|
+
on_click(".button_show_hide_help", coverage.show_hide_help);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
// -- pyfile stuff --
|
|
314
|
+
|
|
315
|
+
coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS";
|
|
316
|
+
|
|
317
|
+
coverage.pyfile_ready = function () {
|
|
318
|
+
// If we're directed to a particular line number, highlight the line.
|
|
319
|
+
var frag = location.hash;
|
|
320
|
+
if (frag.length > 2 && frag[1] === "t") {
|
|
321
|
+
document.querySelector(frag).closest(".n").classList.add("highlight");
|
|
322
|
+
coverage.set_sel(parseInt(frag.substr(2), 10));
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
coverage.set_sel(0);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
on_click(".button_toggle_run", coverage.toggle_lines);
|
|
329
|
+
on_click(".button_toggle_mis", coverage.toggle_lines);
|
|
330
|
+
on_click(".button_toggle_exc", coverage.toggle_lines);
|
|
331
|
+
on_click(".button_toggle_par", coverage.toggle_lines);
|
|
332
|
+
|
|
333
|
+
on_click(".button_next_chunk", coverage.to_next_chunk_nicely);
|
|
334
|
+
on_click(".button_prev_chunk", coverage.to_prev_chunk_nicely);
|
|
335
|
+
on_click(".button_top_of_page", coverage.to_top);
|
|
336
|
+
on_click(".button_first_chunk", coverage.to_first_chunk);
|
|
337
|
+
|
|
338
|
+
on_click(".button_prev_file", coverage.to_prev_file);
|
|
339
|
+
on_click(".button_next_file", coverage.to_next_file);
|
|
340
|
+
on_click(".button_to_index", coverage.to_index);
|
|
341
|
+
|
|
342
|
+
on_click(".button_show_hide_help", coverage.show_hide_help);
|
|
343
|
+
|
|
344
|
+
coverage.filters = undefined;
|
|
345
|
+
try {
|
|
346
|
+
coverage.filters = localStorage.getItem(coverage.LINE_FILTERS_STORAGE);
|
|
347
|
+
} catch(err) {}
|
|
348
|
+
|
|
349
|
+
if (coverage.filters) {
|
|
350
|
+
coverage.filters = JSON.parse(coverage.filters);
|
|
351
|
+
}
|
|
352
|
+
else {
|
|
353
|
+
coverage.filters = {run: false, exc: true, mis: true, par: true};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
for (cls in coverage.filters) {
|
|
357
|
+
coverage.set_line_visibilty(cls, coverage.filters[cls]); // nosemgrep: eslint.detect-object-injection
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
coverage.assign_shortkeys();
|
|
361
|
+
coverage.init_scroll_markers();
|
|
362
|
+
coverage.wire_up_sticky_header();
|
|
363
|
+
|
|
364
|
+
document.querySelectorAll("[id^=ctxs]").forEach(
|
|
365
|
+
cbox => cbox.addEventListener("click", coverage.expand_contexts)
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
// Rebuild scroll markers when the window height changes.
|
|
369
|
+
window.addEventListener("resize", coverage.build_scroll_markers);
|
|
370
|
+
};
|
|
371
|
+
|
|
372
|
+
coverage.toggle_lines = function (event) {
|
|
373
|
+
const btn = event.target.closest("button");
|
|
374
|
+
const category = btn.value
|
|
375
|
+
const show = !btn.classList.contains("show_" + category);
|
|
376
|
+
coverage.set_line_visibilty(category, show);
|
|
377
|
+
coverage.build_scroll_markers();
|
|
378
|
+
coverage.filters[category] = show;
|
|
379
|
+
try {
|
|
380
|
+
localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters));
|
|
381
|
+
} catch(err) {}
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
coverage.set_line_visibilty = function (category, should_show) {
|
|
385
|
+
const cls = "show_" + category;
|
|
386
|
+
const btn = document.querySelector(".button_toggle_" + category);
|
|
387
|
+
if (btn) {
|
|
388
|
+
if (should_show) {
|
|
389
|
+
document.querySelectorAll("#source ." + category).forEach(e => e.classList.add(cls));
|
|
390
|
+
btn.classList.add(cls);
|
|
391
|
+
}
|
|
392
|
+
else {
|
|
393
|
+
document.querySelectorAll("#source ." + category).forEach(e => e.classList.remove(cls));
|
|
394
|
+
btn.classList.remove(cls);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
// Return the nth line div.
|
|
400
|
+
coverage.line_elt = function (n) {
|
|
401
|
+
return document.getElementById("t" + n)?.closest("p");
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
// Set the selection. b and e are line numbers.
|
|
405
|
+
coverage.set_sel = function (b, e) {
|
|
406
|
+
// The first line selected.
|
|
407
|
+
coverage.sel_begin = b;
|
|
408
|
+
// The next line not selected.
|
|
409
|
+
coverage.sel_end = (e === undefined) ? b+1 : e;
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
coverage.to_top = function () {
|
|
413
|
+
coverage.set_sel(0, 1);
|
|
414
|
+
coverage.scroll_window(0);
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
coverage.to_first_chunk = function () {
|
|
418
|
+
coverage.set_sel(0, 1);
|
|
419
|
+
coverage.to_next_chunk();
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
coverage.to_prev_file = function () {
|
|
423
|
+
window.location = document.getElementById("prevFileLink").href;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
coverage.to_next_file = function () {
|
|
427
|
+
window.location = document.getElementById("nextFileLink").href;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
coverage.to_index = function () {
|
|
431
|
+
location.href = document.getElementById("indexLink").href;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
coverage.show_hide_help = function () {
|
|
435
|
+
const helpCheck = document.getElementById("help_panel_state")
|
|
436
|
+
helpCheck.checked = !helpCheck.checked;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Return a string indicating what kind of chunk this line belongs to,
|
|
440
|
+
// or null if not a chunk.
|
|
441
|
+
coverage.chunk_indicator = function (line_elt) {
|
|
442
|
+
const classes = line_elt?.className;
|
|
443
|
+
if (!classes) {
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
const match = classes.match(/\bshow_\w+\b/);
|
|
447
|
+
if (!match) {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
return match[0];
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
coverage.to_next_chunk = function () {
|
|
454
|
+
const c = coverage;
|
|
455
|
+
|
|
456
|
+
// Find the start of the next colored chunk.
|
|
457
|
+
var probe = c.sel_end;
|
|
458
|
+
var chunk_indicator, probe_line;
|
|
459
|
+
while (true) {
|
|
460
|
+
probe_line = c.line_elt(probe);
|
|
461
|
+
if (!probe_line) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
chunk_indicator = c.chunk_indicator(probe_line);
|
|
465
|
+
if (chunk_indicator) {
|
|
466
|
+
break;
|
|
467
|
+
}
|
|
468
|
+
probe++;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// There's a next chunk, `probe` points to it.
|
|
472
|
+
var begin = probe;
|
|
473
|
+
|
|
474
|
+
// Find the end of this chunk.
|
|
475
|
+
var next_indicator = chunk_indicator;
|
|
476
|
+
while (next_indicator === chunk_indicator) {
|
|
477
|
+
probe++;
|
|
478
|
+
probe_line = c.line_elt(probe);
|
|
479
|
+
next_indicator = c.chunk_indicator(probe_line);
|
|
480
|
+
}
|
|
481
|
+
c.set_sel(begin, probe);
|
|
482
|
+
c.show_selection();
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
coverage.to_prev_chunk = function () {
|
|
486
|
+
const c = coverage;
|
|
487
|
+
|
|
488
|
+
// Find the end of the prev colored chunk.
|
|
489
|
+
var probe = c.sel_begin-1;
|
|
490
|
+
var probe_line = c.line_elt(probe);
|
|
491
|
+
if (!probe_line) {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
var chunk_indicator = c.chunk_indicator(probe_line);
|
|
495
|
+
while (probe > 1 && !chunk_indicator) {
|
|
496
|
+
probe--;
|
|
497
|
+
probe_line = c.line_elt(probe);
|
|
498
|
+
if (!probe_line) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
chunk_indicator = c.chunk_indicator(probe_line);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// There's a prev chunk, `probe` points to its last line.
|
|
505
|
+
var end = probe+1;
|
|
506
|
+
|
|
507
|
+
// Find the beginning of this chunk.
|
|
508
|
+
var prev_indicator = chunk_indicator;
|
|
509
|
+
while (prev_indicator === chunk_indicator) {
|
|
510
|
+
probe--;
|
|
511
|
+
if (probe <= 0) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
probe_line = c.line_elt(probe);
|
|
515
|
+
prev_indicator = c.chunk_indicator(probe_line);
|
|
516
|
+
}
|
|
517
|
+
c.set_sel(probe+1, end);
|
|
518
|
+
c.show_selection();
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// Returns 0, 1, or 2: how many of the two ends of the selection are on
|
|
522
|
+
// the screen right now?
|
|
523
|
+
coverage.selection_ends_on_screen = function () {
|
|
524
|
+
if (coverage.sel_begin === 0) {
|
|
525
|
+
return 0;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
const begin = coverage.line_elt(coverage.sel_begin);
|
|
529
|
+
const end = coverage.line_elt(coverage.sel_end-1);
|
|
530
|
+
|
|
531
|
+
return (
|
|
532
|
+
(checkVisible(begin) ? 1 : 0)
|
|
533
|
+
+ (checkVisible(end) ? 1 : 0)
|
|
534
|
+
);
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
coverage.to_next_chunk_nicely = function () {
|
|
538
|
+
if (coverage.selection_ends_on_screen() === 0) {
|
|
539
|
+
// The selection is entirely off the screen:
|
|
540
|
+
// Set the top line on the screen as selection.
|
|
541
|
+
|
|
542
|
+
// This will select the top-left of the viewport
|
|
543
|
+
// As this is most likely the span with the line number we take the parent
|
|
544
|
+
const line = document.elementFromPoint(0, 0).parentElement;
|
|
545
|
+
if (line.parentElement !== document.getElementById("source")) {
|
|
546
|
+
// The element is not a source line but the header or similar
|
|
547
|
+
coverage.select_line_or_chunk(1);
|
|
548
|
+
}
|
|
549
|
+
else {
|
|
550
|
+
// We extract the line number from the id
|
|
551
|
+
coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10));
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
coverage.to_next_chunk();
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
coverage.to_prev_chunk_nicely = function () {
|
|
558
|
+
if (coverage.selection_ends_on_screen() === 0) {
|
|
559
|
+
// The selection is entirely off the screen:
|
|
560
|
+
// Set the lowest line on the screen as selection.
|
|
561
|
+
|
|
562
|
+
// This will select the bottom-left of the viewport
|
|
563
|
+
// As this is most likely the span with the line number we take the parent
|
|
564
|
+
const line = document.elementFromPoint(document.documentElement.clientHeight-1, 0).parentElement;
|
|
565
|
+
if (line.parentElement !== document.getElementById("source")) {
|
|
566
|
+
// The element is not a source line but the header or similar
|
|
567
|
+
coverage.select_line_or_chunk(coverage.lines_len);
|
|
568
|
+
}
|
|
569
|
+
else {
|
|
570
|
+
// We extract the line number from the id
|
|
571
|
+
coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10));
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
coverage.to_prev_chunk();
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
// Select line number lineno, or if it is in a colored chunk, select the
|
|
578
|
+
// entire chunk
|
|
579
|
+
coverage.select_line_or_chunk = function (lineno) {
|
|
580
|
+
var c = coverage;
|
|
581
|
+
var probe_line = c.line_elt(lineno);
|
|
582
|
+
if (!probe_line) {
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
var the_indicator = c.chunk_indicator(probe_line);
|
|
586
|
+
if (the_indicator) {
|
|
587
|
+
// The line is in a highlighted chunk.
|
|
588
|
+
// Search backward for the first line.
|
|
589
|
+
var probe = lineno;
|
|
590
|
+
var indicator = the_indicator;
|
|
591
|
+
while (probe > 0 && indicator === the_indicator) {
|
|
592
|
+
probe--;
|
|
593
|
+
probe_line = c.line_elt(probe);
|
|
594
|
+
if (!probe_line) {
|
|
595
|
+
break;
|
|
596
|
+
}
|
|
597
|
+
indicator = c.chunk_indicator(probe_line);
|
|
598
|
+
}
|
|
599
|
+
var begin = probe + 1;
|
|
600
|
+
|
|
601
|
+
// Search forward for the last line.
|
|
602
|
+
probe = lineno;
|
|
603
|
+
indicator = the_indicator;
|
|
604
|
+
while (indicator === the_indicator) {
|
|
605
|
+
probe++;
|
|
606
|
+
probe_line = c.line_elt(probe);
|
|
607
|
+
indicator = c.chunk_indicator(probe_line);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
coverage.set_sel(begin, probe);
|
|
611
|
+
}
|
|
612
|
+
else {
|
|
613
|
+
coverage.set_sel(lineno);
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
|
|
617
|
+
coverage.show_selection = function () {
|
|
618
|
+
// Highlight the lines in the chunk
|
|
619
|
+
document.querySelectorAll("#source .highlight").forEach(e => e.classList.remove("highlight"));
|
|
620
|
+
for (let probe = coverage.sel_begin; probe < coverage.sel_end; probe++) {
|
|
621
|
+
coverage.line_elt(probe).querySelector(".n").classList.add("highlight");
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
coverage.scroll_to_selection();
|
|
625
|
+
};
|
|
626
|
+
|
|
627
|
+
coverage.scroll_to_selection = function () {
|
|
628
|
+
// Scroll the page if the chunk isn't fully visible.
|
|
629
|
+
if (coverage.selection_ends_on_screen() < 2) {
|
|
630
|
+
const element = coverage.line_elt(coverage.sel_begin);
|
|
631
|
+
coverage.scroll_window(element.offsetTop - 60);
|
|
632
|
+
}
|
|
633
|
+
};
|
|
634
|
+
|
|
635
|
+
coverage.scroll_window = function (to_pos) {
|
|
636
|
+
window.scroll({top: to_pos, behavior: "smooth"});
|
|
637
|
+
};
|
|
638
|
+
|
|
639
|
+
coverage.init_scroll_markers = function () {
|
|
640
|
+
// Init some variables
|
|
641
|
+
coverage.lines_len = document.querySelectorAll("#source > p").length;
|
|
642
|
+
|
|
643
|
+
// Build html
|
|
644
|
+
coverage.build_scroll_markers();
|
|
645
|
+
};
|
|
646
|
+
|
|
647
|
+
coverage.build_scroll_markers = function () {
|
|
648
|
+
const temp_scroll_marker = document.getElementById("scroll_marker")
|
|
649
|
+
if (temp_scroll_marker) temp_scroll_marker.remove();
|
|
650
|
+
// Don't build markers if the window has no scroll bar.
|
|
651
|
+
if (document.body.scrollHeight <= window.innerHeight) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
const marker_scale = window.innerHeight / document.body.scrollHeight;
|
|
656
|
+
const line_height = Math.min(Math.max(3, window.innerHeight / coverage.lines_len), 10);
|
|
657
|
+
|
|
658
|
+
let previous_line = -99, last_mark, last_top;
|
|
659
|
+
|
|
660
|
+
const scroll_marker = document.createElement("div");
|
|
661
|
+
scroll_marker.id = "scroll_marker";
|
|
662
|
+
document.getElementById("source").querySelectorAll(
|
|
663
|
+
"p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par"
|
|
664
|
+
).forEach(element => {
|
|
665
|
+
const line_top = Math.floor(element.offsetTop * marker_scale);
|
|
666
|
+
const line_number = parseInt(element.querySelector(".n a").id.substr(1));
|
|
667
|
+
|
|
668
|
+
if (line_number === previous_line + 1) {
|
|
669
|
+
// If this solid missed block just make previous mark higher.
|
|
670
|
+
last_mark.style.height = `${line_top + line_height - last_top}px`;
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
// Add colored line in scroll_marker block.
|
|
674
|
+
last_mark = document.createElement("div");
|
|
675
|
+
last_mark.id = `m${line_number}`;
|
|
676
|
+
last_mark.classList.add("marker");
|
|
677
|
+
last_mark.style.height = `${line_height}px`;
|
|
678
|
+
last_mark.style.top = `${line_top}px`;
|
|
679
|
+
scroll_marker.append(last_mark);
|
|
680
|
+
last_top = line_top;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
previous_line = line_number;
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
// Append last to prevent layout calculation
|
|
687
|
+
document.body.append(scroll_marker);
|
|
688
|
+
};
|
|
689
|
+
|
|
690
|
+
coverage.wire_up_sticky_header = function () {
|
|
691
|
+
const header = document.querySelector("header");
|
|
692
|
+
const header_bottom = (
|
|
693
|
+
header.querySelector(".content h2").getBoundingClientRect().top -
|
|
694
|
+
header.getBoundingClientRect().top
|
|
695
|
+
);
|
|
696
|
+
|
|
697
|
+
function updateHeader() {
|
|
698
|
+
if (window.scrollY > header_bottom) {
|
|
699
|
+
header.classList.add("sticky");
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
header.classList.remove("sticky");
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
window.addEventListener("scroll", updateHeader);
|
|
707
|
+
updateHeader();
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
coverage.expand_contexts = function (e) {
|
|
711
|
+
var ctxs = e.target.parentNode.querySelector(".ctxs");
|
|
712
|
+
|
|
713
|
+
if (!ctxs.classList.contains("expanded")) {
|
|
714
|
+
var ctxs_text = ctxs.textContent;
|
|
715
|
+
var width = Number(ctxs_text[0]);
|
|
716
|
+
ctxs.textContent = "";
|
|
717
|
+
for (var i = 1; i < ctxs_text.length; i += width) {
|
|
718
|
+
key = ctxs_text.substring(i, i + width).trim();
|
|
719
|
+
ctxs.appendChild(document.createTextNode(contexts[key]));
|
|
720
|
+
ctxs.appendChild(document.createElement("br"));
|
|
721
|
+
}
|
|
722
|
+
ctxs.classList.add("expanded");
|
|
723
|
+
}
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
document.addEventListener("DOMContentLoaded", () => {
|
|
727
|
+
if (document.body.classList.contains("indexfile")) {
|
|
728
|
+
coverage.index_ready();
|
|
729
|
+
}
|
|
730
|
+
else {
|
|
731
|
+
coverage.pyfile_ready();
|
|
732
|
+
}
|
|
733
|
+
});
|