// SortedTable created and licensed by fry @ friedcellcollective
// http://friedcellcollective.net/js/
// This work is licensed under a Creative Commons Attribution-ShareAlike 2.5 License (http://creativecommons.org/licenses/by-sa/2.5/)
// Version: 0.8f

// Initialize the most recent sorted column to jurisdiction
var prvSortedCol = document.getElementById("Col1");
var myTable = null;

function addEvent(obj, type, fn)
{
    if (obj.addEventListener) obj.addEventListener(type, fn, false);
    else if (obj.attachEvent)
    {
        obj["e" + type + fn] = fn;
        obj[type + fn] = function() { obj["e" + type + fn](window.event); }
        obj.attachEvent("on" + type, obj[type + fn]);
    }
}

function removeEvent(obj, type, fn)
{
    if (obj.removeEventListener) obj.removeEventListener(type, fn, false);
    else if (obj.detachEvent)
    {
        obj.detachEvent("on" + type, obj[type + fn]);
        obj[type + fn] = null;
        obj["e" + type + fn] = null;
    }
}

function selRowClick(callObj)
{
    // Select a row via a mouse click - for Section 508 support.
    // Select national public as the default selected row
    // When the sorted table object is first defined.
    var colObj = null;
    myTable.cleanselect();
    for (var row = 0; row < myTable.elements.length; row++)
    {
        colObj = returnColObject("Col1", myTable.elements[row]);
        if (getInnerText(colObj) == getInnerText(callObj))
        {
            myTable.select(myTable.elements[row]);
            break;
        }
    }
    return false;
}

function sortClick(callObj)
{
    // When a sort button inside a <TH> is pressed, simulate the click event and return false to the surrounding <A> tag.
    // This simulates a 508 user clicking on the sort button - rather than the hover / click that relies totally on visual cues.
    return false;
}

function ExportHeader()
{
    var hd = "<div id=\"ReportHeader\">"
           + "<div class=\"ReportHeaderMain\">National Center for Education Statistics (NCES)</div>"
           + "<div class=\"ReportHeaderSub\">Institute of Education Sciences (IES)</div>"
           + "<div class=\"ReportHeaderSub\">National Assessment of Educational Progress (NAEP)</div>"
           + "<div class=\"ReportHeaderSource\">This report was generated using the NAEP State Comparisons Tool.<a href=\"http://nces.ed.gov/nationsreportcard/statecomparisons/\">http://nces.ed.gov/nationsreportcard/statecomparisons/</a></div>"
           + "<br />"
           + "</div>";
    return hd;
}

function GetTable(isExcel)
{
    // Fetch the outer HTML of the table by first cloning it inside of a span,
    // and then asking for the inner HTML of the span
    var pTable = document.getElementById(srtTableID);
    var span = document.createElement("span");
    var iHTML = "";
    var cHTML = "";
    var tiHTML = "";
    span.appendChild(pTable.cloneNode(true));
    
    // Copy the higher precision data into the cell values.
    for (var r = 0; r < span.firstChild.rows.length; r++)
    {
        for (var c = 0; c < span.firstChild.rows[r].childNodes.length; c++)
        {
            // Erase any of the <A> tag links
            iHTML = String(span.firstChild.rows[r].childNodes[c].innerHTML);
            tiHTML = iHTML.toLowerCase();
            if (tiHTML.indexOf("images/map_icon") != -1)
            {
                span.firstChild.rows[r].childNodes[c].innerHTML = iHTML.substring(iHTML.lastIndexOf("</A>") + 4, 10000);
            }
            else if (tiHTML.indexOf("images/jurisdiction") != -1)
            {
                span.firstChild.rows[r].childNodes[c].innerHTML = "Jurisdiction";
            }
            else if (tiHTML.indexOf("images/scale") != -1)
            {
                span.firstChild.rows[r].childNodes[c].innerHTML = "Scale Score";
            }
            else if (tiHTML.indexOf("selrowclick") != -1)
            {
                span.firstChild.rows[r].childNodes[c].innerHTML = getInnerText(span.firstChild.rows[r].childNodes[c].firstChild);
            }

            // Use full precision stat attribute instead of rounded TD value.
//            if (span.firstChild.rows[r].childNodes[c].attributes != null)
//            {
//                if (span.firstChild.rows[r].childNodes[c].attributes["stat"] != null)
//                {
//                    if (isExcel)
//                    {
//                        // For excel, set the html of the table cell to the full precision stat attribute
//                        cHTML = String(span.firstChild.rows[r].childNodes[c].attributes["stat"].value);
//                        span.firstChild.rows[r].childNodes[c].innerHTML = cHTML; 
//                    }
//                }
//            }
        }
    }
    var tblHTML = String(span.innerHTML);

    // Replace all cursor hovers
    // Erase the title and stat attributes.
    // Don't use removeAttribute() - it appears to crash IE.
    var re = new RegExp("style=\"cursor: pointer;?\"", "gi");
    tblHTML = tblHTML.replace(re, "");
    re = new RegExp("title=\".*?\"", "gi");
    tblHTML = tblHTML.replace(re, "");
    re = new RegExp("stat=\".*?\"", "gi");
    tblHTML = tblHTML.replace(re, "");
    return tblHTML;
}

