coverage 7.11.3__cp314-cp314-musllinux_1_2_x86_64.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.

Potentially problematic release.


This version of coverage might be problematic. Click here for more details.

Files changed (59) hide show
  1. coverage/__init__.py +40 -0
  2. coverage/__main__.py +12 -0
  3. coverage/annotate.py +114 -0
  4. coverage/bytecode.py +196 -0
  5. coverage/cmdline.py +1184 -0
  6. coverage/collector.py +486 -0
  7. coverage/config.py +731 -0
  8. coverage/context.py +74 -0
  9. coverage/control.py +1481 -0
  10. coverage/core.py +139 -0
  11. coverage/data.py +227 -0
  12. coverage/debug.py +669 -0
  13. coverage/disposition.py +59 -0
  14. coverage/env.py +135 -0
  15. coverage/exceptions.py +85 -0
  16. coverage/execfile.py +329 -0
  17. coverage/files.py +553 -0
  18. coverage/html.py +856 -0
  19. coverage/htmlfiles/coverage_html.js +733 -0
  20. coverage/htmlfiles/favicon_32.png +0 -0
  21. coverage/htmlfiles/index.html +164 -0
  22. coverage/htmlfiles/keybd_closed.png +0 -0
  23. coverage/htmlfiles/pyfile.html +149 -0
  24. coverage/htmlfiles/style.css +377 -0
  25. coverage/htmlfiles/style.scss +824 -0
  26. coverage/inorout.py +614 -0
  27. coverage/jsonreport.py +188 -0
  28. coverage/lcovreport.py +219 -0
  29. coverage/misc.py +373 -0
  30. coverage/multiproc.py +120 -0
  31. coverage/numbits.py +146 -0
  32. coverage/parser.py +1213 -0
  33. coverage/patch.py +166 -0
  34. coverage/phystokens.py +197 -0
  35. coverage/plugin.py +617 -0
  36. coverage/plugin_support.py +299 -0
  37. coverage/py.typed +1 -0
  38. coverage/python.py +269 -0
  39. coverage/pytracer.py +369 -0
  40. coverage/regions.py +127 -0
  41. coverage/report.py +298 -0
  42. coverage/report_core.py +117 -0
  43. coverage/results.py +471 -0
  44. coverage/sqldata.py +1153 -0
  45. coverage/sqlitedb.py +239 -0
  46. coverage/sysmon.py +482 -0
  47. coverage/templite.py +306 -0
  48. coverage/tomlconfig.py +210 -0
  49. coverage/tracer.cpython-314-x86_64-linux-musl.so +0 -0
  50. coverage/tracer.pyi +43 -0
  51. coverage/types.py +206 -0
  52. coverage/version.py +35 -0
  53. coverage/xmlreport.py +264 -0
  54. coverage-7.11.3.dist-info/METADATA +221 -0
  55. coverage-7.11.3.dist-info/RECORD +59 -0
  56. coverage-7.11.3.dist-info/WHEEL +5 -0
  57. coverage-7.11.3.dist-info/entry_points.txt +4 -0
  58. coverage-7.11.3.dist-info/licenses/LICENSE.txt +177 -0
  59. coverage-7.11.3.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/coveragepy/coveragepy/blob/main/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
+ });