require('Module:No globals')
--case sensitive conf/map table
local conf = {
--[[
'Fish' is the default category tree (year -> century, no decades).
'Spiders' use year -> decade -> century.
Any category group (i.e. Birds/Crustaceans/Molluscs/etc.) NOT explicitly outlined here in conf{} will follow the 'Fish' tree for that group.
To create a customized tree, like 'Spiders' below, add it to the bottom of the conf{} table, following the same general table format.
]]--
['tocmin'] = 401, --integer; minimum category size to place {{Category TOC}} on
['Fish'] = { --group
['min'] = 1758, --integer; lowest possible year displayed in nav bars; 0/nil defaults to 1758 per ICZN Art. 5
['year'] = { --[[Category:Fish described in 1901]]
['description'] = 'This category should only contain species-level articles.',
['parent1'] = 'century', --year/decade/century/formal, i.e. 'century' for [[Category:Fish described in the 20th century]]
['parent2'] = 'Animals', --[[Category:Animals described in the 20th century]]
},
['century'] = { --[[Category:Fish described in the 20th century]]
['description'] = '', --'Description tbd; century; container category, etc....',
['parent1'] = 'formal', --[[Category:Fish by year of formal description]]
['parent2'] = 'Animals', --[[Category:Animals described in the 20th century]]
},
},
['Spiders'] = { --group
['min'] = 1757, --integer; lowest possible year (and thus decade) displayed in nav bars; 0/nil defaults to 1758 per ICZN Art. 5
['year'] = { --[[Category:Spiders described in 1901]]
['description'] = '', --'Description tbd; year; This category should only contain species articles, etc....',
['parent1'] = 'decade', --[[Category:Spiders described in the 1900s]]
['parent2'] = 'Animals', --[[Category:Animals described in 1900]]
},
['decade'] = { --[[Category:Spiders described in the 1900s]]
['description'] = '', --'Description tbd; decade; container category, etc....',
['parent1'] = 'century', --[[Category:Spiders described in the 20th century]]
['parent2'] = 'Animals', --[[Category:Animals described in the 1900s]]
},
['century'] = { --[[Category:Spiders described in the 20th century]]
['description'] = '', --'Description tbd; century; container category, etc....',
['parent1'] = 'formal', --[[Category:Spiders by year of formal description]]
['parent2'] = 'Animals', --[[Category:Animals described in the 20th century]]
},
},
['Plants'] = { --group
['min'] = 1753, --integer; lowest possible year (and thus decade) displayed in nav bars; 0/nil defaults to 1758 per ICZN Art. 5
['year'] = { --[[Category:Plants described in 1901]]
['description'] = "This category includes plants that were ''first formally and validly described'' in %year% according to the rules of the [[International Code of Botanical Nomenclature]]. Use [[WP:RS|reliable sources]] like the [[International Plant Names Index]] to figure out the proper category. For examples see the [[Wikipedia:WikiProject Plants/Description in year categories|WikiProject Plants essay]] on this topic.", --taken from [[Category:Plants described in 1928]]
['parent1'] = 'decade', --[[Category:Plants described in the 1900s]]
['parent2'] = 'Species', --[[Category:Species described in 1900]]
},
['decade'] = { --[[Category:Plants described in the 1900s]]
['description'] = '', --'Description tbd; decade; container category, etc....',
['parent1'] = 'century', --[[Category:Plants described in the 20th century]]
['parent2'] = 'Species', --[[Category:Species described in the 1900s]]
},
['century'] = { --[[Category:Plants described in the 20th century]]
['description'] = '', --'Description tbd; century; container category, etc....',
['parent1'] = 'formal', --[[Category:Plants by year of formal description]]
['parent2'] = 'Species', --[[Category:Species described in the 20th century]]
},
},
}
local function addOrd( i ) --20 -> 20th, etc.
if tonumber(i) then
local s = tostring(i)
local lastd = mw.ustring.match(s, '%d$')
if lastd == '1' then return s..'st'
elseif lastd == '2' then return s..'nd'
elseif lastd == '3' then return s..'rd'
elseif lastd ~= nil then return s..'th'
end
end
return ''
end
local p = {}
function p.autodetect( frame )
local currentTitle = mw.title.getCurrentTitle()
local header = ' ' --header template(s), nav bar, and category description text; whitespace-initialized for convenience
local nav = nil
local description = nil
local toc = nil
local categories = {}
local trackingCategories = {
[1] = nil --placeholder for [[Category:Described in year unknown category]]
}
local outString = nil
--prelim namespace/title determination
local currCat = nil
if currentTitle.namespace == 14 then --Category:
currCat = currentTitle.text --without namespace nor interwiki prefixes
else
--accept 1 unnamed category parameter if not in Category: space; required for testing/doc/etc purposes
local parentArg = frame:getParent().args[1]
if parentArg then currCat = mw.ustring.gsub(parentArg, 'Category:', '')
else return end
end
if currCat then
--determine current/related/adjacent cats' properties/vars/etc
local currGroup = mw.ustring.match(currCat, '^%w+') --Fish/Spiders/default/etc.
if conf[currGroup] == nil then conf[currGroup] = conf['Fish'] end --'Fish' == default
local currYDC = nil --year/decade/century
local currYear = mw.ustring.match(currCat, 'in (%d%d%d%d)$')
local currDeca = mw.ustring.match(currCat, 'the (%d%d%d%d)s$')
local currCent = mw.ustring.match(currCat, 'the (%d+)[snrt][tdh] century')
local parentDeca = nil --used with currYear (i.e. Spiders) & currDeca (i.e. Spiders)
local parentCent = nil --used with currYear (i.e. Fish) & currDeca (i.e. Spiders)
local lastCent, nextCent = nil, nil --used with currYear (i.e. Fish) & currCent (i.e. Fish)
local minYear = tonumber(conf[currGroup].min)
if (minYear == nil or (minYear and minYear <= 1700)) then
minYear = 1758 --default to 1758 per ICZN Art. 5
end
if currYear then
currYDC = 'year'
parentDeca = mw.ustring.match(currYear, '^(%d%d%d)%d$') .. '0'
if mw.ustring.match(currYear, '^%d%d00') then --1900 in 19th century
parentCent = mw.ustring.match(currYear, '^%d%d')
else --1901 in 20th century
parentCent = 1 + mw.ustring.match(currYear, '^%d%d')
end
lastCent = parentCent - 1
nextCent = parentCent + 1
elseif currDeca then
currYDC = 'decade'
parentDeca = mw.ustring.match(currDeca, '^(%d%d%d)%d$') .. '0'
parentCent = mw.ustring.match(parentDeca, '^%d%d') + 1
elseif currCent then
currYDC = 'century'
lastCent = currCent - 1
nextCent = currCent + 1
else
return '' --add err msg/cat?
end
--produce description & toc
description = conf[currGroup][currYDC].description
if mw.ustring.match(description, '%%year%%') then
if currYear then description = mw.ustring.gsub(description, '%%year%%', currYear)
else description = mw.ustring.gsub(description, '%%year%%', 'this year') end
end
if mw.site.stats.pagesInCategory(currCat, 'pages') >= conf['tocmin'] then --expensive
local args = { numerals = 'no' }
toc = frame:expandTemplate{ title = 'Category TOC', args = args }
end
--produce cats & navs
local iparent = 1
local parenti = 'parent' .. iparent
while conf[currGroup][currYDC][parenti] do
local parent = conf[currGroup][currYDC][parenti]
if currYDC == 'year' then
if nav == nil then
local args = { year = currYear, min = minYear, cat = currGroup .. ' described in' }
nav = frame:expandTemplate{ title = 'Category in year', args = args }
end
if parent == 'decade' then
categories[iparent] = '[[Category:' .. currGroup .. ' described in the ' .. parentDeca .. 's]]'
elseif parent == 'century' then
categories[iparent] = '[[Category:' .. currGroup .. ' described in the ' .. addOrd(parentCent) .. ' century]]'
else --i.e. Animals; require capital first letter?
categories[iparent] = '[[Category:' .. parent .. ' described in ' .. currYear .. ']]'
end
elseif currYDC == 'decade' then
if nav == nil then
nav = frame:expandTemplate{ title = 'Container category' }
local args = { decade = currDeca, min = minYear, cat = currGroup .. ' described in the' }
nav = frame:expandTemplate{ title = 'Category by decade', args = args }
end
if parent == 'century' then
categories[iparent] = '[[Category:' .. currGroup .. ' described in the ' .. addOrd(parentCent) .. ' century]]'
else --i.e. Animals; require capital first letter?
categories[iparent] = '[[Category:' .. parent .. ' described in the ' .. currDeca .. 's]]'
end
elseif currYDC == 'century' then
if nav == nil then
nav = frame:expandTemplate{ title = 'Container category' }
local args = { currGroup .. ' described in the ' .. addOrd(lastCent) .. ' century',
currGroup .. ' described in the ' .. addOrd(nextCent) .. ' century' }
nav = nav .. frame:expandTemplate{ title = 'Category pair', args = args }
end
if parent == 'formal' then
categories[iparent] = '[[Category:' .. currGroup .. ' by year of formal description|' .. addOrd(currCent) .. ']]'
else --i.e. Animals; require capital first letter?
categories[iparent] = '[[Category:' .. parent .. ' described in the ' .. addOrd(currCent) .. ' century]]'
end
end
iparent = iparent + 1
parenti = 'parent' .. iparent
end --end while conf[currGroup][currYDC][parenti]
for _, category in pairs(categories) do
local cat = mw.ustring.match(category, '%[%[Category:([%w%s]+)')
if mw.title.new(cat, 14).exists == false then
trackingCategories[1] = '[[Category:Described in year unknown category]]'
break
end
end
end
--produce header
--TODO: test/see if a similar commons cat exists and, if so, include it in the header
if nav then header = nav end
if description and description ~= '' then header = header .. '<br />' .. description end
header = mw.text.trim(header)
if toc then header = header .. '<br />' .. toc end
--append header to outString
if outString then outString = outString .. header
else outString = header end
--append cats to outString
if currentTitle.namespace == 14 then --Category:
outString = outString .. table.concat(categories) .. table.concat(trackingCategories)
else
outString = outString .. '<br />' ..
mw.ustring.gsub(table.concat(categories, '<br />'), '%[%[', '[[:')
if table.maxn(trackingCategories) > 0 then
outString = outString .. '<br />' ..
mw.ustring.gsub(table.concat(trackingCategories, '<br />'), '%[%[', '[[:')
end
end
return outString
end
return p