function PrintIt()
{
    var hd = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";
    hd += "<html xmlns=\"http://www.w3.org/TR/REC-html40\">";
    hd += "<head>";
    hd += "<title>NAEP State Comparisons - Data Table</title>";
    hd += "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=ISO-8859-1\" />";
    hd += "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=EmulateIE7\" />";
    hd += "<meta name=\"ROBOTS\" content=\"NOINDEX\" />";
    hd += "<style id=\"styles1\" type=\"text/css\">";
    hd += "th {font:11px Verdana, Arial, Helvetica, sans-serif;font-weight: normal;background:#e3f0fc;border-bottom: solid 1px #c6dddf;border-top: solid 1px #c6dddf;border-left: solid 1px #c6dddf;border-right: solid 1px #c6dddf;}";
    hd += ".exporttitle {font-family:Verdana; font-size: 14px;}";
    hd += ".ReportHeaderMain {font-size:16pt; font-family:Impact; font-weight:bold;}";
    hd += ".ReportHeaderSub {font-family:Arial; font-size:12pt; font-weight:bold;}";
    hd += ".ReportHeaderSource {font-family:Arial; font-size:10pt; font-weight:bold;}";
    hd += ".ReportDisclaimer {font-family:Arial; font-size:7hd += .5pt;}";
    hd += ".odd td {background-color:#E8ECF1;}";
    hd += ".even td {background-color:#000000;}";
    hd += ".hover td {background-color:#FFFFCC;}";
    hd += ".sortedminus {font:normal 11px/16px Verdana,Arial,Helvetica,sans-serif;color:#000000;text-align:left; vertical-align:bottom}";
    hd += ".sortedplus {font:normal 11px/16px Verdana,Arial,Helvetica,sans-serif;color:#000000;text-align:left; vertical-align:bottom}";
    hd += ".selrow td {background-color:#91B8D8;}";
    hd += ".cl {font:normal 11px/16px Verdana,Arial,Helvetica,sans-serif;color:#000000;background-color:#FFFFFF;vertical-align:bottom;text-align:left;border-bottom:solid 1px #C6DDDF;padding-left: 5px;padding-right: 3px;}";
    hd += ".cl2 {font:normal 11px/16px Verdana,Arial,Helvetica,sans-serif;color:#000000;background-color:#f2f9ff;vertical-align:bottom;text-align:left;border-bottom:solid 1px #C6DDDF;padding-left:5px;padding-right: 3px;}";
    hd += ".cr {font:normal 11px/16px Verdana,Arial,Helvetica,sans-serif;color:#000000;background-color:#FFFFFF;vertical-align:bottom;text-align:right;border-bottom:solid 1px #C6DDDF;padding-left: 3px;padding-right: 5px;}";
    hd += ".cr2 {font:normal 11px/16px Verdana,Arial,Helvetica,sans-serif;color:#000000;background-color:#f2f9ff;vertical-align:bottom;text-align:right;border-bottom:solid 1px #C6DDDF;padding-left: 3px;padding-right: 5px;}";
    hd += ".ceq {BORDER-BOTTOM: solid 1px #C6DDDF;border-right: 1px solid #C6DDDF;border-left: 1px solid #C6DDDF;TEXT-ALIGN: center;FONT-WEIGHT: bold;background-color: #ffffcc;color:black}";
    hd += ".cgt {BORDER-BOTTOM: solid 1px #C6DDDF;border-right: 1px solid #C6DDDF;border-left: 1px solid #C6DDDF;TEXT-ALIGN: center;FONT-WEIGHT: bold;background-color: #ccffcc;color:green}";
    hd += ".clt {BORDER-BOTTOM: solid 1px #C6DDDF;border-right: 1px solid #C6DDDF;border-left: 1px solid #C6DDDF;TEXT-ALIGN: center;FONT-WEIGHT: bold;background-color: #ffc0cb;color:red}";
    hd += "caption { caption-side: top;text-align: left; padding-left:5px; padding-bottom:3px }";
    hd += ".SCTable {font:11px/16px Verdana,Arial,Helvetica,sans-serif;color:#000000;background-color:#FFFFFF;text-align:center;vertical-align:bottom;padding-left: 3px;padding-right: 3px;padding-bottom: 3px;padding-top: 3px;border-left: solid 1px #c6DDDF;border-right: solid 1px #c6DDDF;border-bottom: solid 1px #c6DDDF;}";
    hd += ".SCFtrCls{color:#000000;font-family: Verdana, Arial, Helvetica, Sans-Serif;font-weight:normal;font-size:9px;line-height:14px;text-decoration: none;padding: 5px 5px 5px 5px;}";
    hd += "</style>";
    hd += "</head><body>";

    var x = GetTable(false);
    var PrintWin = window.open("", "prntdotnet", "resizable, toolbar=yes, scrollbars, width=800, height=400, left=0,top=0,screenx=0,screeny=0")
    var pDoc = PrintWin.document;
    pDoc.write(hd)
    pDoc.write(ExportHeader());
    pDoc.write(x)
    pDoc.writeln("</body></html>")
    pDoc.close()
    PrintWin.focus();
}

function SaveIt()
{
    winSv = window.open("", "Sv0dotnet", "resizable, scrollbars, status, width=250, height=200, left=0,top=0,screenx=0,screeny=0")
    var title = "Download HTML File"
    var asppage = "saveas.aspx"
    var tablestr = GetTable(false);
    tablestr = ExportHeader() + tablestr;
    var re = new RegExp("\"", "g");
    tablestr = tablestr.replace(re, "`");
    var filesize = Math.round((tablestr.length + 4600) / 1024, 0);
    
    var html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
	+ "<html><head><title>State Comparisons - " + title + "</title>"
	+ "<style>body { font-family:verdana }<\/style>"
	+ "<\/head>"
	+ "<body leftmargin=\"5\" topmargin=\"5\">"
	+ "<font size=\"3\"><b>File Download</b></font><br>"
	+ "<font size=\"2\">Click an icon below to download the data file to your computer.</font>"
	+ "<center><br>"
	+ "<table width='160' border='0' cellspacing='2' cellpadding='1'>"
	+ "<tr><td align=right>"
	+ "<a href=\"javascript:document.forms[0].submit()\"><img align=\"top\" border=\"0\" src=\"images/html_document.gif\" alt=\"HTML File Icon\"><\/a><\/td>"
	+ "<td align=left><font size=\"2\"><a href=\"javascript:document.forms[0].submit()\">Download html File<\/a><br><small>("
	+ filesize.toString() + "K)<\/small><\/font><\/td>"
	+ "<\/tr><\/table>"
	+ "<BR><BR><FONT size='2'><A HREF=\"javascript:window.close();\">-close window-</A>"
	+ "<\/FONT><\/center>"
	+ "<form action=" + asppage + " id=dataform name=dataform method=post>"
	+ "<input type=\"hidden\" name=\"htmlTable\" value=\"" + tablestr + "\">"
	+ "<\/form>"
	+ "<\/body><\/html>"
    winSv.document.write(html);
    winSv.document.close();
    winSv.focus();
}

