123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176 |
- /*
- jscoverage.js - code coverage for JavaScript
- Copyright (C) 2007, 2008, 2009, 2010 siliconforks.com
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- function jscoverage_openWarningDialog() {
- var id;
- if (jscoverage_isReport) {
- id = 'reportWarningDialog';
- }
- else {
- id = 'warningDialog';
- }
- var dialog = document.getElementById(id);
- dialog.style.display = 'block';
- }
- function jscoverage_closeWarningDialog() {
- var id;
- if (jscoverage_isReport) {
- id = 'reportWarningDialog';
- }
- else {
- id = 'warningDialog';
- }
- var dialog = document.getElementById(id);
- dialog.style.display = 'none';
- }
- /**
- Initializes the _$jscoverage object in a window. This should be the first
- function called in the page.
- @param w this should always be the global window object
- */
- function jscoverage_init(w) {
- try {
- // in Safari, "import" is a syntax error
- Components.utils['import']('resource://app/modules/jscoverage.jsm');
- jscoverage_isInvertedMode = true;
- return;
- }
- catch (e) {}
- // check if we are in inverted mode
- if (w.opener) {
- try {
- if (w.opener.top._$jscoverage) {
- jscoverage_isInvertedMode = true;
- if (! w._$jscoverage) {
- w._$jscoverage = w.opener.top._$jscoverage;
- }
- }
- else {
- jscoverage_isInvertedMode = false;
- }
- }
- catch (e) {
- try {
- if (w.opener._$jscoverage) {
- jscoverage_isInvertedMode = true;
- if (! w._$jscoverage) {
- w._$jscoverage = w.opener._$jscoverage;
- }
- }
- else {
- jscoverage_isInvertedMode = false;
- }
- }
- catch (e2) {
- jscoverage_isInvertedMode = false;
- }
- }
- }
- else {
- jscoverage_isInvertedMode = false;
- }
- if (! jscoverage_isInvertedMode) {
- if (! w._$jscoverage) {
- w._$jscoverage = {};
- }
- }
- }
- var jscoverage_currentFile = null;
- var jscoverage_currentLine = null;
- var jscoverage_inLengthyOperation = false;
- /*
- Possible states:
- isInvertedMode isServer isReport tabs
- normal false false false Browser
- inverted true false false
- server, normal false true false Browser, Store
- server, inverted true true false Store
- report false false true
- */
- var jscoverage_isInvertedMode = false;
- var jscoverage_isServer = false;
- var jscoverage_isReport = false;
- jscoverage_init(window);
- function jscoverage_createRequest() {
- // Note that the IE7 XMLHttpRequest does not support file URL's.
- // http://xhab.blogspot.com/2006/11/ie7-support-for-xmlhttprequest.html
- // http://blogs.msdn.com/ie/archive/2006/12/06/file-uris-in-windows.aspx
- //#JSCOVERAGE_IF
- if (window.ActiveXObject) {
- return new ActiveXObject("Microsoft.XMLHTTP");
- }
- else {
- return new XMLHttpRequest();
- }
- }
- // http://www.quirksmode.org/js/findpos.html
- function jscoverage_findPos(obj) {
- var result = 0;
- do {
- result += obj.offsetTop;
- obj = obj.offsetParent;
- }
- while (obj);
- return result;
- }
- // http://www.quirksmode.org/viewport/compatibility.html
- function jscoverage_getViewportHeight() {
- //#JSCOVERAGE_IF /MSIE/.test(navigator.userAgent)
- if (self.innerHeight) {
- // all except Explorer
- return self.innerHeight;
- }
- else if (document.documentElement && document.documentElement.clientHeight) {
- // Explorer 6 Strict Mode
- return document.documentElement.clientHeight;
- }
- else if (document.body) {
- // other Explorers
- return document.body.clientHeight;
- }
- else {
- throw "Couldn't calculate viewport height";
- }
- //#JSCOVERAGE_ENDIF
- }
- /**
- Indicates visually that a lengthy operation has begun. The progress bar is
- displayed, and the cursor is changed to busy (on browsers which support this).
- */
- function jscoverage_beginLengthyOperation() {
- jscoverage_inLengthyOperation = true;
- var progressBar = document.getElementById('progressBar');
- progressBar.style.visibility = 'visible';
- ProgressBar.setPercentage(progressBar, 0);
- var progressLabel = document.getElementById('progressLabel');
- progressLabel.style.visibility = 'visible';
- /* blacklist buggy browsers */
- //#JSCOVERAGE_IF
- if (! /Opera|WebKit/.test(navigator.userAgent)) {
- /*
- Change the cursor style of each element. Note that changing the class of the
- element (to one with a busy cursor) is buggy in IE.
- */
- var tabs = document.getElementById('tabs').getElementsByTagName('div');
- var i;
- for (i = 0; i < tabs.length; i++) {
- tabs.item(i).style.cursor = 'wait';
- }
- }
- }
- /**
- Removes the progress bar and busy cursor.
- */
- function jscoverage_endLengthyOperation() {
- var progressBar = document.getElementById('progressBar');
- ProgressBar.setPercentage(progressBar, 100);
- setTimeout(function() {
- jscoverage_inLengthyOperation = false;
- progressBar.style.visibility = 'hidden';
- var progressLabel = document.getElementById('progressLabel');
- progressLabel.style.visibility = 'hidden';
- progressLabel.innerHTML = '';
- var tabs = document.getElementById('tabs').getElementsByTagName('div');
- var i;
- for (i = 0; i < tabs.length; i++) {
- tabs.item(i).style.cursor = '';
- }
- }, 50);
- }
- function jscoverage_setSize() {
- //#JSCOVERAGE_IF /MSIE/.test(navigator.userAgent)
- var viewportHeight = jscoverage_getViewportHeight();
- /*
- border-top-width: 1px
- padding-top: 10px
- padding-bottom: 10px
- border-bottom-width: 1px
- margin-bottom: 10px
- ----
- 32px
- */
- var tabPages = document.getElementById('tabPages');
- var tabPageHeight = (viewportHeight - jscoverage_findPos(tabPages) - 32) + 'px';
- var nodeList = tabPages.childNodes;
- var length = nodeList.length;
- for (var i = 0; i < length; i++) {
- var node = nodeList.item(i);
- if (node.nodeType !== 1) {
- continue;
- }
- node.style.height = tabPageHeight;
- }
- var iframeDiv = document.getElementById('iframeDiv');
- // may not exist if we have removed the first tab
- if (iframeDiv) {
- iframeDiv.style.height = (viewportHeight - jscoverage_findPos(iframeDiv) - 21) + 'px';
- }
- var summaryDiv = document.getElementById('summaryDiv');
- summaryDiv.style.height = (viewportHeight - jscoverage_findPos(summaryDiv) - 21) + 'px';
- var sourceDiv = document.getElementById('sourceDiv');
- sourceDiv.style.height = (viewportHeight - jscoverage_findPos(sourceDiv) - 21) + 'px';
- var storeDiv = document.getElementById('storeDiv');
- if (storeDiv) {
- storeDiv.style.height = (viewportHeight - jscoverage_findPos(storeDiv) - 21) + 'px';
- }
- //#JSCOVERAGE_ENDIF
- }
- /**
- Returns the boolean value of a string. Values 'false', 'f', 'no', 'n', 'off',
- and '0' (upper or lower case) are false.
- @param s the string
- @return a boolean value
- */
- function jscoverage_getBooleanValue(s) {
- s = s.toLowerCase();
- if (s === 'false' || s === 'f' || s === 'no' || s === 'n' || s === 'off' || s === '0') {
- return false;
- }
- return true;
- }
- function jscoverage_removeTab(id) {
- var tab = document.getElementById(id + 'Tab');
- tab.parentNode.removeChild(tab);
- var tabPage = document.getElementById(id + 'TabPage');
- tabPage.parentNode.removeChild(tabPage);
- }
- function jscoverage_isValidURL(url) {
- // RFC 3986
- var matches = /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?/.exec(url);
- if (matches === null) {
- return false;
- }
- var scheme = matches[1];
- if (typeof scheme === 'string') {
- scheme = scheme.toLowerCase();
- return scheme === '' || scheme === 'file:' || scheme === 'http:' || scheme === 'https:';
- }
- return true;
- }
- /**
- Initializes the contents of the tabs. This sets the initial values of the
- input field and iframe in the "Browser" tab and the checkbox in the "Summary"
- tab.
- @param queryString this should always be location.search
- */
- function jscoverage_initTabContents(queryString) {
- var showMissingColumn = false;
- var url = null;
- var windowURL = null;
- var parameters, parameter, i, index, name, value;
- if (queryString.length > 0) {
- // chop off the question mark
- queryString = queryString.substring(1);
- parameters = queryString.split(/&|;/);
- for (i = 0; i < parameters.length; i++) {
- parameter = parameters[i];
- index = parameter.indexOf('=');
- if (index === -1) {
- // still works with old syntax
- url = decodeURIComponent(parameter);
- }
- else {
- name = parameter.substr(0, index);
- value = decodeURIComponent(parameter.substr(index + 1));
- if (name === 'missing' || name === 'm') {
- showMissingColumn = jscoverage_getBooleanValue(value);
- }
- else if (name === 'url' || name === 'u' || name === 'frame' || name === 'f') {
- url = value;
- }
- else if (name === 'window' || name === 'w') {
- windowURL = value;
- }
- }
- }
- }
- var checkbox = document.getElementById('checkbox');
- checkbox.checked = showMissingColumn;
- if (showMissingColumn) {
- jscoverage_appendMissingColumn();
- }
- var isValidURL = function (url) {
- var result = jscoverage_isValidURL(url);
- if (! result) {
- alert('Invalid URL: ' + url);
- }
- return result;
- };
- if (url !== null && isValidURL(url)) {
- // this will automatically propagate to the input field
- frames[0].location = url;
- }
- else if (windowURL !== null && isValidURL(windowURL)) {
- window.open(windowURL);
- }
- // if the browser tab is absent, we have to initialize the summary tab
- if (! document.getElementById('browserTab')) {
- jscoverage_recalculateSummaryTab();
- }
- }
- function jscoverage_body_load() {
- // check if this is a file: URL
- if (window.location && window.location.href && /^file:/i.test(window.location.href)) {
- var warningDiv = document.getElementById('warningDiv');
- warningDiv.style.display = 'block';
- }
- var progressBar = document.getElementById('progressBar');
- ProgressBar.init(progressBar);
- function reportError(e) {
- jscoverage_endLengthyOperation();
- var summaryThrobber = document.getElementById('summaryThrobber');
- summaryThrobber.style.visibility = 'hidden';
- var div = document.getElementById('summaryErrorDiv');
- div.innerHTML = 'Error: ' + e;
- }
- if (jscoverage_isReport) {
- jscoverage_beginLengthyOperation();
- var summaryThrobber = document.getElementById('summaryThrobber');
- summaryThrobber.style.visibility = 'visible';
- var request = jscoverage_createRequest();
- try {
- request.open('GET', 'jscoverage.json', true);
- request.onreadystatechange = function (event) {
- if (request.readyState === 4) {
- try {
- if (request.status !== 0 && request.status !== 200) {
- throw request.status;
- }
- var response = request.responseText;
- if (response === '') {
- throw 404;
- }
- var json;
- if (window.JSON && window.JSON.parse) {
- json = window.JSON.parse(response);
- }
- else {
- json = eval('(' + response + ')');
- }
- var file;
- for (file in json) {
- if (! json.hasOwnProperty(file)) {
- continue;
- }
- var fileCoverage = json[file];
- _$jscoverage[file] = fileCoverage.coverage;
- _$jscoverage[file].source = fileCoverage.source;
- }
- jscoverage_recalculateSummaryTab();
- summaryThrobber.style.visibility = 'hidden';
- }
- catch (e) {
- reportError(e);
- }
- }
- };
- request.send(null);
- }
- catch (e) {
- reportError(e);
- }
- jscoverage_removeTab('browser');
- jscoverage_removeTab('store');
- }
- else {
- if (jscoverage_isInvertedMode) {
- jscoverage_removeTab('browser');
- }
- if (! jscoverage_isServer) {
- jscoverage_removeTab('store');
- }
- }
- jscoverage_initTabControl();
- jscoverage_initTabContents(location.search);
- }
- function jscoverage_body_resize() {
- if (/MSIE/.test(navigator.userAgent)) {
- jscoverage_setSize();
- }
- }
- // -----------------------------------------------------------------------------
- // tab 1
- function jscoverage_updateBrowser() {
- var input = document.getElementById("location");
- frames[0].location = input.value;
- }
- function jscoverage_openWindow() {
- var input = document.getElementById("location");
- var url = input.value;
- window.open(url);
- }
- function jscoverage_input_keypress(e) {
- if (e.keyCode === 13) {
- if (e.shiftKey) {
- jscoverage_openWindow();
- }
- else {
- jscoverage_updateBrowser();
- }
- }
- }
- function jscoverage_openInFrameButton_click() {
- jscoverage_updateBrowser();
- }
- function jscoverage_openInWindowButton_click() {
- jscoverage_openWindow();
- }
- function jscoverage_browser_load() {
- /* update the input box */
- var input = document.getElementById("location");
- /* sometimes IE seems to fire this after the tab has been removed */
- if (input) {
- input.value = frames[0].location;
- }
- }
- // -----------------------------------------------------------------------------
- // tab 2
- function jscoverage_createHandler(file, line) {
- return function () {
- jscoverage_get(file, line);
- return false;
- };
- }
- function jscoverage_createLink(file, line) {
- var link = document.createElement("a");
- link.href = '#';
- link.onclick = jscoverage_createHandler(file, line);
- var text;
- if (line) {
- text = line.toString();
- }
- else {
- text = file;
- }
- link.appendChild(document.createTextNode(text));
- return link;
- }
- function jscoverage_recalculateSummaryTab(cc) {
- var checkbox = document.getElementById('checkbox');
- var showMissingColumn = checkbox.checked;
- if (! cc) {
- cc = window._$jscoverage;
- }
- if (! cc) {
- //#JSCOVERAGE_IF 0
- throw "No coverage information found.";
- //#JSCOVERAGE_ENDIF
- }
- var tbody = document.getElementById("summaryTbody");
- while (tbody.hasChildNodes()) {
- tbody.removeChild(tbody.firstChild);
- }
- var totals = { files:0, statements:0, executed:0 };
- var file;
- var files = [];
- for (file in cc) {
- if (! cc.hasOwnProperty(file)) {
- continue;
- }
- files.push(file);
- }
- files.sort();
- var rowCounter = 0;
- for (var f = 0; f < files.length; f++) {
- file = files[f];
- var lineNumber;
- var num_statements = 0;
- var num_executed = 0;
- var missing = [];
- var fileCC = cc[file];
- var length = fileCC.length;
- var currentConditionalEnd = 0;
- var conditionals = null;
- if (fileCC.conditionals) {
- conditionals = fileCC.conditionals;
- }
- for (lineNumber = 0; lineNumber < length; lineNumber++) {
- var n = fileCC[lineNumber];
- if (lineNumber === currentConditionalEnd) {
- currentConditionalEnd = 0;
- }
- else if (currentConditionalEnd === 0 && conditionals && conditionals[lineNumber]) {
- currentConditionalEnd = conditionals[lineNumber];
- }
- if (currentConditionalEnd !== 0) {
- continue;
- }
- if (n === undefined || n === null) {
- continue;
- }
- if (n === 0) {
- missing.push(lineNumber);
- }
- else {
- num_executed++;
- }
- num_statements++;
- }
- var percentage = ( num_statements === 0 ? 0 : parseInt(100 * num_executed / num_statements) );
- var row = document.createElement("tr");
- row.className = ( rowCounter++ % 2 == 0 ? "odd" : "even" );
- var cell = document.createElement("td");
- cell.className = 'leftColumn';
- var link = jscoverage_createLink(file);
- cell.appendChild(link);
- row.appendChild(cell);
- cell = document.createElement("td");
- cell.className = 'numeric';
- cell.appendChild(document.createTextNode(num_statements));
- row.appendChild(cell);
- cell = document.createElement("td");
- cell.className = 'numeric';
- cell.appendChild(document.createTextNode(num_executed));
- row.appendChild(cell);
- // new coverage td containing a bar graph
- cell = document.createElement("td");
- cell.className = 'coverage';
- var pctGraph = document.createElement("div"),
- covered = document.createElement("div"),
- pct = document.createElement("span");
- pctGraph.className = "pctGraph";
- if( num_statements === 0 ) {
- covered.className = "skipped";
- pct.appendChild(document.createTextNode("N/A"));
- } else {
- covered.className = "covered";
- covered.style.width = percentage + "px";
- pct.appendChild(document.createTextNode(percentage + '%'));
- }
- pct.className = "pct";
- pctGraph.appendChild(covered);
- cell.appendChild(pctGraph);
- cell.appendChild(pct);
- row.appendChild(cell);
- if (showMissingColumn) {
- cell = document.createElement("td");
- for (var i = 0; i < missing.length; i++) {
- if (i !== 0) {
- cell.appendChild(document.createTextNode(", "));
- }
- link = jscoverage_createLink(file, missing[i]);
- // group contiguous missing lines; e.g., 10, 11, 12 -> 10-12
- var j, start = missing[i];
- for (;;) {
- j = 1;
- while (i + j < missing.length && missing[i + j] == missing[i] + j) {
- j++;
- }
- var nextmissing = missing[i + j], cur = missing[i] + j;
- if (isNaN(nextmissing)) {
- break;
- }
- while (cur < nextmissing && ! fileCC[cur]) {
- cur++;
- }
- if (cur < nextmissing || cur >= length) {
- break;
- }
- i += j;
- }
- if (start != missing[i] || j > 1) {
- i += j - 1;
- link.innerHTML += "-" + missing[i];
- }
- cell.appendChild(link);
- }
- row.appendChild(cell);
- }
- tbody.appendChild(row);
- totals['files'] ++;
- totals['statements'] += num_statements;
- totals['executed'] += num_executed;
- // write totals data into summaryTotals row
- var tr = document.getElementById("summaryTotals");
- if (tr) {
- var tds = tr.getElementsByTagName("td");
- tds[0].getElementsByTagName("span")[1].firstChild.nodeValue = totals['files'];
- tds[1].firstChild.nodeValue = totals['statements'];
- tds[2].firstChild.nodeValue = totals['executed'];
- var coverage = parseInt(100 * totals['executed'] / totals['statements']);
- if( isNaN( coverage ) ) {
- coverage = 0;
- }
- tds[3].getElementsByTagName("span")[0].firstChild.nodeValue = coverage + '%';
- tds[3].getElementsByTagName("div")[1].style.width = coverage + 'px';
- }
- }
- jscoverage_endLengthyOperation();
- }
- function jscoverage_appendMissingColumn() {
- var headerRow = document.getElementById('headerRow');
- var missingHeader = document.createElement('th');
- missingHeader.id = 'missingHeader';
- missingHeader.innerHTML = '<abbr title="List of statements missed during execution">Missing</abbr>';
- headerRow.appendChild(missingHeader);
- var summaryTotals = document.getElementById('summaryTotals');
- var empty = document.createElement('td');
- empty.id = 'missingCell';
- summaryTotals.appendChild(empty);
- }
- function jscoverage_removeMissingColumn() {
- var missingNode;
- missingNode = document.getElementById('missingHeader');
- missingNode.parentNode.removeChild(missingNode);
- missingNode = document.getElementById('missingCell');
- missingNode.parentNode.removeChild(missingNode);
- }
- function jscoverage_checkbox_click() {
- if (jscoverage_inLengthyOperation) {
- return false;
- }
- jscoverage_beginLengthyOperation();
- var checkbox = document.getElementById('checkbox');
- var showMissingColumn = checkbox.checked;
- setTimeout(function() {
- if (showMissingColumn) {
- jscoverage_appendMissingColumn();
- }
- else {
- jscoverage_removeMissingColumn();
- }
- jscoverage_recalculateSummaryTab();
- }, 50);
- return true;
- }
- // -----------------------------------------------------------------------------
- // tab 3
- function jscoverage_makeTable() {
- var coverage = _$jscoverage[jscoverage_currentFile];
- var lines = coverage.source;
- // this can happen if there is an error in the original JavaScript file
- if (! lines) {
- lines = [];
- }
- var rows = ['<table id="sourceTable">'];
- var i = 0;
- var progressBar = document.getElementById('progressBar');
- var tableHTML;
- var currentConditionalEnd = 0;
- function joinTableRows() {
- tableHTML = rows.join('');
- ProgressBar.setPercentage(progressBar, 60);
- /*
- This may be a long delay, so set a timeout of 100 ms to make sure the
- display is updated.
- */
- setTimeout(appendTable, 100);
- }
- function appendTable() {
- var sourceDiv = document.getElementById('sourceDiv');
- sourceDiv.innerHTML = tableHTML;
- ProgressBar.setPercentage(progressBar, 80);
- setTimeout(jscoverage_scrollToLine, 0);
- }
- while (i < lines.length) {
- var lineNumber = i + 1;
- if (lineNumber === currentConditionalEnd) {
- currentConditionalEnd = 0;
- }
- else if (currentConditionalEnd === 0 && coverage.conditionals && coverage.conditionals[lineNumber]) {
- currentConditionalEnd = coverage.conditionals[lineNumber];
- }
- var row = '<tr>';
- row += '<td class="numeric">' + lineNumber + '</td>';
- var timesExecuted = coverage[lineNumber];
- if (timesExecuted !== undefined && timesExecuted !== null) {
- if (currentConditionalEnd !== 0) {
- row += '<td class="y numeric">';
- }
- else if (timesExecuted === 0) {
- row += '<td class="r numeric" id="line-' + lineNumber + '">';
- }
- else {
- row += '<td class="g numeric">';
- }
- row += timesExecuted;
- row += '</td>';
- }
- else {
- row += '<td></td>';
- }
- row += '<td><pre>' + lines[i] + '</pre></td>';
- row += '</tr>';
- row += '\n';
- rows[lineNumber] = row;
- i++;
- }
- rows[i + 1] = '</table>';
- ProgressBar.setPercentage(progressBar, 40);
- setTimeout(joinTableRows, 0);
- }
- function jscoverage_scrollToLine() {
- jscoverage_selectTab('sourceTab');
- if (! window.jscoverage_currentLine) {
- jscoverage_endLengthyOperation();
- return;
- }
- var div = document.getElementById('sourceDiv');
- if (jscoverage_currentLine === 1) {
- div.scrollTop = 0;
- }
- else {
- var cell = document.getElementById('line-' + jscoverage_currentLine);
- // this might not be there if there is an error in the original JavaScript
- if (cell) {
- var divOffset = jscoverage_findPos(div);
- var cellOffset = jscoverage_findPos(cell);
- div.scrollTop = cellOffset - divOffset;
- }
- }
- jscoverage_currentLine = 0;
- jscoverage_endLengthyOperation();
- }
- /**
- Loads the given file (and optional line) in the source tab.
- */
- function jscoverage_get(file, line) {
- if (jscoverage_inLengthyOperation) {
- return;
- }
- jscoverage_beginLengthyOperation();
- setTimeout(function() {
- var sourceDiv = document.getElementById('sourceDiv');
- sourceDiv.innerHTML = '';
- jscoverage_selectTab('sourceTab');
- if (file === jscoverage_currentFile) {
- jscoverage_currentLine = line;
- jscoverage_recalculateSourceTab();
- }
- else {
- if (jscoverage_currentFile === null) {
- var tab = document.getElementById('sourceTab');
- tab.className = '';
- tab.onclick = jscoverage_tab_click;
- }
- jscoverage_currentFile = file;
- jscoverage_currentLine = line || 1; // when changing the source, always scroll to top
- var fileDiv = document.getElementById('fileDiv');
- fileDiv.innerHTML = jscoverage_currentFile;
- jscoverage_recalculateSourceTab();
- return;
- }
- }, 50);
- }
- /**
- Calculates coverage statistics for the current source file.
- */
- function jscoverage_recalculateSourceTab() {
- if (! jscoverage_currentFile) {
- jscoverage_endLengthyOperation();
- return;
- }
- var progressLabel = document.getElementById('progressLabel');
- progressLabel.innerHTML = 'Calculating coverage ...';
- var progressBar = document.getElementById('progressBar');
- ProgressBar.setPercentage(progressBar, 20);
- setTimeout(jscoverage_makeTable, 0);
- }
- // -----------------------------------------------------------------------------
- // tabs
- /**
- Initializes the tab control. This function must be called when the document is
- loaded.
- */
- function jscoverage_initTabControl() {
- var tabs = document.getElementById('tabs');
- var i;
- var child;
- var tabNum = 0;
- for (i = 0; i < tabs.childNodes.length; i++) {
- child = tabs.childNodes.item(i);
- if (child.nodeType === 1) {
- if (child.className !== 'disabled') {
- child.onclick = jscoverage_tab_click;
- }
- tabNum++;
- }
- }
- jscoverage_selectTab(0);
- }
- /**
- Selects a tab.
- @param tab the integer index of the tab (0, 1, 2, or 3)
- OR
- the ID of the tab element
- OR
- the tab element itself
- */
- function jscoverage_selectTab(tab) {
- if (typeof tab !== 'number') {
- tab = jscoverage_tabIndexOf(tab);
- }
- var tabs = document.getElementById('tabs');
- var tabPages = document.getElementById('tabPages');
- var nodeList;
- var tabNum;
- var i;
- var node;
- nodeList = tabs.childNodes;
- tabNum = 0;
- for (i = 0; i < nodeList.length; i++) {
- node = nodeList.item(i);
- if (node.nodeType !== 1) {
- continue;
- }
- if (node.className !== 'disabled') {
- if (tabNum === tab) {
- node.className = 'selected';
- }
- else {
- node.className = '';
- }
- }
- tabNum++;
- }
- nodeList = tabPages.childNodes;
- tabNum = 0;
- for (i = 0; i < nodeList.length; i++) {
- node = nodeList.item(i);
- if (node.nodeType !== 1) {
- continue;
- }
- if (tabNum === tab) {
- node.className = 'selected TabPage';
- }
- else {
- node.className = 'TabPage';
- }
- tabNum++;
- }
- }
- /**
- Returns an integer (0, 1, 2, or 3) representing the index of a given tab.
- @param tab the ID of the tab element
- OR
- the tab element itself
- */
- function jscoverage_tabIndexOf(tab) {
- if (typeof tab === 'string') {
- tab = document.getElementById(tab);
- }
- var tabs = document.getElementById('tabs');
- var i;
- var child;
- var tabNum = 0;
- for (i = 0; i < tabs.childNodes.length; i++) {
- child = tabs.childNodes.item(i);
- if (child.nodeType === 1) {
- if (child === tab) {
- return tabNum;
- }
- tabNum++;
- }
- }
- //#JSCOVERAGE_IF 0
- throw "Tab not found";
- //#JSCOVERAGE_ENDIF
- }
- function jscoverage_tab_click(e) {
- if (jscoverage_inLengthyOperation) {
- return;
- }
- var target;
- //#JSCOVERAGE_IF
- if (e) {
- target = e.target;
- }
- else if (window.event) {
- // IE
- target = window.event.srcElement;
- }
- if (target.className === 'selected') {
- return;
- }
- jscoverage_beginLengthyOperation();
- setTimeout(function() {
- if (target.id === 'summaryTab') {
- var tbody = document.getElementById("summaryTbody");
- while (tbody.hasChildNodes()) {
- tbody.removeChild(tbody.firstChild);
- }
- }
- else if (target.id === 'sourceTab') {
- var sourceDiv = document.getElementById('sourceDiv');
- sourceDiv.innerHTML = '';
- }
- jscoverage_selectTab(target);
- if (target.id === 'summaryTab') {
- jscoverage_recalculateSummaryTab();
- }
- else if (target.id === 'sourceTab') {
- jscoverage_recalculateSourceTab();
- }
- else {
- jscoverage_endLengthyOperation();
- }
- }, 50);
- }
- // -----------------------------------------------------------------------------
- // progress bar
- var ProgressBar = {
- init: function(element) {
- element._percentage = 0;
- /* doing this via JavaScript crashes Safari */
- /*
- var pctGraph = document.createElement('div');
- pctGraph.className = 'pctGraph';
- element.appendChild(pctGraph);
- var covered = document.createElement('div');
- covered.className = 'covered';
- pctGraph.appendChild(covered);
- var pct = document.createElement('span');
- pct.className = 'pct';
- element.appendChild(pct);
- */
- ProgressBar._update(element);
- },
- setPercentage: function(element, percentage) {
- element._percentage = percentage;
- ProgressBar._update(element);
- },
- _update: function(element) {
- var pctGraph = element.getElementsByTagName('div').item(0);
- var covered = pctGraph.getElementsByTagName('div').item(0);
- var pct = element.getElementsByTagName('span').item(0);
- pct.innerHTML = element._percentage.toString() + '%';
- covered.style.width = element._percentage + 'px';
- }
- };
- // -----------------------------------------------------------------------------
- // reports
- function jscoverage_pad(s) {
- return '0000'.substr(s.length) + s;
- }
- function jscoverage_quote(s) {
- return '"' + s.replace(/[\u0000-\u001f"\\\u007f-\uffff]/g, function (c) {
- switch (c) {
- case '\b':
- return '\\b';
- case '\f':
- return '\\f';
- case '\n':
- return '\\n';
- case '\r':
- return '\\r';
- case '\t':
- return '\\t';
- // IE doesn't support this
- /*
- case '\v':
- return '\\v';
- */
- case '"':
- return '\\"';
- case '\\':
- return '\\\\';
- default:
- return '\\u' + jscoverage_pad(c.charCodeAt(0).toString(16));
- }
- }) + '"';
- }
- function jscoverage_serializeCoverageToJSON() {
- var json = [];
- for (var file in _$jscoverage) {
- if (! _$jscoverage.hasOwnProperty(file)) {
- continue;
- }
- var coverage = _$jscoverage[file];
- var array = [];
- var length = coverage.length;
- for (var line = 0; line < length; line++) {
- var value = coverage[line];
- if (value === undefined || value === null) {
- value = 'null';
- }
- array.push(value);
- }
- var source = coverage.source;
- var lines = [];
- length = source.length;
- for (var line = 0; line < length; line++) {
- lines.push(jscoverage_quote(source[line]));
- }
- json.push(jscoverage_quote(file) + ':{"coverage":[' + array.join(',') + '],"source":[' + lines.join(',') + ']}');
- }
- return '{' + json.join(',') + '}';
- }
- function jscoverage_storeButton_click() {
- if (jscoverage_inLengthyOperation) {
- return;
- }
- jscoverage_beginLengthyOperation();
- var img = document.getElementById('storeImg');
- img.style.visibility = 'visible';
- var request = jscoverage_createRequest();
- request.open('POST', '/jscoverage-store', true);
- request.onreadystatechange = function (event) {
- if (request.readyState === 4) {
- var message;
- try {
- if (request.status !== 200 && request.status !== 201 && request.status !== 204) {
- throw request.status;
- }
- message = request.responseText;
- }
- catch (e) {
- if (e.toString().search(/^\d{3}$/) === 0) {
- message = e + ': ' + request.responseText;
- }
- else {
- message = 'Could not connect to server: ' + e;
- }
- }
- jscoverage_endLengthyOperation();
- var img = document.getElementById('storeImg');
- img.style.visibility = 'hidden';
- var div = document.getElementById('storeDiv');
- div.appendChild(document.createTextNode(new Date() + ': ' + message));
- div.appendChild(document.createElement('br'));
- }
- };
- request.setRequestHeader('Content-Type', 'application/json');
- var json = jscoverage_serializeCoverageToJSON();
- request.setRequestHeader('Content-Length', json.length.toString());
- request.send(json);
- }
|