Jump to content

User:Dudemanfellabra/NRISOnly.js

From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Dudemanfellabra (talk | contribs) at 05:16, 3 March 2015 (debug). The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.
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.
var errortext = ''   // error text is a global variable
var errorArray = ['No county section found for county ','No table found for county ','Wikitext error for county ','Unknown error for county ']
errorArray.push('Incorrectly formatted table for county')

function CheckPermission() {
    if (mw.user.getName()!="NationalRegisterBot"&&mw.user.getName()!="Dudemanfellabra") {
        alert("Only NationalRegisterBot has been authorized to use the NRIS-only script!")
        return
    } else {
        NRISOnlyButton()
        NRISOnlyButton2()
        NRISOnlyButton3()
    }
}

function NRISOnlyButton() {
    if (wgPageName!="Wikipedia:WikiProject_National_Register_of_Historic_Places/Progress"||location.href.indexOf('action')!=-1) {
        if (wgPageName!="User:Dudemanfellabra/AltSandbox") return;
    }
    var button=document.createElement("input")
    button.setAttribute("type", "button");
    button.setAttribute("value", "Update list for NationalRegisterBot");
    button.setAttribute("id", "nrisonlybutton");
    button.setAttribute("onclick", "NRISOnlyClick()");
    var content=document.getElementById('mw-content-text')

    content.parentNode.insertBefore(button, content)
}

function NRISOnlyButton2() {
    if (wgPageName.indexOf("User:NationalRegisterBot/NRISOnly")==-1||location.href.indexOf('action')!=-1)  return;

    var button=document.createElement("input")
    button.setAttribute("type", "button");
    button.setAttribute("value", "Who created these?");
    button.setAttribute("id", "nrisonlybutton2");
    button.setAttribute("onclick", "NRISOnlyClick2('who')");
    if (wgPageName.indexOf("/All")==-1) {
        var thediv=document.getElementById('tobetagged-div')
    } else {
        var thediv=document.getElementById('NRISOnly-div')
    }
    if (thediv!=null) thediv.parentNode.insertBefore(button, thediv)

    if (wgPageName.indexOf("/All")==-1) {
        var button2=document.createElement("input")
        button2.setAttribute("type", "button");
        button2.setAttribute("value", "Tag these articles");
        button2.setAttribute("id", "nrisonlybutton3");
        button2.setAttribute("onclick", "NRISOnlyClick2('tag')");
        var content=document.getElementById('mw-content-text')
        content.parentNode.insertBefore(button2, content)
    }
}

function NRISOnlyButton3() {
    if (wgPageName!="User:NationalRegisterBot/AllNRHPPages/Duplications"||location.href.indexOf('action')!=-1) return;
    var button=document.createElement("input")
    button.setAttribute("type", "button");
    button.setAttribute("value", "Gather duplicate stats");
    button.setAttribute("id", "nrisonlybutton3");
    button.setAttribute("onclick", "NRISOnlyClick3()");
    var content=document.getElementById('mw-content-text')

    content.parentNode.insertBefore(button, content)
}

function NRISOnlyClick() {   // after button is clicked, disable it and fetch wikitext of Progress page
    var nrisonlybutton = document.getElementById('nrisonlybutton')
    nrisonlybutton.disabled = true
    var ProgressDiv = document.createElement("div")
    ProgressDiv.setAttribute("id", "ProgressDiv")
    ProgressDiv.setAttribute("style", "width:500px; border:5px solid black; padding:5px; position:fixed; background:#ffffff")
    ProgressDiv.style.left = "200px"
    ProgressDiv.style.top = "150px"
    nrisonlybutton.parentNode.insertBefore(ProgressDiv, nrisonlybutton)
    LoadNRISOnlyPages();
}

function NRISOnlyClick2(option) {
    if (option=='who') {
        var nrisonlybutton = document.getElementById('nrisonlybutton2')
    } else {
        var nrisonlybutton = document.getElementById('nrisonlybutton3')
    }
    nrisonlybutton.disabled = true
    var ProgressDiv = document.createElement("div")
    ProgressDiv.setAttribute("id", "ProgressDiv")
    ProgressDiv.setAttribute("style", "width:600px; border:5px solid black; padding:5px; background:#ffffff")
    if (option=='who') {
        QueryEditors();
    } else {
        nrisonlybutton.parentNode.insertBefore(ProgressDiv, nrisonlybutton)
        TagNRISOnly();
    }
}

function NRISOnlyClick3() {
    var nrisonlybutton = document.getElementById('nrisonlybutton3')
    nrisonlybutton.disabled = true
    var ProgressDiv = document.createElement("div")
    ProgressDiv.setAttribute("id", "ProgressDiv")
    ProgressDiv.setAttribute("style", "width:500px; border:5px solid black; padding:5px; background:#ffffff")
    ProgressDiv.style.left = "200px"
    ProgressDiv.style.top = "150px"
    nrisonlybutton.parentNode.insertBefore(ProgressDiv, nrisonlybutton)
    CheckDuplicates();
}