function ExportToExcel()
{
    var winExcel = window.open("", "XL0dotnet", "resizable, scrollbars, status, width=250, height=200, left=0,top=0,screenx=0,screeny=0");
    var title = "Download Excel File"
    var asppage = "excelpage.aspx"
    var tablestr = GetTable(true);
    tablestr = ExportHeader() + tablestr;
    var re = new RegExp("\"", "g");
    tablestr = tablestr.replace(re, "`");
    var filesize = Math.round((tablestr.length + 4600) / 1024, 0);
    var html = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">"
	+ "<html><head><title>State Comparisons - " + title + "</title>"
	+ "<\/head>"
	+ "<style>body { font-family:arial }<\/style>"
	+ "<body leftmargin=\"5\" topmargin=\"5\">"
	+ "<font size=\"3\"><b>File Download</b></font><br>"
	+ "<font size=\"2\">Click an icon below to download the data file to your computer.</font>"
	+ "<center><br>"
	+ "<table width='160' border='0' cellspacing='2' cellpadding='1'>"
	+ "<tr><td align=right>"
	+ "<a href=\"javascript:document.forms[0].submit()\"><img align=\"absmiddle\" border=\"0\" src=\"images\/excel_file.gif\" alt=\"Excel File Icon\"><\/a><\/td>"
	+ "<td align=left><font size=\"2\"><a href=\"javascript:document.forms[0].submit()\">Download Excel File<\/a><br><small>("
	+ filesize.toString() + "K)<\/small><\/font><\/td>"
	+ "<\/tr><\/table>"
	+ "<BR><BR><FONT size='2'><A HREF=\"javascript:window.close();\">-close window-</A>"
	+ "<\/FONT><\/center>"
	+ "<form action=" + asppage + " id=dataform name=dataform method=post>"
	+ "<input type=\"hidden\" name=\"excelTable\" value=\"" + tablestr + "\">"
	+ "<\/form>"
	+ "<\/body><\/html>"
    winExcel.document.write(html);
    winExcel.document.close();
    winExcel.focus();
}

function openSBWin(winName, theURL, theWidth, theHeight)
{
    aWindow = window.open(theURL, winName, "width=" + ((theWidth != null) ? theWidth : 455) + ",height=" + ((theHeight != null) ? theHeight : 300) + ",scrollbars=yes,resizable=yes,screenX=0,screenY=0,top=0,left=0,top=0,left=0, toolbar=yes");
    aWindow.focus();
}

function setupMapHRef(myref,iColID)
{
    // KJB 05/05/09
    // Dynamically change the link to the map page by adding the focal jurisdiction.
    // If the label of the jurisdiction has to be URL encoded because of special characters,
    // then the cheap replacement of space with + isn't going to cut it ...
    var focalJ = document.getElementById(fjClientID);
    var focalJValue = focalJ.value.replace(' ', '+');
    var href = String(myref.href);
    if (href.indexOf("FocalJuris", 0) == -1)
    {
        // There is no focal jurisdiction on the link so add it.
        myref.href = myref.href + "&FocalJuris=" + focalJValue;
    }
    else
    {
        // There is a focal jurisdiction on the link, so replace it.
        var re = new RegExp("\&", "g");
        var hrargs = href.split(re);
        var replhref = String("");
        for (i = 0; i < hrargs.length; i++)
        {
            if (hrargs[i].indexOf("FocalJuris") != -1)
            {
                var eqpos = hrargs[i].indexOf("=");
                hrargs[i] = hrargs[i].substring(0, eqpos+1) + focalJValue;
            }
            if (i > 0) replhref += "&";
            replhref += hrargs[i];
        }
        myref.href = replhref;
    }
    openSBWin("Map" + iColID, myref.href, 1010, 725);
    return false;
}

function returnColObject(colHeadID, curRow)
{
    // Given a set of elements from a table row, return a column based on its header identifier.
    for (var col = 0; col < curRow.childNodes.length; col++)
    {
        if (curRow.childNodes[col].attributes != null)
        {
            if (curRow.childNodes[col].attributes["headers"] != null)
            {
                if (curRow.childNodes[col].attributes["headers"].value == colHeadID)
                {
                    return curRow.childNodes[col];
                }
            }
        }
    }
    return null;
}

function getInnerText(myObj)
{
    // In FireFox, innerText attribute is called textContent.
    return (myObj.innerText == null) ? myObj.textContent : myObj.innerText;
}

function selectNP(myTable)
{
    // Select national public as the default selected row
    // When the sorted table object is first defined.
    var colObj = null;
    for (var row = 0; row < myTable.elements.length; row++)
    {
        colObj = returnColObject("Col1", myTable.elements[row]);
        if (getInnerText(colObj) == "National Public")
        {
            myTable.select(myTable.elements[row]);
            break;
        }
    }
}

