From 64423b175f178ecd68d8f8b8ff1dac5e2bc109c2 Mon Sep 17 00:00:00 2001 From: Septatrix <24257556+Septatrix@users.noreply.github.com> Date: Sat, 27 Mar 2021 13:55:36 +0100 Subject: [PATCH 1/8] Add more development stuff to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c8b41f499..9f81d1d02 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ coverage.xml # Stuff generated by editors. .idea/ +.vscode/ .vimtags # Stuff in the root. @@ -29,6 +30,7 @@ setuptools-*.egg .pytest_cache .hypothesis .ruby-version +.venv # Stuff in the test directory. covmain.zip From 9c1a8c6831eac2726fc3ab7c7fd20f98740a9f80 Mon Sep 17 00:00:00 2001 From: Septatrix <24257556+Septatrix@users.noreply.github.com> Date: Sat, 27 Mar 2021 15:40:10 +0100 Subject: [PATCH 2/8] Update shortcut handling --- coverage/htmlfiles/coverage_html.js | 12 +++++------- coverage/htmlfiles/index.html | 28 ++++++++++++++-------------- coverage/htmlfiles/pyfile.html | 24 ++++++++++++------------ coverage/htmlfiles/style.css | 2 +- coverage/htmlfiles/style.scss | 17 +++++++++-------- tests/gold/html/styled/style.css | 2 +- 6 files changed, 42 insertions(+), 43 deletions(-) diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 30d3a067f..8b08187e7 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -7,14 +7,12 @@ coverage = {}; -// Find all the elements with shortkey_* class, and use them to assign a shortcut key. +// Find all the elements with data-shortcut attribute, and use them to assign a shortcut key. coverage.assign_shortkeys = function () { - $("*[class*='shortkey_']").each(function (i, e) { - $.each($(e).attr("class").split(" "), function (i, c) { - if (/^shortkey_/.test(c)) { - $(document).bind('keydown', c.substr(9), function () { - $(e).click(); - }); + document.querySelectorAll("[data-shortcut]").forEach(element => { + document.addEventListener("keypress", event => { + if (event.key === element.dataset.shortcut) { + element.click(); } }); }); diff --git a/coverage/htmlfiles/index.html b/coverage/htmlfiles/index.html index 3654d66a0..9cad7c794 100644 --- a/coverage/htmlfiles/index.html +++ b/coverage/htmlfiles/index.html @@ -41,15 +41,15 @@

{{ title|escape }}:

Hot-keys on this page

- n - s - m - x + n + s + m + x {% if has_arcs %} - b - p + b + p {% endif %} - c   change column sorting + c   change column sorting

@@ -59,15 +59,15 @@

{{ title|escape }}: {# The title="" attr doesn"t work in Safari. #} - Module - statements - missing - excluded + Module + statements + missing + excluded {% if has_arcs %} - branches - partial + branches + partial {% endif %} - coverage + coverage {# HTML syntax requires thead, tfoot, tbody #} diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html index e15be066f..bc86624ad 100644 --- a/coverage/htmlfiles/pyfile.html +++ b/coverage/htmlfiles/pyfile.html @@ -34,12 +34,12 @@

Coverage for {{relative_filename|escape}} :

{{nums.n_statements}} statements   - - - + + + {% if has_arcs %} - + {% endif %}

@@ -50,20 +50,20 @@

Hot-keys on this page

- r - m - x - p   toggle line displays + r + m + x + p   toggle line displays

- j - k   next/prev highlighted chunk + j + k   next/prev highlighted chunk

- 0   (zero) top of page + 0   (zero) top of page

- 1   (one) first highlighted chunk + 1   (one) first highlighted chunk

diff --git a/coverage/htmlfiles/style.css b/coverage/htmlfiles/style.css index 7f8e32ab0..86ce5b3f1 100644 --- a/coverage/htmlfiles/style.css +++ b/coverage/htmlfiles/style.css @@ -120,7 +120,7 @@ h2.stats { margin-top: .5em; font-size: 1em; } .keyhelp { margin-top: .75em; } -.keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; } +kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; border-radius: 3px; } #source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; } diff --git a/coverage/htmlfiles/style.scss b/coverage/htmlfiles/style.scss index 394323408..5133f019e 100644 --- a/coverage/htmlfiles/style.scss +++ b/coverage/htmlfiles/style.scss @@ -331,15 +331,16 @@ h2.stats { .keyhelp { margin-top: .75em; +} - .key { - border: 1px solid black; - border-color: #888 #333 #333 #888; - padding: .1em .35em; - font-family: $font-code; - font-weight: bold; - background: #eee; - } +kbd { + border: 1px solid black; + border-color: #888 #333 #333 #888; + padding: .1em .35em; + font-family: $font-code; + font-weight: bold; + background: #eee; + border-radius: 3px; } // Source file styles diff --git a/tests/gold/html/styled/style.css b/tests/gold/html/styled/style.css index 7f8e32ab0..86ce5b3f1 100644 --- a/tests/gold/html/styled/style.css +++ b/tests/gold/html/styled/style.css @@ -120,7 +120,7 @@ h2.stats { margin-top: .5em; font-size: 1em; } .keyhelp { margin-top: .75em; } -.keyhelp .key { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; } +kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em .35em; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; font-weight: bold; background: #eee; border-radius: 3px; } #source { padding: 1em 0 1em 3.5rem; font-family: SFMono-Regular, Menlo, Monaco, Consolas, monospace; } From eac1efe1ae0e5dfe870d797b8e31c642029cbd3c Mon Sep 17 00:00:00 2001 From: Septatrix <24257556+Septatrix@users.noreply.github.com> Date: Sat, 27 Mar 2021 16:23:16 +0100 Subject: [PATCH 3/8] Update shortcut popup --- coverage/htmlfiles/coverage_html.js | 21 ------------ coverage/htmlfiles/index.html | 41 ++++++++++++----------- coverage/htmlfiles/pyfile.html | 51 +++++++++++++++-------------- coverage/htmlfiles/style.css | 18 ++++++---- coverage/htmlfiles/style.scss | 22 +++++++++---- tests/gold/html/styled/style.css | 18 ++++++---- 6 files changed, 86 insertions(+), 85 deletions(-) diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 8b08187e7..09c0fe530 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -18,24 +18,6 @@ coverage.assign_shortkeys = function () { }); }; -// Create the events for the help panel. -coverage.wire_up_help_panel = function () { - $("#keyboard_icon").click(function () { - // Show the help panel, and position it so the keyboard icon in the - // panel is in the same place as the keyboard icon in the header. - $(".help_panel").show(); - var koff = $("#keyboard_icon").offset(); - var poff = $("#panel_icon").position(); - $(".help_panel").offset({ - top: koff.top-poff.top-1, - left: koff.left-poff.left-1 - }); - }); - $("#panel_icon").click(function () { - $(".help_panel").hide(); - }); -}; - // Create the events for the filter box. coverage.wire_up_filter = function () { // Cache elements. @@ -218,7 +200,6 @@ coverage.index_ready = function ($) { }); coverage.assign_shortkeys(); - coverage.wire_up_help_panel(); coverage.wire_up_filter(); // Watch for page unload events so we can save the final sort settings: @@ -273,8 +254,6 @@ coverage.pyfile_ready = function ($) { } coverage.assign_shortkeys(); - coverage.wire_up_help_panel(); - coverage.init_scroll_markers(); // Rebuild scroll markers when the window height changes. diff --git a/coverage/htmlfiles/index.html b/coverage/htmlfiles/index.html index 9cad7c794..fbe85bbc2 100644 --- a/coverage/htmlfiles/index.html +++ b/coverage/htmlfiles/index.html @@ -28,7 +28,28 @@

