User:84/nrhp-chrome/UpdateNRHPProgress.js

//copy of User:Dudemanfellabra/UpdateNRHPProgress.js

var wikitext = 'error' var ProgressStructure=[]; // ProgressStructure[table][row].StatName var TotalToQuery=0; var TotalQueried=0; var ErrorCount=0; var WarningCount="",0; // 0=status, 1=count var InitialTime=0; var ProgressDivTimer=0 // timer for updating ProgressDiv var DefaultQueryPause=1 // number of milliseconds to wait between each API query; increased by code if rate limit reached

function ProgressButton { if (mw.config.get('wgPageName')!="Wikipedia:WikiProject_National_Register_of_Historic_Places/Progress"||location.href.indexOf('action')!=-1) return; var button=document.createElement("input") button.setAttribute("type", "button"); button.setAttribute("value", "Update Statistics"); button.setAttribute("id", "button2"); button.setAttribute("onclick", "Click"); var content=document.getElementById('mw-content-text')

content.parentNode.insertBefore(button, content) }

function Click {  // after button is clicked, disable it and fetch wikitext of Progress page var button2 = document.getElementById('button2') button2.disabled = true var ProgressDiv = document.createElement("div") ProgressDiv.setAttribute("id", "ProgressDiv") ProgressDiv.setAttribute("style", "width:500px; border:1px solid black; padding:5px; background:#ffffff") button2.parentNode.insertBefore(ProgressDiv, button2) ProgressDiv.innerHTML = "Initializing..."

getWikitext(mw.config.get('wgPageName')) // after wikitext fetched, SetupTables is called }

// create array of table structure to be populated later function SetupTables { var table=document.getElementsByClassName('wikitable sortable');

// set up national totals var tr=table[0].getElementsByTagName("tr") ProgressStructure[0]=[]; for (var j=1; j<tr.length-3; j++) { var td=tr[j].getElementsByTagName("td") ProgressStructure[0][j-1]={}; ProgressStructure[0][j-1].ID=td[0].innerHTML // state name ProgressStructure[0][j-1].Total=0 ProgressStructure[0][j-1].Illustrated=0 ProgressStructure[0][j-1].Articled=0 ProgressStructure[0][j-1].Stubs=0 ProgressStructure[0][j-1].NRISonly=0 ProgressStructure[0][j-1].StartPlus=0 ProgressStructure[0][j-1].Unassessed=0 ProgressStructure[0][j-1].Untagged=0 }

// special row for Tangier, Morocco var td=tr[tr.length-3].getElementsByTagName("td") ProgressStructure[0][tr.length-4]={}; ProgressStructure[0][tr.length-4].ID="Tangier, Morocco" ProgressStructure[0][tr.length-4].Total=parseFloat(td[1].innerHTML.replace(",","")) ProgressStructure[0][tr.length-4].Illustrated=parseFloat(td[2].innerHTML.replace(",","")) ProgressStructure[0][tr.length-4].Articled=parseFloat(td[4].innerHTML.replace(",","")) ProgressStructure[0][tr.length-4].Stubs=parseFloat(td[6].innerHTML.replace(",","")) ProgressStructure[0][tr.length-4].NRISonly=parseFloat(td[7].innerHTML.replace(",","")) ProgressStructure[0][tr.length-4].StartPlus=parseFloat(td[8].innerHTML.replace(",","")) ProgressStructure[0][tr.length-4].Unassessed=parseFloat(td[10].innerHTML.replace(",","")) ProgressStructure[0][tr.length-4].Untagged=parseFloat(td[11].innerHTML.replace(",",""))

// duplicates row var td=tr[tr.length-2].getElementsByTagName("td") ProgressStructure[0][tr.length-3]={}; ProgressStructure[0][tr.length-3].ID="National Duplicates" ProgressStructure[0][tr.length-3].Total=parseFloat(td[0].innerHTML.replace(",","")) ProgressStructure[0][tr.length-3].Illustrated=parseFloat(td[1].innerHTML.replace(",","")) ProgressStructure[0][tr.length-3].Articled=parseFloat(td[3].innerHTML.replace(",","")) ProgressStructure[0][tr.length-3].Stubs=parseFloat(td[5].innerHTML.replace(",","")) ProgressStructure[0][tr.length-3].NRISonly=parseFloat(td[6].innerHTML.replace(",","")) ProgressStructure[0][tr.length-3].StartPlus=parseFloat(td[7].innerHTML.replace(",","")) ProgressStructure[0][tr.length-3].Unassessed=parseFloat(td[9].innerHTML.replace(",","")) ProgressStructure[0][tr.length-3].Untagged=parseFloat(td[10].innerHTML.replace(",",""))

// national totals ProgressStructure[0][tr.length-2]={}; ProgressStructure[0][tr.length-2].ID="National Totals" ProgressStructure[0][tr.length-2].Total=0 ProgressStructure[0][tr.length-2].Illustrated=0 ProgressStructure[0][tr.length-2].Articled=0 ProgressStructure[0][tr.length-2].Stubs=0 ProgressStructure[0][tr.length-2].NRISonly=0 ProgressStructure[0][tr.length-2].StartPlus=0 ProgressStructure[0][tr.length-2].Unassessed=0 ProgressStructure[0][tr.length-2].Untagged=0

// now data for each state for (var i=1; i<table.length; i++) { var tr=table[i].getElementsByTagName("tr") ProgressStructure[i]=[]; for (var j=1; j<tr.length-2; j++) { // skip title row, statewide duplicates, and totals row var td=tr[j].getElementsByTagName("td") // fill in existing data in case error ProgressStructure[i][j-1]={}; ProgressStructure[i][j-1].ID=td[0].innerHTML.substr(0,5) ProgressStructure[i][j-1].Total=parseFloat(td[3].innerHTML.replace(",","")) ProgressStructure[i][j-1].Illustrated=parseFloat(td[4].innerHTML.replace(",","")) ProgressStructure[i][j-1].Articled=parseFloat(td[6].innerHTML.replace(",","")) ProgressStructure[i][j-1].Stubs=parseFloat(td[8].innerHTML.replace(",","")) ProgressStructure[i][j-1].NRISonly=parseFloat(td[9].innerHTML.replace(",","")) ProgressStructure[i][j-1].StartPlus=parseFloat(td[10].innerHTML.replace(",","")) ProgressStructure[i][j-1].Unassessed=parseFloat(td[12].innerHTML.replace(",","")) ProgressStructure[i][j-1].Untagged=parseFloat(td[13].innerHTML.replace(",","")) var link=td[1].getElementsByTagName("a") if (link.length!=0 && link[0].href.search("#")==-1) { link=decodeURI(link[0].href).split("/") link=link[link.length-1].replace(/_/g," ") ProgressStructure[i][j-1].Link=link

ProgressStructure[i][j-1].ArticleQueried=0 // for querying later ProgressStructure[i][j-1].TalkQueried=0 } else { if (ProgressStructure[i][j-1].ID!="ddddd") { // if no link and not duplicate, must be totals row, so we can zero it                   ProgressStructure[i][j-1].Total=0 ProgressStructure[i][j-1].Illustrated=0 ProgressStructure[i][j-1].Articled=0 ProgressStructure[i][j-1].Stubs=0 ProgressStructure[i][j-1].NRISonly=0 ProgressStructure[i][j-1].StartPlus=0 ProgressStructure[i][j-1].Unassessed=0 ProgressStructure[i][j-1].Untagged=0 }           }        }

// duplicates row var td=tr[tr.length-2].getElementsByTagName("td") ProgressStructure[i][tr.length-3]={}; ProgressStructure[i][tr.length-3].ID=ProgressStructure[0][i-1].ID+" Duplicates" ProgressStructure[i][tr.length-3].Total=parseFloat(td[0].innerHTML.replace(",","")) ProgressStructure[i][tr.length-3].Illustrated=parseFloat(td[1].innerHTML.replace(",","")) ProgressStructure[i][tr.length-3].Articled=parseFloat(td[3].innerHTML.replace(",","")) ProgressStructure[i][tr.length-3].Stubs=parseFloat(td[5].innerHTML.replace(",","")) ProgressStructure[i][tr.length-3].NRISonly=parseFloat(td[6].innerHTML.replace(",","")) ProgressStructure[i][tr.length-3].StartPlus=parseFloat(td[7].innerHTML.replace(",","")) ProgressStructure[i][tr.length-3].Unassessed=parseFloat(td[9].innerHTML.replace(",","")) ProgressStructure[i][tr.length-3].Untagged=parseFloat(td[10].innerHTML.replace(",",""))

// state totals ProgressStructure[i][tr.length-2]={}; ProgressStructure[i][tr.length-2].ID=ProgressStructure[0][i-1].ID+" Totals" ProgressStructure[i][tr.length-2].Total=0 ProgressStructure[i][tr.length-2].Illustrated=0 ProgressStructure[i][tr.length-2].Articled=0 ProgressStructure[i][tr.length-2].Stubs=0 ProgressStructure[i][tr.length-2].NRISonly=0 ProgressStructure[i][tr.length-2].StartPlus=0 ProgressStructure[i][tr.length-2].Unassessed=0 ProgressStructure[i][tr.length-2].Untagged=0 }   for (var i=1; i<ProgressStructure.length; i++) { // count total number of rows to check for (var j=0; j<ProgressStructure[i].length-2; j++) { if (typeof ProgressStructure[i][j].Link!="undefined") TotalToQuery++ // don't count duplicates and total rows }   }    TotalQueried=0;

var ProgressDiv=document.getElementById("ProgressDiv") ProgressDiv.innerHTML+=" Done! "

var ProgressSpan=document.createElement("span") ProgressSpan.setAttribute("id", "ProgressSpan") ProgressDiv.appendChild(ProgressSpan) ProgressSpan.innerHTML = "Querying county data... 0 (0%) of "+TotalToQuery+" lists checked."

var TimeSpan=document.createElement("span") TimeSpan.setAttribute("id", "TimeSpan") ProgressDiv.appendChild(TimeSpan) TimeSpan.innerHTML = ""

InitialTime=new Date // record starting time UpdateProgressDiv; LoadList(1,0); // begin querying first page }

// load next list to query function LoadList(currentTable,currentRow) { // check if we need to go to the next table if (currentRow>ProgressStructure[currentTable].length-3) { currentRow=0 currentTable++ }   // check if there are no more tables if (currentTable>ProgressStructure.length-1) return;

if (typeof ProgressStructure[currentTable][currentRow].Link=="undefined") { // skip duplicate and total rows LoadList(currentTable,currentRow+1) return; }

var title=ProgressStructure[currentTable][currentRow].Link

setTimeout(function{ // short delay to prevent API overload       getProgressListWikitext(title,currentTable,currentRow);        LoadList(currentTable,currentRow+1);    }, DefaultQueryPause); return; }

function WikitextFetched(ajaxResponse,status,title,currentTable,currentRow) { if (status!="success") { NewWarning("Wikitext "+ajaxResponse.errorThrown) setTimeout(function{ // try again after delay if rate limit reached           getProgressListWikitext(title,currentTable,currentRow);        }, 250); return; }   // won't get here unless successful var responseText=JSON.parse(ajaxResponse.responseText) var pagetext=responseText.query.pages[responseText.query.pageids[0]].revisions[0]["*"] if (responseText.query.redirects) { // if redirect, find section var SectionName="Undefined" for (var r in responseText.query.redirects) { if (typeof responseText.query.redirects[r].tofragment!="undefined") SectionName=responseText.query.redirects[r].tofragment.replace(/.27/g,"'") }

var regex = new RegExp("=[ ]*(\\[\\[(.*?\\|)?[ ]*)?"+SectionName+"([ ]*\\]\\])?[ ]*=", "g") var sectionheader=pagetext.match(regex) if (sectionheader == null) { // if no section found, check if one of known empty counties var EmptyCounties=["02270", "12067", "42023", "48017", "48023", "48033", "48069", "48075", "48079", "48083", "48103", "48107", "48119", "48131", "48155", "48165", "48195", "48207", "48219", "48247", "48269", "48279", "48341", "48369", "48389", "48415", "48421", "48431", "48433", "48437", "48445", "48461", "48475", "48501", "51735"]

var ID = ProgressStructure[currentTable][currentRow].ID           var errorcode = 0 for (var k=0; k<EmptyCounties.length; k++) { if (ID==EmptyCounties[k]) {errorcode=-1} }           if (errorcode!=0) { // must be an empty county ProgressStructure[currentTable][currentRow].Total=0 ProgressStructure[currentTable][currentRow].Illustrated=0 ProgressStructure[currentTable][currentRow].Articled=0 ProgressStructure[currentTable][currentRow].Stubs=0 ProgressStructure[currentTable][currentRow].NRISonly=0 ProgressStructure[currentTable][currentRow].StartPlus=0 ProgressStructure[currentTable][currentRow].Unassessed=0 ProgressStructure[currentTable][currentRow].Untagged=0 ProgressStructure[currentTable][currentRow].Link=title

TotalQueried++ if (TotalQueried==TotalToQuery) CalculateProgressTotals return; }           // if we're here, must have been a redirect with no section, and not a known empty county sectionheader=pagetext.match(/{{NRHP header/g) // then look for tables without a section if (sectionheader==null||sectionheader.length>1) { // if still can't find a table or find multiple tables, fatal error ProgressFatalError(0,title,currentTable,currentRow) }       }        var StartIndex=pagetext.indexOf(sectionheader[0]) var sectiontext=pagetext.substr(StartIndex,pagetext.indexOf("\n==",StartIndex)-StartIndex) // only look at relevant section

StartIndex=sectiontext.indexOf("{{NRHP header") if (StartIndex==-1) { if (sectiontext.indexOf("{{NRHP row")!=-1) { ProgressFatalError(2,title,currentTable,currentRow) // incorrectly formatted table } else { // must be an empty county ProgressStructure[currentTable][currentRow].Total=0 ProgressStructure[currentTable][currentRow].Illustrated=0 ProgressStructure[currentTable][currentRow].Articled=0 ProgressStructure[currentTable][currentRow].Stubs=0 ProgressStructure[currentTable][currentRow].NRISonly=0 ProgressStructure[currentTable][currentRow].StartPlus=0 ProgressStructure[currentTable][currentRow].Unassessed=0 ProgressStructure[currentTable][currentRow].Untagged=0 ProgressStructure[currentTable][currentRow].Link=title

TotalQueried++ if (TotalQueried==TotalToQuery) CalculateProgressTotals return; }       }        var tabletext=sectiontext.substr(StartIndex,sectiontext.indexOf("\n|}",StartIndex)-StartIndex) } else { // if not a redirect, default to first table on page var StartIndex=pagetext.indexOf("{{NRHP header") if (StartIndex==-1) { ProgressFatalError(1,title,currentTable,currentRow) // no list found return; }       var tabletext=pagetext.substr(StartIndex,pagetext.indexOf("\n|}",StartIndex)-StartIndex) }

// now that tabletext has only relevant table, extract rows var Rows=[] var str = "{{" var start=0 var commentstart=0 while (true) { commentstart=tabletext.indexOf("",commentstart) commentstart=tabletext.indexOf("<!--",start) start=tabletext.indexOf(str,start) }       if (start==-1) break var open=1 var index=start+str.length while (open!=0 && index<tabletext.length) { // make sure to find correct matching close brackets for row template if (tabletext.substr(index,2)=="}}") { open-- index++ } else if (tabletext.substr(index,2)=="{{") { open++ index++ }           index++ }       var template=tabletext.substr(start,index-start) var regex = new RegExp("{{[\\s]*NRHP row(\\s)*\\|", "g") if (template.match(regex)!=null) Rows.push(template) // make sure it's the row template and not some other one start++ }   for (var i=0; i((?!<[ ]*?/[ ]*?(nowiki|pre)[ ]*?>)(.|\\n))*?"+Rows[i].replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&")+"(.|\\n)*?<[ ]*?/[ ]*?(nowiki|pre)[ ]*?>", "g") if (tabletext.match(regex)!=null) {Rows.splice(i,1); i--} }

// now begin querying statistics var Stats={"ID":ProgressStructure[currentTable][currentRow].ID, "Total":Rows.length, "Illustrated":0, "Articled":0, "Stubs":0, "NRISonly":0, "StartPlus":0, "Unassessed":0, "Untagged":0, "Link":title}

var Titles=[]; for (var i=0; i/g,"").trim if (test!="") { Stats.Illustrated++ // only true if image param there and non-blank }       }

var article=ThisRow.match(/\|[ ]*?article[ ]*?=[ ]*?.*?[\n|\|]/g) var blank=ThisRow.match(/\|[ ]*?article[ ]*?=[ ]*?[\n|\|]/g)                              // default to name param if article if (article==null||blank!=null) article=ThisRow.match(/\|[ ]*?name[ ]*?=[ ]*?.*?[\n|\|]/g) // blank or missing // strip param name, final line break article=article[0].replace(/\|[ ]*?(article|name)[ ]*?=[ ]*?/g,"").replace(/[\n|\|]/g,"").replace(/\<\!\-\-(.|[\r\n])*?\-\-\>/g,"").trim article=decodeURIComponent(article.split("#")[0].trim)    // corrections for weird titles Titles.push(article) }

var StartIndex=0 LoadNextProgressQuery(Rows,Stats,Titles,StartIndex,title,currentTable,currentRow) return; }

// ready next batch of articles to query function LoadNextProgressQuery(Rows,Stats,Titles,StartIndex,title,currentTable,currentRow) { if (StartIndex==Stats.Total) { // all queries begun for this list return; }   // must have some more rows to query if (Stats.Total-StartIndex>50) { var TempTitles=Titles.slice(StartIndex,StartIndex+50) } else { var TempTitles=Titles.slice(StartIndex) }   StartIndex+=TempTitles.length

setTimeout(function{ // short delay to prevent API overload       QueryProgressStats(Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow)        LoadNextProgressQuery(Rows,Stats,Titles,StartIndex,title,currentTable,currentRow)    }, DefaultQueryPause); return; }

// query next batch of articles function QueryProgressStats(Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow) { var TitleList=TempTitles.join("|") $.ajax({       dataType: "json",        url: mw.util.wikiScript('api'),        data: {            format: 'json',            action: 'query',            prop: 'categories',            clcategories: 'Category:All disambiguation pages|Category:All articles sourced only to NRIS',            cllimit: 'max',            titles: TitleList,            redirects: 'true'        },        error: function(ArticlejsonObject,status,errorThrown) {ArticlejsonObject.errorThrown=errorThrown},        complete: function(ArticlejsonObject,status) {                ProgressArticleChecked(ArticlejsonObject,status,Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow)            }    }); return; }

// parse API response for article query function ProgressArticleChecked(ArticlejsonObject,status,Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow) { if (status!="success") { NewWarning("Articles "+ArticlejsonObject.errorThrown)

setTimeout(function{ // try again after delay if rate limit reached           QueryProgressStats(Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow)        }, 250); return; }   // won't get here unless successful ProgressStructure[currentTable][currentRow].ArticleQueried+=TempTitles.length

var responseText=JSON.parse(ArticlejsonObject.responseText) if (responseText.query.normalized) { // normalize any weird titles for (var n in responseText.query.normalized) { for (var i=0; i<TempTitles.length; i++) { if (TempTitles[i]==responseText.query.normalized[n].from) TempTitles[i]=responseText.query.normalized[n].to           } for (var i=0; i<Titles.length; i++) { // also update in Titles array to prepare to query talk pages if (Titles[i]==responseText.query.normalized[n].from) Titles[i]=responseText.query.normalized[n].to           } }   }    if (responseText.query.redirects) { // resolve any redirects also for (var r in responseText.query.redirects) { for (var i=0; i<TempTitles.length; i++) { if (TempTitles[i]==responseText.query.redirects[r].from) TempTitles[i]=responseText.query.redirects[r].to           } for (var i=0; i<Titles.length; i++) { // also update in Titles array to prepare to query talk pages if (Titles[i]==responseText.query.redirects[r].from) Titles[i]=responseText.query.redirects[r].to           } }   }

// now determine the number of bluelinks and NRIS-only articles for (var page in responseText.query.pages) { var articled=true  // default to articled, not NRIS-only var NRISonly=false var pagetitle=responseText.query.pages[page].title if (typeof responseText.query.pages[page].missing!="undefined") { // redlink=unarticled articled=false }       if (responseText.query.pages[page].categories) { for (var category in responseText.query.pages[page].categories) { if (responseText.query.pages[page].categories[category].title=="Category:All disambiguation pages") { // dab=unarticled articled=false }               if (responseText.query.pages[page].categories[category].title.indexOf("sourced only to NRIS")!=-1) { // mark as NRIS-only NRISonly=true }           }        }        for (var i=0; i<TempTitles.length; i++) { // if page is duplicated, count it multiple times if (TempTitles[i]==pagetitle) { if (articled) Stats.Articled++ if (NRISonly) Stats.NRISonly++ }       }    }

if (ProgressStructure[currentTable][currentRow].ArticleQueried==Stats.Total) { // after querying all articles, query talk pages for (var i=0; i50) { var TempTitles=Titles.slice(StartIndex,StartIndex+50) } else { var TempTitles=Titles.slice(StartIndex) }   StartIndex+=TempTitles.length

setTimeout(function{ // short delay to prevent API overload       QueryProgressTalkStats(Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow)        LoadNextProgressTalkQuery(Rows,Stats,Titles,StartIndex,title,currentTable,currentRow)    }, DefaultQueryPause); return; }

// query the next batch of talk pages function QueryProgressTalkStats(Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow) { var catlist='Category:FA-Class National Register of Historic Places articles‎|Category:A-Class National Register of Historic Places ' catlist+='articles‎|Category:GA-Class National Register of Historic Places articles‎|Category:B-Class National Register of Historic ' catlist+='Places articles‎|Category:C-Class National Register of Historic Places articles‎|Category:Start-Class National Register of ' catlist+='Historic Places articles‎|Category:Stub-Class National Register of Historic Places articles‎|Category:Unassessed National ' catlist+='Register of Historic Places articles‎|Category:List-Class National Register of Historic Places articles|Category:Redirect-' catlist+='Class National Register of Historic Places articles'

var TitleList=TempTitles.join("|") $.ajax({       dataType: "json",        url: mw.util.wikiScript('api'),        data: {            format: 'json',            action: 'query',            prop: 'categories',            clcategories: catlist,            cllimit: 'max',            titles: TitleList,            redirects: 'true'        },        error: function(ArticlejsonObject,status,errorThrown) {ArticlejsonObject.errorThrown=errorThrown},        complete: function(ArticlejsonObject,status) {                ProgressTalkChecked(ArticlejsonObject,status,Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow)            }    }); return; }

// parse API response for talk page query function ProgressTalkChecked(ArticlejsonObject,status,Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow) { if (status!="success") { NewWarning("Talk "+ArticlejsonObject.errorThrown)

setTimeout(function{ // try again after delay if rate limit reached           QueryProgressTalkStats(Rows,Stats,Titles,TempTitles,StartIndex,title,currentTable,currentRow)        }, 250); return; }   // won't get here unless successful ProgressStructure[currentTable][currentRow].TalkQueried+=TempTitles.length

// now determine quality statistics var responseText=JSON.parse(ArticlejsonObject.responseText) for (var page in responseText.query.pages) { if (typeof responseText.query.pages[page].missing!="undefined") continue; // skip talk page if not articled

var untagged=true // default to untagged var articled=true // assume articled to check for link to other county/MPS lists var stub=false var unassessed=false var startPlus=false var pagetitle=responseText.query.pages[page].title if (responseText.query.pages[page].categories) { untagged=false // if cat hit, mark as tagged for (var category in responseText.query.pages[page].categories) { var CatTitle=responseText.query.pages[page].categories[category].title if (CatTitle.indexOf("Stub")!=-1) { stub=true // mark as stub }               if (CatTitle.indexOf("Unassessed")!=-1||CatTitle.indexOf("Redirect")!=-1) { unassessed=true // mark as unassessed }               if  (CatTitle.indexOf("List")!=-1) { // count links to other county/MPS lists as unarticled; other list-class as stubs if (responseText.query.pages[page].title.indexOf("National Register of Historic Places")!=-1){ articled=false } else { stub=true }               }            }        }        if (articled&&!untagged&&!unassessed&&!stub) { // if articled, tagged, and assessed, but not stub, must be Start+ startPlus=true }

for (var i=0; i<TempTitles.length; i++) { if (TempTitles[i]==pagetitle) { if (!articled) Stats.Articled-- // reduce the count of articled if links to lists if (untagged) Stats.Untagged++ if (stub) Stats.Stubs++ if (unassessed) Stats.Unassessed++ if (startPlus) Stats.StartPlus++ }       }    }

if (ProgressStructure[currentTable][currentRow].TalkQueried==Stats.Total) { ProgressStructure[currentTable][currentRow].Total=Stats.Total ProgressStructure[currentTable][currentRow].Illustrated=Stats.Illustrated ProgressStructure[currentTable][currentRow].Articled=Stats.Articled ProgressStructure[currentTable][currentRow].Stubs=Stats.Stubs ProgressStructure[currentTable][currentRow].NRISonly=Stats.NRISonly ProgressStructure[currentTable][currentRow].StartPlus=Stats.StartPlus ProgressStructure[currentTable][currentRow].Unassessed=Stats.Unassessed ProgressStructure[currentTable][currentRow].Untagged=Stats.Untagged ProgressStructure[currentTable][currentRow].Link=title

TotalQueried++ if (TotalQueried==TotalToQuery) CalculateProgressTotals } }

// keep track of warnings encountered while querying API function NewWarning(warning) { var NewWarning=true for (var i=0; i<WarningCount.length; i++) { // check if already encountered error if (warning==WarningCount[i][0]||WarningCount[i][0]=="") {WarningCount[i][0]=warning; WarningCount[i][1]++; NewWarning=false;} }   if (NewWarning) WarningCount[WarningCount.length]=[warning,1] // if new warning, make new entry

var test=0 for (var i=0; i<WarningCount.length; i++) { test+=WarningCount[i][1] }   if (test%50==0) DefaultQueryPause++ // for every 50 errors encountered, increase time between each query to throttle speed }

// these errors require user input; can't just be ignored function ProgressFatalError(code,title,currentTable,currentRow) { var errorArray = ['No county section found for ','No list found for ','Incorrectly formatted list for '] var retry=confirm(errorArray[code]+title+"!\n\nCancel=Skip                  OK=Retry") if (retry) { getProgressListWikitext(title,currentTable,currentRow); } else { // if chose to skip, add one to error count TotalQueried++ ErrorCount++ if (TotalQueried==TotalToQuery) CalculateProgressTotals }   return; }

// update ProgressDiv to let user know what's going on function UpdateProgressDiv { var ProgressSpan=document.getElementById("ProgressSpan") var TimeSpan=document.getElementById("TimeSpan")

var PercentQueried=Math.round(TotalQueried/TotalToQuery*1000)/10 ProgressSpan.innerHTML = "Querying county data... "+TotalQueried+" ("+PercentQueried+"%) of "+TotalToQuery+" lists checked."

if (TotalQueried>100) { var CurrentTime=new Date var SecondsElapsed = (CurrentTime-InitialTime)/1000 var Average = SecondsElapsed/TotalQueried SecondsElapsed=Math.round(SecondsElapsed) var MinutesElapsed = 0 while (SecondsElapsed>=60) { SecondsElapsed-=60 MinutesElapsed++ }       var SecondsRemaining = Math.round(Average*(TotalToQuery-TotalQueried)) var MinutesRemaining = 0 while (SecondsRemaining>=60) { SecondsRemaining-=60 MinutesRemaining++ }

var TimeRemainingStr = "" if (MinutesRemaining!=0) TimeRemainingStr=MinutesRemaining+" min " TimeRemainingStr+=SecondsRemaining+" sec" var TimeElapsedStr = "" if (MinutesElapsed!=0) TimeElapsedStr=MinutesElapsed+" min " TimeElapsedStr+=SecondsElapsed+" sec" TimeRemainingStr+=" ("+TimeElapsedStr+" elapsed)" } else { var TimeRemainingStr="Calculating..." }   TimeSpan.innerHTML=" Estimated time remaining: "+TimeRemainingStr

if (TotalQueried!=TotalToQuery) { // update ProgressDiv only at regular intervals to prevent CPU overload; stop once done ProgressDivTimer=setTimeout(function{           UpdateProgressDiv;        }, 500); }   return; }

// after all querying complete, calculate totals for states and counties with multiple sublists function CalculateProgressTotals { clearTimeout(ProgressDivTimer) var ProgressSpan=document.getElementById("ProgressSpan") var TimeSpan=document.getElementById("TimeSpan")

ProgressSpan.innerHTML = "Querying county data... Done! "+TotalToQuery+" lists checked."

var CurrentTime=new Date var SecondsElapsed = (CurrentTime-InitialTime)/1000 SecondsElapsed=Math.round(SecondsElapsed) var MinutesElapsed = 0 while (SecondsElapsed>=60) { SecondsElapsed-=60 MinutesElapsed++ }   TimeSpan.innerHTML=" Time elapsed: " if (MinutesElapsed!=0) TimeSpan.innerHTML+=MinutesElapsed+" min " TimeSpan.innerHTML+=SecondsElapsed+" sec"

for (var i=1; i<ProgressStructure.length; i++) { // i=table number; skip national table until end for (var j=0; j<ProgressStructure[i].length-2; j++) { var TotalsIndex=ProgressStructure[i].length-1 if (!isNaN(parseFloat(ProgressStructure[i][j].ID))) { // if regular county without sublists, add to totals ProgressStructure[i][TotalsIndex].Total+=ProgressStructure[i][j].Total ProgressStructure[i][TotalsIndex].Illustrated+=ProgressStructure[i][j].Illustrated ProgressStructure[i][TotalsIndex].Articled+=ProgressStructure[i][j].Articled ProgressStructure[i][TotalsIndex].Stubs+=ProgressStructure[i][j].Stubs ProgressStructure[i][TotalsIndex].NRISonly+=ProgressStructure[i][j].NRISonly ProgressStructure[i][TotalsIndex].StartPlus+=ProgressStructure[i][j].StartPlus ProgressStructure[i][TotalsIndex].Unassessed+=ProgressStructure[i][j].Unassessed ProgressStructure[i][TotalsIndex].Untagged+=ProgressStructure[i][j].Untagged } else if (ProgressStructure[i][j].ID=="-") { // if county sublist, find total county row and add there var CountyTotalsIndex=j+1 while (CountyTotalsIndex<ProgressStructure[i].length-2) { if (!isNaN(parseFloat(ProgressStructure[i][CountyTotalsIndex].ID))) break; CountyTotalsIndex++ }               ProgressStructure[i][CountyTotalsIndex].Total+=ProgressStructure[i][j].Total ProgressStructure[i][CountyTotalsIndex].Illustrated+=ProgressStructure[i][j].Illustrated ProgressStructure[i][CountyTotalsIndex].Articled+=ProgressStructure[i][j].Articled ProgressStructure[i][CountyTotalsIndex].Stubs+=ProgressStructure[i][j].Stubs ProgressStructure[i][CountyTotalsIndex].NRISonly+=ProgressStructure[i][j].NRISonly ProgressStructure[i][CountyTotalsIndex].StartPlus+=ProgressStructure[i][j].StartPlus ProgressStructure[i][CountyTotalsIndex].Unassessed+=ProgressStructure[i][j].Unassessed ProgressStructure[i][CountyTotalsIndex].Untagged+=ProgressStructure[i][j].Untagged } else if (ProgressStructure[i][j].ID=="ddddd") { // if county duplicate row, subtract from county total ProgressStructure[i][j+1].Total-=ProgressStructure[i][j].Total ProgressStructure[i][j+1].Illustrated-=ProgressStructure[i][j].Illustrated ProgressStructure[i][j+1].Articled-=ProgressStructure[i][j].Articled ProgressStructure[i][j+1].Stubs-=ProgressStructure[i][j].Stubs ProgressStructure[i][j+1].NRISonly-=ProgressStructure[i][j].NRISonly ProgressStructure[i][j+1].StartPlus-=ProgressStructure[i][j].StartPlus ProgressStructure[i][j+1].Unassessed-=ProgressStructure[i][j].Unassessed ProgressStructure[i][j+1].Untagged-=ProgressStructure[i][j].Untagged } else { // unknown ID; skip it               alert("Error! Unknown ID="+ProgressStructure[i][j].ID+" in Table "+i+", Row "+j+". Skipping this list in totals.") }       }        // subtract state duplicates ProgressStructure[i][TotalsIndex].Total-=ProgressStructure[i][TotalsIndex-1].Total ProgressStructure[i][TotalsIndex].Illustrated-=ProgressStructure[i][TotalsIndex-1].Illustrated ProgressStructure[i][TotalsIndex].Articled-=ProgressStructure[i][TotalsIndex-1].Articled ProgressStructure[i][TotalsIndex].Stubs-=ProgressStructure[i][TotalsIndex-1].Stubs ProgressStructure[i][TotalsIndex].NRISonly-=ProgressStructure[i][TotalsIndex-1].NRISonly ProgressStructure[i][TotalsIndex].StartPlus-=ProgressStructure[i][TotalsIndex-1].StartPlus ProgressStructure[i][TotalsIndex].Unassessed-=ProgressStructure[i][TotalsIndex-1].Unassessed ProgressStructure[i][TotalsIndex].Untagged-=ProgressStructure[i][TotalsIndex-1].Untagged

// record state totals in national table ProgressStructure[0][i-1].Total=ProgressStructure[i][TotalsIndex].Total ProgressStructure[0][i-1].Illustrated=ProgressStructure[i][TotalsIndex].Illustrated ProgressStructure[0][i-1].Articled=ProgressStructure[i][TotalsIndex].Articled ProgressStructure[0][i-1].Stubs=ProgressStructure[i][TotalsIndex].Stubs ProgressStructure[0][i-1].NRISonly=ProgressStructure[i][TotalsIndex].NRISonly ProgressStructure[0][i-1].StartPlus=ProgressStructure[i][TotalsIndex].StartPlus ProgressStructure[0][i-1].Unassessed=ProgressStructure[i][TotalsIndex].Unassessed ProgressStructure[0][i-1].Untagged=ProgressStructure[i][TotalsIndex].Untagged

// add state totals to national totals var NationalTotalsIndex=ProgressStructure[0].length-1 ProgressStructure[0][NationalTotalsIndex].Total+=ProgressStructure[0][i-1].Total ProgressStructure[0][NationalTotalsIndex].Illustrated+=ProgressStructure[0][i-1].Illustrated ProgressStructure[0][NationalTotalsIndex].Articled+=ProgressStructure[0][i-1].Articled ProgressStructure[0][NationalTotalsIndex].Stubs+=ProgressStructure[0][i-1].Stubs ProgressStructure[0][NationalTotalsIndex].NRISonly+=ProgressStructure[0][i-1].NRISonly ProgressStructure[0][NationalTotalsIndex].StartPlus+=ProgressStructure[0][i-1].StartPlus ProgressStructure[0][NationalTotalsIndex].Unassessed+=ProgressStructure[0][i-1].Unassessed ProgressStructure[0][NationalTotalsIndex].Untagged+=ProgressStructure[0][i-1].Untagged }   // special row for Tangier, Morocco ProgressStructure[0][NationalTotalsIndex].Total+=ProgressStructure[0][NationalTotalsIndex-2].Total ProgressStructure[0][NationalTotalsIndex].Illustrated+=ProgressStructure[0][NationalTotalsIndex-2].Illustrated ProgressStructure[0][NationalTotalsIndex].Articled+=ProgressStructure[0][NationalTotalsIndex-2].Articled ProgressStructure[0][NationalTotalsIndex].Stubs+=ProgressStructure[0][NationalTotalsIndex-2].Stubs ProgressStructure[0][NationalTotalsIndex].NRISonly+=ProgressStructure[0][NationalTotalsIndex-2].NRISonly ProgressStructure[0][NationalTotalsIndex].StartPlus+=ProgressStructure[0][NationalTotalsIndex-2].StartPlus ProgressStructure[0][NationalTotalsIndex].Unassessed+=ProgressStructure[0][NationalTotalsIndex-2].Unassessed ProgressStructure[0][NationalTotalsIndex].Untagged+=ProgressStructure[0][NationalTotalsIndex-2].Untagged

// subtract national duplicates ProgressStructure[0][NationalTotalsIndex].Total-=ProgressStructure[0][NationalTotalsIndex-1].Total ProgressStructure[0][NationalTotalsIndex].Illustrated-=ProgressStructure[0][NationalTotalsIndex-1].Illustrated ProgressStructure[0][NationalTotalsIndex].Articled-=ProgressStructure[0][NationalTotalsIndex-1].Articled ProgressStructure[0][NationalTotalsIndex].Stubs-=ProgressStructure[0][NationalTotalsIndex-1].Stubs ProgressStructure[0][NationalTotalsIndex].NRISonly-=ProgressStructure[0][NationalTotalsIndex-1].NRISonly ProgressStructure[0][NationalTotalsIndex].StartPlus-=ProgressStructure[0][NationalTotalsIndex-1].StartPlus ProgressStructure[0][NationalTotalsIndex].Unassessed-=ProgressStructure[0][NationalTotalsIndex-1].Unassessed ProgressStructure[0][NationalTotalsIndex].Untagged-=ProgressStructure[0][NationalTotalsIndex-1].Untagged

setTimeout(function {ParseProgressWikitext},1); // small delay for non-Firefox browsers to update screen }

// update wikitext with queried totals function ParseProgressWikitext { var newwikitext=wikitext var TableStartIndex=wikitext.indexOf("==State totals") for (var i=0; i<ProgressStructure.length; i++) { var TableStartIndex=wikitext.indexOf("{|",TableStartIndex+1)   // find next table in old wikitext var TableEndIndex=wikitext.indexOf("|}",TableStartIndex)+2 var oldTable=wikitext.substr(TableStartIndex,TableEndIndex-TableStartIndex) var newTable=oldTable

var RowStartIndex=0 for (var j=0; j<ProgressStructure[i].length-2; j++) { RowStartIndex=oldTable.indexOf("\n|-",RowStartIndex+1) // find next row in old table if (ProgressStructure[i][j].ID=="ddddd") continue;  // skip duplicate rows var RowEndIndex=oldTable.indexOf("\n|-",RowStartIndex+1) var oldRow=oldTable.substr(RowStartIndex,RowEndIndex-RowStartIndex) var firstColumn=oldRow.indexOf("\n|") var stop=4       // skip one cell for national table, three for states if (i==0) stop=2 for (var inc=0; inc999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); // add thousands separators newRow+="\n| "+str // illustrated str=temp.Illustrated.toString if (temp.Illustrated>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n| "+str // illustrated percent if (temp.Total==0) { str="-" } else { str = Math.round(temp.Illustrated/temp.Total*1000)/10 var test = str.toString.indexOf(".") if (test==-1 && str!=100 && str!=0) str+=".0" // force decimal str+="%" }           newRow+="\n| "+str // articled str=temp.Articled.toString if (temp.Articled>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n| "+str // articled percent if (temp.Total==0) { str="-" } else { str = Math.round(temp.Articled/temp.Total*1000)/10 var test = str.toString.indexOf(".") if (test==-1 && str!=100 && str!=0) str+=".0" str+="%" }           newRow+="\n| "+str // stubs str=temp.Stubs.toString if (temp.Stubs>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n| "+str // NRIS-only str=temp.NRISonly.toString if (temp.NRISonly>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n| "+str // Start+ str=temp.StartPlus.toString if (temp.StartPlus>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n| "+str // Start+ percent if (temp.Total==0) { str="-" } else { str = Math.round(temp.StartPlus/temp.Total*1000)/10 var test = str.toString.indexOf(".") if (test==-1 && str!=100 && str!=0) str+=".0" // force decimal str+="%" }           newRow+="\n| "+str // unassessed str=temp.Unassessed.toString if (temp.Unassessed>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n| "+str // untagged str=temp.Untagged.toString if (temp.Untagged>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n| "+str // net quality if (temp.Total==0) { str="-" } else { str=temp.StartPlus+0.5*temp.Stubs+0.5*temp.Unassessed-0.5*temp.Untagged-0.75*temp.NRISonly str=Math.round((0.75*str/temp.Total+0.25*temp.Illustrated/temp.Total)*1000)/10 if (str<0) str=0 var test=str.toString.indexOf(".") if (test==-1 && str!=100 && str!=0) str+=".0" str+="%" }           newRow+="\n| "+str

// update new table with new row newTable=newTable.replace(oldRow,newRow) }       RowStartIndex=oldTable.indexOf("\n|-",RowStartIndex+1) // skip duplicate row RowStartIndex=oldTable.indexOf("\n|-",RowStartIndex+1) RowEndIndex=oldTable.indexOf("\n|}",RowStartIndex+1) oldRow=oldTable.substr(RowStartIndex,RowEndIndex-RowStartIndex) firstColumn=oldRow.indexOf("\n!") // totals row uses ! instead of | firstColumn=oldRow.indexOf("\n!",firstColumn+1) // skip first cell

// build up new totals row var newRow=oldRow.substr(0,firstColumn) var temp=ProgressStructure[i][ProgressStructure[i].length-1] // total var str=temp.Total.toString if (temp.Total>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); // add thousands separators newRow+="\n! "+str // illustrated str=temp.Illustrated.toString if (temp.Illustrated>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n! "+str // illustrated percent if (temp.Total==0) { str="-" } else { str = Math.round(temp.Illustrated/temp.Total*1000)/10 var test = str.toString.indexOf(".") if (test==-1 && str!=100 && str!=0) str+=".0" // force decimal str+="%" }       newRow+="\n! "+str // articled str=temp.Articled.toString if (temp.Articled>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n! "+str // articled percent if (temp.Total==0) { str="-" } else { str = Math.round(temp.Articled/temp.Total*1000)/10 var test = str.toString.indexOf(".") if (test==-1 && str!=100 && str!=0) str+=".0" // force decimal str+="%" }       newRow+="\n! "+str // stubs str=temp.Stubs.toString if (temp.Stubs>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n! "+str // NRIS-only str=temp.NRISonly.toString if (temp.NRISonly>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n! "+str // Start+ str=temp.StartPlus.toString if (temp.StartPlus>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n! "+str // Start+ percent if (temp.Total==0) { str="-" } else { str = Math.round(temp.StartPlus/temp.Total*1000)/10 var test = str.toString.indexOf(".") if (test==-1 && str!=100 && str!=0) str+=".0" // force decimal str+="%" }       newRow+="\n! "+str // unassessed str=temp.Unassessed.toString if (temp.Unassessed>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n! "+str // untagged str=temp.Untagged.toString if (temp.Untagged>999) str=str.replace(/\B(?=(\d{3})+(?!\d))/g, ","); newRow+="\n! "+str // net quality if (temp.Total==0) { str="-" } else { str=temp.StartPlus+0.5*temp.Stubs+0.5*temp.Unassessed-0.5*temp.Untagged-0.75*temp.NRISonly str=Math.round((0.75*str/temp.Total+0.25*temp.Illustrated/temp.Total)*1000)/10 if (str<0) str=0 var test=str.toString.indexOf(".") if (test==-1 && str!=100 && str!=0) str+=".0" str+="%" }       newRow+="\n! "+str

// update new wikitext with new table newTable=newTable.replace(oldRow,newRow) newwikitext=newwikitext.replace(oldTable,newTable) }

// now edit page with new wikitext var ProgressDiv=document.getElementById("ProgressDiv") ProgressDiv.innerHTML+=" Editing page (this might take up to one minute)... " InitializeEdit(newwikitext) }

// initialize edit function InitializeEdit(newwikitext) { 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

regex=/(January|February|March|April|May|June|July|August|September|October|November|December) [0-9]{1,2}, [0-9]{4}/g var tempstring=newwikitext.split('==County totals==') // ignore dates in lead (e.g. date of last map update) newwikitext=tempstring[0]+'==County totals=='+tempstring[1].replace(regex,DateStr) // update date strings above tables

var ErrorStr = '' if (ErrorCount>0) ErrorStr = " Errors encountered for "+ErrorCount+" counties, which were skipped. Human attention needed." var summary='Updating county data as of '+DateStr+' using script.'+ErrorStr editPage(newwikitext,mw.config.get('wgPageName'),summary) }

function PageEdited(ajaxResponse,status,newwikitext) { var ProgressDiv=document.getElementById("ProgressDiv") if (status!="success") { var retry=confirm("Error: "+ajaxResponse.errorThrown+" while editing page!\n\nCancel=Abort                  OK=Retry") if (retry) { ProgressDiv.innerHTML+="Retrying... " InitializeEdit(newwikitext) // try again } else { ProgressDiv.innerHTML+="Edit failure! Script aborted!" }       return; }   var responseText=JSON.parse(ajaxResponse.responseText) var diff=responseText.edit.newrevid var linkStr="//en.wikipedia.org/w/index.php?diff="+diff ProgressDiv.innerHTML+="Page edited! Click here for diff."

// output technical information to console var WarningText="NRHP Progress Warnings: " for (var i=0; i<WarningCount.length; i++) { WarningText+=WarningCount[i][0]+" ("+WarningCount[i][1]+"), " }   if (WarningCount[0][0]!="") { WarningText=WarningText.substr(0,WarningText.length-2) } else { WarningText="NRHP Progress Warnings: none" }   console.log(WarningText) }

function editPage(text,title,summary) { // edit page when done $.ajax({       dataType: 'json',        url: mw.util.wikiScript( 'api' ),        type: 'POST',        data: {            format: 'json',            action: 'edit',            title: title,            text: text,            summary: summary,            token: mw.user.tokens.get( 'editToken' )        },        error: function(ajaxResponse,status,errorThrown) {ajaxResponse.errorThrown=errorThrown},        complete: function(ajaxResponse,status) {PageEdited(ajaxResponse,status,text)}    }) }

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

function getProgressListWikitext(title,currentTable,currentRow) {  // asynchronous fetch of each list's wikitext $.ajax({       dataType: "json",        url: mw.util.wikiScript('api'),        data: {            format: 'json',            action: 'query',            prop: 'revisions',            rvprop: 'content',            titles: title,            indexpageids: true,            redirects: 'true'        },        error: function(ajaxResponse,status,errorThrown) {ajaxResponse.errorThrown=errorThrown},        complete: function(ajaxResponse,status) {WikitextFetched(ajaxResponse,status,title,currentTable,currentRow)}    }) }

$(ProgressButton);