Home | History | Annotate | Download | only in htmlfiles
      1 // Coverage.py HTML report browser code.
      2 /*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, indent: 4 */
      3 /*global coverage: true, document, window, $ */
      4 
      5 coverage = {};
      6 
      7 // Find all the elements with shortkey_* class, and use them to assign a shotrtcut key.
      8 coverage.assign_shortkeys = function () {
      9     $("*[class*='shortkey_']").each(function (i, e) {
     10         $.each($(e).attr("class").split(" "), function (i, c) {
     11             if (/^shortkey_/.test(c)) {
     12                 $(document).bind('keydown', c.substr(9), function () {
     13                     $(e).click();
     14                 });
     15             }
     16         });
     17     });
     18 };
     19 
     20 // Create the events for the help panel.
     21 coverage.wire_up_help_panel = function () {
     22     $("#keyboard_icon").click(function () {
     23         // Show the help panel, and position it so the keyboard icon in the
     24         // panel is in the same place as the keyboard icon in the header.
     25         $(".help_panel").show();
     26         var koff = $("#keyboard_icon").offset();
     27         var poff = $("#panel_icon").position();
     28         $(".help_panel").offset({
     29             top: koff.top-poff.top,
     30             left: koff.left-poff.left
     31         });
     32     });
     33     $("#panel_icon").click(function () {
     34         $(".help_panel").hide();
     35     });
     36 };
     37 
     38 // Loaded on index.html
     39 coverage.index_ready = function ($) {
     40     // Look for a cookie containing previous sort settings:
     41     var sort_list = [];
     42     var cookie_name = "COVERAGE_INDEX_SORT";
     43     var i;
     44 
     45     // This almost makes it worth installing the jQuery cookie plugin:
     46     if (document.cookie.indexOf(cookie_name) > -1) {
     47         var cookies = document.cookie.split(";");
     48         for (i = 0; i < cookies.length; i++) {
     49             var parts = cookies[i].split("=");
     50 
     51             if ($.trim(parts[0]) === cookie_name && parts[1]) {
     52                 sort_list = eval("[[" + parts[1] + "]]");
     53                 break;
     54             }
     55         }
     56     }
     57 
     58     // Create a new widget which exists only to save and restore
     59     // the sort order:
     60     $.tablesorter.addWidget({
     61         id: "persistentSort",
     62 
     63         // Format is called by the widget before displaying:
     64         format: function (table) {
     65             if (table.config.sortList.length === 0 && sort_list.length > 0) {
     66                 // This table hasn't been sorted before - we'll use
     67                 // our stored settings:
     68                 $(table).trigger('sorton', [sort_list]);
     69             }
     70             else {
     71                 // This is not the first load - something has
     72                 // already defined sorting so we'll just update
     73                 // our stored value to match:
     74                 sort_list = table.config.sortList;
     75             }
     76         }
     77     });
     78 
     79     // Configure our tablesorter to handle the variable number of
     80     // columns produced depending on report options:
     81     var headers = [];
     82     var col_count = $("table.index > thead > tr > th").length;
     83 
     84     headers[0] = { sorter: 'text' };
     85     for (i = 1; i < col_count-1; i++) {
     86         headers[i] = { sorter: 'digit' };
     87     }
     88     headers[col_count-1] = { sorter: 'percent' };
     89 
     90     // Enable the table sorter:
     91     $("table.index").tablesorter({
     92         widgets: ['persistentSort'],
     93         headers: headers
     94     });
     95 
     96     coverage.assign_shortkeys();
     97     coverage.wire_up_help_panel();
     98 
     99     // Watch for page unload events so we can save the final sort settings:
    100     $(window).unload(function () {
    101         document.cookie = cookie_name + "=" + sort_list.toString() + "; path=/";
    102     });
    103 };
    104 
    105 // -- pyfile stuff --
    106 
    107 coverage.pyfile_ready = function ($) {
    108     // If we're directed to a particular line number, highlight the line.
    109     var frag = location.hash;
    110     if (frag.length > 2 && frag[1] === 'n') {
    111         $(frag).addClass('highlight');
    112         coverage.set_sel(parseInt(frag.substr(2), 10));
    113     }
    114     else {
    115         coverage.set_sel(0);
    116     }
    117 
    118     $(document)
    119         .bind('keydown', 'j', coverage.to_next_chunk_nicely)
    120         .bind('keydown', 'k', coverage.to_prev_chunk_nicely)
    121         .bind('keydown', '0', coverage.to_top)
    122         .bind('keydown', '1', coverage.to_first_chunk)
    123         ;
    124 
    125     coverage.assign_shortkeys();
    126     coverage.wire_up_help_panel();
    127 };
    128 
    129 coverage.toggle_lines = function (btn, cls) {
    130     btn = $(btn);
    131     var hide = "hide_"+cls;
    132     if (btn.hasClass(hide)) {
    133         $("#source ."+cls).removeClass(hide);
    134         btn.removeClass(hide);
    135     }
    136     else {
    137         $("#source ."+cls).addClass(hide);
    138         btn.addClass(hide);
    139     }
    140 };
    141 
    142 // Return the nth line div.
    143 coverage.line_elt = function (n) {
    144     return $("#t" + n);
    145 };
    146 
    147 // Return the nth line number div.
    148 coverage.num_elt = function (n) {
    149     return $("#n" + n);
    150 };
    151 
    152 // Return the container of all the code.
    153 coverage.code_container = function () {
    154     return $(".linenos");
    155 };
    156 
    157 // Set the selection.  b and e are line numbers.
    158 coverage.set_sel = function (b, e) {
    159     // The first line selected.
    160     coverage.sel_begin = b;
    161     // The next line not selected.
    162     coverage.sel_end = (e === undefined) ? b+1 : e;
    163 };
    164 
    165 coverage.to_top = function () {
    166     coverage.set_sel(0, 1);
    167     coverage.scroll_window(0);
    168 };
    169 
    170 coverage.to_first_chunk = function () {
    171     coverage.set_sel(0, 1);
    172     coverage.to_next_chunk();
    173 };
    174 
    175 coverage.is_transparent = function (color) {
    176     // Different browsers return different colors for "none".
    177     return color === "transparent" || color === "rgba(0, 0, 0, 0)";
    178 };
    179 
    180 coverage.to_next_chunk = function () {
    181     var c = coverage;
    182 
    183     // Find the start of the next colored chunk.
    184     var probe = c.sel_end;
    185     while (true) {
    186         var probe_line = c.line_elt(probe);
    187         if (probe_line.length === 0) {
    188             return;
    189         }
    190         var color = probe_line.css("background-color");
    191         if (!c.is_transparent(color)) {
    192             break;
    193         }
    194         probe++;
    195     }
    196 
    197     // There's a next chunk, `probe` points to it.
    198     var begin = probe;
    199 
    200     // Find the end of this chunk.
    201     var next_color = color;
    202     while (next_color === color) {
    203         probe++;
    204         probe_line = c.line_elt(probe);
    205         next_color = probe_line.css("background-color");
    206     }
    207     c.set_sel(begin, probe);
    208     c.show_selection();
    209 };
    210 
    211 coverage.to_prev_chunk = function () {
    212     var c = coverage;
    213 
    214     // Find the end of the prev colored chunk.
    215     var probe = c.sel_begin-1;
    216     var probe_line = c.line_elt(probe);
    217     if (probe_line.length === 0) {
    218         return;
    219     }
    220     var color = probe_line.css("background-color");
    221     while (probe > 0 && c.is_transparent(color)) {
    222         probe--;
    223         probe_line = c.line_elt(probe);
    224         if (probe_line.length === 0) {
    225             return;
    226         }
    227         color = probe_line.css("background-color");
    228     }
    229 
    230     // There's a prev chunk, `probe` points to its last line.
    231     var end = probe+1;
    232 
    233     // Find the beginning of this chunk.
    234     var prev_color = color;
    235     while (prev_color === color) {
    236         probe--;
    237         probe_line = c.line_elt(probe);
    238         prev_color = probe_line.css("background-color");
    239     }
    240     c.set_sel(probe+1, end);
    241     c.show_selection();
    242 };
    243 
    244 // Return the line number of the line nearest pixel position pos
    245 coverage.line_at_pos = function (pos) {
    246     var l1 = coverage.line_elt(1),
    247         l2 = coverage.line_elt(2),
    248         result;
    249     if (l1.length && l2.length) {
    250         var l1_top = l1.offset().top,
    251             line_height = l2.offset().top - l1_top,
    252             nlines = (pos - l1_top) / line_height;
    253         if (nlines < 1) {
    254             result = 1;
    255         }
    256         else {
    257             result = Math.ceil(nlines);
    258         }
    259     }
    260     else {
    261         result = 1;
    262     }
    263     return result;
    264 };
    265 
    266 // Returns 0, 1, or 2: how many of the two ends of the selection are on
    267 // the screen right now?
    268 coverage.selection_ends_on_screen = function () {
    269     if (coverage.sel_begin === 0) {
    270         return 0;
    271     }
    272 
    273     var top = coverage.line_elt(coverage.sel_begin);
    274     var next = coverage.line_elt(coverage.sel_end-1);
    275 
    276     return (
    277         (top.isOnScreen() ? 1 : 0) +
    278         (next.isOnScreen() ? 1 : 0)
    279     );
    280 };
    281 
    282 coverage.to_next_chunk_nicely = function () {
    283     coverage.finish_scrolling();
    284     if (coverage.selection_ends_on_screen() === 0) {
    285         // The selection is entirely off the screen: select the top line on
    286         // the screen.
    287         var win = $(window);
    288         coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop()));
    289     }
    290     coverage.to_next_chunk();
    291 };
    292 
    293 coverage.to_prev_chunk_nicely = function () {
    294     coverage.finish_scrolling();
    295     if (coverage.selection_ends_on_screen() === 0) {
    296         var win = $(window);
    297         coverage.select_line_or_chunk(coverage.line_at_pos(win.scrollTop() + win.height()));
    298     }
    299     coverage.to_prev_chunk();
    300 };
    301 
    302 // Select line number lineno, or if it is in a colored chunk, select the
    303 // entire chunk
    304 coverage.select_line_or_chunk = function (lineno) {
    305     var c = coverage;
    306     var probe_line = c.line_elt(lineno);
    307     if (probe_line.length === 0) {
    308         return;
    309     }
    310     var the_color = probe_line.css("background-color");
    311     if (!c.is_transparent(the_color)) {
    312         // The line is in a highlighted chunk.
    313         // Search backward for the first line.
    314         var probe = lineno;
    315         var color = the_color;
    316         while (probe > 0 && color === the_color) {
    317             probe--;
    318             probe_line = c.line_elt(probe);
    319             if (probe_line.length === 0) {
    320                 break;
    321             }
    322             color = probe_line.css("background-color");
    323         }
    324         var begin = probe + 1;
    325 
    326         // Search forward for the last line.
    327         probe = lineno;
    328         color = the_color;
    329         while (color === the_color) {
    330             probe++;
    331             probe_line = c.line_elt(probe);
    332             color = probe_line.css("background-color");
    333         }
    334 
    335         coverage.set_sel(begin, probe);
    336     }
    337     else {
    338         coverage.set_sel(lineno);
    339     }
    340 };
    341 
    342 coverage.show_selection = function () {
    343     var c = coverage;
    344 
    345     // Highlight the lines in the chunk
    346     c.code_container().find(".highlight").removeClass("highlight");
    347     for (var probe = c.sel_begin; probe > 0 && probe < c.sel_end; probe++) {
    348         c.num_elt(probe).addClass("highlight");
    349     }
    350 
    351     c.scroll_to_selection();
    352 };
    353 
    354 coverage.scroll_to_selection = function () {
    355     // Scroll the page if the chunk isn't fully visible.
    356     if (coverage.selection_ends_on_screen() < 2) {
    357         // Need to move the page. The html,body trick makes it scroll in all
    358         // browsers, got it from http://stackoverflow.com/questions/3042651
    359         var top = coverage.line_elt(coverage.sel_begin);
    360         var top_pos = parseInt(top.offset().top, 10);
    361         coverage.scroll_window(top_pos - 30);
    362     }
    363 };
    364 
    365 coverage.scroll_window = function (to_pos) {
    366     $("html,body").animate({scrollTop: to_pos}, 200);
    367 };
    368 
    369 coverage.finish_scrolling = function () {
    370     $("html,body").stop(true, true);
    371 };
    372 
    373