You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

376 lines
11 KiB

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