require('strict')--[[==========================================================================]]--[[ Local functions ]]--[[==========================================================================]]localfunctionaddOrd(i)--12 -> 12th, etc.iftonumber(i)thenlocals=tostring(i)localtens=string.match(s,'1%d$')localones=string.match(s,'%d$')iftensthenreturns..'th'elseifones=='1'thenreturns..'st'elseifones=='2'thenreturns..'nd'elseifones=='3'thenreturns..'rd'elseifones~=nilthenreturns..'th'endendreturn''endlocalfunctionisNilOrEmpty(thing)return(thing==nilorthing=='')endlocalp={}--[[==========================================================================]]--[[ External function ]]--[[==========================================================================]]functionp.autodetect(frame)localconf=require('Module:Category described in year/config')--configuration modulelocalcommonsLink=require('Module:Commons link')localcurrentTitle=mw.title.getCurrentTitle()localparentArg=frame:getParent().args[1]--accept 1 unnamed category parameter if not in category namespace; required for testing/doc/etc. purposeslocalheader=' '--header template(s), nav bar, and category description text; whitespace-initialized for conveniencelocalnav=nillocalportal=nil--for {{Portal|...}}localcommons=nil--for {{Commons|...}}localwikispecies=nil--for {{Wikispecies|...}}localdescription=nillocaltoc=nillocalcategories={}localtrackingCats={[1]='',--placeholder for [[Category:Described in year unknown category]][2]='',--placeholder for [[Category:Described in year error]][3]='',--placeholder for [[Category:Described in year with manual category]]}localoutString=nillocalbConfError=false--prelim namespace/title determinationlocalcurrCat=nillocalcurrQID=nilifcurrentTitle.namespace==14then--category namespacecurrCat=currentTitle.text--without namespace nor interwiki prefixescurrQID=mw.wikibase.getEntityIdForCurrentPage()elseifparentArgthencurrCat=mw.ustring.gsub(parentArg,'Category:','')currQID=mw.wikibase.getEntityIdForTitle('Category:'..currCat)else--currQID & currCat both nilifcurrentTitle.fullText~='Template:Category described in year'then--ignore self...trackingCats[2]='[[Category:Described in year error|P]]'--missing a category parameter outside category namespaceendendend--find commons & wikispecies link(s); produce {{Commons and category}} and/or {{Wikispecies}} template(s)ifcurrQIDthenifcommonsLink._hasGallery(currQID)orcommonsLink._hasCategory(currQID)thencommons=frame:expandTemplate{title='Commons and category',args={qid=currQID}}endlocalcurrEntity=mw.wikibase.getEntity(currQID)ifcurrEntitythen--check "Other sites" sitelinks for WikispecieslocalcurrSiteLinks=currEntity.sitelinksifcurrSiteLinksthenlocalcurrSpeciesWiki=currEntity.sitelinks.specieswikiifcurrSpeciesWikithenlocalcurrSpeciesWikiTitle=currSpeciesWiki.titleifcurrSpeciesWikiTitlethenwikispecies=frame:expandTemplate{title='Wikispecies',args={currSpeciesWikiTitle}}endendendendend--[[======================================================================]]--[[ Main ]]--[[======================================================================]]ifcurrCatthen--determine current/related/adjacent cats' properties/vars/etc.localcurrGroup=mw.ustring.match(currCat,'^([%w ]+) described in')--Bacteria/Plants/etc.ifisNilOrEmpty(currGroup)thencurrGroup=mw.ustring.match(currCat,'^([%w ]+) by year of formal description')endifconf[currGroup]==nilthenconf[currGroup]=conf['Default']end--default to DefaultlocalcurrYDCF=nil--possible future values: year/decade/century/formallocalcurrYear=mw.ustring.match(currCat,'described in (%d%d%d%d)$')localcurrDeca=mw.ustring.match(currCat,'described in the (%d%d%d%d)s$')--deprecatedlocalcurrCent=mw.ustring.match(currCat,'described in the (%d+)[snrt][tdh] century$')localcurrFrml=mw.ustring.match(currCat,'by year of (formal) description$')localparentCent=nil--used with currYearlocalminYear=tonumber(conf[currGroup].minyear)ifminYear==nilor(minYearand(minYear<=1700orminYear>=2000))thenminYear=1758--default to 1758 per ICZN Art. 5endifcurrYearthencurrYDCF='year'ifmw.ustring.match(currYear,'^%d%d00')then--1900 in 19th centuryparentCent=mw.ustring.match(currYear,'^%d%d')else--1901 in 20th centuryparentCent=1+mw.ustring.match(currYear,'^%d%d')endelseifcurrDecathencurrYDCF='decade'bConfError=truetrackingCats[2]='[[Category:Described in year error|D]]'--invalid decade-parent (deprecated)elseifcurrCentthencurrYDCF='century'elseifcurrFrmlthencurrYDCF='formal'elsebConfError=truetrackingCats[2]='[[Category:Described in year error|N]]'--invalid category nameend--conf error checkng (missing keys)--Numeric sortkeys are unfortunately grouped together under "0-9".--Check phab T203355 (Magic word to force category number headings instead of 0-9).ifbConfError==falsethenifconf[currGroup]==nilthenbConfError=truetrackingCats[2]='[[Category:Described in year error|1]]'--group (Bacteria/Plants/etc.) key missing from confelseifconf[currGroup][currYDCF]==nilthenbConfError=truetrackingCats[2]='[[Category:Described in year error|2]]'--year/century/formal key missingelseifconf[currGroup][currYDCF].description==nilthenbConfError=truetrackingCats[2]='[[Category:Described in year error|3]]'--description key missingendifconf[currGroup][currYDCF].parent1==nilthenbConfError=truetrackingCats[2]='[[Category:Described in year error|4]]'--parent key missingendendendifbConfError==falsethen--produce portalifcurrGroup=='Fossil taxa'orcurrGroup=='Fossil parataxa'thenportal=frame:expandTemplate{title='Portal',args={'Paleontology'}}end--produce description, evaluate %variables%description=conf[currGroup][currYDCF].descriptionifmw.ustring.match(description,'%%year%%')thenifcurrYearthendescription=mw.ustring.gsub(description,'%%year%%',currYear)--"2011"elsedescription=mw.ustring.gsub(description,'%%year%%','this year')endendifmw.ustring.match(description,'%%century%%')thenifcurrCentthendescription=mw.ustring.gsub(description,'%%century%%',addOrd(currCent))--"21st"elsedescription=mw.ustring.gsub(description,'%%century%%','this century')endend--produce cats & navslocaliparent=1localparenti='parent'..iparentlocalsortkeyi='sortkey'..iparentwhileconf[currGroup][currYDCF][parenti]dolocalparent=conf[currGroup][currYDCF][parenti]localsortkey=conf[currGroup][currYDCF][sortkeyi]--[[========================== Year ==========================]]ifcurrYDCF=='year'thenifnav==nilthenlocalargs={['min']=minYear,['skip-gaps']='yes'}ifparentArgandcurrentTitle.namespace~=14thenargs['testcase']=parentArgendnav=frame:expandTemplate{title='Category series navigation',args=args}endifparent=='century'thenifisNilOrEmpty(sortkey)thensortkey=currYearend--default to currYearcategories[iparent]='[[Category:'..currGroup..' described in the '..addOrd(parentCent)..' century|'..sortkey..']]'elseifparent=='biology'thenifisNilOrEmpty(sortkey)thensortkey=''--default to noneelsesortkey='|'..sortkeyendiftonumber(currYear)<1865thencategories[iparent]='[[Category:'..currYear..' in science'..sortkey..']]'--biology cat structure doesn't exist pre-1865, as of 10/2018elsecategories[iparent]='[[Category:'..currYear..' in biology'..sortkey..']]'--if/when all biology cats exists, merge this elseif with 'paleontology'endelseifparent=='paleontology'thenifisNilOrEmpty(sortkey)thensortkey=''--default to noneelsesortkey='|'..sortkeyendcategories[iparent]='[[Category:'..currYear..' in '..parent..sortkey..']]'elseifparent=='environment'thenifisNilOrEmpty(sortkey)thensortkey=''--default to noneelsesortkey='|'..sortkeyendcategories[iparent]='[[Category:'..currYear..' in the environment'..sortkey..']]'elseifmw.ustring.match(parent,'^%u[%l ]+')then--e.g. Animals/Insects/Fossil taxaifisNilOrEmpty(sortkey)thensortkey=''--default to noneelsesortkey='|'..sortkeyendcategories[iparent]='[[Category:'..parent..' described in '..currYear..sortkey..']]'elsetrackingCats[2]='[[Category:Described in year error|Y]]'--invalid year-parentend--[[======================== Century =========================]]elseifcurrYDCF=='century'thenifnav==nilthenlocalargs={}ifparentArgandcurrentTitle.namespace~=14thenargs['testcase']=parentArgendnav=frame:expandTemplate{title='Container category'}..frame:expandTemplate{title='Category series navigation',args=args}endifparent=='formal'thenifisNilOrEmpty(sortkey)thensortkey=addOrd(currCent)end--default to currCentcategories[iparent]='[[Category:'..currGroup..' by year of formal description|'..sortkey..']]'elseifparent=='biology'thenifisNilOrEmpty(sortkey)thensortkey=''--default to noneelsesortkey='|'..sortkeyendiftonumber(currCent)<19thencategories[iparent]='[[Category:'..addOrd(currCent)..' century in science'..sortkey..']]'--biology cat structure doesn't exist pre-1865, as of 10/2018elsecategories[iparent]='[[Category:'..addOrd(currCent)..' century in biology'..sortkey..']]'--if/when all biology cats exists, merge this elseif with 'paleontology'endelseifparent=='paleontology'thenifisNilOrEmpty(sortkey)thensortkey=''--default to noneelsesortkey='|'..sortkeyendcategories[iparent]='[[Category:'..addOrd(currCent)..' century in '..parent..sortkey..']]'elseifparent=='environment'thenifisNilOrEmpty(sortkey)thensortkey=''--default to noneelsesortkey='|'..sortkeyendcategories[iparent]='[[Category:'..addOrd(currCent)..' century in the environment'..sortkey..']]'elseifmw.ustring.match(parent,'^%u[%l ]+')then--e.g. Animals/Insects/Fossil taxaifisNilOrEmpty(sortkey)thensortkey=''--default to noneelsesortkey='|'..sortkeyendcategories[iparent]='[[Category:'..parent..' described in the '..addOrd(currCent)..' century'..sortkey..']]'elsetrackingCats[2]='[[Category:Described in year error|C]]'--invalid century-parentend--[[======================== Formal ==========================]]elseifcurrYDCF=='formal'thenlocalformalParentsDefaultSortkey_Space={['Animals']=true,['Insects']=true,['Molluscs']=true,['Fungi']=true,}localformalParentsDefaultSortkey_None={['Species']=true,['Taxa']=true,['Fossil taxa']=true,}ifnav==nilthennav=frame:expandTemplate{title='Container category'}endifparent=='Group'thenifisNilOrEmpty(sortkey)thensortkey=' Year'end--default to " Year"categories[iparent]='[[Category:'..currGroup..'|'..sortkey..']]'elseifparent=='paleontology'thenifisNilOrEmpty(sortkey)thensortkey=' 'end--default to " "; special parentcategories[iparent]='[[Category:Paleontology by year|'..sortkey..']]'elseifparentthen--allow freeform formal-parents, as long as they existifmw.title.new(parent,'Category').existsthenifsortkeythencategories[iparent]='[[Category:'..parent..'|'..sortkey..']]'elsecategories[iparent]='[[Category:'..parent..']]'endelsetrackingCats[2]='[[Category:Described in year error|G]]'--invalid freeform formal-parentendelseifformalParentsDefaultSortkey_Space[parent]thenifisNilOrEmpty(sortkey)thensortkey=' 'end--default to " "; normal parentcategories[iparent]='[[Category:'..parent..' by year of formal description|'..sortkey..']]'elseifformalParentsDefaultSortkey_None[parent]thenifisNilOrEmpty(sortkey)thensortkey=''--default to none; normal parentelsesortkey='|'..sortkeyendcategories[iparent]='[[Category:'..parent..' by year of formal description'..sortkey..']]'elsetrackingCats[2]='[[Category:Described in year error|F]]'--invalid formal-parentend--[[========================= Error ==========================]]elsetrackingCats[2]='[[Category:Described in year error|U]]'--unknown configurationendiparent=iparent+1parenti='parent'..iparentsortkeyi='sortkey'..iparentend--while conf[currGroup][currYDCF][parenti] doend--if bConfError == false then--check for non-existent catsfor_,categoryinpairs(categories)dolocalcat=mw.ustring.match(category,'%[%[Category:([%w%s]+)')ifmw.title.new(cat,14).exists==falsethentrackingCats[1]='[[Category:Described in year unknown category]]'breakendend--check for manual catsifcurrentTitle.namespace==14then--category namespacelocalcurrContent=mw.title.makeTitle('Category',currCator''):getContent()localmancat=mw.ustring.match(currContentor'','%[%[%s*Category')ifmancatthentrackingCats[3]='[[Category:Described in year with manual category]]'endendend--if currCat then--build headerlocalbr='<br />'localn='\n'ifnavthenheader=navendifportalthenheader=header..n..portalendifcommonsthenheader=header..n..commonsendifwikispeciesthenheader=header..n..wikispeciesendifdescriptionanddescription~=''thenheader=header..descriptionelseifportalorcommonsorwikispeciesthenheader=mw.ustring.gsub(header,br,'')endiftocthenheader=header..br..tocend--rem surrounding whitespaceheader=mw.text.trim(header)header=mw.ustring.gsub(header,'^'..br,'')header=mw.ustring.gsub(header,br..'$','')--append header to outStringifoutStringthenoutString=outString..headerelseoutString=headerend--append cats to outStringifcurrentTitle.namespace==14then--category namespaceiftable.maxn(categories)>0thenoutString=outString..table.concat(categories)endoutString=outString..table.concat(trackingCats)elseiftable.maxn(categories)>0then--might be 0 if there's an error before setting catsoutString=outString..br..mw.ustring.gsub(table.concat(categories,br),'%[%[','[[:')endoutString=outString..br..mw.ustring.gsub(table.concat(trackingCats,br),'%[%[','[[:')--ws cleanupwhilestring.match(outString,br..br)do--rem dup brs produced by empty ('') first/consecutive tracking cat/soutString=string.gsub(outString,br..br,br)endendreturnoutStringendreturnp