User:Magicpiano/NRBot/NRHPmap.js

Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/* jshint maxerr: 3000 */
function NRHPmapbutton() {
    if (mw.config.get('wgPageName')!="Wikipedia:WikiProject_National_Register_of_Historic_Places/Progress"||location.href.indexOf('action')!=-1) {
        return;
    }
    var button=document.getElementById("NRHPUpdateMapButton");
    if (button !== null) return;
    
    button = document.createElement("input");
    button.setAttribute("type", "button");
    button.setAttribute("value", "Generate SVG Output");
    button.setAttribute("onclick", "MapButtonClick()");
    button.id = "NRHPUpdateMapButton";
    var content=document.getElementById('mw-content-text');

    content.parentNode.insertBefore(button, content);
}

function MapButtonClick() {
    var button = document.getElementById('NRHPUpdateMapButton');
    button.disabled = true;
    var SVGDiv = document.getElementById('SVGDiv');
    if (SVGDiv === null) {
	   	SVGDiv = document.createElement('div');
    	SVGDiv.setAttribute('id', 'SVGDiv');
    	SVGDiv.setAttribute("style", "width:500px; border:1px solid black; padding:5px");
    	button.parentNode.insertBefore(SVGDiv, button);
    }

    SVGDiv.innerHTML = "Fetching wikitext...";
    getSVGtext("Wikipedia:WikiProject National Register of Historic Places/Progress/SVG");
}

function getSVGtext(title) { // get SVG code from /SVG page
    var SVGtext="";
    $.ajax({
        dataType: "json",
        url: mw.util.wikiScript('api'),
        data: {
            format: 'json',
            action: 'query',
            prop: 'revisions',
            rvprop: 'content',
            titles: title,
            indexpageids: true,
            redirects: 'true'
        },
        error: function() {SVGtext="error"},
        success: function(output) {
            for (var page in output.query.pages) {
                SVGtext=output.query.pages[page].revisions[0]['*'];
            }
        },
        complete: function() {
            if (SVGtext=="error") {
                var SVGDiv=document.getElementById("SVGDiv");
                SVGDiv.innerHTML+=" Unable to fetch wikitext! Script aborted.";
            } else {
                getProgressText(SVGtext,"Wikipedia:WikiProject National Register of Historic Places/Progress");
            }
        }
    });
}

function getProgressText(SVGtext,title) { // get progress page wikitext
    var ProgressText="";
    $.ajax({
        dataType: "json",
        url: mw.util.wikiScript('api'),
        data: {
            format: 'json',
            action: 'query',
            prop: 'revisions',
            rvprop: 'content',
            titles: title,
            indexpageids: true,
            redirects: 'true'
        },
        error: function() {ProgressText="error"},
        success: function(output) {
            for (var page in output.query.pages) {
                ProgressText=output.query.pages[page].revisions[0]['*'];
            }
        },
        complete: function() {
            if (ProgressText=="error") {
                var SVGDiv=document.getElementById("SVGDiv");
                SVGDiv.innerHTML+=" Unable to fetch wikitext! Script aborted.";
            } else {
                ExtractMapInfo(SVGtext,ProgressText);
            }
        }
    });
}

