|
@@ -0,0 +1,589 @@
|
|
|
+// Licensed under the Apache License: http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+// For details: https://github.com/nedbat/coveragepy/blob/master/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 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) {}
|
|
|
+
|
|
|
+ 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' };
|
|
|
+ }
|
|
|
+ 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 () {
|
|
|
+ try {
|
|
|
+ localStorage.setItem(storage_name, sort_list.toString())
|
|
|
+ } catch(err) {}
|
|
|
+ });
|
|
|
+};
|
|
|
+
|
|
|
+// -- 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] === 't') {
|
|
|
+ $(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 when the window height changes.
|
|
|
+ $(window).resize(coverage.build_scroll_markers);
|
|
|
+};
|
|
|
+
|
|
|
+coverage.toggle_lines = function (btn, cls) {
|
|
|
+ btn = $(btn);
|
|
|
+ var show = "show_"+cls;
|
|
|
+ if (btn.hasClass(show)) {
|
|
|
+ $("#source ." + cls).removeClass(show);
|
|
|
+ btn.removeClass(show);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ $("#source ." + cls).addClass(show);
|
|
|
+ btn.addClass(show);
|
|
|
+ }
|
|
|
+ coverage.build_scroll_markers();
|
|
|
+};
|
|
|
+
|
|
|
+// 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);
|
|
|
+};
|
|
|
+
|
|
|
+// 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();
|
|
|
+};
|
|
|
+
|
|
|
+// 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];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+};
|
|
|
+
|
|
|
+coverage.to_next_chunk = function () {
|
|
|
+ var 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) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ chunk_indicator = c.chunk_indicator(probe_line);
|
|
|
+ if (chunk_indicator) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ probe++;
|
|
|
+ }
|
|
|
+
|
|
|
+ // There's a next chunk, `probe` points to it.
|
|
|
+ var begin = probe;
|
|
|
+
|
|
|
+ // Find the end of this chunk.
|
|
|
+ var next_indicator = chunk_indicator;
|
|
|
+ while (next_indicator === chunk_indicator) {
|
|
|
+ probe++;
|
|
|
+ probe_line = c.line_elt(probe);
|
|
|
+ next_indicator = c.chunk_indicator(probe_line);
|
|
|
+ }
|
|
|
+ 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 chunk_indicator = c.chunk_indicator(probe_line);
|
|
|
+ while (probe > 0 && !chunk_indicator) {
|
|
|
+ probe--;
|
|
|
+ probe_line = c.line_elt(probe);
|
|
|
+ if (probe_line.length === 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ chunk_indicator = c.chunk_indicator(probe_line);
|
|
|
+ }
|
|
|
+
|
|
|
+ // There's a prev chunk, `probe` points to its last line.
|
|
|
+ var end = probe+1;
|
|
|
+
|
|
|
+ // Find the beginning of this chunk.
|
|
|
+ var prev_indicator = chunk_indicator;
|
|
|
+ while (prev_indicator === chunk_indicator) {
|
|
|
+ probe--;
|
|
|
+ probe_line = c.line_elt(probe);
|
|
|
+ prev_indicator = c.chunk_indicator(probe_line);
|
|
|
+ }
|
|
|
+ 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_indicator = c.chunk_indicator(probe_line);
|
|
|
+ if (the_indicator) {
|
|
|
+ // The line is in a highlighted chunk.
|
|
|
+ // Search backward for the first line.
|
|
|
+ var probe = lineno;
|
|
|
+ var indicator = the_indicator;
|
|
|
+ while (probe > 0 && indicator === the_indicator) {
|
|
|
+ probe--;
|
|
|
+ probe_line = c.line_elt(probe);
|
|
|
+ if (probe_line.length === 0) {
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ indicator = c.chunk_indicator(probe_line);
|
|
|
+ }
|
|
|
+ var begin = probe + 1;
|
|
|
+
|
|
|
+ // Search forward for the last line.
|
|
|
+ probe = lineno;
|
|
|
+ indicator = the_indicator;
|
|
|
+ while (indicator === the_indicator) {
|
|
|
+ probe++;
|
|
|
+ probe_line = c.line_elt(probe);
|
|
|
+ indicator = c.chunk_indicator(probe_line);
|
|
|
+ }
|
|
|
+
|
|
|
+ coverage.set_sel(begin, probe);
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ coverage.set_sel(lineno);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+coverage.show_selection = function () {
|
|
|
+ var c = coverage;
|
|
|
+
|
|
|
+ // Highlight the lines in the chunk
|
|
|
+ $(".linenos .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 = $('#source p').length;
|
|
|
+ c.body_h = $('body').height();
|
|
|
+ c.header_h = $('div#header').height();
|
|
|
+
|
|
|
+ // 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();
|
|
|
+ // 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,
|
|
|
+ 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));
|
|
|
+
|
|
|
+ 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;
|
|
|
+ });
|
|
|
+};
|