// load pages in background
function LoadNRISOnlyPages() {

    alert(mw.user.tokens.get( 'editToken' ))

    var Section = "blank"
    var Titles = [];
    var RawTitles = [];
    var TotalTitles = [];
    var ToBeTagged = [];
    var ToBeUntagged = [];
    var Unreferenced = [];
    var OneRefNotNRIS = [];
    var AllNRISOnly = [];
    var AllDabs = [];
    var MissingRefnum = [];
    var Substubs = [];
    var Errors = [];
    var NRISCounty = [];
    var Duplications = [];

    var table=document.getElementsByClassName('wikitable sortable');
    var TotalRows=document.getElementsByTagName('tr')
    var TopTableRows=table[0].getElementsByTagName('tr')
    var counties = TotalRows.length
    counties = counties - TopTableRows.length - 3*(table.length - 1) - 2
    // subtract off rows in top table, TOC, shortcut, and duplicate rows

    var TotalDone = 0

    var ProgressDiv = document.getElementById('ProgressDiv')
    var ProgressSpan = document.createElement("span")
    ProgressSpan.setAttribute("id", "ProgressSpan")
    ProgressDiv.appendChild(ProgressSpan)
    ProgressSpan.innerHTML = "Extracting article titles... 0 (0%) of "+counties+" lists examined."
    var SubpageSpan = document.createElement("span")
    SubpageSpan.setAttribute("id", "SubpageSpan")
    ProgressDiv.appendChild(SubpageSpan)
    SubpageSpan.innerHTML += "<br />Finding NRIS-only articles..."
    var TimeSpan = document.createElement("span")
    TimeSpan.setAttribute("id", "TimeSpan")
    ProgressDiv.appendChild(TimeSpan)
    TimeSpan.innerHTML += "<br />Estimated time remaining: Calculating..."

    var stop=table.length   // table.length is dynamic, so make it static before loading hidden tables
    ProgressDiv.style.top = "5px" // move ProgressDiv higher on screen
    var Timestamps = [];
    for (var i=1; i<stop; i++) {
        var rows=table[i].getElementsByTagName('tr');

        for (var j=1; j<rows.length-2; j++) {
            errortext = errorArray[4]
            Titles=[["error"],[]];

            rows[j].scrollIntoView()                 // highlight current county
            window.scrollBy(0,-100)
            rows[j].style.outline = "2px solid red"

            var tds=rows[j].getElementsByTagName('td');
            if (tds.length < 13) { // skip top table
                rows[j].style.outline = ""
                continue
            }
            var countylink=tds[1].getElementsByTagName('a');

            if (!countylink[0] || countylink[0].href.search("#") != -1) {
                TotalDone++
                rows[j].style.outline = ""
                continue
            }
            var Path = countylink[0].title
            if (countylink[0].className.search("mw-redirect") != -1) { // if redirect, get section name
                Section = countylink[0].innerHTML
            }
            else {
                Section = Path
            }
            Titles=GetArticleTitles(Path,Section,tds);

            if (Titles[0][0] == "error") {
                throw new Error(errortext+parseFloat(TotalDone+1)+"!\nScript aborted!")
            } else {
                for (var k=0; k<Titles[2].length; k++) {
                    Titles[2][k].push(Path)  // also store county name for finding duplications
                    RawTitles[RawTitles.length] = Titles[2][k]
                }
                for (var k=0; k<Titles[3].length; k++) {
                    MissingRefnum[MissingRefnum.length] = [Titles[3][k],Path]
                }
                AllDabs=AllDabs.concat(Titles[1])
                TotalTitles=TotalTitles.concat(Titles[0])
                if (Titles[0][0]!="") {
                    NRISCounty = NRISOnlyQuery(Titles[0],SubpageSpan)
                    ToBeTagged=ToBeTagged.concat(NRISCounty[0])
                    ToBeUntagged=ToBeUntagged.concat(NRISCounty[1])
                    Unreferenced=Unreferenced.concat(NRISCounty[2])
                    OneRefNotNRIS=OneRefNotNRIS.concat(NRISCounty[3])
                    AllNRISOnly = AllNRISOnly.concat(NRISCounty[4])
                    Substubs = Substubs.concat(NRISCounty[5])
                    Errors=Errors.concat(NRISCounty[6])
                    NRISCounty=[]
                }
                SubpageSpan.innerHTML="<br />Finding NRIS-only articles..."
            }

            TotalDone++
            Timestamps.push(new Date())
            if (TotalDone>1) { // calculate time elapsed/remaining
                var MinutesElapsed = (Timestamps[Timestamps.length-1]-Timestamps[0])/60000
                var Average = MinutesElapsed/TotalDone
                MinutesElapsed = Math.round(MinutesElapsed)
                var MinutesRemaining = Math.round(Average*(counties-TotalDone))
                var HoursRemaining = 0
                if (MinutesRemaining == 0) {
                    MinutesRemaining = "Less than one"
                } else {
                    while (MinutesRemaining > 59) {
                        var MinutesRemaining = MinutesRemaining-60
                        HoursRemaining++
                    }
                }
                var HoursElapsed = 0
                if (MinutesElapsed != 0) {
                    while (MinutesElapsed > 59) {
                        var MinutesElapsed = MinutesElapsed-60
                        HoursElapsed++
                    }
                }
                var TimeRemainingStr = MinutesRemaining+" min"
                if (HoursRemaining>0) TimeRemainingStr = HoursRemaining+" hr "+TimeRemainingStr
                var TimeElapsedStr = ""
                if (MinutesElapsed!=0||HoursElapsed!=0) TimeElapsedStr=MinutesElapsed+" min"
                if (HoursElapsed>0) TimeElapsedStr = HoursElapsed+" hr "+TimeElapsedStr
                if (TimeElapsedStr!="") TimeRemainingStr+=" ("+TimeElapsedStr+" elapsed)"
            } else {
                var TimeRemainingStr = "Calculating..."
            }

            var PercentDone = Math.round(TotalDone/counties*1000)/10
            if (PercentDone.toString().indexOf(".") == -1 && PercentDone != 100 && PercentDone != 0) {
                PercentDone += ".0" }
            var ProgressStr="Extracting article titles... " + TotalDone + " (" + PercentDone + "%) of " + counties
            ProgressStr = ProgressStr + " lists examined."
            TimeSpan.innerHTML = "<br />Estimated time remaining: "+TimeRemainingStr
            ProgressSpan.innerHTML = ProgressStr
            rows[j].style.outline = ""
        }
    }

    var MinutesElapsed = Math.round((Timestamps[Timestamps.length-1]-Timestamps[0])/60000)
    var HoursElapsed = 0
    if (MinutesElapsed == 0) {
        MinutesElapsed = "Less than one"
    } else {
        while (MinutesElapsed > 59) {
            var MinutesElapsed = MinutesElapsed-60
            HoursElapsed++
        }
    }
    var TimeElapsedStr = MinutesElapsed+" min"
    if (HoursElapsed>0) TimeElapsedStr = HoursElapsed+" hr "+TimeElapsedStr

    var OutputBase = "User:NationalRegisterBot/AllNRHPPages"

    var ProgressStr="Extracting article titles... "+TotalDone+" ("+PercentDone+"%) of "+counties+" lists examined."
    var SubpageStr="<br />Extraction complete. "+TotalTitles.length+" distinct titles found. Time elapsed: "+TimeElapsedStr
    SubpageStr+="<br />Dumping extracted titles to subpages of <a href='http://en.wikipedia.org/wiki/"+OutputBase+"'>"
    SubpageStr+=OutputBase+"</a>."
    ProgressSpan.innerHTML = ProgressStr
    SubpageSpan.innerHTML = SubpageStr
    TimeSpan.innerHTML="<br />0 subpages edited successfully..."

    for (var i=0;i<RawTitles.length; i++) {           // find articles duplicated across county lines
        Duplications[Duplications.length]=[RawTitles[i][0],RawTitles[i][1],[RawTitles[i][2]]]
        for (var j=i+1;j<RawTitles.length; j++) {
            if (RawTitles[i][1]==RawTitles[j][1]) {
                Duplications[Duplications.length-1][2].push(RawTitles[j][2])
                RawTitles.splice(j,1)
                j--
            }
        }
        if (Duplications[Duplications.length-1][2].length==1) Duplications.pop()
    }

    for (var i=0;i<TotalTitles.length; i++) {           // remove duplicates from total list
        for (var j=i+1;j<TotalTitles.length; j++) {
            if (TotalTitles[i]==TotalTitles[j]) {TotalTitles.splice(j,1);j--}
        }
    }

    TotalTitles.sort()

    var d=new Date();
    var months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
    var year=d.getYear();
    if (year < 1000) year += 1900
    var DateStr = months[d.getMonth()]+" "+d.getDate()+", "+year

    var firstletter=TotalTitles[0].substr(0,1)
    var basewikitext = ""
    var partialwikitext = "=="+firstletter+"==\n{{refbegin|3}}\n"
    var subpages=1
    for (var k=1; k<TotalTitles.length+1; k++) {
        var oldfirstletter = firstletter
        firstletter = TotalTitles[k-1].substr(0,1)
        if (firstletter!=oldfirstletter) {
            partialwikitext+='{{refend}}'

            editPageNRISOnly({
                title: OutputBase+'/'+oldfirstletter,
                text: partialwikitext,
            summary:'Generate list of all NRHP articles beginning with '+oldfirstletter+' linked from county lists as of '+DateStr
            },TimeSpan,subpages,"no");
            subpages++

            basewikitext+='*[[/'+oldfirstletter+']]\n'
            partialwikitext='=='+firstletter+'==\n{{refbegin|3}}\n# <li value="'+k+'">[['+TotalTitles[k-1]+']]\n'
        } else {
            partialwikitext+='# [['+TotalTitles[k-1]+']]\n'
        }
    }
    partialwikitext+="{{refend}}"
    editPageNRISOnly({
        title: OutputBase+'/'+firstletter,
        text: partialwikitext,
        summary:'Generate list of all NRHP articles beginning with '+firstletter+' linked from county lists as of '+DateStr
    },TimeSpan,subpages,"no");

    basewikitext+='*[[/'+firstletter+']]\n<hr />*[[/Duplications]]'

    editPageNRISOnly({
        title: OutputBase,
        text: basewikitext,
        summary: 'Generate list of all NRHP articles linked from county lists as of '+DateStr
    },TimeSpan,subpages,"almost");

    // now dump list of duplicates
    var wikitext = "{{TOC right}}\n<div id=\"duplicates-div\">\n"
    if (Duplications.length>0) {
        var multistate="==Multi-state==\n<div id=\"multistate\">\n{{refbegin|2}}\n"
        var unknown="==Unknown==\n<div id=\"unknown\">\n{{refbegin|2}}\n"
        var oldstate=""
        var state=""
        var prevstate=""
        var multiflag="no"
        var unknownflag="no"
        var firststateflag="yes"
        var partialwikitext=""
        for (var k=1; k<Duplications.length+1; k++) {
            partialwikitext="# [["+Duplications[k-1][0]+"]]&nbsp;(#"+Duplications[k-1][1]+") <small>("
            multiflag="no"
            unknownflag="no"
            for (var l=0; l<Duplications[k-1][2].length; l++) {
                state=getState(Duplications[k-1][2][l])
                if (l>0) {
                    if (oldstate==""||state=="") {
                        unknownflag="yes"
                    } else if (state!=oldstate) {
                        multiflag="yes"
                    }
                }
                oldstate=state
                partialwikitext+="[["+Duplications[k-1][2][l]+"|"
              partialwikitext+=Duplications[k-1][2][l].replace(/National Register of Historic Places listings (i|o)n /g,"")+"]]; "
            }
            partialwikitext=partialwikitext.substr(0,partialwikitext.length-2)
            partialwikitext+=")</small>\n"
            if (unknownflag=="yes") {
                unknown+=partialwikitext
            } else if (multiflag=="yes") {
                multistate+=partialwikitext
            } else {
                if (state!=prevstate) {
                    if (firststateflag=="yes") {
                        wikitext+="=="+state+"==\n{{refbegin|2}}\n"
                        firststateflag="no"
                    } else {
                        wikitext+="{{refend}}\n\n=="+state+"==\n{{refbegin|2}}\n"
                    }
                }
                var smalltext=partialwikitext.match(/<small>(.)*?<\/small>/g)[0]
                var newsmalltext=smalltext.replace(/,[a-zA-Z .,]*?]]/g,"]]") // remove state names from dupes inside single state
                partialwikitext=partialwikitext.replace(smalltext,newsmalltext)
                wikitext+=partialwikitext
                prevstate=state
            }
        }
        wikitext+="{{refend}}\n</div>"
        if (multistate!="==Multi-state==\n<div id=\"multistate\">\n{{refbegin|2}}\n") {
            wikitext+="\n\n"+multistate+"\n{{refend}}\n</div>"
        }
        if (unknown!="==Unknown==\n<div id=\"unknown\">\n{{refbegin|2}}\n") {
            wikitext+="\n\n"+unknown+"\n{{refend}}\n</div>"
        }
    }

    editPageNRISOnly({
        title: OutputBase+'/Duplications',
        text: wikitext,
        summary:'Generate list of all NRHP articles duplicated across county lines as of '+DateStr
    },TimeSpan,subpages,"");

    var OutputPage="User:NationalRegisterBot/NRISOnly"
    TimeSpan.innerHTML+="<br />Outputting data to <a href='http://en.wikipedia.org/wiki/"+OutputPage+"'>"+OutputPage+"</a>..."

    for (var i=0;i<ToBeTagged.length; i++) {                            // get rid of articles duplicated across county lines
        for (var j=i+1;j<ToBeTagged.length; j++) {
            if (ToBeTagged[i]==ToBeTagged[j]) {ToBeTagged.splice(j,1); j--}
        }
    }
    for (var i=0;i<ToBeUntagged.length; i++) {
        for (var j=i+1;j<ToBeUntagged.length; j++) {
            if (ToBeUntagged[i]==ToBeUntagged[j]) {ToBeUntagged.splice(j,1); j--}
        }
    }
    for (var i=0;i<Unreferenced.length; i++) {
        for (var j=i+1;j<Unreferenced.length; j++) {
            if (Unreferenced[i]==Unreferenced[j]) {Unreferenced.splice(j,1); j--}
        }
    }
    for (var i=0;i<OneRefNotNRIS.length; i++) {
        for (var j=i+1;j<OneRefNotNRIS.length; j++) {
            if (OneRefNotNRIS[i]==OneRefNotNRIS[j]) {OneRefNotNRIS.splice(j,1); j--}
        }
    }
    for (var i=0;i<AllNRISOnly.length; i++) {
        for (var j=i+1;j<AllNRISOnly.length; j++) {
            if (AllNRISOnly[i]==AllNRISOnly[j]) {AllNRISOnly.splice(j,1); j--}
        }
    }
    for (var i=0;i<AllDabs.length; i++) {
        for (var j=i+1;j<AllDabs.length; j++) {
            if (AllDabs[i]==AllDabs[j]) {AllDabs.splice(j,1); j--}
        }
    }
    for (var i=0;i<Substubs.length; i++) {
        for (var j=i+1;j<Substubs.length; j++) {
            if (Substubs[i][0]==Substubs[j][0]) {Substubs.splice(j,1); j--}
        }
    }
    for (var i=0;i<Errors.length; i++) {
        for (var j=i+1;j<Errors.length; j++) {
            if (Errors[i]==Errors[j]) {Errors.splice(j,1); j--}
        }
    }

    var ShortButNotNRISOnly=[];     // if substub but not tagged NRIS-only, move to special section
    for (var i=0; i<Substubs.length; i++) {
        var matches=0
        for (var j=0; j<AllNRISOnly.length; j++) {
            if (Substubs[i][0]==AllNRISOnly[j]) matches++
        }
        if (matches==0) {ShortButNotNRISOnly[ShortButNotNRISOnly.length]=Substubs[i]; Substubs.splice(i,1); i--}
    }

    var LongNRISOnly=[];     // if NRIS-only but more than 325 chars, move to special section
    for (var i=0; i<AllNRISOnly.length; i++) {
        var matches=0
        for (var j=0; j<Substubs.length; j++) {
            if (AllNRISOnly[i]==Substubs[j][0]) matches++
        }
        if (matches==0) LongNRISOnly.push(AllNRISOnly[i])
    }

    ToBeTagged.sort()
    ToBeUntagged.sort()
    Unreferenced.sort()
    OneRefNotNRIS.sort()
    AllNRISOnly.sort()
    AllDabs.sort()
    LongNRISOnly.sort()
    Errors.sort()

    // special sort for substubs
    ShortButNotNRISOnly.sort(function(a,b){if (a[1] < b[1]) return -1; if (a[1] > b[1]) return 1; return 0;})
    var WorstNonNRIS=ShortButNotNRISOnly.slice(0,100) // pick out worst 100 non-NRIS-only
    ShortButNotNRISOnly.sort(function(a,b){if (a[0] < b[0]) return -1; if (a[0] > b[0]) return 1; return 0;})
    Substubs.sort(function(a,b){if (a[1] < b[1]) return -1; if (a[1] > b[1]) return 1; return 0;})
    var WorstNRIS=Substubs.slice(0,100)               // pick out worst 100 NRIS-only
    Substubs.sort(function(a,b){if (a[0] < b[0]) return -1; if (a[0] > b[0]) return 1; return 0;})

    wikitext="{{TOC right}}\n"
    if (ToBeTagged.length>0) {
        wikitext+="==Articles that need to be tagged==\n<div id=\"tobetagged-div\">\n{{refbegin|3}}\n"
        for (var k=1; k<ToBeTagged.length+1; k++) {
            wikitext+='# [['+ToBeTagged[k-1]+']]\n'
        }
        wikitext+="{{refend}}</div>\n\n"
    }
    if (ToBeUntagged.length>0) {
        wikitext+="==Articles that need to be untagged==\n<div id=\"tobeuntagged-div\">\n{{refbegin|3}}\n"
        for (var k=1; k<ToBeUntagged.length+1; k++) {
            wikitext+='# [['+ToBeUntagged[k-1]+']]\n'
        }
        wikitext+="{{refend}}</div>\n\n"
    }
    if (Errors.length>0) {
        wikitext+="==Articles which produced errors in the latest dump==\n{{refbegin|3}}\n"
        for (var k=1; k<Errors.length+1; k++) {
            wikitext+='# [['+Errors[k-1]+']]\n'
        }
        wikitext+="{{refend}}\n\n"
    }
    if (AllDabs.length>0) {
        wikitext+="==Links to disambiguation pages==\n{{refbegin|3}}\n"
        for (var k=1; k<AllDabs.length+1; k++) {
            wikitext+='# [['+AllDabs[k-1]+']]&nbsp;([[Special:WhatLinksHere/'+AllDabs[k-1]+'|links]])\n'
        }
        wikitext+="{{refend}}\n\n"
    }
    if (MissingRefnum.length>0) {
        wikitext+="==Missing refnum in county list==\n{{refbegin|3}}\n"
        for (var k=1; k<MissingRefnum.length+1; k++) {
            wikitext+='# [['+MissingRefnum[k-1][0]+']]&nbsp;([['+MissingRefnum[k-1][1]+'|'
            wikitext+=MissingRefnum[k-1][1].replace(/National Register of Historic Places listings (i|o)n /g,"")+']])\n'
        }
        wikitext+="{{refend}}\n\n"
    }
    if (Unreferenced.length>0) {
        wikitext+="==Articles with no references==\n<div id=\"unreferenced-div\">\n{{refbegin|3}}\n"
        for (var k=1; k<Unreferenced.length+1; k++) {
            wikitext+='# [['+Unreferenced[k-1]+']]\n'
        }
        wikitext+="{{refend}}</div>\n\n"
    }
    if (OneRefNotNRIS.length>0) {
        wikitext+="==Articles with only one source that is not NRIS==\n{{refbegin|3}}\n"
        for (var k=1; k<OneRefNotNRIS.length+1; k++) {
            wikitext+='# [['+OneRefNotNRIS[k-1]+']]\n'
        }
        wikitext+="{{refend}}\n\n"
    }

    editPageNRISOnly({
        title: OutputPage,
        text: wikitext,
        summary:'Generate list of all NRHP articles to be tagged/untagged with [[Template:NRIS-only]] as of '+DateStr
    },TimeSpan,subpages,"");

    // now output stats about substubs
    wikitext=""
    if (WorstNRIS.length>0) {
        wikitext+="==Smallest 100 NRIS-only articles by prose size==\n{{refbegin|3}}\n"
        for (var k=1; k<WorstNRIS.length+1; k++) {
            wikitext+='# [['+WorstNRIS[k-1][0]+']] ('+WorstNRIS[k-1][1]+'&nbsp;bytes)\n'
        }
        wikitext+="{{refend}}\n\n"
    }
    if (WorstNonNRIS.length>0) {
        wikitext+="==Smallest 100 articles by prose size NOT tagged NRIS-only==\n{{refbegin|3}}\n"
        for (var k=1; k<WorstNonNRIS.length+1; k++) {
            wikitext+='# [['+WorstNonNRIS[k-1][0]+']] ('+WorstNonNRIS[k-1][1]+'&nbsp;bytes)\n'
        }
        wikitext+="{{refend}}\n\n"
    }
    if (Substubs.length>0||ShortButNotNRISOnly.length>0) {
        wikitext+="==Articles with less than 325 bytes of prose==\n"
        if (ShortButNotNRISOnly.length>0) {
            wikitext+="===Not tagged NRIS-only===\n{{refbegin|3}}\n"
            for (var k=1; k<ShortButNotNRISOnly.length+1; k++) {
                wikitext+='# [['+ShortButNotNRISOnly[k-1][0]+']] ('+ShortButNotNRISOnly[k-1][1]+'&nbsp;bytes)\n'
            }
            wikitext+="{{refend}}\n\n"
            if (Substubs.length>0) wikitext+="===Tagged NRIS-only===\n"
        }
        if (Substubs.length>0) {
            wikitext+="{{refbegin|3}}\n"
            for (var k=1; k<Substubs.length+1; k++) {
                wikitext+='# [['+Substubs[k-1][0]+']] ('+Substubs[k-1][1]+'&nbsp;bytes)\n'
            }
            wikitext+="{{refend}}\n\n"
        }
    }
    if (LongNRISOnly.length>0) {
        wikitext+="==Articles tagged NRIS-only with more than 325 bytes of prose==\n{{refbegin|3}}\n"
        for (var k=1; k<LongNRISOnly.length+1; k++) {
            wikitext+='# [['+LongNRISOnly[k-1]+']]\n'
        }
        wikitext+="{{refend}}\n\n"
    }

    editPageNRISOnly({
        title: "User:NationalRegisterBot/Substubs",
        text: wikitext,
        summary:'Generate list of all minimal (<325 bytes) NRHP stubs as of '+DateStr
    },TimeSpan,subpages,"");

    // now dump list of all NRIS-only articles
    wikitext = "{{TOC right}}\n<div id=\"NRISOnly-div\">\n"
    if (AllNRISOnly.length>0) {
        var firstletter=AllNRISOnly[0].substr(0,1)
        wikitext += "=="+firstletter+"==\n{{refbegin|3}}\n"
        for (var k=1; k<AllNRISOnly.length+1; k++) {
            var oldfirstletter = firstletter
            firstletter = AllNRISOnly[k-1].substr(0,1)
            if (firstletter!=oldfirstletter) {
                wikitext+='{{refend}}\n=='+firstletter+'==\n{{refbegin|3}}\n# <li value="'+k+'">[['+AllNRISOnly[k-1]+']]\n'
            } else {
                wikitext+='# [['+AllNRISOnly[k-1]+']]\n'
            }
        }
        wikitext+="{{refend}}</div>"
    }

    editPageNRISOnly({
        title: OutputPage+"/All",
        text: wikitext,
        summary:'Generate list of all NRHP articles sourced only to NRIS as of '+DateStr
    },TimeSpan,subpages,"yes");
}