function populateVariantColumns(myTable, elm, callType)
{
    // Note that this routine is called after the indicated column sort is completed.
    // The jurisdictions column might therefore be not in the same order as when the table was painted.
    //alert('Set the variant columns for column ' + elm.parentElement.id + '.');
    var srow = 0;
    var icol = 0;
    var colofs = null;
    if (callType == "COL")
    {
        if (elm != prvSortedCol)
        {
            if (prvSortedCol != null)
            {
                // De-designate the prior sorted column (remove the arrow from the html)
                var pre = RegExp("_up\.gif|_down\.gif", "gi");
                var phTxt = prvSortedCol.innerHTML;
                phTxt = phTxt.replace(pre, ".gif");
                prvSortedCol.innerHTML = phTxt;

                // Figure out what column to highlight as sorted by finding the header ID / column ID
                colofs = -1;
                for (icol = 0; icol < myTable.elements[0].childNodes.length; icol++)
                {
                    if (myTable.elements[0].childNodes[icol].headers == prvSortedCol.id)
                    {
                        colofs = icol;
                        break;
                    }
                }
                
                for (srow = 0; srow < myTable.elements.length; srow++)
                {
                    // Colorize the selected column
                    myTable.elements[srow].childNodes[colofs].style.backgroundColor = "";
                }
            }
            prvSortedCol = elm;
        }
        
        // Set the arrow to the right of the sorted column header.
        var srcImage = (elm.className == "sortedminus") ? "_down.gif" : "_up.gif";
        var hTxt = String(elm.innerHTML);
        if (hTxt.indexOf("_up.gif") == -1 && hTxt.indexOf("_down.gif") == -1)
        {
            // There is no existing up/down arrow in the image source file name.
            var re1 = new RegExp("\.gif", "gi");
            hTxt = hTxt.replace(re1, srcImage);
            elm.innerHTML = hTxt;
        }
        else
        {
            // There is an existing image tag - replace that content in the HTML.
            var re = new RegExp("_up\.gif|_down\.gif", "gi");
            hTxt = hTxt.replace(re, srcImage);
            elm.innerHTML = hTxt;
        }

        // Figure out what column to highlight as sorted by finding the header ID / column ID
        colofs = -1;
        for (icol = 0; icol < myTable.elements[0].childNodes.length; icol++)
        {
            if (myTable.elements[0].childNodes[icol].headers == elm.id)
            {
                colofs = icol;
                break;
            }
        }
        
        for (srow = 0; srow < myTable.elements.length; srow++)
        {
            // Colorize the selected column
            myTable.elements[srow].childNodes[colofs].style.backgroundColor = "#BDD4E5";
        }
    }

    var row = 0;    
    if (elm.id == "Col1")
    {
        // clear out various cells and set order column to NA.
        for (row = 0; row < myTable.elements.length; row++)
        {
            returnColObject("Col0", myTable.elements[row]).innerHTML = "N/A";
            returnColObject("Col2", myTable.elements[row]).className = "cl";
            returnColObject("Col2", myTable.elements[row]).innerHTML = "&nbsp;";
            returnColObject("Col3", myTable.elements[row]).innerHTML = "&nbsp;";
            returnColObject("Col4", myTable.elements[row]).innerHTML = "&nbsp;";
            returnColObject("Col5", myTable.elements[row]).innerHTML = "&nbsp;";
        }

        // label the table
        myTable.table.caption.innerHTML = tblTitleArr[0];
    }
    else
    {
        // Figure out what index in the SigData array we should be using
        var i = 0;
        var jindex = -1;
        var iHigher = 0;
        var iLower = 0;
        var iEqual = 0;
        var sSymbol = "";
        var selRowJurisData = "";
        var curRowJurisData = "";
        var numJurises = jurisIndex.length;
        var bNPRow = false;
        var bNPSelRow = false;
        var iStartPoint = 0;
        var iRowOrder = (elm.className == "sortedplus") ? numJurises - 1 : 1;
        var sigDataIdx = 0;
        var numNoncomputes = 0;
        var setFirstRow = false;
        // Flip the sense of the significance for percentile differences (ie 25th to 75th).
        var flipTheSigSign = (tableType == "AcrossYear" && numVars == 1 && (elm.id == "Col24" || elm.id == "Col26" || elm.id == "Col28")) || (tableType == "WithinYear" && numVars == 1 && (elm.id == "Col9" || elm.id == "Col12"));
        var bValidDataRow = false;
        if (tableType == 'WithinYear')
        {
            if (!stdErrorExists)
            {
                // No standard error - calculate index into stats from column ID
                sigDataIdx = elm.id.substring(3, 4) - 6;
            }
            else
            {
                // Standard error exists - calculate index into stats from column ID
                sigDataIdx = (elm.id.substring(3, 5) - 6) / 2;
            }
        }
        else
        {
            // Across year. Remember that the sig tests are performed as most recent year - prior year,
            // but the columns are presented as older year, newer year. So, when the user is sorting - we have to
            // flip the positions of the current and prior year sig test array indeces when de-referencing 
            sigDataIdx = (elm.id.substring(3, 5) - 6) / 2;
            if (sigDataIdx == 0 || sigDataIdx == 3 || sigDataIdx == 6 || sigDataIdx == 9)
                sigDataIdx++;
            else if (sigDataIdx == 1 || sigDataIdx == 4 || sigDataIdx == 7 || sigDataIdx == 10)
                sigDataIdx--;
        }

        // label the table
        myTable.table.caption.innerHTML = tblTitleArr[sigDataIdx + 1];

        // Figure out what the sig data offset is for the selected row
        var sigDataIdxForSelRow = -1;
        var selRowJurisLabel = getInnerText(returnColObject("Col1", myTable.selectedElements[0]));
        for (jindex = 0; jindex < numJurises; jindex++)
        {
            if (jurisIndex[jindex] == selRowJurisLabel)
            {
                sigDataIdxForSelRow = jindex;
                bNPSelRow = (jurisIndex[jindex] == "National Public");
                selRowJurisData = sigDataArr[sigDataIdx].substring((numJurises * sigDataIdxForSelRow), ((numJurises * sigDataIdxForSelRow) + numJurises));
                break;
            }
        }

        var sigDataIdxForCurRow = -1;
        var curRowJurisLabel = "";
        for (row = 0; row < myTable.elements.length; row++)
        {
            // Take the jurisdiction on the current row, and look up the index in the sig array data
            sigDataIdxForCurRow = -1;
            curRowJurisLabel = getInnerText(returnColObject("Col1", myTable.elements[row]));
            for (jindex = 0; jindex < numJurises; jindex++)
            {
                if (jurisIndex[jindex] == curRowJurisLabel)
                {
                    sigDataIdxForCurRow = jindex;
                    curRowJurisData = sigDataArr[sigDataIdx].substring((numJurises * sigDataIdxForCurRow), ((numJurises * sigDataIdxForCurRow) + numJurises));
                    break;
                }
            }
            if (sigDataIdxForCurRow == -1)
            {
                // Just exit this process if we can't find a jurisdiction.
                break;
            }

            // For this juris row of data, count up the < > and = jurises relative to it.
            iHigher = 0;
            iLower = 0;
            iEqual = 0;
            sSymbol = "";
            iSymbol = "";
            numNoncomputes = 0;

            // NP is in the zeroeth col of each data row. If we're counting, do so across all jurisdictions.
            // If we're counting any other juris against any other juris, then do not count NP.
            bNPRow = (curRowJurisLabel == "National Public");
            iStartPoint = (bNPRow) ? 0 : 1;
            for (i = iStartPoint; i < numJurises; i++)
            {
                sSymbol = curRowJurisData.substring(i, i + 1);
                if (flipTheSigSign && sSymbol == "1")
                {
                    // turn higher into lower for percentile across year change columns only
                    sSymbol = "3";
                }
                else if (flipTheSigSign && sSymbol == "3")
                {
                    // turn lower into higher for percentile across year change columns only
                    sSymbol = "1";
                }
                
                if (sSymbol == "1")
                {
                    iHigher++;
                }
                else if (sSymbol == "3")
                {
                    iLower++;
                }
                else if (sSymbol == "5")
                {
                    iEqual++;
                }
                else
                {
                    numNoncomputes++;
                }
            }

            if (!setFirstRow && elm.className == "sortedplus")
            {
                // When counting from N down to 1, don't count the non computed states in the row number.
                setFirstRow = true;
                iRowOrder -= numNoncomputes-1;    
            }

            if (bNPRow)
            {
                // We're on the national public row - don't give it a row order or a sig sign.
                returnColObject("Col0", myTable.elements[row]).innerHTML = "&nbsp;";
            }
            
            // This is a non national public row, so mark the row order and then decrement.
            sSymbol = curRowJurisData.substring(sigDataIdxForSelRow, sigDataIdxForSelRow + 1);
            if (flipTheSigSign && sSymbol == "1")
            {
                // turn higher into lower for percentile across year change columns only
                sSymbol = "3";
            }
            else if (flipTheSigSign && sSymbol == "3")
            {
                // turn lower into higher for percentile across year change columns only
                sSymbol = "1";
            }
            if (sSymbol == "1")
            {
                // Higher
                sSymbol = "&gt;";
                returnColObject("Col2", myTable.elements[row]).className = "cgt";
                returnColObject("Col2", myTable.elements[row]).innerHTML = sSymbol;
            }
            else if (sSymbol == "3")
            {
                // Lower
                sSymbol = "&lt;";
                returnColObject("Col2", myTable.elements[row]).className = "clt";
                returnColObject("Col2", myTable.elements[row]).innerHTML = sSymbol;
            }
            else if (sSymbol == "5")
            {
                // Equal
                sSymbol = "=";
                returnColObject("Col2", myTable.elements[row]).className = "ceq";
                returnColObject("Col2", myTable.elements[row]).innerHTML = sSymbol;
            }
            else
            {
                // Anything else. We set the order # for the row as long as there was a test.
                // a zero means no test could be computed - 9 means against ourselves.
                // The "zero" rows should get the N/A ... and the 9 rows should get a count.
                sSymbol = "&nbsp";
                returnColObject("Col2", myTable.elements[row]).className = "cl";
                returnColObject("Col2", myTable.elements[row]).innerHTML = sSymbol;
            }

            bValidDataRow = ((iLower + iEqual + iHigher) > 0);
            if (bValidDataRow && !bNPRow)
            {
                returnColObject("Col0", myTable.elements[row]).innerHTML = iRowOrder;
                if (elm.className == "sortedplus")
                {
                    iRowOrder--;
                }
                else
                {
                    iRowOrder++;
                }
            }
            else
            {
                // No row order applies
                returnColObject("Col0", myTable.elements[row]).innerHTML = (bNPRow) ? "&nbsp;" : "N/A";                    
            }

            returnColObject("Col3", myTable.elements[row]).innerHTML = (bValidDataRow) ? iLower : "&nbsp;";
            returnColObject("Col4", myTable.elements[row]).innerHTML = (bValidDataRow) ? iEqual : "&nbsp;";
            returnColObject("Col5", myTable.elements[row]).innerHTML = (bValidDataRow) ? iHigher : "&nbsp;";
            //alert("Number of nonncomputes for '" + myTable.elements[row].childNodes[1].innerText + "': " + numNoncomputes);
        }
    }
}