function ExtractMapInfo(SVGtext,ProgressText) {
    var SVGDiv=document.getElementById("SVGDiv");
    SVGDiv.innerHTML+=" Done!";

    ProgressText=ProgressText.split("==State totals==")[1]; // get rid of header

    var StateNames= ["OVERALL STATE PERCENTAGES","ALABAMA","ALASKA","ARIZONA","ARKANSAS","CALIFORNIA","COLORADO","CONNECTICUT","DELAWARE"];
    StateNames.push("D.C.","FLORIDA","GEORGIA","HAWAII","IDAHO","ILLINOIS","INDIANA","IOWA","KANSAS","KENTUCKY","LOUISIANA","MAINE");
    StateNames.push("MARYLAND","MASSACHUSETTS","MICHIGAN","MINNESOTA","MISSISSIPPI","MISSOURI","MONTANA","NEBRASKA","NEVADA");
    StateNames.push("NEW HAMPSHIRE","NEW JERSEY","NEW MEXICO","NEW YORK","NORTH CAROLINA","NORTH DAKOTA","OHIO","OKLAHOMA","OREGON");
    StateNames.push("PENNSYLVANIA","RHODE ISLAND","SOUTH CAROLINA","SOUTH DAKOTA","TENNESSEE","TEXAS","UTAH","VERMONT");
    StateNames.push("VIRGINIA","WASHINGTON","WEST VIRGINIA","WISCONSIN","WYOMING");

    var StateAbbrs= ['XX','AL','AK','AZ','AR','CA','CO','CT','DE','DC','FL','GA','HI','ID','IL','IN','IA','KS','KY','LA','ME','MD'];
    StateAbbrs.push('MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ','NM','NY','NC','ND','OH','OK','OR','PA','RI','SC','SD');
    StateAbbrs.push('TN','TX','UT','VT','VA','WA','WV','WI','WY','PR','GU','VI','MP','AS');

    var TempStartIndex;
	var TempEndIndex;
	var i,j,k;
    var nextID;
    var nextRowText;
    var StatsToCheck=["Illustrated","Articled","Start+","Net Quality"];
    for (var currentStat=0; currentStat<StatsToCheck.length; currentStat++ ) { // loop through each statistic to create SVG code
        var ThisStatText="";
        var OverallStatePercentagesText="";
        var NoListings=[];

        var total;
        var illustrated;
        var stubs;
        var NRISonly;
        var startPlus;
        var unassessed;
        var untagged;
        var articled;
        
	    var Percentage=0;
    	var TableStartIndex=0;
        for (i=0; i<StateNames.length; i++) { // loop through all states, i=0 corresponds to national table
            var StateTable={"100Percent":[], "90Percent":[], "80Percent":[], "70Percent":[], "60Percent":[], "50Percent":[], "40Percent":[], "30Percent":[], "20Percent":[], "10Percent":[], "00Percent":[]};

            TableStartIndex=ProgressText.indexOf("{|",TableStartIndex+1);
            var TableEndIndex=ProgressText.indexOf("|}",TableStartIndex)+2;
            var TableText=ProgressText.substr(TableStartIndex,TableEndIndex-TableStartIndex);

            var RowStartIndex=0;
            var RowNumber=0;
            var DCdone=false;
            while (RowStartIndex!=-1) { // loop until no more rows
                RowStartIndex=TableText.indexOf("\n|-",RowStartIndex+1); // find next row in table
                var RowEndIndex=TableText.indexOf("\n|-",RowStartIndex+1);
                if (RowEndIndex==-1) RowEndIndex=TableText.length; // last row ends at end of table
                var RowText=TableText.substr(RowStartIndex,RowEndIndex-RowStartIndex);

                RowNumber++;
                if (i===0&&RowNumber==StateAbbrs.length) break; // manual break for national table since some rows aren't on the map

                var ID=RowText.match(/(\d{5}|-----|ddddd)/g);
                if (i===0) ID=StateAbbrs[RowNumber];
                if (ID===null||ID[0]=="ddddd"||ID[0]=="-----") continue; // skip duplicate rows, sublists, and state total row
                if (i!==0) ID=ID[0];
                if (ID=="02230"||ID=="02275"||ID=="02063") continue; // skip special cases in Alaska, taken care of below

                if (ID=="02105") { // special case for Hoonah–Angoon Census Area, Alaska
                    TempStartIndex=RowStartIndex;
                    nextID="00000";
                    while (nextID!="02230") { // look for Skagway
                        TempStartIndex=TableText.indexOf("\n|-",TempStartIndex+1);
                        TempEndIndex=TableText.indexOf("\n|-",TempStartIndex+1);
                        if (TempEndIndex==-1) TempEndIndex=TableText.length;
                        nextRowText=TableText.substr(TempStartIndex,TempEndIndex-TempStartIndex);
                        nextID=nextRowText.match(/(\d{5}|-----|ddddd)/g);
                        if (nextID!==null) nextID=nextID[0];
                    }
                    var HoonahStats=RowText.match(/[^\d](\d{1,3},)*\d{1,3}(?!(%|\.|\d))/g);
                    var SkagwayStats=nextRowText.match(/[^\d](\d{1,3},)*\d{1,3}(?!(%|\.|\d))/g);

                    switch(currentStat) {
                        case 0: // Illustrated
                            total=parseFloat(HoonahStats[0])+parseFloat(SkagwayStats[0]);
                            illustrated=parseFloat(HoonahStats[1])+parseFloat(SkagwayStats[1]);
                            Percentage=Math.round(illustrated/total*1000)/10;
                            break;
                        case 1: // Articled
                            total=parseFloat(HoonahStats[0])+parseFloat(SkagwayStats[0]);
                            articled=parseFloat(HoonahStats[2])+parseFloat(SkagwayStats[2]);
                            Percentage=Math.round(articled/total*1000)/10;
                            break;
                        case 2: // Start+
                            total=parseFloat(HoonahStats[0])+parseFloat(SkagwayStats[0]);
                            startPlus=parseFloat(HoonahStats[5])+parseFloat(SkagwayStats[5]);
                            Percentage=Math.round(startPlus/total*1000)/10;
                            break;
                        case 3: // Net Quality
                            total=parseFloat(HoonahStats[0])+parseFloat(SkagwayStats[0]);
                            illustrated=parseFloat(HoonahStats[1])+parseFloat(SkagwayStats[1]);
                            stubs=parseFloat(HoonahStats[3])+parseFloat(SkagwayStats[3]);
                            NRISonly=parseFloat(HoonahStats[4])+parseFloat(SkagwayStats[4]);
                            startPlus=parseFloat(HoonahStats[5])+parseFloat(SkagwayStats[5]);
                            unassessed=parseFloat(HoonahStats[6])+parseFloat(SkagwayStats[6]);
                            untagged=parseFloat(HoonahStats[7])+parseFloat(SkagwayStats[7]);

                            Percentage=startPlus+0.5*stubs+0.5*unassessed-0.5*untagged-0.75*NRISonly;
                            Percentage=Math.round((0.75*Percentage/total+0.25*illustrated/total)*1000)/10;
                            if (Percentage<0) Percentage = 0;
                            break;
                    }
                    ID="02232"; // set special ID for map
                } else if (ID=="02280") { // special case for Petersburg Census Area, Alaska
                    TempStartIndex=RowStartIndex;
                    nextID="00000";
                    while (nextID!="02275") { // look for Wrangell
                        TempStartIndex=TableText.indexOf("\n|-",TempStartIndex+1);
                        TempEndIndex=TableText.indexOf("\n|-",TempStartIndex+1);
                        if (TempEndIndex==-1) TempEndIndex=TableText.length;
                        nextRowText=TableText.substr(TempStartIndex,TempEndIndex-TempStartIndex);
                        nextID=nextRowText.match(/(\d{5}|-----|ddddd)/g);
                        if (nextID!==null) nextID=nextID[0];
                    }
                    var PetersburgStats=RowText.match(/[^\d](\d{1,3},)*\d{1,3}(?!(%|\.|\d))/g);
                    var WrangellStats=nextRowText.match(/[^\d](\d{1,3},)*\d{1,3}(?!(%|\.|\d))/g);

                    Percentage=0;
                    switch(currentStat) {
                        case 0: // Illustrated
                            total=parseFloat(PetersburgStats[0])+parseFloat(WrangellStats[0]);
                            illustrated=parseFloat(PetersburgStats[1])+parseFloat(WrangellStats[1]);
                            Percentage=Math.round(illustrated/total*1000)/10;
                            break;
                        case 1: // Articled
                            total=parseFloat(PetersburgStats[0])+parseFloat(WrangellStats[0]);
                            articled=parseFloat(PetersburgStats[2])+parseFloat(WrangellStats[2]);
                            Percentage=Math.round(articled/total*1000)/10;
                            break;
                        case 2: // Start+
                            total=parseFloat(PetersburgStats[0])+parseFloat(WrangellStats[0]);
                            startPlus=parseFloat(PetersburgStats[5])+parseFloat(WrangellStats[5]);
                            Percentage=Math.round(startPlus/total*1000)/10;
                            break;
                        case 3: // Net Quality
                            total=parseFloat(PetersburgStats[0])+parseFloat(WrangellStats[0]);
                            illustrated=parseFloat(PetersburgStats[1])+parseFloat(WrangellStats[1]);
                            stubs=parseFloat(PetersburgStats[3])+parseFloat(WrangellStats[3]);
                            NRISonly=parseFloat(PetersburgStats[4])+parseFloat(WrangellStats[4]);
                            startPlus=parseFloat(PetersburgStats[5])+parseFloat(WrangellStats[5]);
                            unassessed=parseFloat(PetersburgStats[6])+parseFloat(WrangellStats[6]);
                            untagged=parseFloat(PetersburgStats[7])+parseFloat(WrangellStats[7]);

                            Percentage=startPlus+0.5*stubs+0.5*unassessed-0.5*untagged-0.75*NRISonly;
                            Percentage=Math.round((0.75*Percentage/total+0.25*illustrated/total)*1000)/10;
                            if (Percentage<0) Percentage = 0;
                            break;
                    }
                } else if (ID == "02061") { // former Valdez-Cordova Census Area, now placeholder for Copper River
                    TempStartIndex=RowStartIndex;
                    nextID="00000";
                    while (nextID!="02063") { // look for Chugach Census Area
                        TempStartIndex=TableText.indexOf("\n|-",TempStartIndex+1);
                        TempEndIndex=TableText.indexOf("\n|-",TempStartIndex+1);
                        if (TempEndIndex==-1) TempEndIndex=TableText.length;
                        nextRowText=TableText.substr(TempStartIndex,TempEndIndex-TempStartIndex);
                        nextID=nextRowText.match(/(\d{5}|-----|ddddd)/g);
                        if (nextID!==null) nextID=nextID[0];
                    }
                    var CopperRiverStats=RowText.match(/[^\d](\d{1,3},)*\d{1,3}(?!(%|\.|\d))/g);
                    var ChugachStats=nextRowText.match(/[^\d](\d{1,3},)*\d{1,3}(?!(%|\.|\d))/g);

                    Percentage=0;
                    switch(currentStat) {
                        case 0: // Illustrated
                            total=parseFloat(CopperRiverStats[0])+parseFloat(ChugachStats[0]);
                            illustrated=parseFloat(CopperRiverStats[1])+parseFloat(ChugachStats[1]);
                            Percentage=Math.round(illustrated/total*1000)/10;
                            break;
                        case 1: // Articled
                            total=parseFloat(CopperRiverStats[0])+parseFloat(ChugachStats[0]);
                            articled=parseFloat(CopperRiverStats[2])+parseFloat(ChugachStats[2]);
                            Percentage=Math.round(articled/total*1000)/10;
                            break;
                        case 2: // Start+
                            total=parseFloat(CopperRiverStats[0])+parseFloat(ChugachStats[0]);
                            startPlus=parseFloat(CopperRiverStats[5])+parseFloat(ChugachStats[5]);
                            Percentage=Math.round(startPlus/total*1000)/10;
                            break;
                        case 3: // Net Quality
                            total=parseFloat(CopperRiverStats[0])+parseFloat(ChugachStats[0]);
                            illustrated=parseFloat(CopperRiverStats[1])+parseFloat(ChugachStats[1]);
                            stubs=parseFloat(CopperRiverStats[3])+parseFloat(ChugachStats[3]);
                            NRISonly=parseFloat(CopperRiverStats[4])+parseFloat(ChugachStats[4]);
                            startPlus=parseFloat(CopperRiverStats[5])+parseFloat(ChugachStats[5]);
                            unassessed=parseFloat(CopperRiverStats[6])+parseFloat(ChugachStats[6]);
                            untagged=parseFloat(CopperRiverStats[7])+parseFloat(ChugachStats[7]);

                            Percentage=startPlus+0.5*stubs+0.5*unassessed-0.5*untagged-0.75*NRISonly;
                            Percentage=Math.round((0.75*Percentage/total+0.25*illustrated/total)*1000)/10;
                            if (Percentage<0) Percentage = 0;
                            break;
                    }
                } else if (ID=="11001") { // special case for DC
                    if (DCdone) continue; // only record DC once
                    DCdone=true;
                    TempStartIndex=RowStartIndex;
                    var matched=false;
                    while (!matched) { // look for total row
                        TempStartIndex=TableText.indexOf("\n|-",TempStartIndex+1);
                        TempEndIndex=TableText.indexOf("\n|-",TempStartIndex+1);
                        if (TempEndIndex==-1) TempEndIndex=TableText.length;
                        nextRowText=TableText.substr(TempStartIndex,TempEndIndex-TempStartIndex);
                        var test=nextRowText.match("State Total");
                        if (test!==null) matched=true;
                    }
                    Percentage=nextRowText.match(/(\d|\.)+(?=%)/g);
                    Percentage=parseFloat(Percentage[currentStat]);
                } else {
                    Percentage=RowText.match(/(\d|\.)+(?=%)/g);           // extract percentages from row text
                    if (Percentage===null) {NoListings.push(ID); continue}    // if no percentages, must be no listings
                    Percentage=parseFloat(Percentage[currentStat]);           // get relevant percentage for this stat
                }

                if (Percentage==100) {StateTable["100Percent"].push(ID)}
                else if (Percentage>=90) {StateTable["90Percent"].push(ID)}
                else if (Percentage>=80) {StateTable["80Percent"].push(ID)}
                else if (Percentage>=70) {StateTable["70Percent"].push(ID)}
                else if (Percentage>=60) {StateTable["60Percent"].push(ID)}
                else if (Percentage>=50) {StateTable["50Percent"].push(ID)}
                else if (Percentage>=40) {StateTable["40Percent"].push(ID)}
                else if (Percentage>=30) {StateTable["30Percent"].push(ID)}
                else if (Percentage>=20) {StateTable["20Percent"].push(ID)}
                else if (Percentage>=10) {StateTable["10Percent"].push(ID)}
                else {StateTable["00Percent"].push(ID)}
            }
            ThisStatText+="/* *******************************************************************\n";
            ThisStatText+="                              " + StateNames[i] + "\n";
            ThisStatText+="******************************************************************** */\n";

            var colors=["313695","4575B4","74ADD1","ABD9E9","E0F3F8","FFFFBF","FEE090","FDAE61","F46D43","D73027","A50026"];
            for (j=10; j>-1; j--) { // loop through each percentage
                ThisStatText+="/* ";
                if (j!==0) ThisStatText+=j;
                ThisStatText+="0% "+StatsToCheck[currentStat]+" */\n      ";
                for (k=0; k<StateTable[j+"0Percent"].length; k++) {
                    if (i===0) {
                        ThisStatText+="."+StateTable[j+"0Percent"][k]+", ";
                    } else {
                        ThisStatText+=".c"+StateTable[j+"0Percent"][k]+", ";
                    }
                }
                if (StateTable[j+"0Percent"].length>0) ThisStatText=ThisStatText.substr(0,ThisStatText.length-2)+" ";
                ThisStatText+="{fill: #"+colors[j]+"; }\n";
            }
            ThisStatText+="\n";
            if (i===0) { // wait to add state totals to end of list
                OverallStatePercentagesText=ThisStatText.substr(0,ThisStatText.length-2); // remove final line breaks
                ThisStatText="";
            }
        }
        // now add no listings text
        ThisStatText+="/* *******************************************************************\n";
        ThisStatText+="                        NO LISTINGS IN COUNTY\n";
        ThisStatText+="******************************************************************** */\n      ";
        for (k=0; k<NoListings.length; k++) {
            ThisStatText+=".c"+NoListings[k]+", ";
        }
        if (NoListings.length>0) ThisStatText=ThisStatText.substr(0,ThisStatText.length-2)+" ";
        ThisStatText+="{fill: #000000; }\n\n";

        // finally add state totals text
        ThisStatText+=OverallStatePercentagesText;

        // now update SVG text
        var StartIndex = 0;
        var InsertLocations = [];
        var SearchStr = 'overflow:scroll">';
        for (i=0; i<StatsToCheck.length; i++) {
            InsertLocations[i] = SVGtext.indexOf(SearchStr, StartIndex) + SearchStr.length + 1;
            StartIndex = InsertLocations[i];
        }

        StartIndex = 0;
        var EndInsertLocations = [];
        SearchStr = '</pre>';
        for (i=0; i<StatsToCheck.length; i++) {
            EndInsertLocations[i] = SVGtext.indexOf(SearchStr, StartIndex) - 1;
            StartIndex = EndInsertLocations[i] + SearchStr.length + 1;
        }
        var oldStatText=SVGtext.substr(InsertLocations[currentStat],EndInsertLocations[currentStat]-InsertLocations[currentStat]);
        SVGtext=SVGtext.replace(oldStatText,ThisStatText);
    }
    // now edit page with updated SVG text
    var d=new Date();
    var months = ['January','February','March','April','May','June','July','August','September','October'];
    months.push('November','December');
    var year=d.getYear();
    if (year < 1000) year += 1900;
    var DateStr = months[d.getMonth()]+" "+d.getDate()+", "+year;

    var summary="Updating SVG output as of "+DateStr+" using [[User:Magicpiano/NRBot/NRHPmap.js|script]].";

    SVGDiv.innerHTML+="<br>Editing page...";
    editMapPage("Wikipedia:WikiProject National Register of Historic Places/Progress/SVG",SVGtext,summary);
}