function NRISOnlyQuery(toQuery,SubpageSpan) { // look through wikitext of each article to find NRIS-only ones
    var ToBeTagged=[];
    var ToBeUntagged=[];
    var Unreferenced=[];
    var OneRefNotNRIS=[];
    var AllNRISOnly=[];
    var Substubs=[];
    var Errors=[];
    var isTagged="no"
    for (var k=1;k<toQuery.length+1; k++) {
        SubpageSpan.innerHTML = "<br />Finding NRIS-only articles... Querying page "+k+" of "+toQuery.length+" in this county..."
        var wikitext=getNRISOnlyWikitext(toQuery[k-1])
        isTagged="no"
        if (wikitext!="error") {
            // calculate prose size
            var prose=wikitext.replace(/\<\!\-\-(.|[\r\n])*?\-\-\>/g,"")                        // strip comments
            // strip refs
            prose=prose.replace(/\<ref([ ]+?name[ ]*?=[ ]*?"?[^\>]*?"?[ ]*?\/[ ]*?\>|[^e](.|[\r\n])*?\<\/ref[ ]*?\>)/gi,"")
            prose=prose.replace(/==[ ]*?External links[ ]*?==(.|\n)*?(?=(==.*?==|$))/gi,"")     // strip external links section
            prose=prose.replace(/==[ ]*?See also[ ]*?==(.|\n)*?(?=(==.*?==|$))/gi,"")           // strip see also section
            prose=prose.replace(/==[ ]*?(References|Notes)[ ]*?==(.|\n)*?(?=(==.*?==|$))/gi,"") // strip references section
            // strip further reading section
            prose=prose.replace(/==[ ]*?(Further|Additional) reading[ ]*?==(.|\n)*?(?=(==.*?==|$))/gi,"")
            prose=prose.replace(/={2,5}.*?={2,5}/g,"")                                          // strip section titles
            // replace wikilinks with displayed text
            prose=prose.replace(/\[\[(?![ ]*?Category:|[ ]*?Image:|[ ]*?File:)([^\]]*?\|)?(.*?)\]\]/gi,"$2")
            prose=prose.replace(/\[[ ]*?http.*? (.*?)\]/g,"$1")                 // replace inline external links with displayed text
            prose=prose.replace(/'{2,5}(.*?)'{2,5}/g,"$1")                      // replace bold/italic with displayed text
            prose=prose.replace(/\[\[[ ]*?Category:.*?\]\]/g,"")                                // strip categories
            prose=prose.replace(/\[\[[ ]*?(Image|File):.*?\]\]/g,"")                            // strip images
            prose=prose.replace(/\<[ ]*?gallery(.|\n)*?\<[ ]*?\/[ ]*?gallery[ ]*?\>/gi,"")      // strip galleries
            while(true) {                                                                       // strip templates
                var str="{{"
                var start=prose.indexOf(str)
                if (start==-1) break
                var open=1
                var index=start+str.length
                while (open!=0 && index<prose.length) {
                    if (prose.substr(index,2)=="}}") {
                        open--
                        index++
                    } else if (prose.substr(index,2)=="{{") {
                        open++
                        index++
                    }
                    index++
                }
                prose=prose.replace(prose.substr(start,index-start),"")
            }
            prose=prose.replace(/{\|(.|\n)*?\|}/g,"")                   // strip tables
            prose=prose.replace(/&nbsp;/g," ")                          // replace nbsp with regular space
            prose=prose.replace(/<[ ]*?br.*?>/g, "\n")                  // replace HTML line break with string line break
            prose=prose.replace(/[ \n]+/g," ")                          // replace multiple spaces/linebreaks with single space
            prose=prose.trim()

            if (prose.length<325) Substubs[Substubs.length]=[toQuery[k-1],prose.length]

            if (wikitext.match(/{{( |[\r\n])*?NRIS-only(.|[\r\n])*?}}/g)!=null) isTagged="yes"
            if (wikitext.indexOf("{{GR")!=-1||wikitext.indexOf("{{sfn")!=-1||wikitext.indexOf("{{Sfn")!=-1) {
                if (isTagged=="yes") ToBeUntagged.push(toQuery[k-1])
                continue
            }
            var Refs=wikitext.match(/\<ref[^e](.|[\r\n])*?([ ]*?\/|\<\/ref[ ]*?)\>/gi)
            var comments=wikitext.match(/\<\!\-\-(.|[\r\n])*?\-\-\>/g)

            if (Refs==null) {     // if no refs, count as unreferenced
                Unreferenced.push(toQuery[k-1])
                continue
            }

            if (comments!=null) {
                for (var l=0; l<comments.length; l++) {
                    var CommentedRefs=comments[l].match(/\<ref[^e](.|[\r\n])*?([ ]*?\/|\<\/ref[ ]*?)\>/gi)
                    if (CommentedRefs==null) continue
                    for (var m=0; m<CommentedRefs.length; m++) {
                        for (var n=0; n<Refs.length; n++) {
                            if (Refs[n]==CommentedRefs[m]) {Refs.splice(n,1); n--}
                        }
                    }
                }
            }

            if (Refs.length==0) {    // if all refs commented out, count as unreferenced
                Unreferenced.push(toQuery[k-1])
                continue
            }

            var citesNRIS="no"
            for (var l=0; l<Refs.length; l++) {
                if (Refs[l].indexOf("{{NRISref")!=-1) citesNRIS="yes"
            }

            var namedRefs=[];
            var Duplications=[];
            for (var l=0; l<Refs.length; l++) {
                var nameOfRef=Refs[l].match(/name[ ]*?=.*?(\/|\>)/gi)
                if (nameOfRef==null) {
                    continue
                } else {
                    nameOfRef = nameOfRef[0].replace(/("| )/g,'')
                    nameOfRef = nameOfRef.substr(5,nameOfRef.length-6)
                }
                namedRefs.push(nameOfRef)
                Duplications.push(1)
                for (var m=0; m<namedRefs.length-1; m++) {   // if title is duplicated, count how many times
                    if (nameOfRef==namedRefs[m]) {
                        namedRefs.splice(m,1)
                        Duplications[Duplications.length-1] = Duplications[m] + 1
                        Duplications.splice(m,1);
                        m--
                    }
                }
            }
            if (namedRefs.length==2&&namedRefs[0]==namedRefs[1]) Duplications[0]++ // fix for if all refs are same
            var toSubtract=0
            for (var l=0; l<Duplications.length; l++) {
                toSubtract = toSubtract + Duplications[l] - 1
            }

            var DistinctRefs = Refs.length-toSubtract

            if (DistinctRefs>1) {
                if (isTagged=="yes") ToBeUntagged.push(toQuery[k-1]) // untag if ref has been added since tag placed
                continue
            }
            if (citesNRIS=="no") {OneRefNotNRIS.push(toQuery[k-1]);continue} // if only one ref and not NRIS, count that
            if (isTagged=="no") ToBeTagged.push(toQuery[k-1]) // only push if the only ref is NRISref and it's not already tagged
            AllNRISOnly.push(toQuery[k-1])
        } else {
            Errors.push(toQuery[k-1]);
            continue;
        }
    }
    return [ToBeTagged,ToBeUntagged,Unreferenced,OneRefNotNRIS,AllNRISOnly,Substubs,Errors]
}

function getNRISOnlyWikitext(title) {   // fetch Wikitext of each page
    try {
        var output=JSON.parse(
            $.ajax({
                dataType: "json",
                url: mw.util.wikiScript('api'),
                data: {
                    format: 'json',
                    action: 'query',
                    prop: 'revisions',
                    rvprop: 'content',
                    titles: title,
                    indexpageids: true,
                    redirects: 'true'
                },
                async:false
            })
            .responseText
        );
        for (page in output.query.pages) {
            wikitext = output.query.pages[page].revisions[0]['*'];
        }
        return wikitext
    }
    catch(err) {
        return "error"
    }
}

// Generate list of titles in a county
function GetArticleTitles(PageName,SectionName,tds) {
    var wikitext=getNRISOnlyWikitext(PageName)
    if (wikitext=="error") {
        errortext = errorArray[2] // wikitext error
        return [["error"],[],[],[]];
    }
    var Rows=[];

    if (PageName != SectionName) {  // was a redirect; now look for section
        var regex = new RegExp("=[ ]*(\\[\\[(.*?\\|)?[ ]*)?"+SectionName+"([ ]*\\]\\])?[ ]*=", "g")
        var sectionheader=wikitext.match(regex)
        if (sectionheader == null) { // if no section found, check against hard-coded empty counties w/o sections
            var EmptyCounties=["02270","12041","12067","42023","46007","48017","48023","48033","48069","48075","48079","48083"]
                EmptyCounties.push("48103","48107","48119","48131","48155","48165","48195","48207","48219","48247","48269")
                EmptyCounties.push("48279","48341","48369","48389","48415","48421","48431","48433","48437","48445","48461")
                EmptyCounties.push("48475","48501","48507","51685","51720","51735")
            var ID = tds[0].innerHTML.substr(0,5)
            errortext = errorArray[0] // not one of the known empty counties
            var error="yes"
            for (var k=0; k<EmptyCounties.length; k++) {
                if (ID==EmptyCounties[k]) {
                    errortext = ''
                    error="no"
                }
            }
            if (error=="no") {
                return [[],[],[],[]];
            } else {
                return [["error"],[],[],[]];
            }
        }

        // if section found, extract table wikitext; exit if no table
        var StartIndex=wikitext.indexOf(sectionheader[0])
        var sectiontext=wikitext.substr(StartIndex,wikitext.indexOf("\n==",StartIndex)-StartIndex)

        var StartIndex=sectiontext.indexOf("{{NRHP header")
        if (StartIndex==-1) {
            if (sectiontext.indexOf("{{NRHP row")!=-1) {
                errortext = errorArray[4] // incorrectly formatted table
                return [["error"],[],[],[]];
            } else {
                return [[],[],[],[]];  // must be an empty county
            }
        }
        var tabletext=sectiontext.substr(StartIndex,sectiontext.indexOf("\n|}",StartIndex)-StartIndex)
    }
    else { // if no redirect
        var StartIndex=wikitext.indexOf("{{NRHP header")  // find first table
        if (StartIndex==-1) {
            errortext = errorArray[1]  // no table found
            return [["error"],[],[],[]];
        }
        var tabletext=wikitext.substr(StartIndex,wikitext.indexOf("\n|}",StartIndex)-StartIndex)
    }
    // now tabletext has info regardless if redirect or not
    tabletext=tabletext.replace(/\<\!\-\-(.|[\r\n])*?\-\-\>/g, "") // get rid of commented out rows

    var index, RowLocations = [];
    var str = "{{NRHP row"
    var skip = str.length
    StartIndex = 0
    while ((index = tabletext.indexOf(str, StartIndex)) > -1) {
        RowLocations.push(index);
        StartIndex = index + skip;
    }
    RowLocations.push(tabletext.length) // last row ends at end of table

    for (var i=0; i<RowLocations.length-1; i++) {
        Rows.push(tabletext.substr(RowLocations[i],RowLocations[i+1]-RowLocations[i]))
    }

    var TitlesArray = [];
    var AllDabs = [];
    var Refnums = [];
    var MissingRefnum = [];

    for (var j=0; j<Rows.length; j++) {
        var link=Rows[j].match(/\|[ ]*?article[ ]*?=[ ]*?.*?[\n|\|]/g)
        var blank=Rows[j].match(/\|[ ]*?article[ ]*?=[ ]*?[\n|\|]/g)                         // default to name param if article
        if (link==null||blank!=null) link=Rows[j].match(/\|[ ]*?name[ ]*?=[ ]*?.*?[\n|\|]/g) // blank or missing

        link=link[0].replace(/\|[ ]*?(article|name)[ ]*?=[ ]*?/g,"").replace(/[\n|\|]/g,"")  // strip parameter name, final line break
        link=decodeURIComponent(link.split("#")[0].replace(/\[\[/g,"").replace(/]]/g,"").trim())     // corrections for weird titles

        if (link=="") {
            errortext = 'Blank link in row '+parseFloat(j+1)+', county '
            return [["error"],[],[],[]];
        }

        var refnum=Rows[j].match(/[0-9]{8}/g)
        if (refnum!=null) {
            if (refnum[0]=="98000562"||refnum[0]=="98000563") refnum[0]="98000562,98000563"   // hard-code troublesome
            if (refnum[0]=="01000363"||refnum[0]=="01000364") refnum[0]="01000363,01000364"   // multistate duplicates
            if (refnum[0]=="02000529"||refnum[0]=="02000530") refnum[0]="02000529,02000530"   // ...

            Refnums[Refnums.length] = [link,refnum[0]]
        } else {
            MissingRefnum.push(link)
        }

        TitlesArray.push(link)
        for (var k=0; k<TitlesArray.length-1; k++) {  // if title is duplicated, only count it once
            if (link==TitlesArray[k]) {
                TitlesArray.splice(k,1);
                k--
            }
        }
    }
    toQuery=TitlesArray.length

    var FinalTitlesArray=[]; // will dump titles here after resolving redirects, removing dabs, etc.
    var done = "no"
    var loops = 0
    while (done=="no") {
        if (toQuery>50) {
            var TempTitlesArray = TitlesArray.slice(50*loops,50*(loops+1))
        } else {
            var TempTitlesArray = TitlesArray.slice(50*loops)
        }
        TempTitlesArray.sort()  // query below returns results in alphabetical order

        var temptitle=TempTitlesArray[0]
        for (var k=1; k<TempTitlesArray.length; k++) {
            temptitle+="|"+TempTitlesArray[k]
        }

        var dabs=JSON.parse(
            $.ajax({
                dataType: "json",
                url: mw.util.wikiScript('api'),
                data: {
                    format: 'json',
                    action: 'query',
                    prop: 'categories',
                    clcategories: 'Category:All disambiguation pages',
                    cllimit: 'max',
                    titles: temptitle,
                    redirects: 'true'
                },
                async:false
            })
            .responseText
        );

        if (dabs.query.normalized) { // normalize any weird titles
            for (var n in dabs.query.normalized) {
                for (var k=0; k<TempTitlesArray.length; k++) {
                    if (TempTitlesArray[k]==dabs.query.normalized[n].from) TempTitlesArray[k]=dabs.query.normalized[n].to
                }
            }
        }

        if (dabs.query.redirects) { // resolve any redirects
            for (var r in dabs.query.redirects) {
                for (var k=0; k<TempTitlesArray.length; k++) {
                    if (TempTitlesArray[k]==dabs.query.redirects[r].from) TempTitlesArray[k]=dabs.query.redirects[r].to
                }
            }
        }

        for (var k=0; k<TempTitlesArray.length; k++) {   // recheck for duplicates after resolving redirects and resort
            for (var l=k+1; l<TempTitlesArray.length; l++) {
                if (TempTitlesArray[k]==TempTitlesArray[l]) {
                    TempTitlesArray.splice(l,1);
                    l--
                }
            }
        }
        TempTitlesArray.sort()

        for (var page in dabs.query.pages) {
            if (typeof dabs.query.pages[page].missing!="undefined") {  // if redlink, get rid of it
                for (var l=0; l<TempTitlesArray.length; l++) {
                    if (TempTitlesArray[l]==dabs.query.pages[page].title) {
                        TempTitlesArray.splice(l,1)
                        l--
                    }
                }
            }
            if (dabs.query.pages[page].categories) {    // if dab, count it then remove it
                AllDabs.push(dabs.query.pages[page].title)
                for (var l=0; l<TempTitlesArray.length; l++) {
                    if (TempTitlesArray[l]==dabs.query.pages[page].title) {
                        TempTitlesArray.splice(l,1)
                        l--
                    }
                }
            }
        }

        FinalTitlesArray=FinalTitlesArray.concat(TempTitlesArray)
        if (toQuery>50) {
            toQuery = toQuery-50
            loops++
        } else {
            done = "yes"
        }
    }

    for (var i=0;i<FinalTitlesArray.length; i++) {            // after refining, some may point to same place
        for (var j=i+1;j<FinalTitlesArray.length; j++) {      // if they were in different groups of 50
            if (FinalTitlesArray[i]==FinalTitlesArray[j]) {FinalTitlesArray.splice(j,1); j--}
        }
    }
    return [FinalTitlesArray,AllDabs,Refnums,MissingRefnum];
}

function QueryEditors() {
    if (wgPageName.indexOf("/All")==-1) {
        var content=document.getElementById("tobetagged-div")
    } else {
        var content=document.getElementById("NRISOnly-div")
    }
    var list=content.getElementsByClassName("refbegin")

    for (var j=0; j<list.length; j++) {
    var pages=list[j].getElementsByTagName("li")
    for (var i=0; i<pages.length; i++) {
        var output=JSON.parse(
            $.ajax({
                dataType: "json",
                url: mw.util.wikiScript('api'),
                data: {
                    format: 'json',
                    action: 'query',
                    prop: 'revisions',
                    rvprop: 'user|timestamp',
                    rvlimit: '1',
                    rvdir: 'newer',
                    titles: pages[i].firstChild.title,
                    indexpageids: true
                },
                async:false
            })
            .responseText
        );
        for (page in output.query.pages) {
            var user = output.query.pages[page].revisions[0]['user'];
            var timestamp = output.query.pages[page].revisions[0]['timestamp'];
        }
        timestamp = timestamp.replace("T","&nbsp;– ").replace("Z","&nbsp;UTC")

        var node=document.createElement("span")
        node.style.fontSize="90%"
        node.innerHTML="<br />(<a href='http://en.wikipedia.org/wiki/User:"+user+"'>"+user+"</a>&nbsp;– "
        node.innerHTML+=timestamp+")"
        pages[i].appendChild(node)
    }
    }
}

function TagNRISOnly() {
    var tobetagged=document.getElementById('tobetagged-div')
    var tobeuntagged=document.getElementById('tobeuntagged-div')
    var ProgressDiv = document.getElementById('ProgressDiv')

    var HiddenSpan=document.createElement("span")
    HiddenSpan.setAttribute("id", "HiddenSpan")
    HiddenSpan.setAttribute("style", "display:none")
    ProgressDiv.appendChild(HiddenSpan)

    var TotalDone=0

    var d=new Date();
    var months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
    var year=d.getYear();
    if (year < 1000) year += 1900
    var DateStr = months[d.getMonth()]+" "+year

    if (tobetagged!=null) {
        ProgressDiv.innerHTML = "Tagging articles that need to be tagged..."
        var ProgressSpan = document.createElement("span")
        ProgressSpan.setAttribute("id", "ProgressSpan")
        ProgressDiv.appendChild(ProgressSpan)
        ProgressSpan.innerHTML="<br />"

        var sections=tobetagged.getElementsByClassName('refbegin')
        var total=0
        for (var i=0; i<sections.length; i++) {
            var links=sections[i].getElementsByTagName('a')
            total+=links.length
        }
        var tagged=0
        var skipped=0
        ProgressSpan.innerHTML="<br />0 of "+total+" articles examined: 0 tagged, 0 skipped..."
        for (var i=0; i<sections.length; i++) {
            var links=sections[i].getElementsByTagName('a')
            for (var j=0; j<links.length; j++) {
                var wikitext=getNRISOnlyWikitext(links[j].title)
                if (wikitext!="error") {
                    if (wikitext.match(/{{( |[\r\n])*?NRIS-only(.|[\r\n])*?}}/g)!=null) {  // don't tag if already tagged
                        TotalDone++
                        skipped++
                        links[j].style.backgroundColor = "yellow"
                        ProgressSpan.innerHTML="<br />"+TotalDone+" of "+total+" articles examined: "+tagged+" tagged, "
                        ProgressSpan.innerHTML+=skipped+" skipped..."
                        continue
                    }
                    var result=NRISOnlyQuery([links[j].title],HiddenSpan)
                    if (result[0][0]!=links[j].title) {     // check again to make sure list isn't outdated
                        TotalDone++
                        skipped++
                        links[j].style.backgroundColor = "yellow"
                        ProgressSpan.innerHTML="<br />"+TotalDone+" of "+total+" articles examined: "+tagged+" tagged, "
                        ProgressSpan.innerHTML+=skipped+" skipped..."
                        continue
                    }

                    var multipleIssues=wikitext.match(/{{( )*?(multiple|many|mi|article|issues)( )?(issues)?( |[\r\n])*?\|/gi)
                    var NRISstr="{{NRIS-only|date="+DateStr+"}}"

                    if (multipleIssues!=null) {           // if multiple issues tag, add NRIS-only to bottom of list
                        var open=1
                        var start=wikitext.indexOf(multipleIssues[0])
                        var index=start+multipleIssues[0].length
                        while (open!=0 && index<wikitext.length) {
                            if (wikitext.substr(index,2)=="}}") {
                                open--
                                index++
                            } else if (wikitext.substr(index,2)=="{{") {
                                open++
                                index++
                            }
                            index++
                        }
                        index-=2
                        var full=wikitext.substr(start,index-start)
                        if (wikitext.substr(index-1,1)!="\n") full+="\n"
                        var oldsyntax=full.substr(2,full.length-2).match(/{{/g)
                        if (oldsyntax==null) {    // if using old syntax
                            full = full.replace(/\|( )*?one( )?source( )*?=(.|[\r\n])*?(\||$)/g, "|") // replace "|one source"
                            if (full.substr(full.length-1,1)=="|") {
                                full+="\n"
                            } else {
                                NRISstr="|"+NRISstr
                            }
                        } else {                  // replace "{{one source}}"
                            full = full.replace(/{{( )*?(one|single|1)( |-)?(source|ref)( |[\r\n])*?\|(.|[\r\n])*?}}[\r\n]?/gi,"")
                        }
                        full+=NRISstr+"\n"
                        wikitext = wikitext.substr(0,start)+full+wikitext.substr(index,wikitext.length-index)
                    } else {
                      wikitext=wikitext.replace(/{{( )*?(one|single|1)( |-)?(source|ref)( |[\r\n])*?\|(.|[\r\n])*?}}[\r\n]?/gi,"")
                        wikitext = NRISstr+"\n"+wikitext
                    }
                    editPageNRISOnly({
                        title: links[j].title,
                        text: wikitext,
                        summary:'Tag as only sourced to the [[National Register Information System]]'
                    },ProgressSpan,TotalDone,"tag");
                    TotalDone++
                    tagged++
                    ProgressSpan.innerHTML="<br />"+TotalDone+" of "+total+" articles examined: "+tagged+" tagged, "
                    ProgressSpan.innerHTML+=skipped+" skipped..."
                } else {
                    TotalDone++      // skip if wikitext error
                    skipped++
                    links[j].style.backgroundColor = "orange"
                    ProgressSpan.innerHTML="<br />"+TotalDone+" of "+total+" articles examined: "+tagged+" tagged, "
                    ProgressSpan.innerHTML+=skipped+" skipped..."
                }
            }
        }
        ProgressSpan.innerHTML="<br />Tagging complete! "+tagged+" articles tagged successfully! "+skipped
        ProgressSpan.innerHTML+=" skipped as unnecessary."
    }
    if (tobeuntagged!=null) {
        var str=""
        if (tobetagged!=null) {
            str="<br />Now u"
        } else {
            str="U"
        }
        ProgressDiv.innerHTML+=str+"ntagging articles that need to be untagged..."
        var ProgressSpan2 = document.createElement("span")
        ProgressSpan2.setAttribute("id", "ProgressSpan2")
        ProgressDiv.appendChild(ProgressSpan2)
        ProgressSpan2.innerHTML="<br />"

        var TotalDone=0

        var sections=tobeuntagged.getElementsByClassName('refbegin')
        var total=0
        for (var i=0; i<sections.length; i++) {
            var links=sections[i].getElementsByTagName('a')
            total+=links.length
        }
        var untagged=0
        var skipped=0
        ProgressSpan2.innerHTML="<br />0 of "+total+" articles examined: 0 untagged, 0 skipped..."

        for (var i=0; i<sections.length; i++) {
            var links=sections[i].getElementsByTagName('a')
            for (var j=0; j<links.length; j++) {
                var wikitext=getNRISOnlyWikitext(links[j].title)
                if (wikitext!="error") {
                    if (wikitext.match(/{{( |[\r\n])*?NRIS-only(.|[\r\n])*?}}[\r\n]?/g)==null) { //don't untag if already untagged
                        TotalDone++
                        skipped++
                        links[j].style.backgroundColor = "yellow"
                        ProgressSpan2.innerHTML="<br />"+TotalDone+" of "+total+" articles examined: "+untagged+" untagged, "
                        ProgressSpan2.innerHTML+=skipped+" skipped..."
                        continue
                    }
                    var result=NRISOnlyQuery([links[j].title],HiddenSpan)
                    if (result[1][0]!=links[j].title) {     // check again to make sure list isn't outdated
                        TotalDone++
                        skipped++
                        links[j].style.backgroundColor = "yellow"
                        ProgressSpan2.innerHTML="<br />"+TotalDone+" of "+total+" articles examined: "+untagged+" untagged, "
                        ProgressSpan2.innerHTML+=skipped+" skipped..."
                        continue
                    }

                    var multipleIssues=wikitext.match(/{{( )*?(multiple|many|mi|article|issues)( )?(issues)?( |[\r\n])*?\|/gi)

                    if (multipleIssues!=null) {     // if multiple issues tag, check to see we're not leaving only one tag behind
                        var open=1
                        var start=wikitext.indexOf(multipleIssues[0])
                        var index=start+multipleIssues[0].length
                        while (open!=0 && index<wikitext.length) {
                            if (wikitext.substr(index,2)=="}}") {
                                open--
                                index++
                            } else if (wikitext.substr(index,2)=="{{") {
                                open++
                                index++
                            }
                            index++
                        }
                        index-=2
                        var full=wikitext.substr(start,index-start)
                        var oldsyntax=full.substr(2,full.length-2).match(/{{( )*?[^(NRIS)](.|[\r\n])*?}}/g) //match non-NRIS temp

                        if (oldsyntax==null) {
                            var numberTemplates=full.substr(2,full.length-2).match(/\|/g).length - 2
                        } else {
                            var numberTemplates=oldsyntax.length
                        }

                        if (numberTemplates==0) {      // if NRIS-only is only template in multiple issues, remove entire block
                            wikitext=wikitext.replace(full+"}}\n","")
                        } else if (numberTemplates==1 && oldsyntax!=null) {       // if one template left, remove multiple issues
                            wikitext = wikitext.replace(full+"}}", oldsyntax[0])  // and replace with other template
                        } else {      // just remove if more than one other or if using old syntax
                            wikitext = wikitext.replace(/\|?{{( |[\r\n])*?NRIS-only(.|[\r\n])*?}}[\r\n]?/g, '')
                        }
                    } else {
                        wikitext = wikitext.replace(/{{( |[\r\n])*?NRIS-only(.|[\r\n])*?}}[\r\n]?/g, '')
                    }

                    editPageNRISOnly({
                        title: links[j].title,
                        text: wikitext,
                        summary:'Remove [[Template:NRIS-only]]; article has more than one reference'
                    },ProgressSpan2,TotalDone,"tag");
                    TotalDone++
                    untagged++
                    ProgressSpan2.innerHTML="<br />"+TotalDone+" of "+total+" articles examined: "+untagged+" untagged, "
                    ProgressSpan2.innerHTML+=skipped+" skipped..."
                } else {
                    TotalDone++      // skip if wikitext error
                    skipped++
                    links[j].style.backgroundColor = "orange"
                    ProgressSpan2.innerHTML="<br />"+TotalDone+" of "+total+" articles examined: "+untagged+" untagged, "
                    ProgressSpan2.innerHTML+=skipped+" skipped..."
                }
            }
        }
        ProgressSpan2.innerHTML="<br />Untagging complete! "+untagged+" articles untagged successfully! "+skipped
        ProgressSpan2.innerHTML+=" skipped as unnecessary."
    }
}

function getState(title) {
    var temp=title.split(", ")
    var state=temp[temp.length-1]
    if (state=="Philadelphia") state="Pennsylvania"
    if (temp[0]==state||state.indexOf(':')!=-1) {
        if (title.indexOf("Chicago")!=-1) state="Illinois"
        else if (title.indexOf("St. Louis")!=-1) state="Missouri"
        else if (title.indexOf("Kansas City")!=-1) state="Missouri"
        else if (title.indexOf("Baltimore")!=-1) state="Maryland"
        else if (title.indexOf("Manhattan")!=-1) state="New York"
        else if (title.indexOf("Philadelphia")!=-1) state="Pennsylvania"
        else if (title.indexOf("Zion")!=-1) state="Utah"
        else state=""
    }
    return state
}

function getDuplicateStats(item) {
        var IllustratedStr = "illustrated"
        var countytext=""
        var Illustrated=0
        for (var j=2; j<item.length; j++) {
            countytext=getNRISOnlyWikitext(item[j])
            if (countytext!="error") {
                var StartIndex = 0
                var str = "{{NRHP row"
                var skip = str.length;
                var index, RowLocations = [];
                while ((index = countytext.indexOf(str, StartIndex)) > -1) {
                    RowLocations.push(index);
                    StartIndex = index + skip;
                }
                RowLocations.push(countytext.length)    // if duplicated entry happens to be last in table

                var k=0
                while (RowLocations[k]<countytext.indexOf(item[0])) {
                    k++
                }
                var CountyRow=countytext.substr(RowLocations[k-1],RowLocations[k]-RowLocations[k-1])
                CountyRow=CountyRow.replace(/\<\!\-\-(.|[\r\n])*?\-\-\>/g, "")      // get rid of commented out pictures
                if (CountyRow.match(/\|[ ]*?image[ ]*?=[ ]*?[a-zA-Z0-9]/g)!=null) Illustrated++
            } else {
                alert("Error checking duplicate stats! Script aborted!")
                return
            }
        }

        if (Illustrated!=item.length-2&&Illustrated!=0) IllustratedStr="partially-"+IllustratedStr
        if (Illustrated==0) IllustratedStr="un"+IllustratedStr

        var ArticledStr="articled"
        var AssessmentStr=""
        var NRISStr=""
        var title=""

        if(Object.prototype.toString.call(item[1]) === '[object Array]') { // if in multistate, pick out first item in array
            var title=item[1][0]
        } else {
            var title = item[1]
        }

        var nrisonlyquery=JSON.parse(  // check if NRIS-only
            $.ajax({
                dataType: "json",
                url: mw.util.wikiScript('api'),
                data: {
                    format: 'json',
                    action: 'query',
                    prop: 'categories',
                    clcategories: 'Category:All articles sourced only to NRIS',
                    cllimit: 'max',
                    titles: title,
                    redirects: 'true'
                },
                async:false
            })
            .responseText
        );

        if (nrisonlyquery.query.redirects) { // resolve any redirects
            for (var r in nrisonlyquery.query.redirects) {
                title=nrisonlyquery.query.redirects[r].to
            }
        }

        for (var page in nrisonlyquery.query.pages) {
            if (typeof nrisonlyquery.query.pages[page].missing!="undefined") {
                ArticledStr="un"+ArticledStr
                continue
            }
            if (nrisonlyquery.query.pages[page].categories) {
                NRISStr="NRIS-only"
            }
        }

        var catlist='Category:FA-Class National Register of Historic Places articles‎|Category:A-Class National Register of '
        catlist+='Historic Places articles‎|Category:GA-Class National Register of Historic Places articles‎|Category:B-Class '
        catlist+='National Register of Historic Places articles‎|Category:C-Class National Register of Historic Places '
        catlist+='articles‎|Category:Start-Class National Register of Historic Places articles‎|Category:Stub-Class National '
        catlist+='Register of Historic Places articles‎|Category:Unassessed National Register of Historic Places '
        catlist+='articles‎|Category:List-Class National Register of Historic Places articles|Category:Redirect-Class National '
        catlist+='Register of Historic Places articles'

        if (ArticledStr!="unarticled") {
            var statsquery=JSON.parse(  // look at quality stats
                $.ajax({
                    dataType: "json",
                    url: mw.util.wikiScript('api'),
                    data: {
                        format: 'json',
                        action: 'query',
                        prop: 'categories',
                        clcategories: catlist,
                        cllimit: 'max',
                        titles: 'Talk:'+title,
                        redirects: 'true'
                    },
                    async:false
                })
                .responseText
            );

            for (var page in statsquery.query.pages) {
                var tagged = "no"
                if (statsquery.query.pages[page].categories) {
                    tagged = "yes"
                    for (var category in statsquery.query.pages[page].categories) {
                        var CatTitle=statsquery.query.pages[page].categories[category].title
                        if (CatTitle.indexOf("Stub")!=-1) {
                            AssessmentStr="Stub-class"
                            continue
                        }
                        if (CatTitle.indexOf("Unassessed")!=-1||CatTitle.indexOf("Redirect")!=-1) { // also count rdr unassessed
                            AssessmentStr="unassessed"
                            continue
                        }
                        if  (CatTitle.indexOf("List")!=-1) {
                            if (statsquery.query.pages[page].title.indexOf("National Register of Historic Places")!=-1){
                                ArticledStr="un"+ArticledStr
                                continue
                            } else {
                                AssessmentStr="Stub-class"
                                continue
                            }
                        }
                        AssessmentStr="Start+"
                    }
                }
                if (tagged=="no") {
                    AssessmentStr="untagged"
                }
            }
        }

        var StatsStr = ArticledStr+", "+IllustratedStr
        if (AssessmentStr!="") StatsStr+=", "+AssessmentStr
        if (NRISStr!="") StatsStr+=", "+NRISStr

        return StatsStr
}

function GenerateDuplicateTable(StateStructure,i,list,stateName,CountySpan,ThisStateSpan) {
    var wikitext=""
    var switchtabletext=""
    // check county-level duplicates
    CountySpan.innerHTML="<br />First checking county-level duplications in this state..."
    var NumberFound=0

    for (var j=1; j<StateStructure[i].length; j++) {
        wikitext+="==="+StateStructure[i][j][0]+", "+StateStructure[i][0]+"===\n"
        var TotalCountyDuplicates=0
        var TotalCountyArticled=0
        var TotalCountyIllustrated=0
        var TotalCountyStub=0
        var TotalCountyNRISOnly=0
        var TotalCountyStartPlus=0
        var TotalCountyUnassessed=0
        var TotalCountyUntagged=0

        for (var k=0; k<list.length; k++) {
            var numberlists=list[k].length-2
            var NumberInThisCounty=0
            var temp=[];
            for (var l=0; l<list[k].length; l++) { // make temp static
                temp[l]=list[k][l]
            }
            for (var l=2; l<temp.length; l++) {
                var ListName=temp[l].replace(/National Register of Historic Places listings (i|o)n /g,"")
                ListName=ListName.replace(", "+stateName,"")
                var oldnumber=NumberInThisCounty
                for (var m=0; m<StateStructure[i][j][1].length; m++) {
                    if (ListName==StateStructure[i][j][1][m]) NumberInThisCounty++
                }
                if (NumberInThisCounty==oldnumber) {temp.splice(l,1); l--}  // get rid of sublists not in this county
            }
            if (NumberInThisCounty>1) {
                NumberFound++
                CountySpan.innerHTML="<br />First checking county-level duplications in this state... "
                CountySpan.innerHTML+=NumberFound+" found so far..."
                TotalCountyDuplicates++
                if (TotalCountyDuplicates==1) {
                    wikitext+="{| class=\"wikitable sortable\" width=100%\n! {{NRHP color}} width=30% | Site\n! {{NRHP "
                    wikitext+="color}} width=30% | Lists\n! {{NRHP color}} width=10% | Number of Duplicates\n! {{NRHP "
                    wikitext+="color}} width=30% colspan=7 | Stats\n"
                }
                wikitext+="|-\n| [["+temp[1]+"]] (#"+temp[0]+")\n|\n"
                for (var m=2; m<temp.length; m++) {
                    wikitext+="*[["+temp[m]+"|"+temp[m].replace(/National Register of Historic Places listings (i|o)n /g,"")
                    wikitext+="]]\n"
                }
                var duplications=NumberInThisCounty-1
                TotalCountyDuplicates+=duplications-1
                wikitext+="| align=center | "+duplications+"\n| colspan=7 | "

                var StatsStr=getDuplicateStats(temp)
                if (StatsStr.indexOf("unarticled")==-1) TotalCountyArticled+=duplications
                if (StatsStr.indexOf(", illustrated")!=-1) TotalCountyIllustrated+=duplications
                if (StatsStr.indexOf("Stub-class")!=-1) TotalCountyStub+=duplications
                if (StatsStr.indexOf("NRIS-only")!=-1) TotalCountyNRISOnly+=duplications
                if (StatsStr.indexOf("Start+")!=-1) TotalCountyStartPlus+=duplications
                if (StatsStr.indexOf("unassessed")!=-1) TotalCountyUnassessed+=duplications
                if (StatsStr.indexOf("untagged")!=-1) TotalCountyUntagged+=duplications

                wikitext+=StatsStr+"\n"

                if (NumberInThisCounty==numberlists) {  // if only duplicated inside this county, get rid of it
                    list.splice(k,1)
                    k--
                }
            }
        }
        if (TotalCountyDuplicates==0) {
            wikitext+="There are no duplications across sublists of "+StateStructure[i][j][0]
            wikitext+=", "+StateStructure[i][0]+".\n\n"

            switchtabletext+="    |"+StateStructure[i][j][0]+", "+StateStructure[i][0]+"=0</td><td>0</td><td>-</td><td>"
            switchtabletext+="0</td><td>-</td><td>0</td><td>0</td><td>0</td><td>-</td><td>0</td><td>0</td><td>-</td>\n"

            CountySpan.innerHTML="<br />First checking county-level duplications in this state... None found."
        } else {
            wikitext+="|-\n! colspan=2 | Total\n! "+TotalCountyDuplicates+"\n! "+TotalCountyIllustrated+"\n! "
            wikitext+=TotalCountyArticled+"\n! "+TotalCountyStub+"\n! "+TotalCountyNRISOnly+"\n! "+TotalCountyStartPlus
            wikitext+="\n! "+TotalCountyUnassessed+"\n! "+TotalCountyUntagged+"\n|}\n\n"

            switchtabletext+="    |"+StateStructure[i][j][0]+", "+StateStructure[i][0]+"="+TotalCountyDuplicates+"</td><td>"
            switchtabletext+=TotalCountyIllustrated+"</td><td>-</td><td>"+TotalCountyArticled+"</td><td>-</td><td>"
            switchtabletext+=TotalCountyStub+"</td><td>"+TotalCountyNRISOnly+"</td><td>"+TotalCountyStartPlus
            switchtabletext+="</td><td>-</td><td>"+TotalCountyUnassessed+"</td><td>"+TotalCountyUntagged+"</td><td>-</td>\n"

            CountySpan.innerHTML="<br />First checking county-level duplications in this state... Complete! "+TotalCountyDuplicates+" found."
        }
    }
    if (StateStructure[i].length==1) {
        CountySpan.innerHTML="<br />First checking county-level duplications in this state... None found."
    }
    ThisStateSpan.innerHTML="<br />Now checking statewide duplicates..."
    if (StateStructure[i].length>1) wikitext+="==="+StateStructure[i][0]+" Statewide===\n"
    if (list.length==0) {
        wikitext+="There are no duplications across sublists of "+StateStructure[i][0]+".\n\n"

        switchtabletext+="|"+StateStructure[i][0]+"=0</td><td>0</td><td>-</td><td>0</td><td>-</td><td>0</td><td>0</td><td>0"
        switchtabletext+="</td><td>-</td><td>0</td><td>0</td><td>-</td>\n"
    } else {
        var TotalStateDuplicates=0
        var TotalStateArticled=0
        var TotalStateIllustrated=0
        var TotalStateStub=0
        var TotalStateNRISOnly=0
        var TotalStateStartPlus=0
        var TotalStateUnassessed=0
        var TotalStateUntagged=0

        wikitext+="{| class=\"wikitable sortable\" width=100%\n! {{NRHP color}} width=30% | Site\n! {{NRHP "
        wikitext+="color}} width=30% | Lists\n! {{NRHP color}} width=10% | Number of Duplicates\n! {{NRHP "
        wikitext+="color}} width=30% colspan=7 | Stats\n"

        for (var j=0; j<list.length; j++) {
            ThisStateSpan.innerHTML="<br />Now checking statewide duplicates... "
            ThisStateSpan.innerHTML+=j+" of "+list.length+" examined so far..."

            wikitext+="|-\n| [["+list[j][1]+"]] (#"+list[j][0]+")\n|\n"
            for (var m=2; m<list[j].length; m++) {
                wikitext+="*[["+list[j][m]+"|"
                wikitext+=list[j][m].replace(/National Register of Historic Places listings (i|o)n /g,"")+"]]\n"
            }

            var thisDuplicateStates=[];
            for (var l=2; l<list[j].length; l++) {
                var ListName=list[j][l].replace(/National Register of Historic Places listings (i|o)n /g,"")
                ListName=ListName.replace(", "+stateName,"")
                thisDuplicateStates.push(ListName)
                for (var k=1; k<StateStructure[i].length; k++) {
                    for (var m=0; m<StateStructure[i][k][1].length; m++) {
                        if (ListName==StateStructure[i][k][1][m]) {
                            thisDuplicateStates[thisDuplicateStates.length-1]=StateStructure[i][k][0]
                        }
                    }
                }
            }

            for (var l=0;l<thisDuplicateStates.length; l++) {    // shrink list if duplicated inside single county
                for (var m=l+1;m<thisDuplicateStates.length; m++) {
                    if (thisDuplicateStates[l]==thisDuplicateStates[m]) {thisDuplicateStates.splice(m,1);m--}
                }
            }
            var duplications=thisDuplicateStates.length-1

            TotalStateDuplicates+=duplications
            wikitext+="| align=center | "+duplications+"\n| colspan=7 | "

            var StatsStr=getDuplicateStats(list[j])
            if (StatsStr.indexOf("unarticled")==-1) TotalStateArticled+=duplications
            if (StatsStr.indexOf(", illustrated")!=-1) TotalStateIllustrated+=duplications
            if (StatsStr.indexOf("Stub-class")!=-1) TotalStateStub+=duplications
            if (StatsStr.indexOf("NRIS-only")!=-1) TotalStateNRISOnly+=duplications
            if (StatsStr.indexOf("Start+")!=-1) TotalStateStartPlus+=duplications
            if (StatsStr.indexOf("unassessed")!=-1) TotalStateUnassessed+=duplications
            if (StatsStr.indexOf("untagged")!=-1) TotalStateUntagged+=duplications

            wikitext+=StatsStr+"\n"
        }
        wikitext+="|-\n! colspan=2 | Total\n! "+TotalStateDuplicates+"\n! "+TotalStateIllustrated+"\n! "
        wikitext+=TotalStateArticled+"\n! "+TotalStateStub+"\n! "+TotalStateNRISOnly+"\n! "+TotalStateStartPlus
        wikitext+="\n! "+TotalStateUnassessed+"\n! "+TotalStateUntagged+"\n|}\n\n"

        switchtabletext+="|"+StateStructure[i][0]+"="+TotalStateDuplicates+"</td><td>"+TotalStateIllustrated+"</td><td>-</td><td>"
        switchtabletext+=TotalStateArticled+"</td><td>-</td><td>"+TotalStateStub+"</td><td>"+TotalStateNRISOnly+"</td><td>"
        switchtabletext+=TotalStateStartPlus+"</td><td>-</td><td>"+TotalStateUnassessed+"</td><td>"+TotalStateUntagged
        switchtabletext+="</td><td>-</td>\n"
    }
    return [wikitext,switchtabletext]
}

function CheckDuplicates() {
    var ProgressDiv=document.getElementById("ProgressDiv")
    ProgressDiv.innerHTML="Checking multi-state duplicates..."
    var MultiSpan = document.createElement("span")
    ProgressDiv.appendChild(MultiSpan)

    var multistate=[];
    var li=document.getElementById("multistate").getElementsByClassName("refbegin")[0].getElementsByTagName("li")
    for (var k=0; k<li.length; k++) {
        var links=li[k].getElementsByTagName("a")
        var refnum=li[k].innerHTML.match(/[0-9]{8}/)[0]
        var titles=[refnum]                                   // make refnum element 0; title=1; county lists=2,3,4,...
        for (var l=0; l<links.length; l++) {
            var temp=links[l].href.split("/")
            if (temp[temp.length-1].indexOf("index")!=-1) {   // handle redlinks
                temp[temp.length-1]=temp[temp.length-1].split("=")[1].split("&")[0]
            }
            temp[temp.length-1]=decodeURIComponent(temp[temp.length-1])
            titles.push(temp[temp.length-1].replace(/_/g," "))
        }
        multistate[multistate.length]=titles
    }
    var wikitext="==National==\nThe following listings are included in two or more lists which are located in different states."
    wikitext+="\n{| class=\"wikitable sortable\" width=100%\n! {{NRHP color}} width=30% | Site\n! {{NRHP color}} width=30% | "
    wikitext+="Lists\n! {{NRHP color}} width=10% | Number of Duplicates\n! {{NRHP color}} width=30% colspan=7 | Stats\n"

    var switchtabletext="<includeonly>{{#tag:ref|[[WP:NRHPPROGRESS/Duplicates#{{{1}}}{{#if:{{{statewide|}}}|&nbsp;Statewide}}"
    switchtabletext+="{{!}}Click here]] for {{{1}}} duplicate information.}}{{#ifeq:{{{1}}}|National|</th>|{{#ifeq:{{{1}}}|"
    switchtabletext+="{{First word|{{{1}}}|sep=,}}|</th>|</td><td>[[{{Remove first word|{{{1}}}|sep=,}}]]</td>}}}}<td>{{#switch:"
    switchtabletext+="{{{1}}}\n"

    var Total=0
    var TotalIllustrated=0
    var TotalArticled=0
    var TotalStub=0
    var TotalStartPlus=0
    var TotalUntagged=0
    var TotalUnassessed=0
    var TotalNRISOnly=0
    for (var i=0; i<multistate.length; i++) {
        MultiSpan.innerHTML = " "+i+" of "+multistate.length+" complete..."
        wikitext+="|-\n| [["+multistate[i][1]+"]] (#"+multistate[i][0]+")\n|\n"
        var temp=[multistate[i][1],0]  // 0=not duplicated inside single state by default
        multistate[i][1]=temp
        var oldstate=""
        var state=""
        var totalstates=0

        for (var j=2; j<multistate[i].length; j++) {
        wikitext+="*[["+multistate[i][j]+"|"+multistate[i][j].replace(/National Register of Historic Places listings (i|o)n /g,"")
            wikitext+="]]\n"
            state=getState(multistate[i][j])
            if (state!=oldstate) {
                totalstates++
            } else {
                if (multistate[i][1][1]==0) {  // tag as being duplicated inside one state (and record which state) for later use
                    multistate[i][1][1]=state
                } else {
                    multistate[i][1][multistate[i][1].length]=state
                }
            }
            oldstate=state
        }
        var duplications=totalstates-1
        Total+=duplications
        wikitext+="| align=center | "+duplications+"\n| colspan=7 | "

        var StatsStr=getDuplicateStats(multistate[i])
        if (StatsStr.indexOf("unarticled")==-1) TotalArticled+=duplications
        if (StatsStr.indexOf(", illustrated")!=-1) TotalIllustrated+=duplications
        if (StatsStr.indexOf("Stub-class")!=-1) TotalStub+=duplications
        if (StatsStr.indexOf("NRIS-only")!=-1) TotalNRISOnly+=duplications
        if (StatsStr.indexOf("Start+")!=-1) TotalStartPlus+=duplications
        if (StatsStr.indexOf("unassessed")!=-1) TotalUnassessed+=duplications
        if (StatsStr.indexOf("untagged")!=-1) TotalUntagged+=duplications

        wikitext+=StatsStr+"\n"
    }
    wikitext+="|-\n! colspan=2 | Total\n! "+Total+"\n! "+TotalIllustrated+"\n! "+TotalArticled+"\n! "+TotalStub+"\n! "
    wikitext+=TotalNRISOnly+"\n! "+TotalStartPlus+"\n! "+TotalUnassessed+"\n! "+TotalUntagged+"\n|}\n\n"

    switchtabletext+="|National="+Total+"</td><td>"+TotalIllustrated+"</td><td>-</td><td>"+TotalArticled+"</td><td>-</td><td>"
    switchtabletext+=TotalStub+"</td><td>"+TotalNRISOnly+"</td><td>"+TotalStartPlus+"</td><td>-</td><td>"+TotalUnassessed
    switchtabletext+="</td><td>"+TotalUntagged+"</td><td>-</td>\n"

    MultiSpan.innerHTML = " Complete!"

    for (var i=0; i<multistate.length; i++) {
        if (multistate[i][1][1]==0) {multistate.splice(i,1); i--}   // get rid of those not duplicated inside single states
    }

    var StateSpan = document.createElement("span")
    ProgressDiv.appendChild(StateSpan)
    StateSpan.inerHTML="test"
    var CountySpan = document.createElement("span")
    ProgressDiv.appendChild(CountySpan)
    CountySpan.inerHTML="test"
    var ThisStateSpan = document.createElement("span")
    ProgressDiv.appendChild(ThisStateSpan)
    ThisStateSpan.inerHTML="test"

    var StateStructure=[
        ["Alabama",
            ["Jefferson County",["Jefferson County", "Birmingham"]],
            ["Mobile County",["Mobile County", "Mobile"]]],
        ["Alaska"],
        ["Arizona",
            ["Maricopa County",["Maricopa County", "Phoenix"]],
            ["Yavapai County",["Yavapai County", "Prescott"]]],
        ["Arkansas",
            ["Pulaski County",["Pulaski County", "Little Rock"]]],
        ["California",
            ["Los Angeles County",["Los Angeles County", "Los Angeles", "Pasadena"]]],
        ["Colorado",
            ["Denver County",["downtown Denver","northeast Denver","southeast Denver","West Denver"]]],
        ["Connecticut",
            ["Fairfield County",["Fairfield County","Bridgeport","Greenwich","Stamford"]],
            ["Hartford County",["Hartford County","Hartford","Southington","West Hartford","Windsor"]],
            ["Middlesex County",["Middlesex County","Middletown"]],
            ["New Haven County",["New Haven County","New Haven"]]],
        ["Delaware",
            ["New Castle County",["northern New Castle County","southern New Castle County","Wilmington"]]],
        ["D.C."],
        ["Florida",
            ["Miami-Dade County",["Miami-Dade County","Miami"]]],
        ["Georgia"],
        ["Hawaii",
            ["Honolulu County",["Oahu","Northwestern Hawaiian Islands"]],
            ["Maui County",["Maui","Kahoolawe","Lanai","Molokai"]]],
        ["Idaho"],
        ["Illinois",
            ["Cook County",["Cook County","Central Chicago","North Side Chicago","South Side Chicago","West Side Chicago","Evanston"]]],
        ["Indiana",
            ["Marion County",["Marion County","Center Township, Marion County"]]],
        ["Iowa",
            ["Scott County",["Scott County","Downtown Davenport","east Davenport","west Davenport"]]],
        ["Kansas"],
        ["Kentucky",
            ["Jefferson County",["Jefferson County","Anchorage","Downtown Louisville","The Highlands, Louisville","Old Louisville","Portland, Louisville","Louisville's West End"]]],
        ["Louisiana"],
        ["Maine",
            ["Cumberland County",["Cumberland County","Portland"]]],
        ["Maryland",
            ["City of Baltimore",["Central Baltimore","East and Northeast Baltimore","North and Northwest Baltimore","South and Southeast Baltimore","West and Southwest Baltimore"]]],
        ["Massachusetts",
            ["Barnstable County",["Barnstable County","Barnstable"]],
            ["Bristol County",["Bristol County","Fall River","New Bedford","Taunton"]],
            ["Essex County",["Essex County","Andover","Gloucester","Ipswich","Lawrence","Lynn","Methuen","Salem"]],
            ["Hampden County",["Hampden County","Springfield"]],
            ["Middlesex County",["Middlesex County","Arlington","Cambridge","Concord","Framingham","Lowell","Marlborough","Medford","Newton","Reading","Sherborn","Somerville","Stoneham","Wakefield","Waltham","Weston","Winchester"]],
            ["Norfolk County",["Norfolk County","Brookline","Milton","Quincy"]],
            ["Suffolk County",["Suffolk County","northern Boston","southern Boston"]],
            ["Worcester County",["Worcester County","Southbridge","Uxbridge","eastern Worcester","northwestern Worcester","southwestern Worcester"]]],
        ["Michigan",
            ["Wayne County",["Wayne County","Downtown and Midtown Detroit","Detroit"]]],
        ["Minnesota"],
        ["Mississippi"],
        ["Missouri",
            ["Jackson County",["Jackson County: Downtown Kansas City","Jackson County: Kansas City other"]],
            ["St. Louis",["Downtown and Downtown West St. Louis","St. Louis north and west of downtown","St. Louis south and west of downtown"]]],
        ["Montana"],
        ["Nebraska"],
        ["Nevada"],
        ["New Hampshire"],
        ["New Jersey",
            ["Bergen County",["Bergen County","Closter","Franklin Lakes","Ridgewood","Saddle River","Wyckoff"]]],
        ["New Mexico"],
        ["New York",
            ["Albany County",["Albany County","Albany"]],
            ["Dutchess County",["Dutchess County","Poughkeepsie","Rhinebeck"]],
            ["Erie County",["Erie County","Buffalo"]],
            ["Monroe County",["Monroe County","Rochester"]],
            ["Nassau County",["Hempstead (town)","North Hempstead (town)","Oyster Bay (town)"]],
            ["New York County",["Manhattan below 14th Street","Manhattan from 14th to 59th Streets","Manhattan above 59th to 110th Streets","Manhattan above 110th Street","Manhattan on islands"]],
            ["Onondaga County",["Onondaga County","Syracuse"]],
            ["Suffolk County",["Babylon (town)","Brookhaven (town)","East Hampton (town)","Huntington (town)","Islip (town)","Riverhead (town)","Shelter Island (town)","Smithtown (town)","Southampton (town)","Southold (town)"]],
            ["Westchester County",["northern Westchester County","southern Westchester County","New Rochelle","Peekskill","Yonkers"]]],
        ["North Carolina"],
        ["North Dakota"],
        ["Ohio",
            ["Cuyahoga County",["Cuyahoga County","Cleveland"]],
            ["Erie County",["Erie County","Sandusky"]],
            ["Franklin County",["Franklin County","Columbus"]],
            ["Hamilton County",["Hamilton County","downtown Cincinnati","eastern Cincinnati","western Cincinnati"]],
            ["Montgomery County",["Montgomery County","Dayton"]],
            ["Summit County",["Summit County","Akron"]]],
        ["Oklahoma"],
        ["Oregon",
            ["Multnomah County",["Multnomah County","North Portland","Northeast Portland","Northwest Portland","Southeast Portland","Southwest Portland"]]],
        ["Pennsylvania",
            ["Allegheny County",["Allegheny County","Pittsburgh"]],
            ["Chester County",["eastern Chester County","northern Chester County","southern Chester County"]],
            ["Lancaster County",["Lancaster County","Lancaster"]],
            ["Philadelphia",["Center City, Philadelphia","North Philadelphia","Northeast Philadelphia","Northwest Philadelphia","South Philadelphia","Southwest Philadelphia","West Philadelphia"]]],
        ["Rhode Island",
            ["Providence County",["Providence County","Pawtucket","Providence"]]],
        ["South Carolina",
            ["Charleston County",["Charleston County","Charleston"]],
            ["Greenville County",["Greenville County","Greenville"]],
            ["Richland County",["Richland County","Columbia"]],
            ["York County",["York County","Rock Hill"]]],
        ["South Dakota"],
        ["Tennessee"],
        ["Texas"],
        ["Utah",
            ["Salt Lake County",["Salt Lake County","Salt Lake City"]],
            ["Washington County",["Washington County","Zion National Park"]]],
        ["Vermont"],
        ["Virginia"],
        ["Washington",
            ["King County",["King County","Seattle"]],
            ["Pierce County",["Pierce County","Tacoma"]],
            ["Spokane County",["Spokane County","Spokane"]]],
        ["West Virginia"],
        ["Wisconsin",
            ["Dane County",["Dane County","Madison"]],
            ["Milwaukee County",["Milwaukee County","Milwaukee"]]],
        ["Wyoming"],
        ["Puerto Rico"],
        ["Guam"],
        ["Virgin Islands"],
        ["Northern Mariana Islands"],
        ["American Samoa"],
        ["Federated States of Micronesia"],
        ["Palau"],
        ["Marshall Islands"],
        ["U.S. Minor Outlying Islands"]
    ]

    var states=document.getElementById("duplicates-div").getElementsByClassName("refbegin")
    var currentState=0
    for (var i=0; i<StateStructure.length; i++) {
        StateSpan.innerHTML = "<br />Now working on individual states... "+i+" of "+StateStructure.length+" complete..."
        var thisstate=[];
        var li=states[currentState].getElementsByTagName("li")
        for (var j=0; j<li.length; j++) {
            var links=li[j].getElementsByTagName("a")
            var refnum=li[j].innerHTML.match(/[0-9]{8}/)[0]
            var titles=[refnum]                                   // make refnum element 0; title=1; county lists=2,3,4,...
            for (var l=0; l<links.length; l++) {
                var temp=links[l].href.split("/")
                if (temp[temp.length-1].indexOf("index")!=-1) {   // handle redlinks
                    temp[temp.length-1]=temp[temp.length-1].split("=")[1].split("&")[0]
                }
                temp[temp.length-1]=decodeURIComponent(temp[temp.length-1])
                titles.push(temp[temp.length-1].replace(/_/g," "))
            }
            thisstate[thisstate.length]=titles
        }
        var stateName=getState(thisstate[0][2])
        wikitext+="=="+StateStructure[i][0]+"==\n"
        while (i<StateStructure.length&&stateName!=StateStructure[i][0]) {
            // if the state we're looking at isn't the next one in the structure array
            var tempstate=[];
            for (var j=0; j<multistate.length; j++) {
                var inThisState=0
                for (var k=1; k<multistate[j][1].length; k++) {                    // check if any multistate duplicates are
                    if (multistate[j][1][k]==StateStructure[i][0]) inThisState=1   // duplicated in this state
                }
                if (inThisState==1) {
                    var temp=[];
                    for (var k=0; k<multistate[j].length; k++) { // make temp static
                        temp[k]=multistate[j][k]
                    }
                    for (var k=2; k<temp.length; k++) {
                        if (getState(temp[k])!=StateStructure[i][0]) {temp.splice(k,1); k--} // only pick out those in this state
                    }
                    tempstate[tempstate.length] = [temp[0],temp[1][0],temp[2],temp[3]] // refnum, title, list1, list2
                    for (var k=4; k<temp.length; k++) {
                        tempstate[tempstate.length-1].push(temp[k]) // list3, list4,...
                    }
                }
            }
            var result=GenerateDuplicateTable(StateStructure,i,tempstate,stateName,CountySpan,ThisStateSpan)
            wikitext+=result[0]
            switchtabletext+=result[1]

            i++  // look at next state in StateStructure and repeat until we find the one we're looking at
            StateSpan.innerHTML = "<br />Now working on individual states... "+i+" of "+StateStructure.length+" complete..."
            if (i!=StateStructure.length) wikitext+="=="+StateStructure[i][0]+"==\n"
        }
        if (i!=StateStructure.length) {
            // now we know the state we're looking at has the duplications in thisstate array
            for (var j=0; j<multistate.length; j++) {
                var inThisState=0
                for (var k=1; k<multistate[j][1].length; k++) {         // check if any multistate duplicates are also
                    if (multistate[j][1][k]==stateName) inThisState=1   // duplicated in the current state
                }
                if (inThisState==1) {
                    var temp=[];
                    for (var k=0; k<multistate[j].length; k++) { // make temp static
                        temp[k]=multistate[j][k]
                    }
                    for (var k=2; k<temp.length; k++) {
                        if (getState(temp[k])!=stateName) {temp.splice(k,1); k--} // only pick out those in this state
                    }
                    thisstate[thisstate.length] = [temp[0],temp[1][0],temp[2],temp[3]] // refnum, title, list1, list2
                    for (var k=4; k<temp.length; k++) {
                        thisstate[thisstate.length-1].push(temp[k]) // list3, list4,...
                    }
                }
            }
            var result=GenerateDuplicateTable(StateStructure,i,thisstate,stateName,CountySpan,ThisStateSpan)
            wikitext+=result[0]
            switchtabletext+=result[1]

            CountySpan.innerHTML="<br />&nbsp;"
            ThisStateSpan.innerHTML="<br />&nbsp;"
            if (currentState<states.length-1) currentState++  // next state on the page
        }
    }
    CountySpan.innerHTML=""
    ThisStateSpan.innerHTML=""
    StateSpan.innerHTML = "<br />Now working on individual states... Complete!"
    ProgressDiv.innerHTML+="<br />Now outputting data to "
    ProgressDiv.innerHTML+="<a href='http://en.wikipedia.org/wiki/WP:NRHPPROGRESS/Duplicates'>WP:NRHPPROGRESS/Duplicates</a>..."

    switchtabletext+="|error</td><td>error</td><td>-</td><td>error</td><td>-</td><td>error</td><td>error</td><td>error</td><td>"
    switchtabletext+="-</td><td>error</td><td>error</td><td>-</td>\n}}</includeonly><noinclude>\n"

    var TOCtext="__NOTOC__\n{{anchor|top}}\n{| style=\"margin:.5em; border:1px solid #000; padding:0;"
    TOCtext+=" text-align:center\"\n| '''[[#National|National]]'''\n<hr />\n"

    for (var i=0; i<StateStructure.length; i++) {
        TOCtext+="[[#"+StateStructure[i][0]
        if (StateStructure[i].length>1) TOCtext+=" Statewide"
        TOCtext+="|"+StateStructure[i][0]+"]] "
        for (var j=1; j<StateStructure[i].length; j++) {
            if (j==1) TOCtext+="("
            TOCtext+="[[#"+StateStructure[i][j][0]+", "+StateStructure[i][0]+"|"
            TOCtext+=StateStructure[i][j][0].replace(", "+StateStructure[i][0],"").replace(" County","").replace("City of ","")
            TOCtext+="]], "
        }
        if (StateStructure[i].length>1) {
            TOCtext=TOCtext.substr(0,TOCtext.length-2)  // remove final comma
            TOCtext+=")"
        }
        if (StateStructure[i][0]=="Wyoming") TOCtext+="\n<hr />\n"
        else if (i!=StateStructure.length-1) TOCtext+=" – "
    }
    TOCtext+="\n|}\n\n"

    wikitext=switchtabletext+TOCtext+wikitext+"</noinclude>"

    var d=new Date();
    var months = ['January','February','March','April','May','June','July','August','September','October','November','December'];
    var year=d.getYear();
    if (year < 1000) year += 1900
    var DateStr = months[d.getMonth()]+" "+d.getDate()+", "+year

    // edit page with total wikitext
    editPageNRISOnly({
        title: 'Wikipedia:WikiProject National Register of Historic Places/Progress/Duplicates',
        text: wikitext,
    summary:'Generate statistics about duplicates in NRHP lists as of '+DateStr
    },ProgressDiv,0,"yes");
}

function editPageNRISOnly(info,Span,subpages,lastedit) { // edit page
    $.ajax({
        url: mw.util.wikiScript( 'api' ),
        type: 'POST',
        dataType: 'json',
        async: false,
        data: {
            format: 'json',
            action: 'edit',
            title: info.title,
            text: info.text,
            summary: info.summary,
            token: mw.user.tokens.get( 'editToken' ),
            bot: 'true'
        }
    })
        .done (function( data ) {
            if (data && data.edit && data.edit.result && data.edit.result == 'Success') {
                if (lastedit=="almost") {
                    Span.innerHTML = "<br />Dump complete! "+subpages+" subpages edited successfully!"
                } else if (lastedit=="yes") {
                    Span.innerHTML += "<br />Output saved! Click link to see it."
                } else if (lastedit=="no") {
                    Span.innerHTML = "<br />"+subpages+" subpages edited successfully..."
                }
            } else {
                Span.innerHTML += " The edit query returned an error! Aborting script."
                alert('Edit query error:\n'+data.error.code+': '+data.error.info+'\nClick OK to view raw wikitext for latest subpage.');
                alert(mw.user.tokens.get( 'editToken' ))
                var popup = open("");
                var div = popup.document.createElement("div");
                div.innerHTML=info.title+"<br><hr><br>"+info.text
                popup.document.body.appendChild(div);
                throw new Error()
            }
        })
        .fail (function() {
            alert('Ajax failure. Click OK to view raw wikitext for latest subpage.');
            var popup = open("");
            var div = popup.document.createElement("div");
            div.innerHTML=info.text
            popup.document.body.appendChild(div);
        });
}

addOnloadHook(CheckPermission);