{{ title|escape }}: {{totals.pc_covered_str}}%

- Show keyboard shortcuts +
+ + +
+

Shortcuts on this page

+
+

+ n + s + m + x + {% if has_arcs %} + b + p + {% endif %} + c   change column sorting +

+
+
+
@@ -36,24 +57,6 @@

{{ title|escape }}: -
- Hide keyboard shortcuts -

Hot-keys on this page

-
-

- n - s - m - x - {% if has_arcs %} - b - p - {% endif %} - c   change column sorting -

-
-
-
diff --git a/coverage/htmlfiles/pyfile.html b/coverage/htmlfiles/pyfile.html index bc86624ad..692709a31 100644 --- a/coverage/htmlfiles/pyfile.html +++ b/coverage/htmlfiles/pyfile.html @@ -30,7 +30,33 @@

Coverage for {{relative_filename|escape}} : {{nums.pc_covered_str}}%

- Show keyboard shortcuts +
+ + +
+

Shortcuts on this page

+
+

+ r + m + x + p   toggle line displays +

+

+ j + k   next/prev highlighted chunk +

+

+ 0   (zero) top of page +

+

+ 1   (one) first highlighted chunk +

+
+
+

{{nums.n_statements}} statements   @@ -45,29 +71,6 @@

-
- Hide keyboard shortcuts -

Hot-keys on this page

-
-

- r - m - x - p   toggle line displays -

-

- j - k   next/prev highlighted chunk -

-

- 0   (zero) top of page -

-

- 1   (one) first highlighted chunk -

