/[jscoverage]/trunk/jscoverage.js
ViewVC logotype

Contents of /trunk/jscoverage.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 32 - (show annotations)
Sun Aug 19 17:06:31 2007 UTC (11 years, 7 months ago) by siliconforks
File MIME type: application/javascript
File size: 27680 byte(s)
Reject checkbox click during lengthy operation.

1 /*
2 jscoverage.js - code coverage for JavaScript
3 Copyright (C) 2007 siliconforks.com
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 if (window.opener) {
21 /*
22 Presumably we are in inverted mode.
23 */
24 if (('_$jscoverage' in window.opener.top) && ('_$jscoverage' in window)) {
25 /*
26 Presumably the opener has already set our _$jscoverage to point to the
27 opener's _$jscoverage. We don't have to do anything.
28 */
29 }
30 else if ('_$jscoverage' in window.opener.top) {
31 /*
32 Presumably the opener has already run its tests.
33 */
34 window._$jscoverage = window.opener.top._$jscoverage;
35 }
36 else if ('_$jscoverage' in window) {
37 /*
38 Presumably the opener has not run its tests yet. Not sure why there is a
39 _$jscoverage object here already; we'll assume whoever put it here knew
40 what they were doing, and we'll us it.
41 */
42 window.opener.top._$jscoverage = window._$jscoverage;
43 }
44 else {
45 window.opener.top._$jscoverage = window._$jscoverage = {};
46 }
47 }
48 else {
49 /*
50 No opener. This is what happens when jscoverage.html is opened in a web
51 browser.
52 */
53 if (!('_$jscoverage' in window)) {
54 window._$jscoverage = {};
55 }
56 }
57
58 var gCurrentFile = null;
59 var gCurrentLine = null;
60 var gCurrentSource = null;
61 var gCurrentLines = null;
62 var gMissing = null;
63 var gInLengthyOperation = false;
64
65 // http://www.quirksmode.org/js/findpos.html
66 function findPos(obj) {
67 var result = 0;
68 do {
69 result += obj.offsetTop;
70 obj = obj.offsetParent;
71 }
72 while (obj);
73 return result;
74 }
75
76 // http://www.quirksmode.org/viewport/compatibility.html
77 function getViewportHeight() {
78 if (self.innerHeight) {
79 // all except Explorer
80 return self.innerHeight;
81 }
82 else if (document.documentElement && document.documentElement.clientHeight) {
83 // Explorer 6 Strict Mode
84 return document.documentElement.clientHeight;
85 }
86 else if (document.body) {
87 // other Explorers
88 return document.body.clientHeight;
89 }
90 else {
91 throw "Couldn't calculate viewport height";
92 }
93 }
94
95 /**
96 Indicates visually that a lengthy operation has begun. The progress bar is
97 displayed, and the cursor is changed to busy (on browsers which support this).
98 */
99 function beginLengthyOperation() {
100 gInLengthyOperation = true;
101
102 var progressBar = document.getElementById('progressBar');
103 progressBar.style.visibility = 'visible';
104 ProgressBar.setPercentage(progressBar, 0);
105 var progressLabel = document.getElementById('progressLabel');
106 progressLabel.style.visibility = 'visible';
107
108 /* blacklist buggy browsers */
109 if (BrowserDetect.browser === 'Opera' || BrowserDetect.browser === 'Safari') {
110 return;
111 }
112 var body = document.getElementsByTagName('body').item(0);
113 /*
114 Change the cursor style of each element. Note that changing the class of the
115 element (to one with a busy cursor) is buggy in IE.
116 */
117 body.style.cursor = 'wait';
118 var tabs = document.getElementById('tabs').getElementsByTagName('div');
119 var i;
120 for (i = 0; i < tabs.length; i++) {
121 tabs.item(i).style.cursor = 'wait';
122 }
123 }
124
125 /**
126 Removes the progress bar and busy cursor.
127 */
128 function endLengthyOperation() {
129 gInLengthyOperation = false;
130
131 var progressBar = document.getElementById('progressBar');
132 progressBar.style.visibility = 'hidden';
133 var progressLabel = document.getElementById('progressLabel');
134 progressLabel.style.visibility = 'hidden';
135 progressLabel.innerHTML = '';
136 var body = document.getElementsByTagName('body').item(0);
137 body.style.cursor = '';
138 var tabs = document.getElementById('tabs').getElementsByTagName('div');
139 var i;
140 for (i = 0; i < tabs.length; i++) {
141 tabs.item(i).style.cursor = '';
142 }
143 }
144
145 /**
146 Sets the sizes of various elements according to the viewport size. This
147 function must be called:
148 1. When the document is loaded
149 2. When the window is resized
150 3. When a tab is selected
151 */
152 function setSize() {
153 var viewportHeight = getViewportHeight();
154
155 /*
156 border-top-width: 1px
157 padding-top: 10px
158 padding-bottom: 10px
159 border-bottom-width: 1px
160 margin-bottom: 10px
161 ----
162 32px
163 */
164 var tabPages = document.getElementById('tabPages');
165 tabPages.style.height = (viewportHeight - findPos(tabPages) - 32) + 'px';
166
167 var browserIframe = document.getElementById('browserIframe');
168 // may not exist if we have removed the first tab
169 if (browserIframe) {
170 browserIframe.height = viewportHeight - findPos(browserIframe) - 23;
171 }
172
173 var summaryDiv = document.getElementById('summaryDiv');
174 summaryDiv.style.height = (viewportHeight - findPos(summaryDiv) - 21) + 'px';
175
176 var sourceDiv = document.getElementById('sourceDiv');
177 sourceDiv.style.height = (viewportHeight - findPos(sourceDiv) - 21) + 'px';
178 }
179
180 function getBooleanValue(s) {
181 s = s.toLowerCase();
182 if (s === 'false' || s === 'f' || s === 'no' || s === 'n' || s === 'off' || s === '0') {
183 return false;
184 }
185 return true;
186 }
187
188 function body_load() {
189 if (window.opener) {
190 var tabs = document.getElementById('tabs');
191 var browserTab = document.getElementById('browserTab');
192 tabs.removeChild(browserTab);
193 var tabPages = document.getElementById('tabPages');
194 var browserTabPage = tabPages.getElementsByTagName('div').item(0);
195 tabPages.removeChild(browserTabPage);
196 }
197
198 var progressBar = document.getElementById('progressBar');
199 ProgressBar.init(progressBar);
200
201 initTabControl();
202
203 setSize();
204
205 // check if a URL was passed in the query string
206 var queryString, parameters, parameter, i, index, url, name, value;
207 if (location.search.length > 0) {
208 queryString = location.search.substring(1);
209 parameters = queryString.split(/&|;/);
210 for (i = 0; i < parameters.length; i++) {
211 parameter = parameters[i];
212 index = parameter.indexOf('=');
213 if (index === -1) {
214 // still works with old syntax
215 url = parameter;
216 }
217 else {
218 name = parameter.substr(0, index);
219 value = parameter.substr(index + 1);
220 if (name === 'missing' || name === 'm') {
221 gMissing = getBooleanValue(value);
222 }
223 else if (name === 'url' || name === 'u') {
224 url = value;
225 }
226 }
227 }
228 }
229
230 var checkbox = document.getElementById('checkbox');
231 checkbox.checked = gMissing;
232 if (gMissing) {
233 appendMissingColumn();
234 }
235
236 // this will automatically propagate to the input field
237 if (url) {
238 frames[0].location = url;
239 }
240
241 if (window.opener) {
242 recalculateSummaryTab();
243 }
244 }
245
246 function body_resize() {
247 setSize();
248 }
249
250 // -----------------------------------------------------------------------------
251 // tab 1
252
253 function updateBrowser() {
254 var input = document.getElementById("location");
255 frames[0].location = input.value;
256 }
257
258 function updateInput() {
259 var input = document.getElementById("location");
260 input.value = frames[0].location;
261 }
262
263 function input_keypress(e) {
264 if (e.keyCode === 13) {
265 updateBrowser();
266 }
267 }
268
269 function button_click() {
270 updateBrowser();
271 }
272
273 function browser_load() {
274 updateInput();
275 }
276
277 // -----------------------------------------------------------------------------
278 // tab 2
279
280 function createLink(file, line) {
281 var link = document.createElement("a");
282
283 var url;
284 var call;
285 var text;
286 if (line) {
287 url = file + ".jscoverage.html?" + line;
288 call = "get('" + file + "', " + line + ");";
289 text = line.toString();
290 }
291 else {
292 url = file + ".jscoverage.html";
293 call = "get('" + file + "');";
294 text = file;
295 }
296
297 link.setAttribute('href', 'javascript:' + call);
298 link.appendChild(document.createTextNode(text));
299
300 return link;
301 }
302
303 function recalculateSummaryTab(cc) {
304 if (! cc) {
305 cc = window._$jscoverage;
306 }
307 if (! cc) {
308 throw "No coverage information found.";
309 }
310
311 var tbody = document.getElementById("summaryTbody");
312 while (tbody.hasChildNodes()) {
313 tbody.removeChild(tbody.firstChild);
314 }
315
316 var totals = { files:0, statements:0, executed:0, coverage:0, skipped:0 };
317
318 var rowCounter = 0;
319 for (var file in cc) {
320 var i;
321 var num_statements = 0;
322 var num_executed = 0;
323 var missing = [];
324 var length = cc[file].length;
325 for (i = 0; i < length; i++) {
326 if (cc[file][i] === undefined) {
327 continue;
328 }
329 else if (cc[file][i] === 0) {
330 missing.push(i);
331 }
332 else {
333 num_executed++;
334 }
335 num_statements++;
336 }
337
338 var percentage = ( num_statements === 0 ? 0 : parseInt(100 * num_executed / num_statements) );
339
340 var row = document.createElement("tr");
341 row.className = ( rowCounter++ % 2 == 0 ? "odd" : "even" );
342
343 var cell = document.createElement("td");
344 var link = createLink(file);
345 cell.appendChild(link);
346
347 row.appendChild(cell);
348
349 cell = document.createElement("td");
350 cell.className = 'numeric';
351 cell.appendChild(document.createTextNode(num_statements));
352 row.appendChild(cell);
353
354 cell = document.createElement("td");
355 cell.className = 'numeric';
356 cell.appendChild(document.createTextNode(num_executed));
357 row.appendChild(cell);
358
359 // new coverage td containing a bar graph
360 cell = document.createElement("td");
361 cell.className = 'coverage';
362 var pctGraph = document.createElement("div"),
363 covered = document.createElement("div"),
364 pct = document.createElement("span");
365 pctGraph.className = "pctGraph";
366 if( num_statements === 0 ) {
367 covered.className = "skipped";
368 pct.appendChild(document.createTextNode("N/A"));
369 } else {
370 covered.className = "covered";
371 covered.style.width = percentage + "px";
372 pct.appendChild(document.createTextNode(percentage + '%'));
373 }
374 pct.className = "pct";
375 pctGraph.appendChild(covered);
376 cell.appendChild(pctGraph);
377 cell.appendChild(pct);
378 row.appendChild(cell);
379
380 if (gMissing) {
381 cell = document.createElement("td");
382 for (i = 0; i < missing.length; i++) {
383 if (i !== 0) {
384 cell.appendChild(document.createTextNode(", "));
385 }
386 link = createLink(file, missing[i]);
387 cell.appendChild(link);
388 }
389 row.appendChild(cell);
390 }
391
392 tbody.appendChild(row);
393
394 totals['files'] ++;
395 totals['statements'] += num_statements;
396 totals['executed'] += num_executed;
397 totals['coverage'] += percentage;
398 if( num_statements === 0 ) {
399 totals['skipped']++;
400 }
401
402 // write totals data into summaryTotals row
403 var tr = document.getElementById("summaryTotals");
404 if (tr) {
405 var tds = tr.getElementsByTagName("td");
406 tds[0].getElementsByTagName("span")[1].firstChild.nodeValue = totals['files'];
407 tds[1].firstChild.nodeValue = totals['statements'];
408 tds[2].firstChild.nodeValue = totals['executed'];
409
410 var coverage = parseInt(totals['coverage'] / ( totals['files'] - totals['skipped'] ) );
411 if( isNaN( coverage ) ) {
412 coverage = 0;
413 }
414 tds[3].getElementsByTagName("span")[0].firstChild.nodeValue = coverage + '%';
415 tds[3].getElementsByTagName("div")[1].style.width = coverage + 'px';
416 }
417
418 }
419 endLengthyOperation();
420 }
421
422 function appendMissingColumn() {
423 var headerRow = document.getElementById('headerRow');
424 var missingHeader = document.createElement('th');
425 missingHeader.id = 'missingHeader';
426 missingHeader.innerHTML = '<abbr title="List of statements missed during execution">Missing</abbr>';
427 headerRow.appendChild(missingHeader);
428 var summaryTotals = document.getElementById('summaryTotals');
429 var empty = document.createElement('td');
430 empty.id = 'missingCell';
431 summaryTotals.appendChild(empty);
432 }
433
434 function removeMissingColumn() {
435 var missingNode;
436 missingNode = document.getElementById('missingHeader');
437 missingNode.parentNode.removeChild(missingNode);
438 missingNode = document.getElementById('missingCell');
439 missingNode.parentNode.removeChild(missingNode);
440 }
441
442 function checkbox_click() {
443 if (gInLengthyOperation) {
444 return false;
445 }
446 beginLengthyOperation();
447 setTimeout(function() {
448 var checkbox = document.getElementById('checkbox');
449 gMissing = checkbox.checked;
450 if (gMissing) {
451 appendMissingColumn();
452 }
453 else {
454 removeMissingColumn();
455 }
456 recalculateSummaryTab();
457 }, 100);
458 return true;
459 }
460
461 // -----------------------------------------------------------------------------
462 // tab 3
463
464 function makeTable() {
465 var coverage = _$jscoverage[gCurrentFile];
466 var lines = gCurrentLines;
467 var rows = ['<table id="sourceTable">'];
468 var i = 0;
469 var progressBar = document.getElementById('progressBar');
470 var tableHTML;
471 var oldDate = new Date().valueOf();
472 function makeTableRows() {
473 while (i < lines.length) {
474 var lineNumber = i + 1;
475
476 var row = '<tr>';
477 row += '<td class="numeric">' + lineNumber + '</td>';
478 if (coverage[lineNumber] !== undefined) {
479 var timesExecuted = coverage[lineNumber];
480 if (timesExecuted === 0) {
481 row += '<td class="r numeric" id="line-' + lineNumber + '">';
482 }
483 else {
484 row += '<td class="g numeric">';
485 }
486 row += timesExecuted;
487 row += '</td>';
488 }
489 else {
490 row += '<td></td>';
491 }
492 row += '<td><pre class="sh_sourceCode sh_javascript">' + lines[i] + '</pre></td>';
493 row += '</tr>';
494 row += '\n';
495 rows[lineNumber] = row;
496 i++;
497 var newDate = new Date().valueOf();
498 if (newDate - oldDate > 250) {
499 oldDate = newDate;
500 ProgressBar.setPercentage(progressBar, parseInt(50 * i / lines.length));
501 setTimeout(makeTableRows, 0);
502 return;
503 }
504 }
505 rows[i + 1] = '</table>';
506 ProgressBar.setPercentage(progressBar, 50);
507 setTimeout(joinTableRows, 0);
508 }
509
510 function joinTableRows() {
511 tableHTML = rows.join('');
512 ProgressBar.setPercentage(progressBar, 75);
513 /*
514 This may be a long delay, so set a timeout of 100 ms to make sure the
515 display is updated.
516 */
517 setTimeout(appendTable, 100);
518 }
519
520 function appendTable() {
521 var sourceDiv = document.getElementById('sourceDiv');
522 while (sourceDiv.hasChildNodes()) {
523 sourceDiv.removeChild(sourceDiv.firstChild);
524 }
525 sourceDiv.innerHTML = tableHTML;
526 ProgressBar.setPercentage(progressBar, 100);
527 setTimeout(scrollToLine, 0);
528 }
529
530 setTimeout(makeTableRows, 0);
531 }
532
533 function countLines(text) {
534 var length = text.length;
535 var pos = 0;
536 var count = 0;
537 while (pos < length) {
538 pos = text.indexOf('\n', pos);
539 if (pos == -1) {
540 break;
541 }
542 count++;
543 pos++;
544 }
545 return count;
546 }
547
548 function highlightSource() {
549 var progressLabel = document.getElementById('progressLabel');
550 progressLabel.innerHTML = 'Loading source ...';
551
552 // set file name
553 var fileDiv = document.getElementById('fileDiv');
554 fileDiv.innerHTML = gCurrentFile;
555
556 // highlight source and break into lines
557 var builder = {
558 lines: [],
559 currentLine: null,
560 _initCurrentLine: function() {
561 if (this.currentLine === null) {
562 this.currentLine = "";
563 }
564 },
565 startElement: function(style) {
566 this._initCurrentLine();
567 this.currentLine += '<span class="' + style + '">';
568 },
569 endElement: function() {
570 this._initCurrentLine();
571 this.currentLine += '</span>';
572 },
573 text: function(s) {
574 this._initCurrentLine();
575 if (s === '\r\n' || s === '\r' || s === '\n') {
576 this.close();
577 this.currentLine = null;
578 }
579 else {
580 this.currentLine += s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
581 }
582 },
583 close: function() {
584 if (this.currentLine !== null) {
585 this.lines.push(this.currentLine);
586 }
587 }
588 };
589 var progressBar = document.getElementById('progressBar');
590 var numLines = countLines(gCurrentSource);
591 var oldDate = new Date().valueOf();
592 var i = 0;
593 function updateFunction() {
594 i++;
595 var newDate = new Date().valueOf();
596 if (newDate - oldDate > 250) {
597 ProgressBar.setPercentage(progressBar, parseInt(100 * i / numLines));
598 oldDate = newDate;
599 return true;
600 }
601 else {
602 return false;
603 }
604 }
605 sh_highlightString(gCurrentSource, sh_languages['javascript'], builder, updateFunction, function() {
606 builder.close();
607 gCurrentLines = builder.lines;
608 ProgressBar.setPercentage(progressBar, 100);
609 // coverage
610 recalculateSourceTab();
611 });
612 }
613
614 function scrollToLine() {
615 selectTab('sourceTab');
616 if (! window.gCurrentLine) {
617 endLengthyOperation();
618 return;
619 }
620 var div = document.getElementById('sourceDiv');
621 if (gCurrentLine === 1) {
622 div.scrollTop = 0;
623 }
624 else {
625 var cell = document.getElementById('line-' + gCurrentLine);
626 var divOffset = findPos(div);
627 var cellOffset = findPos(cell);
628 div.scrollTop = cellOffset - divOffset;
629 }
630 gCurrentLine = 0;
631 endLengthyOperation();
632 }
633
634 function setThrobber() {
635 var throbberImg = document.getElementById('throbberImg');
636 throbberImg.style.visibility = 'visible';
637 }
638
639 function clearThrobber() {
640 var throbberImg = document.getElementById('throbberImg');
641 throbberImg.style.visibility = 'hidden';
642 }
643
644 function httpError(file) {
645 gCurrentFile = null;
646 clearThrobber();
647 var fileDiv = document.getElementById('fileDiv');
648 fileDiv.innerHTML = '';
649 var sourceDiv = document.getElementById('sourceDiv');
650 sourceDiv.innerHTML = "Error retrieving document " + file + ".";
651 selectTab('sourceTab');
652 endLengthyOperation();
653 }
654
655 /**
656 Loads the given file (and optional line) in the source tab.
657 */
658 function get(file, line) {
659 if (gInLengthyOperation) {
660 return;
661 }
662 beginLengthyOperation();
663 if (file === gCurrentFile) {
664 setTimeout(function() {
665 selectTab('sourceTab');
666 gCurrentLine = line;
667 recalculateSourceTab();
668 }, 50);
669 }
670 else {
671 if (gCurrentFile === null) {
672 var tab = document.getElementById('sourceTab');
673 tab.className = '';
674 tab.onclick = tab_click;
675 }
676 setTimeout(function() {
677 selectTab('sourceTab');
678 setThrobber();
679 // Note that the IE7 XMLHttpRequest does not support file URL's.
680 // http://xhab.blogspot.com/2006/11/ie7-support-for-xmlhttprequest.html
681 // http://blogs.msdn.com/ie/archive/2006/12/06/file-uris-in-windows.aspx
682 var request;
683 if (window.ActiveXObject) {
684 request = new ActiveXObject("Microsoft.XMLHTTP");
685 }
686 else {
687 request = new XMLHttpRequest();
688 }
689 request.open("GET", file + ".jscoverage.js", true);
690 request.onreadystatechange = function(event) {
691 if (request.readyState === 4) {
692 if (request.status === 0 || request.status === 200) {
693 var response = request.responseText;
694 // opera returns status zero even if there is a missing file???
695 if (response === '') {
696 httpError(file);
697 }
698 else {
699 clearThrobber();
700 gCurrentFile = file;
701 gCurrentLine = line || 1;
702 gCurrentSource = response;
703 highlightSource();
704 }
705 }
706 else {
707 httpError(file);
708 }
709 }
710 };
711 if ('onerror' in request) {
712 request.onerror = function(event) {
713 httpError(file);
714 };
715 }
716 try {
717 request.send(null);
718 }
719 catch (e) {
720 httpError(file);
721 }
722 }, 50);
723 }
724 }
725
726 /**
727 Calculates coverage statistics for the current source file.
728 */
729 function recalculateSourceTab() {
730 if (! gCurrentFile) {
731 endLengthyOperation();
732 return;
733 }
734 var progressLabel = document.getElementById('progressLabel');
735 progressLabel.innerHTML = 'Calculating coverage ...';
736 var progressBar = document.getElementById('progressBar');
737 ProgressBar.setPercentage(progressBar, 0);
738 setTimeout(makeTable, 0);
739 }
740
741 // -----------------------------------------------------------------------------
742 // tabs
743
744 /**
745 Initializes the tab control. This function must be called when the document is
746 loaded.
747 */
748 function initTabControl() {
749 var tabs = document.getElementById('tabs');
750 var i;
751 var child;
752 var tabNum = 0;
753 for (i = 0; i < tabs.childNodes.length; i++) {
754 child = tabs.childNodes.item(i);
755 if (child.nodeType === 1) {
756 if (child.className !== 'disabled') {
757 child.onclick = tab_click;
758 }
759 tabNum++;
760 }
761 }
762 selectTab(0);
763 }
764
765 /**
766 Selects a tab.
767 @param tab the integer index of the tab (0, 1, 2, or 3)
768 OR
769 the ID of the tab element
770 OR
771 the tab element itself
772 */
773 function selectTab(tab) {
774 if (typeof tab !== 'number') {
775 tab = tabIndexOf(tab);
776 }
777 var tabControl = document.getElementById("tabControl");
778 var tabs = document.getElementById('tabs');
779 var tabPages = document.getElementById('tabPages');
780 var i;
781 var child;
782 var tabNum = 0;
783 for (i = 0; i < tabs.childNodes.length; i++) {
784 child = tabs.childNodes.item(i);
785 if (child.nodeType === 1) {
786 if (child.className !== 'disabled') {
787 child.className = tabNum === tab? 'selected': '';
788 }
789 tabNum++;
790 }
791 }
792 tabNum = 0;
793 for (i = 0; i < tabPages.childNodes.length; i++) {
794 child = tabPages.childNodes.item(i);
795 if (child.nodeType === 1) {
796 child.style.display = tabNum === tab? 'block': 'none';
797 tabNum++;
798 }
799 }
800 setSize();
801 }
802
803 /**
804 Returns an integer (0, 1, 2, or 3) representing the index of a given tab.
805 @param tab the ID of the tab element
806 OR
807 the tab element itself
808 */
809 function tabIndexOf(tab) {
810 if (typeof tab === 'string') {
811 tab = document.getElementById(tab);
812 }
813 var tabs = document.getElementById('tabs');
814 var i;
815 var child;
816 var tabNum = 0;
817 for (i = 0; i < tabs.childNodes.length; i++) {
818 child = tabs.childNodes.item(i);
819 if (child.nodeType === 1) {
820 if (child === tab) {
821 return tabNum;
822 }
823 tabNum++;
824 }
825 }
826 throw "Tab not found";
827 }
828
829 function tab_click(e) {
830 if (gInLengthyOperation) {
831 return;
832 }
833 var target;
834 if (e) {
835 target = e.target;
836 }
837 else if (window.event) {
838 // IE
839 target = window.event.srcElement;
840 }
841 if (target.className === 'selected') {
842 return;
843 }
844 beginLengthyOperation();
845 setTimeout(function() {
846 selectTab(target);
847 if (target.id === 'summaryTab') {
848 recalculateSummaryTab();
849 }
850 else if (target.id === 'sourceTab') {
851 recalculateSourceTab();
852 }
853 else {
854 endLengthyOperation();
855 }
856 }, 50);
857 }
858
859 // -----------------------------------------------------------------------------
860 // progress bar
861
862 var ProgressBar = {
863 init: function(element) {
864 element._percentage = 0;
865
866 /* doing this via JavaScript crashes Safari */
867 /*
868 var pctGraph = document.createElement('div');
869 pctGraph.className = 'pctGraph';
870 element.appendChild(pctGraph);
871 var covered = document.createElement('div');
872 covered.className = 'covered';
873 pctGraph.appendChild(covered);
874 var pct = document.createElement('span');
875 pct.className = 'pct';
876 element.appendChild(pct);
877 */
878
879 ProgressBar._update(element);
880 },
881 setPercentage: function(element, percentage) {
882 element._percentage = percentage;
883 ProgressBar._update(element);
884 },
885 _update: function(element) {
886 var pctGraph = element.getElementsByTagName('div').item(0);
887 var covered = pctGraph.getElementsByTagName('div').item(0);
888 var pct = element.getElementsByTagName('span').item(0);
889 pct.innerHTML = element._percentage.toString() + '%';
890 covered.style.width = element._percentage + 'px';
891 }
892 };
893
894 // -----------------------------------------------------------------------------
895 // browser detection
896
897 // http://www.quirksmode.org/js/detect.html
898 var BrowserDetect = {
899 init: function () {
900 this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
901 this.version = this.searchVersion(navigator.userAgent)
902 || this.searchVersion(navigator.appVersion)
903 || "an unknown version";
904 this.OS = this.searchString(this.dataOS) || "an unknown OS";
905 },
906 searchString: function (data) {
907 for (var i=0;i<data.length;i++) {
908 var dataString = data[i].string;
909 var dataProp = data[i].prop;
910 this.versionSearchString = data[i].versionSearch || data[i].identity;
911 if (dataString) {
912 if (dataString.indexOf(data[i].subString) != -1)
913 return data[i].identity;
914 }
915 else if (dataProp)
916 return data[i].identity;
917 }
918 },
919 searchVersion: function (dataString) {
920 var index = dataString.indexOf(this.versionSearchString);
921 if (index == -1) return;
922 return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
923 },
924 dataBrowser: [
925 { string: navigator.userAgent,
926 subString: "OmniWeb",
927 versionSearch: "OmniWeb/",
928 identity: "OmniWeb"
929 },
930 {
931 string: navigator.vendor,
932 subString: "Apple",
933 identity: "Safari"
934 },
935 {
936 prop: window.opera,
937 identity: "Opera"
938 },
939 {
940 string: navigator.vendor,
941 subString: "iCab",
942 identity: "iCab"
943 },
944 {
945 string: navigator.vendor,
946 subString: "KDE",
947 identity: "Konqueror"
948 },
949 {
950 string: navigator.userAgent,
951 subString: "Firefox",
952 identity: "Firefox"
953 },
954 {
955 string: navigator.vendor,
956 subString: "Camino",
957 identity: "Camino"
958 },
959 { // for newer Netscapes (6+)
960 string: navigator.userAgent,
961 subString: "Netscape",
962 identity: "Netscape"
963 },
964 {
965 string: navigator.userAgent,
966 subString: "MSIE",
967 identity: "Explorer",
968 versionSearch: "MSIE"
969 },
970 {
971 string: navigator.userAgent,
972 subString: "Gecko",
973 identity: "Mozilla",
974 versionSearch: "rv"
975 },
976 { // for older Netscapes (4-)
977 string: navigator.userAgent,
978 subString: "Mozilla",
979 identity: "Netscape",
980 versionSearch: "Mozilla"
981 }
982 ],
983 dataOS : [
984 {
985 string: navigator.platform,
986 subString: "Win",
987 identity: "Windows"
988 },
989 {
990 string: navigator.platform,
991 subString: "Mac",
992 identity: "Mac"
993 },
994 {
995 string: navigator.platform,
996 subString: "Linux",
997 identity: "Linux"
998 }
999 ]
1000
1001 };
1002 BrowserDetect.init();

  ViewVC Help
Powered by ViewVC 1.1.24