function pageOnloadStuff()
{
    var curLoc = String(document.location);
    curLoc = curLoc.toLowerCase();
    if (curLoc.indexOf("default.aspx") != -1)
    {
        // We're on the default page. Operate the javascript methods to populate the form, expand boxes, etc.
        // If the user hits BACK with selections made, then this ensures that expanded divs/spans are visible.
        scatterSelections();
    }
    else
    {
        myTable = new SortedTable();
        if (myTable.table != null)
        {
            // Don't allow "no row" to be selected
            // Only allow 1 row at a time to be selected
            myTable.allowDeselect = false;
            myTable.allowMultiple = false;

            // When the table is loaded, select NP as the default row.
            selectNP(myTable);

            // label the table
            myTable.table.caption.innerHTML = tblTitleArr[0];

            // Define custom functions for clicks on the header, selecting a row, and sorting the table.
            myTable.processHeadClick = function(elm, e)
            {
                // At the point where the column header is clicked - before sort - we don't need to do anything.
                ;
            }
            myTable.processOnSelect = function(elm)
            {
                // KJB 6/4/09: This event occurs after the user selects a row
                var sortCol = myTable.findSort();
                var focalJ = document.getElementById(fjClientID);
                var colObj = returnColObject("Col1", elm);
                focalJ.value = getInnerText(colObj);
                populateVariantColumns(myTable, sortCol, "ROW");
            }
            myTable.processOnSort = function(elm)
            {
                // KJB 6/4/09: This event occurs after the user causes a sort (because of clicking a column header.
                populateVariantColumns(myTable, elm, "COL");
            }
            myTable.onheadclick = myTable.processHeadClick;
            myTable.onselect = myTable.processOnSelect;
            myTable.onsort = myTable.processOnSort;
            myTable.sort();
        }
    }
}

SortedTable = function(id)
{
	this.table = null;
	if (!document.getElementById || !document.getElementsByTagName) return false;
	if (id) this.init(document.getElementById(id));
	else this.init(this.findTable());
	this.prep();
	if (!id && this.findTable()) new SortedTable();
}