function editMapPage(title,text,summary) { // edit page when done
    $.ajax({
        dataType: 'json',
        url: mw.util.wikiScript( 'api' ),
        type: 'POST',
        data: {
            format: 'json',
            action: 'edit',
            title: title,
            text: text,
            summary: summary,
            token: mw.user.tokens.get( 'csrfToken' )
        },
        success: function( data ) {
            if (data && data.edit && data.edit.result && data.edit.result == 'Success') {
            	console.log('Page edit succeeded');
            } else {
            	var msg;
            	if (data && data.error) {
            		msg = "Error editing page: ";
            		msg += " code=" + data.error.code;
            		msg += " info=" + data.error.info;
        		} else {
        			msg = "Error editing page (no data or error)";
        		}
                alert(msg);
            }
        },
        error: function(ajaxResponse,status,errorThrown) {ajaxResponse.errorThrown=errorThrown},
        complete: function(ajaxResponse,status) {MapPageEdited(ajaxResponse,status)}
    });
}

function MapPageEdited(ajaxResponse,status) {
    var SVGDiv=document.getElementById("SVGDiv");
    if (status!="success") {
        SCGDiv.innerHTML+="Edit failed!";
        return;
    }
    var responseText=JSON.parse(ajaxResponse.responseText);
    var diff=responseText.edit.newrevid;
    var linkStr="//en.wikipedia.org/w/index.php?diff="+diff;
    SVGDiv.innerHTML+="Page edited! Click <a href='"+linkStr+"'>here</a> for diff.";
}

function MakePreCopyable() {
    if (mw.config.get('wgPageName') != "Wikipedia:WikiProject_National_Register_of_Historic_Places/Progress/SVG") return;
    var pres = document.getElementsByTagName('pre');
    while (pres.length>0) {
        var textarea = document.createElement('textarea');
        textarea.innerHTML = pres[0].innerHTML;
        pres[0].parentNode.insertBefore(textarea,pres[0].nextSibling);
        pres[0].parentNode.removeChild(pres[0]);
        textarea.setAttribute('style', 'width: 580px; height: 300px');
        textarea.setAttribute('onclick', 'this.focus();this.select()');
    }
}

$.when($.ready).then(function () { NRHPmapbutton(); MakePreCopyable();});