OLD | NEW |
| (Empty) |
1 // Coverage.py HTML report browser code. | |
2 /*jslint browser: true, sloppy: true, vars: true, plusplus: true, maxerr: 50, in
dent: 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 shotrtc
ut 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 | |
OLD | NEW |