// static
SortedTable.tables = new Array();
SortedTable.move = function(d, elm)
{
	var st = SortedTable.getSortedTable(elm);
	if (st) st.move(d,elm);
}
SortedTable.moveSelected = function(d, elm)
{
	var st = SortedTable.getSortedTable(elm);
	if (st) st.move(d);
}
SortedTable.findParent = function(elm, tag)
{
	while (elm && elm.tagName && elm.tagName.toLowerCase()!=tag) elm = elm.parentNode;
	return elm;
}
SortedTable.getEventElement = function(e)
{
	if (!e) e = window.event;
	return (e.target)? e.target : e.srcElement;
}
SortedTable.getSortedTable = function(elm)
{
	elm = SortedTable.findParent(elm,'table');
	for (var i=0;i<SortedTable.tables.length;i++) {
		var t = SortedTable.tables[i].table;
		if (t==elm) return SortedTable.tables[i];
	}
	return null;
}
SortedTable.gecko = (navigator.product=="Gecko");
SortedTable.removeBeforeSort = SortedTable.gecko;

function correctForNP(valstr, isDesc)
{
    var lval = String(valstr).toLowerCase();
    if (lval.indexOf("national public") != -1)
    {
        // We want national public to always float to the top.
        lval = (!isDesc) ? " " + valstr : "zz" + valstr;
    }
    else
    {
        // Anything else is sorted normally.
        lval = valstr;
    }

    // Return the string value for comparison.
    return lval;
}

