123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585 |
- // Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
- // For details: https://bitbucket.org/ned/coveragepy/src/default/NOTICE.txt
- // Coverage.py HTML report browser code.
- /*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
- /*global coverage: true, document, window, $ */
- coverage = {};
- // Find all the elements with shortkey_* class, 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();
- });
- }
- });
- });
- };
- // 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,
- left: koff.left-poff.left
- });
- });
- $("#panel_icon").click(function () {
- $(".help_panel").hide();
- });
- };
- // 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");
- // 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);
- // Observe filter keyevents.
- $("#filter").on("keyup change", $.debounce(150, function (event) {
- var filter_value = $(this).val();
- if (filter_value === "") {
- // Filter box is empty, remove all filtering.
- table_rows.removeClass("hidden");
- // Show standard footer, hide dynamic footer.
- table_footer.removeClass("hidden");
- table_dynamic_footer.addClass("hidden");
- // Hide placeholder, show table.
- if (no_rows.length > 0) {
- no_rows.hide();
- }
- table.show();
- }
- 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();
- }
- }
- // 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;
- }
- 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) + '%');
- }
- else {
- footer_cell.text(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");
- }
- }
- }));
- // Trigger change event on setup, to force filter on page refresh
- // (filter value may still be present).
- $("#filter").trigger("change");
- };
- // Loaded on index.html
- coverage.index_ready = function ($) {
- // Look for a cookie containing previous sort settings:
- var sort_list = [];
- var cookie_name = "COVERAGE_INDEX_SORT";
- var i;
- // This almost makes it worth installing the jQuery cookie plugin:
- if (document.cookie.indexOf(cookie_name) > -1) {
- var cookies = document.cookie.split(";");
- for (i = 0; i < cookies.length; i++) {
- var parts = cookies[i].split("=");
- if ($.trim(parts[0]) === cookie_name && parts[1]) {
- sort_list = eval("[[" + parts[1] + "]]");
- break;
- }
- }
- }
- // 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' };
- }
- headers[col_count-1] = { sorter: 'percent' };
- // Enable the table sorter:
- $("table.index").tablesorter({
- widgets: ['persistentSort'],
- headers: headers
- });
- 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:
- $(window).unload(function () {
- document.cookie = cookie_name + "=" + sort_list.toString() + "; path=/";
- });
- };
- // -- pyfile stuff --
- 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] === 'n') {
- $(frag).addClass('highlight');
- coverage.set_sel(parseInt(frag.substr(2), 10));
- }
- 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)
- ;
- $(".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");});
- coverage.assign_shortkeys();
- coverage.wire_up_help_panel();
- coverage.init_scroll_markers();
- // Rebuild scroll markers after window high changing
- $(window).resize(coverage.resize_scroll_markers);
- };
- coverage.toggle_lines = function (btn, cls) {
- btn = $(btn);
- var hide = "hide_"+cls;
- if (btn.hasClass(hide)) {
- $("#source ."+cls).removeClass(hide);
- btn.removeClass(hide);
- }
- else {
- $("#source ."+cls).addClass(hide);
- btn.addClass(hide);
- }
- };
- // Return the nth line div.
- coverage.line_elt = function (n) {
- return $("#t" + n);
- };
- // Return the nth line number div.
- coverage.num_elt = function (n) {
- return $("#n" + n);
- };
- // Return the container of all the code.
- coverage.code_container = function () {
- return $(".linenos");
- };
- // Set the selection. b and e are line numbers.
- coverage.set_sel = function (b, e) {
- // The first line selected.
- coverage.sel_begin = b;
- // The next line not selected.
- coverage.sel_end = (e === undefined) ? b+1 : e;
- };
- coverage.to_top = function () {
- coverage.set_sel(0, 1);
- coverage.scroll_window(0);
- };
- coverage.to_first_chunk = function () {
- coverage.set_sel(0, 1);
- coverage.to_next_chunk();
- };
- coverage.is_transparent = function (color) {
- // Different browsers return different colors for "none".
- return color === "transparent" || color === "rgba(0, 0, 0, 0)";
- };
- coverage.to_next_chunk = function () {
- var c = coverage;
- // Find the start of the next colored chunk.
- var probe = c.sel_end;
- var color, probe_line;
- while (true) {
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- color = probe_line.css("background-color");
- if (!c.is_transparent(color)) {
- break;
- }
- probe++;
- }
- // There's a next chunk, `probe` points to it.
- var begin = probe;
- // Find the end of this chunk.
- var next_color = color;
- while (next_color === color) {
- probe++;
- probe_line = c.line_elt(probe);
- next_color = probe_line.css("background-color");
- }
- c.set_sel(begin, probe);
- c.show_selection();
- };
- coverage.to_prev_chunk = function () {
- var c = coverage;
- // Find the end of the prev colored chunk.
- var probe = c.sel_begin-1;
- var probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- var color = probe_line.css("background-color");
- while (probe > 0 && c.is_transparent(color)) {
- probe--;
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- return;
- }
- color = probe_line.css("background-color");
- }
- // There's a prev chunk, `probe` points to its last line.
- var end = probe+1;
- // Find the beginning of this chunk.
- var prev_color = color;
- while (prev_color === color) {
- probe--;
- probe_line = c.line_elt(probe);
- prev_color = probe_line.css("background-color");
- }
- c.set_sel(probe+1, end);
- 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 () {
- if (coverage.sel_begin === 0) {
- return 0;
- }
- var top = coverage.line_elt(coverage.sel_begin);
- var next = coverage.line_elt(coverage.sel_end-1);
- return (
- (top.isOnScreen() ? 1 : 0) +
- (next.isOnScreen() ? 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()));
- }
- 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()));
- }
- coverage.to_prev_chunk();
- };
- // Select line number lineno, or if it is in a colored chunk, select the
- // entire chunk
- coverage.select_line_or_chunk = function (lineno) {
- var c = coverage;
- var probe_line = c.line_elt(lineno);
- if (probe_line.length === 0) {
- return;
- }
- var the_color = probe_line.css("background-color");
- if (!c.is_transparent(the_color)) {
- // The line is in a highlighted chunk.
- // Search backward for the first line.
- var probe = lineno;
- var color = the_color;
- while (probe > 0 && color === the_color) {
- probe--;
- probe_line = c.line_elt(probe);
- if (probe_line.length === 0) {
- break;
- }
- color = probe_line.css("background-color");
- }
- var begin = probe + 1;
- // Search forward for the last line.
- probe = lineno;
- color = the_color;
- while (color === the_color) {
- probe++;
- probe_line = c.line_elt(probe);
- color = probe_line.css("background-color");
- }
- coverage.set_sel(begin, probe);
- }
- else {
- coverage.set_sel(lineno);
- }
- };
- coverage.show_selection = function () {
- var c = coverage;
- // Highlight the lines in the chunk
- c.code_container().find(".highlight").removeClass("highlight");
- for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) {
- c.num_elt(probe).addClass("highlight");
- }
- c.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);
- }
- };
- coverage.scroll_window = function (to_pos) {
- $("html,body").animate({scrollTop: to_pos}, 200);
- };
- coverage.finish_scrolling = function () {
- $("html,body").stop(true, true);
- };
- coverage.init_scroll_markers = function () {
- var c = coverage;
- // Init some variables
- c.lines_len = $('td.text p').length;
- c.body_h = $('body').height();
- c.header_h = $('div#header').height();
- c.missed_lines = $('td.text p.mis, td.text p.par');
- // Build html
- c.resize_scroll_markers();
- };
- coverage.resize_scroll_markers = function () {
- var c = coverage,
- min_line_height = 3,
- max_line_height = 10,
- visible_window_h = $(window).height();
- $('#scroll_marker').remove();
- // Don't build markers if the window has no scroll bar.
- if (c.body_h <= visible_window_h) {
- return;
- }
- $("body").append("<div id='scroll_marker'> </div>");
- 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;
- c.missed_lines.each(function () {
- var line_top = Math.round($(this).offset().top * marker_scale),
- id_name = $(this).attr('id'),
- line_number = parseInt(id_name.substring(1, id_name.length));
- 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 {
- // Add colored line in scroll_marker block.
- scroll_marker.append('<div id="m' + line_number + '" class="marker"></div>');
- last_mark = $('#m' + line_number);
- last_mark.css({
- 'height': line_height,
- 'top': line_top
- });
- last_top = line_top;
- }
- previous_line = line_number;
- });
- };
|