-
-
-
{% for line in lines -%} {% joined %} diff --git a/coverage/htmlfiles/style.css b/coverage/htmlfiles/style.css index 86ce5b3f1..70fd68893 100644 --- a/coverage/htmlfiles/style.css +++ b/coverage/htmlfiles/style.css @@ -102,21 +102,25 @@ h2.stats { margin-top: .5em; font-size: 1em; } @media (prefers-color-scheme: dark) { .stats button.par.show_par { background: #650; } } -.help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; } +#help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; } #source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; } -#keyboard_icon { float: right; margin: 5px; cursor: pointer; } +#help_panel_wrapper { float: right; position: relative; } -.help_panel { padding: .75em; border: 1px solid #883; } +#keyboard_icon { margin: 5px; } -.help_panel .legend { font-style: italic; margin-bottom: 1em; } +#help_panel_state { display: none; } -.indexfile .help_panel { width: 25em; } +#help_panel { top: 25px; right: 0; padding: .75em; border: 1px solid #883; } -.pyfile .help_panel { width: 18em; } +#help_panel .legend { font-style: italic; margin-bottom: 1em; } -#panel_icon { float: right; cursor: pointer; } +.indexfile #help_panel { width: 25em; } + +.pyfile #help_panel { width: 18em; } + +#help_panel_state:checked ~ #help_panel { display: block; } .keyhelp { margin-top: .75em; } diff --git a/coverage/htmlfiles/style.scss b/coverage/htmlfiles/style.scss index 5133f019e..5ec1c8464 100644 --- a/coverage/htmlfiles/style.scss +++ b/coverage/htmlfiles/style.scss @@ -297,14 +297,23 @@ h2.stats { } // Help panel -#keyboard_icon { +#help_panel_wrapper { float: right; + position: relative; +} + +#keyboard_icon { margin: 5px; - cursor: pointer; } -.help_panel { +#help_panel_state { + display: none; +} + +#help_panel { @extend %popup; + top: 25px; + right: 0; padding: .75em; border: 1px solid #883; @@ -322,11 +331,10 @@ h2.stats { width: 18em; //min-height: 8em; } -} -#panel_icon { - float: right; - cursor: pointer; + #help_panel_state:checked ~ & { + display: block; + } } .keyhelp { diff --git a/tests/gold/html/styled/style.css b/tests/gold/html/styled/style.css index 86ce5b3f1..70fd68893 100644 --- a/tests/gold/html/styled/style.css +++ b/tests/gold/html/styled/style.css @@ -102,21 +102,25 @@ h2.stats { margin-top: .5em; font-size: 1em; } @media (prefers-color-scheme: dark) { .stats button.par.show_par { background: #650; } } -.help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; } +#help_panel, #source p .annotate.long { display: none; position: absolute; z-index: 999; background: #ffffcc; border: 1px solid #888; border-radius: .2em; color: #333; padding: .25em .5em; } #source p .annotate.long { white-space: normal; float: right; top: 1.75em; right: 1em; height: auto; } -#keyboard_icon { float: right; margin: 5px; cursor: pointer; } +#help_panel_wrapper { float: right; position: relative; } -.help_panel { padding: .75em; border: 1px solid #883; } +#keyboard_icon { margin: 5px; } -.help_panel .legend { font-style: italic; margin-bottom: 1em; } +#help_panel_state { display: none; } -.indexfile .help_panel { width: 25em; } +#help_panel { top: 25px; right: 0; padding: .75em; border: 1px solid #883; } -.pyfile .help_panel { width: 18em; } +#help_panel .legend { font-style: italic; margin-bottom: 1em; } -#panel_icon { float: right; cursor: pointer; } +.indexfile #help_panel { width: 25em; } + +.pyfile #help_panel { width: 18em; } + +#help_panel_state:checked ~ #help_panel { display: block; } .keyhelp { margin-top: .75em; } From 7b6443ec5bb44f2946c6f3c221884e33413fdb05 Mon Sep 17 00:00:00 2001 From: Septatrix <24257556+Septatrix@users.noreply.github.com> Date: Sat, 27 Mar 2021 22:11:43 +0100 Subject: [PATCH 4/8] Update filter --- coverage/htmlfiles/coverage_html.js | 209 +++++++++++++++------------- coverage/htmlfiles/index.html | 27 ++-- 2 files changed, 125 insertions(+), 111 deletions(-) diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 09c0fe530..27af5db5b 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -7,10 +7,23 @@ coverage = {}; +function debounce(callback, wait) { + let timeoutId = null; + return function(...args) { + clearTimeout(timeoutId); + timeoutId = setTimeout(() => { + callback.apply(this, args); + }, wait); + }; +}; + // Find all the elements with data-shortcut attribute, and use them to assign a shortcut key. coverage.assign_shortkeys = function () { document.querySelectorAll("[data-shortcut]").forEach(element => { document.addEventListener("keypress", event => { + if (event.target.tagName.toLowerCase() === "input") { + return; // ignore keypress from search filter + } if (event.key === element.dataset.shortcut) { element.click(); } @@ -21,130 +34,132 @@ coverage.assign_shortkeys = function () { // Create the events for the filter box. coverage.wire_up_filter = function () { // Cache elements. - var table = $("table.index"); - var table_rows = table.find("tbody tr"); - var table_row_names = table_rows.find("td.name a"); - var no_rows = $("#no_rows"); + const table = document.querySelector("table.index"); + const table_rows = table.querySelectorAll("tbody tr"); + const table_row_names = table.querySelectorAll("tbody tr td.name a"); + const no_rows = document.getElementById("no_rows"); // Create a duplicate table footer that we can modify with dynamic summed values. - var table_footer = $("table.index tfoot tr"); - var table_dynamic_footer = table_footer.clone(); - table_dynamic_footer.attr('class', 'total_dynamic hidden'); - table_footer.after(table_dynamic_footer); + const table_footer = document.querySelector("table.index tfoot tr"); + const table_dynamic_footer = table_footer.cloneNode(true); + table_dynamic_footer.className = "total_dynamic hidden"; + table_footer.insertAdjacentElement('afterend', table_dynamic_footer); // Observe filter keyevents. - $("#filter").on("keyup change", $.debounce(150, function (event) { - var filter_value = $(this).val(); + document.getElementById("filter").addEventListener("input", debounce(event => { + const filter_value = event.target.value; if (filter_value === "") { // Filter box is empty, remove all filtering. - table_rows.removeClass("hidden"); + table_rows.forEach(element => element.classList.remove("hidden")); // Show standard footer, hide dynamic footer. - table_footer.removeClass("hidden"); - table_dynamic_footer.addClass("hidden"); + table_footer.classList.remove("hidden"); + table_dynamic_footer.classList.add("hidden"); // Hide placeholder, show table. if (no_rows.length > 0) { - no_rows.hide(); + no_rows.style.display = "none"; } - table.show(); - + table.style.display = null; + return; } - else { - // Filter table items by value. - var hidden = 0; - var shown = 0; - - // Hide / show elements. - $.each(table_row_names, function () { - var element = $(this).parents("tr"); - - if ($(this).text().indexOf(filter_value) === -1) { - // hide - element.addClass("hidden"); - hidden++; - } - else { - // show - element.removeClass("hidden"); - shown++; - } - }); - // Show placeholder if no rows will be displayed. - if (no_rows.length > 0) { - if (shown === 0) { - // Show placeholder, hide table. - no_rows.show(); - table.hide(); - } - else { - // Hide placeholder, show table. - no_rows.hide(); - table.show(); - } + // Filter table items by value. + let hidden = 0; + let shown = 0; + + // Hide / show elements. + table_row_names.forEach(element => { + const row = element.closest("tr"); + + if (!element.textContent.includes(filter_value)) { + // hide + row.classList.add("hidden"); + hidden++; + } else { + // show + row.classList.remove("hidden"); + shown++; + } + }); + + // Show placeholder if no rows will be displayed. + if (no_rows.length > 0) { + if (shown === 0) { + // Show placeholder, hide table. + no_rows.style.display = "block"; + table.style.display = "none"; + } + else { + // Hide placeholder, show table. + no_rows.style.display = "none"; + table.style.display = null; } + } - // Manage dynamic header: - if (hidden > 0) { - // Calculate new dynamic sum values based on visible rows. - for (var column = 2; column < 20; column++) { - // Calculate summed value. - var cells = table_rows.find('td:nth-child(' + column + ')'); - if (!cells.length) { - // No more columns...! - break; + // Manage dynamic header: + if (hidden > 0) { + // Calculate new dynamic sum values based on visible rows. + for (let column = 1; column < table.tHead.rows[0].cells.length; column++) { + // Calculate summed value. + const cells = table.querySelectorAll(`tbody td:nth-child(${column + 1})`); + + let sum = 0, numer = 0, denom = 0; + cells.forEach(element => { + if (!( + element.offsetWidth + || element.offsetHeight + || element.getClientRects().length + )) { + // element is not visible i.e. filtered out + return; } - - var sum = 0, numer = 0, denom = 0; - $.each(cells.filter(':visible'), function () { - var ratio = $(this).data("ratio"); - if (ratio) { - var splitted = ratio.split(" "); - numer += parseInt(splitted[0], 10); - denom += parseInt(splitted[1], 10); - } - else { - sum += parseInt(this.innerHTML, 10); - } - }); - - // Get footer cell element. - var footer_cell = table_dynamic_footer.find('td:nth-child(' + column + ')'); - - // Set value into dynamic footer cell element. - if (cells[0].innerHTML.indexOf('%') > -1) { - // Percentage columns use the numerator and denominator, - // and adapt to the number of decimal places. - var match = /\.([0-9]+)/.exec(cells[0].innerHTML); - var places = 0; - if (match) { - places = match[1].length; - } - var pct = numer * 100 / denom; - footer_cell.text(pct.toFixed(places) + '%'); + if ("ratio" in element.dataset) { + let splitted = element.dataset.ratio.split(" "); + numer += parseInt(splitted[0], 10); + denom += parseInt(splitted[1], 10); + } else { + sum += parseInt(element.textContent, 10); } - else { - footer_cell.text(sum); + }); + + + // Get footer cell element. + let footer_cell = table_dynamic_footer.querySelector(`td:nth-child(${column + 1})`); + console.log(sum, numer, denom, footer_cell) + + // Set value into dynamic footer cell element. + if (cells[0].textContent.includes('%')) { + // Percentage columns use the numerator and denominator, + // and adapt to the number of decimal places. + var match = /\.([0-9]+)/.exec(cells[0].textContent); + var places = 0; + if (match) { + places = match[1].length; } + var pct = numer * 100 / denom; + footer_cell.textContent = `${pct.toFixed(places)}%`; + } + else { + footer_cell.textContent = sum; } - - // Hide standard footer, show dynamic footer. - table_footer.addClass("hidden"); - table_dynamic_footer.removeClass("hidden"); - } - else { - // Show standard footer, hide dynamic footer. - table_footer.removeClass("hidden"); - table_dynamic_footer.addClass("hidden"); } + + // Hide standard footer, show dynamic footer. + table_footer.classList.add("hidden"); + table_dynamic_footer.classList.remove("hidden"); + } + else { + // Show standard footer, hide dynamic footer. + table_footer.classList.remove("hidden"); + table_dynamic_footer.classList.add("hidden"); } })); // Trigger change event on setup, to force filter on page refresh // (filter value may still be present). - $("#filter").trigger("change"); + document.getElementById("filter").dispatchEvent(new Event("change")); }; // Loaded on index.html diff --git a/coverage/htmlfiles/index.html b/coverage/htmlfiles/index.html index fbe85bbc2..627d1d79d 100644 --- a/coverage/htmlfiles/index.html +++ b/coverage/htmlfiles/index.html @@ -73,20 +73,6 @@

{{ title|escape }}:

- {# HTML syntax requires thead, tfoot, tbody #} - - - - - - - {% if has_arcs %} - - - {% endif %} - - - {% for file in files %} @@ -102,6 +88,19 @@

{{ title|escape }}:

{% endfor %} + + + + + + + {% if has_arcs %} + + + {% endif %} + + +
coverage
Total{{totals.n_statements}}{{totals.n_missing}}{{totals.n_excluded}}{{totals.n_branches}}{{totals.n_partial_branches}}{{totals.pc_covered_str}}%
Total{{totals.n_statements}}{{totals.n_missing}}{{totals.n_excluded}}{{totals.n_branches}}{{totals.n_partial_branches}}{{totals.pc_covered_str}}%

From 950fd5ac409a4d73d7656ae3d8e6b64fe3f29996 Mon Sep 17 00:00:00 2001 From: Septatrix <24257556+Septatrix@users.noreply.github.com> Date: Sat, 27 Mar 2021 22:13:40 +0100 Subject: [PATCH 5/8] Iterate totals over rows rather than columns --- coverage/htmlfiles/coverage_html.js | 159 ++++++++++------------------ 1 file changed, 55 insertions(+), 104 deletions(-) diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 27af5db5b..66c7020ec 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -35,125 +35,76 @@ coverage.assign_shortkeys = function () { coverage.wire_up_filter = function () { // Cache elements. const table = document.querySelector("table.index"); - const table_rows = table.querySelectorAll("tbody tr"); - const table_row_names = table.querySelectorAll("tbody tr td.name a"); + const table_body_rows = table.querySelectorAll("tbody tr"); const no_rows = document.getElementById("no_rows"); - // Create a duplicate table footer that we can modify with dynamic summed values. - const table_footer = document.querySelector("table.index tfoot tr"); - const table_dynamic_footer = table_footer.cloneNode(true); - table_dynamic_footer.className = "total_dynamic hidden"; - table_footer.insertAdjacentElement('afterend', table_dynamic_footer); - // Observe filter keyevents. document.getElementById("filter").addEventListener("input", debounce(event => { - const filter_value = event.target.value; - - if (filter_value === "") { - // Filter box is empty, remove all filtering. - table_rows.forEach(element => element.classList.remove("hidden")); - - // Show standard footer, hide dynamic footer. - table_footer.classList.remove("hidden"); - table_dynamic_footer.classList.add("hidden"); - - // Hide placeholder, show table. - if (no_rows.length > 0) { - no_rows.style.display = "none"; - } - table.style.display = null; - return; - } - - // Filter table items by value. - let hidden = 0; - let shown = 0; + // Keep running total of each metric, first index contains number of shown rows + const totals = new Array(table.rows[0].cells.length).fill(0); + // Accumulate the percentage as fraction + totals[totals.length - 1] = { "numer": 0, "denom": 0 }; // Hide / show elements. - table_row_names.forEach(element => { - const row = element.closest("tr"); - - if (!element.textContent.includes(filter_value)) { + table_body_rows.forEach(row => { + if (!row.cells[0].textContent.includes(event.target.value)) { // hide row.classList.add("hidden"); - hidden++; - } else { - // show - row.classList.remove("hidden"); - shown++; + return; + } + + // show + row.classList.remove("hidden"); + totals[0]++; + + for (let column = 1; column < totals.length; column++) { + // Accumulate dynamic totals + cell = row.cells[column] + if (column === totals.length - 1) { + // Last column contains percentage + const [numer, denom] = cell.dataset.ratio.split(" "); + totals[column]["numer"] += parseInt(numer, 10); + totals[column]["denom"] += parseInt(denom, 10); + } else { + totals[column] += parseInt(cell.textContent, 10); + } } }); + console.log(totals) + // Show placeholder if no rows will be displayed. - if (no_rows.length > 0) { - if (shown === 0) { - // Show placeholder, hide table. - no_rows.style.display = "block"; - table.style.display = "none"; - } - else { - // Hide placeholder, show table. - no_rows.style.display = "none"; - table.style.display = null; - } + if (!totals[0]) { + // Show placeholder, hide table. + no_rows.style.display = "block"; + table.style.display = "none"; + return; } - // Manage dynamic header: - if (hidden > 0) { - // Calculate new dynamic sum values based on visible rows. - for (let column = 1; column < table.tHead.rows[0].cells.length; column++) { - // Calculate summed value. - const cells = table.querySelectorAll(`tbody td:nth-child(${column + 1})`); - - let sum = 0, numer = 0, denom = 0; - cells.forEach(element => { - if (!( - element.offsetWidth - || element.offsetHeight - || element.getClientRects().length - )) { - // element is not visible i.e. filtered out - return; - } - if ("ratio" in element.dataset) { - let splitted = element.dataset.ratio.split(" "); - numer += parseInt(splitted[0], 10); - denom += parseInt(splitted[1], 10); - } else { - sum += parseInt(element.textContent, 10); - } - }); - - - // Get footer cell element. - let footer_cell = table_dynamic_footer.querySelector(`td:nth-child(${column + 1})`); - console.log(sum, numer, denom, footer_cell) - - // Set value into dynamic footer cell element. - if (cells[0].textContent.includes('%')) { - // Percentage columns use the numerator and denominator, - // and adapt to the number of decimal places. - var match = /\.([0-9]+)/.exec(cells[0].textContent); - var places = 0; - if (match) { - places = match[1].length; - } - var pct = numer * 100 / denom; - footer_cell.textContent = `${pct.toFixed(places)}%`; - } - else { - footer_cell.textContent = sum; - } + // Hide placeholder, show table. + no_rows.style.display = null; + table.style.display = null; + + // Calculate new dynamic sum values based on visible rows. + for (let column = 1; column < totals.length; column++) { + // Get footer cell element. + const cell = table.tFoot.rows[0].cells[column]; + + // Set value into dynamic footer cell element. + if (column === totals.length - 1) { + // Percentage column uses the numerator and denominator, + // and adapts to the number of decimal places. + const match = /\.([0-9]+)/.exec(cell.textContent); + const places = match ? match[1].length : 0; + const { numer, denom } = totals[column]; + cell.dataset.ratio = `${numer} ${denom}`; + // Check denom to prevent NaN if filtered files contain no statements + cell.textContent = denom + ? `${(numer * 100 / denom).toFixed(places)}%` + : `${(100).toFixed(places)}%`; + } else { + cell.textContent = totals[column]; } - - // Hide standard footer, show dynamic footer. - table_footer.classList.add("hidden"); - table_dynamic_footer.classList.remove("hidden"); - } - else { - // Show standard footer, hide dynamic footer. - table_footer.classList.remove("hidden"); - table_dynamic_footer.classList.add("hidden"); } })); From c7b2db378aad1f14284d67a1596da136cd834261 Mon Sep 17 00:00:00 2001 From: Septatrix <24257556+Septatrix@users.noreply.github.com> Date: Sun, 28 Mar 2021 17:27:36 +0200 Subject: [PATCH 6/8] Update scroll marker building --- coverage/htmlfiles/coverage_html.js | 81 +++++++++++------------------ 1 file changed, 31 insertions(+), 50 deletions(-) diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 66c7020ec..1ce060a0b 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -17,6 +17,10 @@ function debounce(callback, wait) { }; }; +function clamp(n, min, max) { + return Math.min(Math.max(min, n), max); +} + // Find all the elements with data-shortcut attribute, and use them to assign a shortcut key. coverage.assign_shortkeys = function () { document.querySelectorAll("[data-shortcut]").forEach(element => { @@ -482,73 +486,50 @@ coverage.finish_scrolling = function () { coverage.init_scroll_markers = function () { var c = coverage; // Init some variables - c.lines_len = $('#source p').length; - c.body_h = $('body').height(); - c.header_h = $('div#header').height(); + c.lines_len = document.querySelectorAll('#source p').length; // Build html c.build_scroll_markers(); }; coverage.build_scroll_markers = function () { - var c = coverage, - min_line_height = 3, - max_line_height = 10, - visible_window_h = $(window).height(); - - c.lines_to_mark = $('#source').find('p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par'); - $('#scroll_marker').remove(); + const temp_scroll_marker = document.getElementById('scroll_marker') + if (temp_scroll_marker) temp_scroll_marker.remove(); // Don't build markers if the window has no scroll bar. - if (c.body_h <= visible_window_h) { + if (document.body.scrollHeight <= window.innerHeight) { return; } - $("body").append("

 
"); - var scroll_marker = $('#scroll_marker'), - marker_scale = scroll_marker.height() / c.body_h, - line_height = scroll_marker.height() / c.lines_len; - - // Line height must be between the extremes. - if (line_height > min_line_height) { - if (line_height > max_line_height) { - line_height = max_line_height; - } - } - else { - line_height = min_line_height; - } - - var previous_line = -99, - last_mark, - last_top, - offsets = {}; - - // Calculate line offsets outside loop to prevent relayouts - c.lines_to_mark.each(function() { - offsets[this.id] = $(this).offset().top; - }); - c.lines_to_mark.each(function () { - var id_name = $(this).attr('id'), - line_top = Math.round(offsets[id_name] * marker_scale), - line_number = parseInt(id_name.substring(1, id_name.length)); + const marker_scale = window.innerHeight / document.body.scrollHeight; + const line_height = clamp(window.innerHeight / coverage.lines_len, 3, 10); + + let previous_line = -99, last_mark, last_top; + + const scroll_marker = document.createElement("div"); + scroll_marker.id = "scroll_marker"; + document.getElementById('source').querySelectorAll( + 'p.show_run, p.show_mis, p.show_exc, p.show_exc, p.show_par' + ).forEach(element => { + const line_top = Math.floor(element.offsetTop * marker_scale); + const line_number = parseInt(element.id.substr(1)); if (line_number === previous_line + 1) { // If this solid missed block just make previous mark higher. - last_mark.css({ - 'height': line_top + line_height - last_top - }); - } - else { + last_mark.style.height = `${line_top + line_height - last_top}px`; + } else { // Add colored line in scroll_marker block. - scroll_marker.append('
'); - last_mark = $('#m' + line_number); - last_mark.css({ - 'height': line_height, - 'top': line_top - }); + last_mark = document.createElement("div"); + last_mark.id = `m${line_number}`; + last_mark.classList.add("marker"); + last_mark.style.height = `${line_height}px`; + last_mark.style.top = `${line_top}px`; + scroll_marker.append(last_mark); last_top = line_top; } previous_line = line_number; }); + + // Append last to prevent layout calculation + document.body.append(scroll_marker); }; From d9e392be36c0adb94a9a2dabd51954c00dd695a7 Mon Sep 17 00:00:00 2001 From: Septatrix <24257556+Septatrix@users.noreply.github.com> Date: Sun, 17 Oct 2021 20:20:33 +0200 Subject: [PATCH 7/8] Remove last usages of jQuery --- coverage/html.py | 5 - coverage/htmlfiles/coverage_html.js | 296 +++++++++--------- coverage/htmlfiles/index.html | 25 +- .../jquery.ba-throttle-debounce.min.js | 9 - coverage/htmlfiles/jquery.hotkeys.js | 99 ------ coverage/htmlfiles/jquery.isonscreen.js | 53 ---- coverage/htmlfiles/jquery.min.js | 2 - coverage/htmlfiles/jquery.tablesorter.min.js | 2 - coverage/htmlfiles/pyfile.html | 25 +- coverage/htmlfiles/style.css | 8 +- coverage/htmlfiles/style.scss | 11 +- 11 files changed, 181 insertions(+), 354 deletions(-) delete mode 100644 coverage/htmlfiles/jquery.ba-throttle-debounce.min.js delete mode 100644 coverage/htmlfiles/jquery.hotkeys.js delete mode 100644 coverage/htmlfiles/jquery.isonscreen.js delete mode 100644 coverage/htmlfiles/jquery.min.js delete mode 100644 coverage/htmlfiles/jquery.tablesorter.min.js diff --git a/coverage/html.py b/coverage/html.py index 1fbac4b36..e56e30797 100644 --- a/coverage/html.py +++ b/coverage/html.py @@ -159,11 +159,6 @@ class HtmlReporter: # directory. STATIC_FILES = [ ("style.css", ""), - ("jquery.min.js", "jquery"), - ("jquery.ba-throttle-debounce.min.js", "jquery-throttle-debounce"), - ("jquery.hotkeys.js", "jquery-hotkeys"), - ("jquery.isonscreen.js", "jquery-isonscreen"), - ("jquery.tablesorter.min.js", "jquery-tablesorter"), ("coverage_html.js", ""), ("keybd_closed.png", ""), ("keybd_open.png", ""), diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 1ce060a0b..1e66dd861 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -7,6 +7,7 @@ coverage = {}; +// General helpers function debounce(callback, wait) { let timeoutId = null; return function(...args) { @@ -17,8 +18,52 @@ function debounce(callback, wait) { }; }; -function clamp(n, min, max) { - return Math.min(Math.max(min, n), max); +function checkVisible(element) { + var rect = element.getBoundingClientRect(); + var viewHeight = Math.max(document.documentElement.clientHeight, window.innerHeight); + return !(rect.bottom < 0 || rect.top - viewHeight >= 0); +} + +// Helpers for table sorting +function getCellValue(row, column = 0) { + const cell = row.cells[column] + if (cell.childElementCount == 1) { + const child = cell.firstElementChild + if (child instanceof HTMLTimeElement && child.dateTime) { + return child.dateTime + } else if (child instanceof HTMLDataElement && child.value) { + return child.value + } + } + return cell.innerText || cell.textContent; +} + +function rowComparator(rowA, rowB, column = 0) { + let valueA = getCellValue(rowA, column); + let valueB = getCellValue(rowB, column); + if (!isNaN(valueA) && !isNaN(valueB)) { + return valueA - valueB + } + return valueA.localeCompare(valueB, undefined, {numeric: true}); +} + +function sortColumn(th) { + // Get the current sorting direction of the selected header, + // clear state on other headers and then set the new sorting direction + const currentSortOrder = th.ariaSort; + [...th.parentElement.cells].forEach(header => header.ariaSort = "none"); + if (currentSortOrder === "none") { + th.ariaSort = th.dataset.defaultSortOrder || "ascending" + } else { + th.ariaSort = currentSortOrder === "ascending" ? "descending" : "ascending"; + } + + const column = [...th.parentElement.cells].indexOf(th) + + // Sort all rows and afterwards append them in order to move them in the DOM + Array.from(th.closest("table").querySelectorAll("tbody tr")) + .sort((rowA, rowB) => rowComparator(rowA, rowB, column) * (th.ariaSort === "ascending" ? 1 : -1)) + .forEach(tr => tr.parentElement.appendChild(tr) ); } // Find all the elements with data-shortcut attribute, and use them to assign a shortcut key. @@ -89,10 +134,11 @@ coverage.wire_up_filter = function () { no_rows.style.display = null; table.style.display = null; + const footer = table.tFoot.rows[0]; // Calculate new dynamic sum values based on visible rows. for (let column = 1; column < totals.length; column++) { // Get footer cell element. - const cell = table.tFoot.rows[0].cells[column]; + const cell = footer.cells[column]; // Set value into dynamic footer cell element. if (column === totals.length - 1) { @@ -117,65 +163,27 @@ coverage.wire_up_filter = function () { document.getElementById("filter").dispatchEvent(new Event("change")); }; +coverage.INDEX_SORT_STORAGE = "COVERAGE_INDEX_SORT"; + // Loaded on index.html -coverage.index_ready = function ($) { +coverage.index_ready = function () { + coverage.assign_shortkeys(); + coverage.wire_up_filter(); + document.querySelectorAll('[data-sortable] th[aria-sort]').forEach( + th => th.addEventListener('click', e => sortColumn(e.target)) + ); + // Look for a localStorage item containing previous sort settings: - var sort_list = []; - var storage_name = "COVERAGE_INDEX_SORT"; - var stored_list = undefined; - try { - stored_list = localStorage.getItem(storage_name); - } catch(err) {} + const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE); if (stored_list) { - sort_list = JSON.parse('[[' + stored_list + ']]'); - } - - // Create a new widget which exists only to save and restore - // the sort order: - $.tablesorter.addWidget({ - id: "persistentSort", - - // Format is called by the widget before displaying: - format: function (table) { - if (table.config.sortList.length === 0 && sort_list.length > 0) { - // This table hasn't been sorted before - we'll use - // our stored settings: - $(table).trigger('sorton', [sort_list]); - } - else { - // This is not the first load - something has - // already defined sorting so we'll just update - // our stored value to match: - sort_list = table.config.sortList; - } - } - }); - - // Configure our tablesorter to handle the variable number of - // columns produced depending on report options: - var headers = []; - var col_count = $("table.index > thead > tr > th").length; - - headers[0] = { sorter: 'text' }; - for (i = 1; i < col_count-1; i++) { - headers[i] = { sorter: 'digit' }; + sort_list = JSON.parse(stored_list); } - headers[col_count-1] = { sorter: 'percent' }; - - // Enable the table sorter: - $("table.index").tablesorter({ - widgets: ['persistentSort'], - headers: headers - }); - - coverage.assign_shortkeys(); - coverage.wire_up_filter(); // Watch for page unload events so we can save the final sort settings: - $(window).on("unload", function () { + window.addEventListener("unload", function () { try { - localStorage.setItem(storage_name, sort_list.toString()) + localStorage.setItem(storage_name, JSON.stringify(sort_list)) } catch(err) {} }); }; @@ -184,28 +192,25 @@ coverage.index_ready = function ($) { coverage.LINE_FILTERS_STORAGE = "COVERAGE_LINE_FILTERS"; -coverage.pyfile_ready = function ($) { +coverage.pyfile_ready = function () { // If we're directed to a particular line number, highlight the line. var frag = location.hash; if (frag.length > 2 && frag[1] === 't') { - $(frag).addClass('highlight'); + document.getElementById(frag.substring(1)).classList.add("highlight"); coverage.set_sel(parseInt(frag.substr(2), 10)); - } - else { + } else { coverage.set_sel(0); } - $(document) - .bind('keydown', 'j', coverage.to_next_chunk_nicely) - .bind('keydown', 'k', coverage.to_prev_chunk_nicely) - .bind('keydown', '0', coverage.to_top) - .bind('keydown', '1', coverage.to_first_chunk) - ; + document.querySelector(".button_toggle_run").addEventListener("click", coverage.toggle_lines); + document.querySelector(".button_toggle_mis").addEventListener("click", coverage.toggle_lines); + document.querySelector(".button_toggle_exc").addEventListener("click", coverage.toggle_lines); + document.querySelector(".button_toggle_par").addEventListener("click", coverage.toggle_lines); - $(".button_toggle_run").click(function (evt) {coverage.toggle_lines(evt.target, "run");}); - $(".button_toggle_exc").click(function (evt) {coverage.toggle_lines(evt.target, "exc");}); - $(".button_toggle_mis").click(function (evt) {coverage.toggle_lines(evt.target, "mis");}); - $(".button_toggle_par").click(function (evt) {coverage.toggle_lines(evt.target, "par");}); + document.querySelector(".button_next_chunk").addEventListener("click", coverage.to_next_chunk_nicely); + document.querySelector(".button_prev_chunk").addEventListener("click", coverage.to_prev_chunk_nicely); + document.querySelector(".button_top_of_page").addEventListener("click", coverage.to_top); + document.querySelector(".button_first_chunk").addEventListener("click", coverage.to_first_chunk); coverage.filters = undefined; try { @@ -227,35 +232,37 @@ coverage.pyfile_ready = function ($) { coverage.init_scroll_markers(); // Rebuild scroll markers when the window height changes. - $(window).resize(coverage.build_scroll_markers); + window.addEventListener("resize", coverage.build_scroll_markers); }; -coverage.toggle_lines = function (btn, cls) { - var onoff = !$(btn).hasClass("show_" + cls); - coverage.set_line_visibilty(cls, onoff); +coverage.toggle_lines = function (event) { + const btn = event.target; + const category = btn.value + const show = !btn.classList.contains("show_" + category); + coverage.set_line_visibilty(category, show); coverage.build_scroll_markers(); - coverage.filters[cls] = onoff; + coverage.filters[category] = show; try { localStorage.setItem(coverage.LINE_FILTERS_STORAGE, JSON.stringify(coverage.filters)); } catch(err) {} }; -coverage.set_line_visibilty = function (cls, onoff) { - var show = "show_" + cls; - var btn = $(".button_toggle_" + cls); - if (onoff) { - $("#source ." + cls).addClass(show); - btn.addClass(show); +coverage.set_line_visibilty = function (category, should_show) { + const cls = "show_" + category; + const btn = document.querySelector(".button_toggle_" + category); + if (should_show) { + document.querySelectorAll("#source ." + category).forEach(e => e.classList.add(cls)); + btn.classList.add(cls); } else { - $("#source ." + cls).removeClass(show); - btn.removeClass(show); + document.querySelectorAll("#source ." + category).forEach(e => e.classList.remove(cls)); + btn.classList.remove(cls); } }; // Return the nth line div. coverage.line_elt = function (n) { - return $("#t" + n); + return document.getElementById("t" + n); }; // Set the selection. b and e are line numbers. @@ -279,25 +286,26 @@ coverage.to_first_chunk = function () { // Return a string indicating what kind of chunk this line belongs to, // or null if not a chunk. coverage.chunk_indicator = function (line_elt) { - var klass = line_elt.attr('class'); - if (klass) { - var m = klass.match(/\bshow_\w+\b/); - if (m) { - return m[0]; - } + const classes = line_elt.className; + if (!classes) { + return null; + } + const match = classes.match(/\bshow_\w+\b/); + if (!match) { + return null; } - return null; + return match[0]; }; coverage.to_next_chunk = function () { - var c = coverage; + const c = coverage; // Find the start of the next colored chunk. var probe = c.sel_end; var chunk_indicator, probe_line; while (true) { probe_line = c.line_elt(probe); - if (probe_line.length === 0) { + if (!probe_line) { return; } chunk_indicator = c.chunk_indicator(probe_line); @@ -322,7 +330,7 @@ coverage.to_next_chunk = function () { }; coverage.to_prev_chunk = function () { - var c = coverage; + const c = coverage; // Find the end of the prev colored chunk. var probe = c.sel_begin-1; @@ -334,7 +342,7 @@ coverage.to_prev_chunk = function () { while (probe > 0 && !chunk_indicator) { probe--; probe_line = c.line_elt(probe); - if (probe_line.length === 0) { + if (!probe_line) { return; } chunk_indicator = c.chunk_indicator(probe_line); @@ -354,28 +362,6 @@ coverage.to_prev_chunk = function () { c.show_selection(); }; -// Return the line number of the line nearest pixel position pos -coverage.line_at_pos = function (pos) { - var l1 = coverage.line_elt(1), - l2 = coverage.line_elt(2), - result; - if (l1.length && l2.length) { - var l1_top = l1.offset().top, - line_height = l2.offset().top - l1_top, - nlines = (pos - l1_top) / line_height; - if (nlines < 1) { - result = 1; - } - else { - result = Math.ceil(nlines); - } - } - else { - result = 1; - } - return result; -}; - // Returns 0, 1, or 2: how many of the two ends of the selection are on // the screen right now? coverage.selection_ends_on_screen = function () { @@ -383,31 +369,49 @@ coverage.selection_ends_on_screen = function () { return 0; } - var top = coverage.line_elt(coverage.sel_begin); - var next = coverage.line_elt(coverage.sel_end-1); + const begin = coverage.line_elt(coverage.sel_begin); + const end = coverage.line_elt(coverage.sel_end-1); return ( - (top.isOnScreen() ? 1 : 0) + - (next.isOnScreen() ? 1 : 0) + (checkVisible(begin) ? 1 : 0) + + (checkVisible(end) ? 1 : 0) ); }; coverage.to_next_chunk_nicely = function () { - coverage.finish_scrolling(); if (coverage.selection_ends_on_screen() === 0) { - // The selection is entirely off the screen: select the top line on - // the screen. - var win = $(window); - coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop())); + // The selection is entirely off the screen: + // Set the top line on the screen as selection. + + // This will select the top-left of the viewport + // As this is most likely the span with the line number we take the parent + const line = document.elementFromPoint(0, 0).parentElement; + if (line.parentElement !== document.getElementById("source")) { + // The element is not a source line but the header or similar + coverage.select_line_or_chunk(1); + } else { + // We extract the line number from the id + coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10)); + } } coverage.to_next_chunk(); }; coverage.to_prev_chunk_nicely = function () { - coverage.finish_scrolling(); if (coverage.selection_ends_on_screen() === 0) { - var win = $(window); - coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height())); + // The selection is entirely off the screen: + // Set the lowest line on the screen as selection. + + // This will select the bottom-left of the viewport + // As this is most likely the span with the line number we take the parent + const line = document.elementFromPoint(document.documentElement.clientHeight-1, 0).parentElement; + if (line.parentElement !== document.getElementById("source")) { + // The element is not a source line but the header or similar + coverage.select_line_or_chunk(coverage.lines_len); + } else { + // We extract the line number from the id + coverage.select_line_or_chunk(parseInt(line.id.substring(1), 10)); + } } coverage.to_prev_chunk(); }; @@ -453,43 +457,33 @@ coverage.select_line_or_chunk = function (lineno) { }; coverage.show_selection = function () { - var c = coverage; - // Highlight the lines in the chunk - $("#source .highlight").removeClass("highlight"); - for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) { - c.line_elt(probe).addClass("highlight"); + document.querySelectorAll("#source .highlight").forEach(e => e.classList.remove("highlight")); + for (let probe = coverage.sel_begin; probe < coverage.sel_end; probe++) { + coverage.line_elt(probe).classList.add("highlight"); } - c.scroll_to_selection(); + coverage.scroll_to_selection(); }; coverage.scroll_to_selection = function () { // Scroll the page if the chunk isn't fully visible. if (coverage.selection_ends_on_screen() < 2) { - // Need to move the page. The html,body trick makes it scroll in all - // browsers, got it from http://stackoverflow.com/questions/3042651 - var top = coverage.line_elt(coverage.sel_begin); - var top_pos = parseInt(top.offset().top, 10); - coverage.scroll_window(top_pos - 30); + const element = coverage.line_elt(coverage.sel_begin); + coverage.scroll_window(element.offsetTop - 30); } }; coverage.scroll_window = function (to_pos) { - $("html,body").animate({scrollTop: to_pos}, 200); -}; - -coverage.finish_scrolling = function () { - $("html,body").stop(true, true); + window.scroll({top: to_pos, behavior: "smooth"}); }; coverage.init_scroll_markers = function () { - var c = coverage; // Init some variables - c.lines_len = document.querySelectorAll('#source p').length; + coverage.lines_len = document.querySelectorAll('#source > p').length; // Build html - c.build_scroll_markers(); + coverage.build_scroll_markers(); }; coverage.build_scroll_markers = function () { @@ -501,7 +495,7 @@ coverage.build_scroll_markers = function () { } const marker_scale = window.innerHeight / document.body.scrollHeight; - const line_height = clamp(window.innerHeight / coverage.lines_len, 3, 10); + const line_height = Math.min(Math.max(3, window.innerHeight / coverage.lines_len), 10); let previous_line = -99, last_mark, last_top; @@ -533,3 +527,11 @@ coverage.build_scroll_markers = function () { // Append last to prevent layout calculation document.body.append(scroll_marker); }; + +document.addEventListener("DOMContentLoaded", () => { + if (document.body.classList.contains("indexfile")) { + coverage.index_ready(); + } else { + coverage.pyfile_ready(); + } +}); diff --git a/coverage/htmlfiles/index.html b/coverage/htmlfiles/index.html index 627d1d79d..37dd6a892 100644 --- a/coverage/htmlfiles/index.html +++ b/coverage/htmlfiles/index.html @@ -11,14 +11,7 @@ {% if extra_css %} {% endif %} - - - - - - + @@ -58,19 +51,19 @@

{{ title|escape }}:

- +
{# The title="" attr doesn"t work in Safari. #} - - - - + + + + {% if has_arcs %} - - + + {% endif %} - + diff --git a/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js b/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js deleted file mode 100644 index 648fe5d3c..000000000 --- a/coverage/htmlfiles/jquery.ba-throttle-debounce.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/* - * jQuery throttle / debounce - v1.1 - 3/7/2010 - * http://benalman.com/projects/jquery-throttle-debounce-plugin/ - * - * Copyright (c) 2010 "Cowboy" Ben Alman - * Dual licensed under the MIT and GPL licenses. - * http://benalman.com/about/license/ - */ -(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this); diff --git a/coverage/htmlfiles/jquery.hotkeys.js b/coverage/htmlfiles/jquery.hotkeys.js deleted file mode 100644 index 09b21e03c..000000000 --- a/coverage/htmlfiles/jquery.hotkeys.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * jQuery Hotkeys Plugin - * Copyright 2010, John Resig - * Dual licensed under the MIT or GPL Version 2 licenses. - * - * Based upon the plugin by Tzury Bar Yochay: - * http://github.com/tzuryby/hotkeys - * - * Original idea by: - * Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/ -*/ - -(function(jQuery){ - - jQuery.hotkeys = { - version: "0.8", - - specialKeys: { - 8: "backspace", 9: "tab", 13: "return", 16: "shift", 17: "ctrl", 18: "alt", 19: "pause", - 20: "capslock", 27: "esc", 32: "space", 33: "pageup", 34: "pagedown", 35: "end", 36: "home", - 37: "left", 38: "up", 39: "right", 40: "down", 45: "insert", 46: "del", - 96: "0", 97: "1", 98: "2", 99: "3", 100: "4", 101: "5", 102: "6", 103: "7", - 104: "8", 105: "9", 106: "*", 107: "+", 109: "-", 110: ".", 111 : "/", - 112: "f1", 113: "f2", 114: "f3", 115: "f4", 116: "f5", 117: "f6", 118: "f7", 119: "f8", - 120: "f9", 121: "f10", 122: "f11", 123: "f12", 144: "numlock", 145: "scroll", 191: "/", 224: "meta" - }, - - shiftNums: { - "`": "~", "1": "!", "2": "@", "3": "#", "4": "$", "5": "%", "6": "^", "7": "&", - "8": "*", "9": "(", "0": ")", "-": "_", "=": "+", ";": ": ", "'": "\"", ",": "<", - ".": ">", "/": "?", "\\": "|" - } - }; - - function keyHandler( handleObj ) { - // Only care when a possible input has been specified - if ( typeof handleObj.data !== "string" ) { - return; - } - - var origHandler = handleObj.handler, - keys = handleObj.data.toLowerCase().split(" "); - - handleObj.handler = function( event ) { - // Don't fire in text-accepting inputs that we didn't directly bind to - if ( this !== event.target && (/textarea|select/i.test( event.target.nodeName ) || - event.target.type === "text") ) { - return; - } - - // Keypress represents characters, not special keys - var special = event.type !== "keypress" && jQuery.hotkeys.specialKeys[ event.which ], - character = String.fromCharCode( event.which ).toLowerCase(), - key, modif = "", possible = {}; - - // check combinations (alt|ctrl|shift+anything) - if ( event.altKey && special !== "alt" ) { - modif += "alt+"; - } - - if ( event.ctrlKey && special !== "ctrl" ) { - modif += "ctrl+"; - } - - // TODO: Need to make sure this works consistently across platforms - if ( event.metaKey && !event.ctrlKey && special !== "meta" ) { - modif += "meta+"; - } - - if ( event.shiftKey && special !== "shift" ) { - modif += "shift+"; - } - - if ( special ) { - possible[ modif + special ] = true; - - } else { - possible[ modif + character ] = true; - possible[ modif + jQuery.hotkeys.shiftNums[ character ] ] = true; - - // "$" can be triggered as "Shift+4" or "Shift+$" or just "$" - if ( modif === "shift+" ) { - possible[ jQuery.hotkeys.shiftNums[ character ] ] = true; - } - } - - for ( var i = 0, l = keys.length; i < l; i++ ) { - if ( possible[ keys[i] ] ) { - return origHandler.apply( this, arguments ); - } - } - }; - } - - jQuery.each([ "keydown", "keyup", "keypress" ], function() { - jQuery.event.special[ this ] = { add: keyHandler }; - }); - -})( jQuery ); diff --git a/coverage/htmlfiles/jquery.isonscreen.js b/coverage/htmlfiles/jquery.isonscreen.js deleted file mode 100644 index 28fb99bc6..000000000 --- a/coverage/htmlfiles/jquery.isonscreen.js +++ /dev/null @@ -1,53 +0,0 @@ -/* Copyright (c) 2010 - * @author Laurence Wheway - * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) - * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. - * - * @version 1.2.0 - */ -(function($) { - jQuery.extend({ - isOnScreen: function(box, container) { - //ensure numbers come in as integers (not strings) and remove 'px' is it's there - for(var i in box){box[i] = parseFloat(box[i])}; - for(var i in container){container[i] = parseFloat(container[i])}; - - if(!container){ - container = { - left: $(window).scrollLeft(), - top: $(window).scrollTop(), - width: $(window).width(), - height: $(window).height() - } - } - - if( box.left+box.width-container.left > 0 && - box.left < container.width+container.left && - box.top+box.height-container.top > 0 && - box.top < container.height+container.top - ) return true; - return false; - } - }) - - - jQuery.fn.isOnScreen = function (container) { - for(var i in container){container[i] = parseFloat(container[i])}; - - if(!container){ - container = { - left: $(window).scrollLeft(), - top: $(window).scrollTop(), - width: $(window).width(), - height: $(window).height() - } - } - - if( $(this).offset().left+$(this).width()-container.left > 0 && - $(this).offset().left < container.width+container.left && - $(this).offset().top+$(this).height()-container.top > 0 && - $(this).offset().top < container.height+container.top - ) return true; - return false; - } -})(jQuery); diff --git a/coverage/htmlfiles/jquery.min.js b/coverage/htmlfiles/jquery.min.js deleted file mode 100644 index c4c6022f2..000000000 --- a/coverage/htmlfiles/jquery.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v3.6.0 | (c) OpenJS Foundation and other contributors | jquery.org/license */ -!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,v=n.hasOwnProperty,a=v.toString,l=a.call(Object),y={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.0",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"
ModulestatementsmissingexcludedModulestatementsmissingexcludedbranchespartialbranchespartialcoveragecoverage
","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 01){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;ib)?1:0));};function sortTextDesc(a,b){return((ba)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){$this.trigger("sortStart");var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){var $cell=$(this);var i=this.column;this.order=this.count++%2;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i {% endif %} - - - - - + diff --git a/coverage/htmlfiles/style.css b/coverage/htmlfiles/style.css index 70fd68893..1f0c29ec7 100644 --- a/coverage/htmlfiles/style.css +++ b/coverage/htmlfiles/style.css @@ -266,13 +266,13 @@ kbd { border: 1px solid black; border-color: #888 #333 #333 #888; padding: .1em @media (prefers-color-scheme: dark) { #index th:hover { background: #333; } } -#index th.headerSortDown, #index th.headerSortUp { white-space: nowrap; background: #eee; } +#index th[aria-sort="ascending"], #index th[aria-sort="descending"] { white-space: nowrap; background: #eee; padding-left: .5em; } -@media (prefers-color-scheme: dark) { #index th.headerSortDown, #index th.headerSortUp { background: #333; } } +@media (prefers-color-scheme: dark) { #index th[aria-sort="ascending"], #index th[aria-sort="descending"] { background: #333; } } -#index th.headerSortDown:after { content: " ↑"; } +#index th[aria-sort="ascending"]::after { content: "↑"; } -#index th.headerSortUp:after { content: " ↓"; } +#index th[aria-sort="descending"]::after { content: "↓"; } #index td.name a { text-decoration: none; color: inherit; } diff --git a/coverage/htmlfiles/style.scss b/coverage/htmlfiles/style.scss index 5ec1c8464..aa3580a0a 100644 --- a/coverage/htmlfiles/style.scss +++ b/coverage/htmlfiles/style.scss @@ -613,16 +613,17 @@ $border-indicator-width: .2em; background: $light-gray2; @include background-dark($dark-gray2); } - &.headerSortDown, &.headerSortUp { + &[aria-sort="ascending"], &[aria-sort="descending"] { white-space: nowrap; background: $light-gray2; @include background-dark($dark-gray2); + padding-left: .5em; } - &.headerSortDown:after { - content: " ↑"; + &[aria-sort="ascending"]::after { + content: "↑"; } - &.headerSortUp:after { - content: " ↓"; + &[aria-sort="descending"]::after { + content: "↓"; } } td.name a { From cdae781beeba644e9426c71c2aa5113642f2fab7 Mon Sep 17 00:00:00 2001 From: Septatrix <24257556+Septatrix@users.noreply.github.com> Date: Sun, 17 Oct 2021 20:30:13 +0200 Subject: [PATCH 8/8] Persistent sorting --- coverage/htmlfiles/coverage_html.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/coverage/htmlfiles/coverage_html.js b/coverage/htmlfiles/coverage_html.js index 1e66dd861..fe48428de 100644 --- a/coverage/htmlfiles/coverage_html.js +++ b/coverage/htmlfiles/coverage_html.js @@ -169,22 +169,30 @@ coverage.INDEX_SORT_STORAGE = "COVERAGE_INDEX_SORT"; coverage.index_ready = function () { coverage.assign_shortkeys(); coverage.wire_up_filter(); - document.querySelectorAll('[data-sortable] th[aria-sort]').forEach( - th => th.addEventListener('click', e => sortColumn(e.target)) + document.querySelectorAll("[data-sortable] th[aria-sort]").forEach( + th => th.addEventListener("click", e => sortColumn(e.target)) ); // Look for a localStorage item containing previous sort settings: const stored_list = localStorage.getItem(coverage.INDEX_SORT_STORAGE); if (stored_list) { - sort_list = JSON.parse(stored_list); + const {column, direction} = JSON.parse(stored_list); + const th = document.querySelector("[data-sortable]").tHead.rows[0].cells[column]; + th.ariaSort = direction === "ascending" ? "descending" : "ascending"; + th.click() } // Watch for page unload events so we can save the final sort settings: window.addEventListener("unload", function () { - try { - localStorage.setItem(storage_name, JSON.stringify(sort_list)) - } catch(err) {} + const th = document.querySelector('[data-sortable] th[aria-sort="ascending"], [data-sortable] [aria-sort="descending"]'); + if (!th) { + return; + } + localStorage.setItem(coverage.INDEX_SORT_STORAGE, JSON.stringify({ + column: [...th.parentElement.cells].indexOf(th), + direction: th.ariaSort, + })); }); };