// dynamic
SortedTable.prototype = {
    // before init finished
    init: function(elm)
    {
        if (!elm) return false;
        // main DOM properties
        this.table = elm;
        this.head = elm.getElementsByTagName('thead')[0];
        this.body = elm.getElementsByTagName('tbody')[0];
        this.foot = elm.getElementsByTagName('tfoot')[0];
        if (this.hasClass(this.table, 'regroup')) this.regroup();
        this.elements = this.body.getElementsByTagName('tr');
        // other properties
        this.allowMultiple = false; // set this to false to disallow multiple selection
        this.allowDeselect = false; // set this to false to disallow deselection
        // prepare the table
        this.parseCols();
        this.selectedElements = new Array();
    },
    findTable: function()
    {
        var elms = document.getElementsByTagName('table');
        for (var i = 0; i < elms.length; i++)
        {
            if (this.hasClass(elms[i], 'SCTable') && !SortedTable.getSortedTable(elms[i])) return elms[i];
        }
        return null;
    },
    parseCols: function()
    {
        var i = 0;
        if (!this.table) return;
        this.cols = new Array();
        var ths = this.head.getElementsByTagName('th');
        for (i = 0; i < ths.length; i++)
        {
            this.cols[ths[i].id] = new Array();
        }
        for (i = 0; i < this.elements.length; i++)
        {
            var tds = this.elements[i].getElementsByTagName('td');
            for (var j = 0; j < tds.length; j++)
            {
                var headers = tds[j].headers.split(' ');
                for (var k = 0; k < headers.length; k++)
                {
                    if (this.cols[headers[k]]) this.cols[headers[k]].push(tds[j]);
                }
            }
        }
    },
    prep: function()
    {
        if (!this.table || SortedTable.getSortedTable(this.table)) return;
        this.register();
        this.prepBody();
        this.prepHeader();
    },
    register: function()
    {
        SortedTable.tables.push(this);
    },
    regroup: function()
    {
        var tbs = this.table.getElementsByTagName('tbody');
        for (var i = tbs.length - 1; i > 0; i--)
        {
            var trs = tbs[i].getElementsByTagName('tr');
            for (var j = trs.length - 1; j >= 0; j--)
            {
                this.body.appendChild(trs[j]);
            }
            this.table.removeChild(tbs[i]);
        }
    },
    // helpers
    trim: function(str)
    {
        while (str.substr(0, 1) == ' ') str = str.substr(1);
        while (str.substr(str.length - 1, 1) == ' ') str = str.substr(0, str.length - 1);
        return str;
    },
    hasClass: function(elm, findclass)
    {
        if (!elm) return null;
        return (' ' + elm.className + ' ').indexOf(' ' + findclass + ' ') + 1;
    },
    changeClass: function(elm, oldclass, newclass)
    {
        if (!elm) return null;
        var c = elm.className.split(' ');
        for (var i = 0; i < c.length; i++)
        {
            c[i] = this.trim(c[i]);
            if (c[i] == oldclass || c[i] == newclass || c[i] == '') c.splice(i, 1);
        }
        c.push(newclass);
        elm.className = this.trim(c.join(' '));
    },
    elementIndex: function(elm)
    {
        for (var i = 0; i < this.elements.length; i++)
        {
            if (this.elements[i] == elm) return i;
        }
        return -1;
    },
    findParent: SortedTable.findParent,
    // events
    callBodyHover: function(e)
    {
        var elm = SortedTable.getEventElement(e);
        var st = SortedTable.getSortedTable(elm);
        if (!st) return false;
        if (typeof (st.onbodyhover) == 'function') st.onbodyhover(elm, e);
        elm = st.findParent(elm, 'tr');
        if (e.type == 'mouseover') st.changeClass(elm, '', 'hover');
        else if (e.type == 'mouseout') st.changeClass(elm, 'hover', '');
        return false;
    },
    callBodyClick: function(e)
    {
        var elm = SortedTable.getEventElement(e);
        var st = SortedTable.getSortedTable(elm);
        if (!st) return false;
        if (typeof (st.onbodyclick) == 'function') st.onbodyclick(elm, e);
        elm = st.findParent(elm, 'tr');
        if (e.shiftKey && st.allowMultiple) st.selectRange(elm);
        else
        {
            if (st.selected(elm))
            {
                if (st.allowDeselect) st.deselect(elm);
            } else
            {
                if (!e.ctrlKey || !st.allowMultiple) st.cleanselect();
                st.select(elm);
            }
        }
        return false;
    },
    callBodyDblClick: function(e)
    {
        var elm = SortedTable.getEventElement(e);
        var st = SortedTable.getSortedTable(elm);
        if (!st) return false;
        if (typeof (st.onbodydblclick) == 'function') st.onbodydblclick(elm, e);
        return false;
    },
    callHeadHover: function(e)
    {
        var elm = SortedTable.getEventElement(e);
        var st = SortedTable.getSortedTable(elm);
        if (!st) return false;
        if (typeof (st.onheadhover) == 'function') st.onheadhover(elm, e);
        return false;
    },
    callHeadClick: function(e)
    {
        var elm = SortedTable.getEventElement(e);
        var st = SortedTable.getSortedTable(elm);
        if (!st) return false;
        if (typeof (st.onheadclick) == 'function') st.onheadclick(elm, e);
        elm = st.findParent(elm, 'th');
        st.resort(elm);
        return false;
    },
    callHeadDblClick: function(e)
    {
        var elm = SortedTable.getEventElement(e);
        var st = SortedTable.getSortedTable(elm);
        if (!st) return false;
        if (typeof (st.onheaddblclick) == 'function') st.onheaddblclick(elm, e);
        return false;
    },
    // inited
    prepHeader: function()
    {
        var ths = this.head.getElementsByTagName('th');
        for (var i = 0; i < ths.length; i++)
        {
            if (this.hasClass(ths[i], 'nosort')) continue;
            ths[i].style.cursor = 'pointer';
            addEvent(ths[i], 'click', this.callHeadClick);
            addEvent(ths[i], 'dblclick', this.callHeadDblClick);
            addEvent(ths[i], 'mouseover', this.callHeadHover);
            addEvent(ths[i], 'mouseout', this.callHeadHover);
            if (this.hasClass(ths[i], 'sortedplus') || this.hasClass(ths[i], 'sortedminus')) this.sort(ths[i]);
        }
    },
    prepBody: function()
    {
        var elm = this.body.lastChild;
        var pelm;
        while (elm)
        {
            pelm = elm.previousSibling;
            if (elm.nodeType != 1) this.body.removeChild(elm);
            elm = pelm;
        }
        var trs = this.body.getElementsByTagName('tr');
        for (var i = 0; i < trs.length; i++)
        {
            trs[i].style.cursor = 'pointer';
            addEvent(trs[i], 'click', this.callBodyClick);
            addEvent(trs[i], 'dblclick', this.callBodyDblClick);
            addEvent(trs[i], 'mouseover', this.callBodyHover);
            addEvent(trs[i], 'mouseout', this.callBodyHover);
        }
    },
    // selecting
    selected: function(elm)
    {
        return this.hasClass(elm, 'selrow');
    },
    select: function(elm)
    {
        this.changeClass(elm, '', 'selrow');
        this.selectedElements.push(elm);
        if (typeof (this.onselect) == 'function') this.onselect(elm);
    },
    deselect: function(elm)
    {
        this.changeClass(elm, 'selrow', '');
        for (var i = 0; i < this.selectedElements.length; i++)
        {
            if (this.selectedElements[i] == elm) this.selectedElements.splice(i, 1);
        }
        if (typeof (this.ondeselect) == 'function') this.ondeselect(elm);
    },
    selectRange: function(elm1)
    {
        if (this.selectedElements.length == 0)
        {
            this.select(elm1);
            return false;
        }
        var elm0 = this.selectedElements[this.selectedElements.length - 1];
        var d = (this.elementIndex(elm0) < this.elementIndex(elm1));
        var elm = elm0;
        if (this.selected(elm1)) { if (this.selected(elm0)) this.deselect(elm0); }
        else { if (!this.selected(elm0)) this.select(elm0); }
        do
        {
            elm = (d) ? elm.nextSibling : elm.previousSibling;
            if (this.selected(elm)) this.deselect(elm);
            else this.select(elm);
        } while (elm != elm1);
        return true;
    },
    cleanselect: function()
    {
        for (var i = 0; i < this.elements.length; i++)
        {
            if (this.selected(this.elements[i])) this.deselect(this.elements[i]);
        }
        this.selectedElements = new Array();
    },
    // sorting
    compareSmart: function(v1, v2)
    {
        v1 = (v1) ? v1.split(' ') : [];
        v2 = (v2) ? v2.split(' ') : [];
        l = Math.max(v1.length, v2.length);
        var r = 0;
        for (var i = 0; i < l; i++)
        {
            if (v1[i] == v2[i]) continue;
            if (!v1[i]) v1[i] = "";
            if (!v2[i]) v2[i] = "";
            if (!isNaN(parseFloat(v1[i]))) v1[i] = parseFloat(v1[i]);
            if (!isNaN(parseFloat(v2[i]))) v2[i] = parseFloat(v2[i]);
            if (isNaN(v1[i]) && !isNaN(v2[i])) return 1;
            else if (!isNaN(v1[i]) && isNaN(v2[i])) return -1;
            else if (v1[i] > v2[i]) return 1;
            else if (v1[i] < v2[i]) return -1;
        }
        return 0;
    },
    compare: function(v1, v2, lst, isDescSort, isGapCol)
    {
        var st = (!lst) ? SortedTable.getSortedTable(v1) : lst;
        if (v1 == null || v2 == null) return 0;
        var axis = v1.axis.toLowerCase();
        var v1s = (v1.title) ? v1.title : (v1.innerHTML) ? v1.innerHTML : '';
        var v2s = (v2.title) ? v2.title : (v2.innerHTML) ? v2.innerHTML : '';
        if (axis == 'string')
        {
            v1s = correctForNP(v1s, isDescSort);
            v2s = correctForNP(v2s, isDescSort);
            return st.compareSmart(v1s.toLowerCase(), v2s.toLowerCase());
        }
        else if (axis == 'sstring')
        {
            return st.compareSmart(v1s, v2s);
        }
        else if (axis == 'number')
        {
            //v1 = parseFloat(v1s);
            v1 = parseFloat(v1.attributes["stat"].value);
            if (isNaN(v1))
            {
                // Not a number. We always want non-numeric values to float to the bottom, so
                // use an appropriate version of infinity that will force that sort to happen based on ascending / descending.
                v1 = (isDescSort) ? -Infinity : Infinity;
            }
            else if (isGapCol)
            {
                // In the case of a gap column we multiply the number by -1 for comparison
                // so that the "best" gaps float to the top on the initial sort.
                // In gaps, better gaps are smaller gaps, so to the sense of the sort is reversed.
                v1 = -1.0 * v1;
            }

            //v2 = parseFloat(v2s);
            v2 = parseFloat(v2.attributes["stat"].value);
            if (isNaN(v2))
            {
                // Not a number. We always want non-numeric values to float to the bottom, so
                // use an appropriate version of infinity that will force that sort to happen based on ascending / descending.
                v2 = (isDescSort) ? -Infinity : Infinity;
            }
            else if (isGapCol)
            {
                // In the case of a gap column we multiply the number by -1 for comparison
                // so that the "best" gaps float to the top on the initial sort.
                // In gaps, better gaps are smaller gaps, so to the sense of the sort is reversed.
                v2 = -1.0 * v2;
            }
        }
        else
        {
            v1 = (v1s != '') ? v1s : v1;
            v2 = (v2s != '') ? v2s : v2;
        }
        if (v1 == null || v2 == null) return 0;
        else if (v1 > v2) return 1
        else if (v1 < v2) return -1;
        return 0;
    },
    findSort: function()
    {
        var ths = this.head.getElementsByTagName('th');
        for (var i = 0; i < ths.length; i++)
        {
            if (this.hasClass(ths[i], 'sortedminus') || this.hasClass(ths[i], 'sortedplus')) return ths[i];
        }
        return null;
    },
    sort: function(elm, reverseonly)
    {
        var st = this;
        if (!elm) elm = this.findSort();
        if (!elm) return false;
        var isDescSort = (this.hasClass(elm, 'sortedminus'));
        var isGapCol = ((tableType == 'WithinYear' && (elm.id == 'Col9' || elm.id == 'Col12')) || (tableType == 'AcrossYear' && (elm.id == 'Col24' || elm.id == 'Col26' || elm.id == 'Col28')));
        var comparator = function(v1, v2)
        {
            return st.compare(v1, v2, st, isDescSort, isGapCol);
        }
        var col = this.cols[elm.id];
        // KJB 06/02/09 - always sort because we have special logic for floating non-data to the bottom of the table.
        //if (!lreverseonly) col.sort(comparator);
        col.sort(comparator);
        //if (this.hasClass(elm, 'sortedminus') || lreverseonly) col.reverse();
        if (isDescSort) col.reverse();

        var b_sibling, b_parent;
        if (SortedTable.removeBeforeSort)
        {
            b_sibling = this.body.nextSibling;
            b_parent = this.body.parentNode;
            b_parent.removeChild(this.body);
        }
        for (var i = 0; i < col.length; i++)
        {
            this.body.appendChild(this.findParent(col[i], 'tr'));
        }
        if (SortedTable.removeBeforeSort)
        {
            b_parent.insertBefore(this.body, b_sibling);
        }
        if (typeof (this.onsort) == 'function') this.onsort(elm);
    },
    resort: function(elm)
    {
        if (!elm) return false;
        this.cleansort(elm);
        var reverseonly = false;
        if (this.hasClass(elm, 'sortedplus'))
        {
            this.changeClass(elm, 'sortedplus', 'sortedminus');
            reverseonly = true;
        } else if (this.hasClass(elm, 'sortedminus'))
        {
            this.changeClass(elm, 'sortedminus', 'sortedplus');
            reverseonly = true;
        } else
        {
            // KJB 04/08/2009
            if (elm.id == "Col1")
            {
                // If we're sorting the jurisdictions (and transitioning from the non sorted state), sort ascending by default.
                this.changeClass(elm, 'sortedminus', 'sortedplus');
            }
            else
            {
                // If we're sorting a data column (and transitioning from the non sorted state), sort descending by default.
                this.changeClass(elm, 'sortedplus', 'sortedminus');
            }
        }
        this.sort(elm, reverseonly);
    },
    cleansort: function(except)
    {
        var ths = this.head.getElementsByTagName('th');
        for (var i = 0; i < ths.length; i++)
        {
            if (ths[i] == except) continue;
            if (this.hasClass(ths[i], 'sortedminus')) this.changeClass(ths[i], 'sortedminus', '');
            else if (this.hasClass(ths[i], 'sortedplus')) this.changeClass(ths[i], 'sortedplus', '');
        }
    },
    // movement
    compareindex: function(v1, v2)
    {
        var st = SortedTable.getSortedTable(v1);
        if (!st) return 0;
        v1 = st.elementIndex(v1);
        v2 = st.elementIndex(v2);
        if (v1 == null || v2 == null) return 0;
        else if (v1 < v2) return 1
        else if (v2 < v1) return -1;
        return 0;
    },
    move: function(d, elm)
    {
        var i = 0;
        if (elm) this.moverow(d, elm);
        else
        {
            var m = true;
            for (i = 0; i < this.selectedElements.length; i++)
            {
                if (!this.canMove(d, this.selectedElements[i])) m = false;
            }
            if (m)
            {
                var moving = this.selectedElements.slice(0, this.selectedElements.length);
                moving.sort(this.compareindex);
                if (d > 0) moving.reverse();
                for (i = 0; i < moving.length; i++)
                {
                    this.moverow(d, moving[i]);
                }
            }
        }
        if (typeof (this.onmove) == 'function') this.onmove(d, elm);
    },
    moverow: function(d, elm)
    {
        this.cleansort();
        var parent = elm.parentNode;
        var sibling = this.canMove(d, elm);
        if (!sibling) return false;
        if (d > 0)
        {
            parent.removeChild(elm);
            parent.insertBefore(elm, sibling);
        } else
        {
            parent.removeChild(elm);
            if (sibling.nextSibling) parent.insertBefore(elm, sibling.nextSibling);
            else parent.appendChild(elm);
        }
    },
    canMove: function(d, elm)
    {
        if (d > 0) return elm.previousSibling;
        else return elm.nextSibling;
    }
}