text: function BranchData() {
      this.position = -1;
      this.nodeLength = -1;
      this.src = null;
      this.evalFalse = 0;
      this.evalTrue = 0;
  
      this.init = function(position, nodeLength, src) {
          this.position = position;
          this.nodeLength = nodeLength;
          this.src = src;
          return this;
      }
  
      this.ranCondition = function(result) {
          if (result)
              this.evalTrue++;
          else
              this.evalFalse++;
      };
  
      this.pathsCovered = function() {
          var paths = 0;
          if (this.evalTrue > 0)
            paths++;
          if (this.evalFalse > 0)
            paths++;
          return paths;
      };
  
      this.covered = function() {
          return this.evalTrue > 0 && this.evalFalse > 0;
      };
  
      this.toJSON = function() {
          return '{"position":' + this.position
              + ',"nodeLength":' + this.nodeLength
              + ',"src":' + jscoverage_quote(this.src)
              + ',"evalFalse":' + this.evalFalse
              + ',"evalTrue":' + this.evalTrue + '}';
      };
  
      this.message = function() {
          if (this.evalTrue === 0 && this.evalFalse === 0)
              return 'Condition never evaluated         :\t' + this.src;
          else if (this.evalTrue === 0)
              return 'Condition never evaluated to true :\t' + this.src;
          else if (this.evalFalse === 0)
              return 'Condition never evaluated to false:\t' + this.src;
          else
              return 'Condition covered';
      };
  }
  
  BranchData.fromJson = function(jsonString) {
      var json = eval('(' + jsonString + ')');
      var branchData = new BranchData();
      branchData.init(json.position, json.nodeLength, json.src);
      branchData.evalFalse = json.evalFalse;
      branchData.evalTrue = json.evalTrue;
      return branchData;
  };
  
  BranchData.fromJsonObject = function(json) {
      var branchData = new BranchData();
      branchData.init(json.position, json.nodeLength, json.src);
      branchData.evalFalse = json.evalFalse;
      branchData.evalTrue = json.evalTrue;
      return branchData;
  };
  
  function buildBranchMessage(conditions) {
      var message = 'The following was not covered:';
      for (var i = 0; i < conditions.length; i++) {
          if (conditions[i] !== undefined && conditions[i] !== null && !conditions[i].covered())
            message += '\n- '+ conditions[i].message();
      }
      return message;
  };
  
  function convertBranchDataConditionArrayToJSON(branchDataConditionArray) {
      var array = [];
      var length = branchDataConditionArray.length;
      for (var condition = 0; condition < length; condition++) {
          var branchDataObject = branchDataConditionArray[condition];
          if (branchDataObject === undefined || branchDataObject === null) {
              value = 'null';
          } else {
              value = branchDataObject.toJSON();
          }
          array.push(value);
      }
      return '[' + array.join(',') + ']';
  }
  
  function convertBranchDataLinesToJSON(branchData) {
      if (branchData === undefined) {
          return '{}'
      }
      var json = '';
      for (var line in branchData) {
          if (isNaN(line))
              continue;
          if (json !== '')
              json += ','
          json += '"' + line + '":' + convertBranchDataConditionArrayToJSON(branchData[line]);
      }
      return '{' + json + '}';
  }
  
  function convertBranchDataLinesFromJSON(jsonObject) {
      if (jsonObject === undefined) {
          return {};
      }
      for (var line in jsonObject) {
          var branchDataJSON = jsonObject[line];
          if (branchDataJSON !== null) {
              for (var conditionIndex = 0; conditionIndex < branchDataJSON.length; conditionIndex ++) {
                  var condition = branchDataJSON[conditionIndex];
                  if (condition !== null) {
                      branchDataJSON[conditionIndex] = BranchData.fromJsonObject(condition);
                  }
              }
          }
      }
      return jsonObject;
  }
  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 getArrayJSON(coverage) {
      var array = [];
      if (coverage === undefined)
          return 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);
      }
      return array;
  }
  
  function jscoverage_serializeCoverageToJSON() {
      var json = [];
      for (var file in _$jscoverage) {
          var lineArray = getArrayJSON(_$jscoverage[file].lineData);
          var fnArray = getArrayJSON(_$jscoverage[file].functionData);
  
          json.push(jscoverage_quote(file) + ':{"lineData":[' + lineArray.join(',') + '],"functionData":[' + fnArray.join(',') + '],"branchData":' + convertBranchDataLinesToJSON(_$jscoverage[file].branchData) + '}');
      }
      return '{' + json.join(',') + '}';
  }
  
  function jscoverage_parseCoverageJSON(data) {
      var result = {};
      var json = eval('(' + data + ')');
      var file;
      for (file in json) {
          var fileCoverage = json[file];
          result[file] = {};
          result[file].lineData = fileCoverage.lineData;
          result[file].functionData = fileCoverage.functionData;
          result[file].branchData = convertBranchDataLinesFromJSON(fileCoverage.branchData);
      }
      return result;
  }
  
  function jscoverage_pad(s) {
      return '0000'.substr(s.length) + s;
  }
  
  function jscoverage_html_escape(s) {
      return s.replace(/[<>\&\"\']/g, function (c) {
          return '&#' + c.charCodeAt(0) + ';';
      });
  }
  /*
      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.
  */
  
  /**
  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 = '<span class="fadeToBackground">Done</span>';
  
      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');
    if(tab){
        tab.parentNode.removeChild(tab);
    }
    var tabPage = document.getElementById(id + 'TabPage');
    if(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() {
    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;
              }
              _$jscoverage = jscoverage_parseCoverageJSON(response);
              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,'jscoverage_window');
  }
  
  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 = '#'+file;
    link.onclick = jscoverage_createHandler(file, line);
  
    var text;
    if (line) {
      text = line.toString();
    }
    else {
      text = file;
    }
  
    link.appendChild(document.createTextNode(text));
  
    return link;
  }
  
  var sortOrder = 0;
  var sortColumn = 'Coverage';
  var sortReOrder = true;
  var sortedFiles = null;
  
  function jscoverage_recalculateSummaryTabBy(type) {
    sortReOrder = true;
    if (sortColumn !== type)
      sortOrder = 1;
    sortColumn = type;
    jscoverage_recalculateSummaryTab(null);
  }
  
  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 totals = { files:0, statements:0, executed:0, branches:0, branches_covered:0 , functions:0, functions_covered:0 };
  
    var file;
    var files = [];
    for (file in cc) {
      files.push(file);
    }
    if (files.length === 0)
      return;
  
    if (sortReOrder || files.length != sortedFiles.length) {
      sortedFiles = getFilesSortedByCoverage(files);
      sortOrder++;
      sortReOrder = false;
    }
    files = sortedFiles;
  
    var tbody = document.getElementById("summaryTbody");
    while (tbody.hasChildNodes()) {
      tbody.removeChild(tbody.firstChild);
    }
  
  
    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].lineData;
      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 num_functions = 0;
      var num_executed_functions = 0;
      var fileFunctionCC = cc[file].functionData;
      if (fileFunctionCC) {
          num_functions += fileFunctionCC.length;
          for (var fnNumber = 0; fnNumber < fileFunctionCC.length; fnNumber++) {
              var fnHits = fileFunctionCC[fnNumber];
              if (fnHits !== undefined && fnHits !== null && fnHits > 0)
                  num_executed_functions++;
          }
      }
      var percentageFn = ( num_functions === 0 ? 0 : parseInt(100 * num_executed_functions / num_functions));
  
      var num_branches = 0;
      var num_executed_branches = 0;
      var fileBranchCC = cc[file].branchData;
      if (fileBranchCC) {
          for (var lineNumber in fileBranchCC) {
            var conditions = fileBranchCC[lineNumber];
            var covered = undefined;
            if (conditions !== undefined && conditions !== null && conditions.length) {
                covered = true;
                for (var conditionIndex = 0; conditionIndex < conditions.length; conditionIndex++) {
                    var branchData = fileBranchCC[lineNumber][conditionIndex];
                    if (branchData === undefined || branchData === null)
                      continue;
                    num_branches += 2;
                    num_executed_branches += branchData.pathsCovered();
                    if (!branchData.covered()) {
                        covered = false;
                    }
                }
            }
          }
          var percentageBranch = ( num_branches === 0 ? 0 : parseInt(100 * num_executed_branches / num_branches));
      }
  
  
      var row = document.createElement("tr");
      row.className = ( rowCounter++ % 2 == 0 ? "odd" : "even" );
  
      var cell = document.createElement("td");
      row.id = "row-"+file;
      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);
  
      cell = document.createElement("td");
      cell.className = 'numeric';
      cell.appendChild(document.createTextNode(num_branches));
      row.appendChild(cell);
  
      cell = document.createElement("td");
      cell.className = 'numeric';
      cell.appendChild(document.createTextNode(num_executed_branches));
      row.appendChild(cell);
  
      cell = document.createElement("td");
      cell.className = 'numeric';
      cell.appendChild(document.createTextNode(num_functions));
      row.appendChild(cell);
  
      cell = document.createElement("td");
      cell.className = 'numeric';
      cell.appendChild(document.createTextNode(num_executed_functions));
      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);
  
      // new coverage td containing a branch bar graph
      cell = document.createElement("td");
      cell.className = 'coverage';
      pctGraph = document.createElement("div"),
          covered = document.createElement("div"),
          pct = document.createElement("span");
      pctGraph.className = "pctGraph";
      if(fileBranchCC === undefined || num_branches === 0 ) {
          covered.className = "skipped";
          pct.appendChild(document.createTextNode("N/A"));
      } else {
          covered.className = "covered";
          covered.style.width = percentageBranch + "px";
          pct.appendChild(document.createTextNode(percentageBranch + '%'));
      }
      pct.className = "pct";
      pctGraph.appendChild(covered);
      cell.appendChild(pctGraph);
      cell.appendChild(pct);
      row.appendChild(cell);
  
      // new coverage td containing a function bar graph
      cell = document.createElement("td");
      cell.className = 'coverage';
      pctGraph = document.createElement("div"),
          covered = document.createElement("div"),
          pct = document.createElement("span");
      pctGraph.className = "pctGraph";
      if(fileFunctionCC === undefined || num_functions === 0 ) {
          covered.className = "skipped";
          pct.appendChild(document.createTextNode("N/A"));
      } else {
          covered.className = "covered";
          covered.style.width = percentageFn + "px";
          pct.appendChild(document.createTextNode(percentageFn + '%'));
      }
      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;
      totals['branches'] += num_branches;
      totals['branches_covered'] += num_executed_branches;
      totals['functions'] += num_functions;
      totals['functions_covered'] += num_executed_functions;
  
      // 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'];
          tds[3].firstChild.nodeValue = totals['branches'];
          tds[4].firstChild.nodeValue = totals['branches_covered'];
          tds[5].firstChild.nodeValue = totals['functions'];
          tds[6].firstChild.nodeValue = totals['functions_covered'];
  
          var coverage = parseInt(100 * totals['executed'] / totals['statements']);
          if( isNaN( coverage ) ) {
              coverage = 0;
          }
          tds[7].getElementsByTagName("span")[0].firstChild.nodeValue = coverage + '%';
          tds[7].getElementsByTagName("div")[1].style.width = coverage + 'px';
  
          coverage = 0;
          if (fileBranchCC !== undefined) {
              coverage = parseInt(100 * totals['branches_covered'] / totals['branches']);
              if( isNaN( coverage ) ) {
                  coverage = 0;
              }
          }
          tds[8].getElementsByTagName("span")[0].firstChild.nodeValue = coverage + '%';
          tds[8].getElementsByTagName("div")[1].style.width = coverage + 'px';
  
          coverage = 0;
          if (fileFunctionCC !== undefined) {
              coverage = parseInt(100 * totals['functions_covered'] / totals['functions']);
              if( isNaN( coverage ) ) {
                  coverage = 0;
              }
          }
          tds[9].getElementsByTagName("span")[0].firstChild.nodeValue = coverage + '%';
          tds[9].getElementsByTagName("div")[1].style.width = coverage + 'px';
      }
  
    }
    jscoverage_endLengthyOperation();
  }
  
  function getFilesSortedByCoverage(filesIn) {
    var tbody = document.getElementById("summaryTbody");
    if (tbody.children.length < 2) {
      sortOrder=1;
      return filesIn;
    }
    var files = [];
    for (var i=0;i<tbody.children.length;i++) {
      files[i] = {};
    	files[i].file = tbody.children[i].children[0].children[0].innerHTML;
    	files[i].perc = parseInt(tbody.children[i].children[7].children[1].innerHTML, 10);
    	files[i].brPerc = parseInt(tbody.children[i].children[8].children[1].innerHTML, 10);
    	files[i].fnPerc = parseInt(tbody.children[i].children[9].children[1].innerHTML, 10);
      if (isNaN(files[i].brPerc))
        files[i].brPerc = -1;
      if (isNaN(files[i].fnPerc))
        files[i].fnPerc = -1;
    }
  
    if (sortOrder%3===1) {
      if (sortColumn == 'Coverage')
        files.sort(function(file1,file2) {return file1.perc-file2.perc});
      else if (sortColumn == 'Branch')
        files.sort(function(file1,file2) {return file1.brPerc-file2.brPerc});
      else if (sortColumn == 'Function')
        files.sort(function(file1,file2) {return file1.fnPerc-file2.fnPerc});
      else
        files.sort(function(file1,file2) {return file1.file>=file2.file});
    } else if (sortOrder%3===2) {
       if (sortColumn == 'Coverage')
        files.sort(function(file1,file2) {return file2.perc-file1.perc});
       else if (sortColumn == 'Branch')
         files.sort(function(file1,file2) {return file2.brPerc-file1.brPerc});
       else if (sortColumn == 'Function')
         files.sort(function(file1,file2) {return file2.fnPerc-file1.fnPerc});
       else
         files.sort(function(file1,file2) {return file2.file>=file1.file});
    } else {
        return filesIn.sort();
    }
    var result = [];
    for (var i=0;i<files.length;i++) {
      result[i] = files[i].file;
    }
    return result;
  }
  
  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(lines) {
    var coverage = _$jscoverage[jscoverage_currentFile].lineData;
    var branchData = _$jscoverage[jscoverage_currentFile].branchData;
  
    // 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(function() {appendTable(jscoverage_currentFile);}, 100);
    }
  
    function appendTable(jscoverage_currentFile) {
      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>';
      }
  
      lineNumber = '' + lineNumber;
      if (branchData !== undefined) {
          var branchClass = '';
          var branchText = '&#160;';
          if (branchData[lineNumber] !== undefined && branchData[lineNumber] !== null) {
              branchClass = 'g';
              for (var conditionIndex = 0; conditionIndex < branchData[lineNumber].length; conditionIndex++) {
                  if (branchData[lineNumber][conditionIndex] !== undefined && branchData[lineNumber][conditionIndex] !== null && !branchData[lineNumber][conditionIndex].covered()) {
                      branchClass = 'r';
                      break;
                  }
              }
  
          }
          if (branchClass === 'r') {
              branchText = '<a href="#" onclick="alert(buildBranchMessage(_$jscoverage[\''+jscoverage_currentFile+'\'].branchData[\''+lineNumber+'\']));">info</a>';
          }
          row += '<td class="numeric '+branchClass+'"><pre>' + branchText + '</pre></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);
    var request = jscoverage_createRequest();
    try {
      var relativeUrl = jscoverage_currentFile;
      if (relativeUrl.charAt(0) !== '/')
        relativeUrl = '/' + relativeUrl;
      if (!jscoverage_isServer)
        relativeUrl = 'original-src' + relativeUrl;
      request.open('GET', relativeUrl, true);
      request.setRequestHeader("NoInstrument", "true");
      request.onreadystatechange = function (event) {
        if (request.readyState === 4) {
          try {
            if (request.status !== 0 && request.status !== 200) {
              throw request.status;
            }
            var response = request.responseText;
            var displaySource = function() {
                var lines = response.split(/\n/);
                for (var i = 0; i < lines.length; i++)
                    lines[i] = jscoverage_html_escape(lines[i]);
                jscoverage_makeTable(lines);
            }
            setTimeout(displaySource, 0);
            summaryThrobber.style.visibility = 'hidden';
          }
          catch (e) {
            reportError(e);
          }
        }
      };
      request.send(null);
    }
    catch (e) {
      reportError(e);
    }
  }
  
  // -----------------------------------------------------------------------------
  // 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_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');
    request.send(jscoverage_serializeCoverageToJSON());
  }
  
  function jscoverage_stopButton_click() {
    if (jscoverage_inLengthyOperation) {
      return;
    }
  
    jscoverage_beginLengthyOperation();
    var img = document.getElementById('storeImg');
    img.style.visibility = 'visible';
  
    var request = jscoverage_createRequest();
    request.open('GET', '/stop', 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.send();
  }
  
  jscoverage_isReport = true;