Mô đun:Autotaxobox/sandbox

--
This module provides support to the automated taxobox system – the templates
Automatic taxobox, Speciesbox, Subspeciesbox, Infraspeciesbox, etc.

In particular it provides a way of traversing the taxonomic hierarchy encoded
in taxonomy templates (templates with names of the form
"Bản mẫu:Taxonomy/TAXON_NAME") without causing template expansion depth
errors.

require('strict')
local TaxonItalics = require('Module:TaxonItalics') -- use a function from Module:TaxonItalics to italicize a taxon name
local TableRow = '|-\n'
local TableEnd = '|}\n'
local p = {} -- functions made public
local l = {} -- internal functions, kept separate
local colour = -- colour for taxobox and taxonomy listings

--
Limit the maximum depth of a taxonomic hierarchy that can be traversed;
avoids excessive processing time and protects against incorrectly set up
hierarchies, e.g. loops.
The value can be obtained externally via

local MaxSearchLevels = 100

function p.getMaxSearchLevels()
return MaxSearchLevels
end

-- taxoboxColour
Determines the correct colour for a taxobox, by searching up the taxonomic
hierarchy from the supplied taxon for the first taxon (other than
'incertae sedis') that sets a taxobox colour. It is assumed that a valid
taxobox colour is defined using CSS rgb() syntax.
If no taxon that sets a taxobox colour is found, then 'transparent' is
returned unless the taxonomic hierarchy is too deep, when the error colour is
returned.
Usage:
=============================================================================
function p.taxoboxColour(frame)
return p.getTaxoboxColour(frame, frame.args[1] or )
end

function p.getTaxoboxColour(frame, currTaxon)
-- note that colour is global to this function; default is empty string
local i = 1 -- count levels processed
local searching = currTaxon ~= -- still searching for a colour?
local foundICTaxon = false -- record whether 'incertae sedis' found
while searching and i = i, , virus)
end
-- display all taxa above possible parent, with authority if given
for i = math.min(topTaxonN, 5), 2, -1 do
res = res .. l.showTaxon(frame, taxonTable[i], taxonRankTable[i], topTaxonN==i, authTable[i], displayN >= i, , virus)
end
-- display target taxon, always displayed and emboldened
res = res .. l.showTaxon(frame, taxonTable[1], taxonRankTable[1], topTaxonN==1, authTable[1], true, boldFirst, virus)
return res
end

--= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Show one taxon row in a taxobox.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function l.showTaxon(frame, taxon, rank, isTopTaxon, auth, force, boldFirst, virus)
-- it's an error if this is the top taxon and it's not a top level taxon (e.g. "Life")
if isTopTaxon then
if l.topLevelTaxon(taxon) then
return -- don't display a top level taxon
elseif (mw.title.new('Taxonomy/'..taxon, 'Template') or {}).exists then
-- taxonomy template for this taxon has no parent specified
return frame:expandTemplate{ title = 'Bản mẫu:Create taxonomy', args = {taxon, msg='Taxonomy template does not specify a parent'} } .. '\n' .. TableRow
else
-- no taxonomy template for this taxon
return frame:expandTemplate{ title = 'Bản mẫu:Create taxonomy', args = {taxon, msg='Missing taxonomy template'} } .. '\n' .. TableRow
end
else
-- if showing is not already forced, force if it's a principal rank or an authority is specified
force = force or frame:expandTemplate{ title = 'Bản mẫu:Principal rank', args = {rank} } == 'yes' or
auth ~=
if not force then
-- if showing is still not already forced, force if the taxonomy template has 'always_display' set
local ok, alwaysDisplay = p.getTaxonInfoItem(frame, taxon, 'always_display')
force = alwaysDisplay 'yes' or alwaysDisplay 'true'
end
if force then
local res = l.tableCell(frame:expandTemplate{ title = 'Bản mẫu:Anglicise rank', args = {rank} } .. ':')
local bold = 'no'
if boldFirst == 'bold' then bold = 'yes' end
if auth ~= then
auth = .. auth ..
end
local res = res .. l.tableCell(l.getTaxonLink(frame, taxon, rank, bold, , , virus) .. auth) -- italic, abbreviated
return res .. TableRow
else
return
end
end
end

-- taxonomyList
Returns the cells of the taxonomy table displayed on the right hand side of
"Bản mẫu:Taxonomy...." pages.
Usage:
=============================================================================
function p.taxonomyList(frame)
local currTaxon = frame.args[1] or
if currTaxon == then
return '{|class="infobox biota"\n' .. TableRow .. l.tableCell() .. l.tableCell('LỖI: không có taxon nào được cung cấp') .. TableEnd
end
local taxonTable, taxonRankTable = l.makeTable(frame, currTaxon)
local rankValTable = l.getRankTable()
local lastRankVal = 1000000
local orderOk = true
-- check whether the taxonomy is for viruses; use already determined taxon colour if possible
local virus = 'no'
local taxoColour = colour
if taxoColour then
if taxonTable[taxonTable.n] 'Ichnos' or taxonTable[taxonTable.n] == 'Veterovata' then
taxoColour = frame:expandTemplate{ title = 'Bản mẫu:Taxobox colour', args = { taxonTable[taxonTable.n] } }
else
taxoColour = frame:expandTemplate{ title = 'Bản mẫu:Taxobox colour', args = { taxonTable[taxonTable.n - 1] } }
end
end
if taxoColour == frame:expandTemplate{ title = 'Bản mẫu:Taxobox colour', args = { 'virus' } } then
virus = 'yes'
end
-- add information message
local res = 'Các cấp được in đậm là phân loại sẽ hiển thị trong bảng phân loạivì là phân loại quan trọng hoặc always_display=yes.\n'

-- start table
res = res .. '{| class="infobox biota" style="text-align: left; font-size:100%"\n' .. TableRow .. '! colspan=4 style="text-align: center; color:inherit; background-color: '
.. taxoColour .. '"|Ancestral taxa\n'
-- deal first with the top level taxon; if there are no errors, it should be Life/Veterovata/Ichnos, which are
-- not displayed
local taxon = taxonTable[taxonTable.n]
if not l.topLevelTaxon(taxon) then
local msg = 'Taxonomy template missing'
if mw.title.new('Taxonomy/'..taxon, 'Template').exists then
msg = 'Parent taxon needed'
end
res = res .. TableRow .. l.tableCell('colspan=2', frame:expandTemplate{title = 'Bản mẫu:Create taxonomy', args = {taxon, msg = msg}})
end
-- now output the rest of the table
local currRankVal
for i = taxonTable.n-1, 1, -1 do
-- check ranks are in right order in the hierarchy
taxon = taxonTable[i]
local rank = taxonRankTable[i]
currRankVal = l.lookupRankVal(rankValTable, rank)
if currRankVal then
orderOk = currRankVal ' .. anglicizedRank .. ':'
else
cellContent = cellContent .. anglicizedRank .. ':'
end
if error == 'yes' then
cellContent = .. cellContent ..
end
res = res .. l.tableCell(cellContent)
.. l.tableCell( .. link .. )
.. l.tableCell( .. qualifier .. )
.. l.tableCell( .. frame:expandTemplate{ title = 'Bản mẫu:Edit a taxon', args = { taxon } } .. )
return res
end

-- callTaxonomyKey
Prepares for, and then calls, Bản mẫu:Taxonomy key to display a taxonomy
template page. It does this by building up the information the template
requires, following one 'same as' link, if required.
Usage:

=============================================================================
local PARENT = 1
local RANK = 2
local LINK_TARGET = 3
local LINK_TEXT = 4
local ALWAYS_DISPLAY = 5
local EXTINCT = 6
local SAME_AS = 7
local REFS = 8

function p.callTaxonomyKey(frame)
local taxon = frame.args['taxon'] or
local parent = frame.args['parent'] or
local rank = frame.args['rank'] or
local extinct = string.lower(frame.args['extinct']) or
local alwaysDisplay = string.lower(frame.args['always_display']) or
local linkTarget = frame.args['link_target'] or
local linkText = frame.args['link_text'] or -- this is the "raw" link text, and can be
local refs = frame.args['refs'] or
local sameAsTaxon = frame.args['same_as'] or
if sameAsTaxon ~= then
-- try using the 'same as' taxon; it's an error if it doesn't exist
local ok, sameAsInfoStr = pcall(frame.expandTemplate, frame, { title = 'Bản mẫu:Taxonomy/' .. sameAsTaxon, args = {['machine code'] = 'all' } })
if ok then
local sameAsInfo = mw.text.split(sameAsInfoStr, '$', true)
--'same as' taxon's taxonomy template must not have a 'same as' link
if sameAsInfo[SAME_AS] then
if parent then parent = sameAsInfo[PARENT] end
if rank == then rank = sameAsInfo[RANK] end
if extinct == then extinct = string.lower(sameAsInfo[EXTINCT]) end
if alwaysDisplay == then alwaysDisplay = string.lower(sameAsInfo[ALWAYS_DISPLAY]) end
if linkTarget == then linkTarget = sameAsInfo[LINK_TARGET] end
if linkText == then linkText = sameAsInfo[LINK_TEXT] end
if refs and parent sameAsInfo[PARENT] then refs = sameAsInfo[REFS] end
else
return 'Lỗi: cố gắng theo hai liên kết "same as": same_as = ' .. sameAsTaxon .. ', nhưng Bản mẫu:Taxonomy/' .. sameAsTaxon .. ' cũng có một tham số same_as.'
end
else
return frame:expandTemplate{ title = 'Bản mẫu:Taxonomy key/missing template', args = {taxon=sameAsTaxon, msg='given as the value of same as'} }
end
end
local link = linkTarget
if linkText ~= and linkText ~= linkTarget then link = link .. "|" .. linkText end
-- check consistency of extinct status; if this taxon is not extinct, parent must not be either
local extinctError = 'no'
if parent ~= and (extinct or extinct 'no' or extinct == 'false') then
local ok, parentExtinct = p.getTaxonInfoItem(frame, parent, 'extinct')
if ok and (parentExtinct 'yes' or parentExtinct 'true') then extinctError = 'yes' end
end
return frame:expandTemplate{ title = 'Bản mẫu:Taxonomy key',
args = {taxon=taxon, parent=parent, rank=rank, extinct=extinct, always_display=alwaysDisplay, link_target=linkTarget, link=link, refs=refs, same_as=sameAsTaxon, extinct_error = extinctError} }
end

-- showRefs
Shows the refs field in a taxonomy template, handing incertae sedis taxa and
using '–' for absent refs.
Usage:
=============================================================================
function p.showRefs(frame)
local taxonName = frame.args[1] or
local refs = frame.args[2] or
return l.doShowRefs(taxonName, refs)
end

--= = = = = = = = = = = = = = doShowRefs = = = = = = = = = = = = = = = = =
Show the refs field in a taxonomy template.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function l.doShowRefs(taxonName, refs)
if mw.text.split(taxonName, '/', true)[1] == 'Incertae sedis' then
refs = 'not applicable (incertae sedis)'
elseif refs == then
refs = '–'
end
return refs
end

-- taxonInfo
Extracts and returns information from Bản mẫu:Taxonomy/TAXON, following
one 'same as' link if required.
Usage:
ITEM is one of: 'parent', 'rank', 'link target', 'link text', 'extinct',
'always display', 'refs', 'same as' or 'all'.
If ITEM is not specified, the default is 'all' – all values in a single string
separated by '$'.
=============================================================================
function p.taxonInfo(frame)
local taxon = frame.args[1] or
local item = frame.args[2] or
if item == then item = 'all' end
local ok, info = p.getTaxonInfoItem(frame, taxon, item)
return info
end

--= = = = = = = = = = = getTaxonInfoItem = = = = = = = = = = = = = = = = =
Utility function to extract an item of information from a
taxonomy template, following one 'same as' link if required.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function p.getTaxonInfoItem(frame, taxon, item)
local ok, info
-- item 'dagger' is a special case
if item 'dagger' then
ok, info = p.getTaxonInfoItem(frame, taxon, 'extinct')
if ok then
if info 'yes' or info 'true' then
info = '&dagger;'
else
info =
end
end
-- item ~= 'dagger'
else
ok, info = pcall(frame.expandTemplate, frame, { title = 'Bản mẫu:Taxonomy/' .. taxon, args = {['machine code'] = item } })
if ok then
if info == then
-- try 'same as'
local sameAsTaxon = frame:expandTemplate{ title = 'Bản mẫu:Taxonomy/' .. taxon, args = {['machine code'] = 'same as' } }
if sameAsTaxon ~= then
ok, info = pcall(frame.expandTemplate, frame, { title = 'Bản mẫu:Taxonomy/' .. sameAsTaxon, args = {['machine code'] = item } })
end
end
end
end
if ok then
-- if item is 'link_text', trim info and check whether '(?)' needs to be added
if item == 'link_text' then
-- there is a newline at the end of linkText when taxonomy template has "|link = LINK_TARGET|LINK_TEXT"
info = mw.text.trim(info)
if string.sub(taxon, -2) == '/?' and not string.find(info, '?', 1, true) then
info = info .. '&nbsp;(?)'
end
end
else
info = 'Bản mẫu:Taxonomy/' .. taxon .. --error indicator in code before conversion to Lua
end
return ok, info
end

-- taxonLink
Returns a wikilink to a taxon, if required including '†' before it and
' (?)' after it, and optionally italicized or bolded without a wikilink.
Usage:

=============================================================================
function p.taxonLink(frame)
local taxon = frame.args['taxon'] or
local extinct = string.lower(frame.args['extinct'] or )
local bold = frame.args['bold'] or
local italic = frame.args['italic'] or
local abbreviated = frame.args['abbreviated'] or
local linkTarget = frame.args['link_target'] or
local linkText = frame.args['link_text'] or frame.args['plain_link_text'] or --temporarily allow alternative args
return l.makeLink(taxon, extinct, bold, italic, abbreviated, linkTarget, linkText)
end

--= = = = = = = = = = = = = = getTaxonLink = = = = = = = = = = = = = = = =
Internal function to drive l.makeLink().
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function l.getTaxonLink(frame, taxon, rank, bold, italic, abbreviated, virus)
local ok, extinct = p.getTaxonInfoItem(frame, taxon, 'extinct')
if italic == then
italic = frame:expandTemplate{ title = 'Bản mẫu:Is italic taxon', args = { rank, virus = virus } }
end
local ok, linkTarget = p.getTaxonInfoItem(frame, taxon, 'link_target')
local ok, linkText = p.getTaxonInfoItem(frame, taxon, 'link_text')
return l.makeLink(taxon, extinct, bold, italic, abbreviated, linkTarget, linkText)
end

--= = = = = = = = = = = = = = makeLink = = = = = = = = = = = = = = = = = =
Actually make the link.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function l.makeLink(taxon, extinct, bold, italic, abbreviated, linkTarget, linkText)
local dummy
-- if link text is missing, try to find a replacement
if linkText == then
if string.find(taxon, 'Incertae sedis', 1, true) then
linkText = "incertae sedis"
linkTarget = 'Incertae sedis'
else
linkText, dummy = l.stripExtra(taxon)
end
end
if linkTarget == then linkTarget = linkText end
if italic == 'yes' then linkText = TaxonItalics.italicizeTaxonName(linkText, false, abbreviated=='yes') end
local link =
if bold == 'yes' then link = .. linkText ..
else
if linkTarget == linkText then link = linkText
else link = linkTarget .. '|' .. linkText
end
link = .. link ..
end
if (extinct 'yes' or extinct 'true') and not string.find(link, '†', 1, true) then
link = '†' .. link
end
if string.sub(taxon, -2) == '/?' and not string.find(link, '?', 1, true) then
link = link .. '&nbsp;(?)'
end
return link
end

-- showRankTable
Returns a wikitable showing the ranks and their values as set up by
getRankTable().
Usage:
=============================================================================
function p.showRankTable(frame)
local rankTable = l.getRankTable()
local res = '{| class="wikitable sortable"\n|+ Xếp hạng được chọn trong bản mẫu taxonomy\n! Xếp hạng !! Hiển thị !! Giá trị\n'
for k, v in pairs(rankTable) do
local rankShown = frame:expandTemplate{ title = 'Bản mẫu:Anglicise rank', args = { k } }
res = res .. TableRow .. l.tableCell(k) .. l.tableCell(rankShown) .. l.tableCell(v)
end
return res .. TableEnd
end

-- find
Returns the taxon above the specified taxon with a given rank.
Usage:
=============================================================================
function p.find(frame)
local currTaxon = frame.args[1] or
if currTaxon == then return 'không có taxon nào được cung cấp' end
local rank = frame.args[2] or
if rank == then return 'không có xếp hạng nào được cung cấp' end
local inHierarchy = true -- still in the taxonomic hierarchy or off the top?
local searching = true -- still searching
while inHierarchy and searching do
local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent')
if ok and parent ~= then
currTaxon = parent
local ok, currRank = p.getTaxonInfoItem(frame, currTaxon, 'rank')
if currRank == rank then
searching = false
end
else
inHierarchy = false
end
end
if inHierarchy and not searching then return currTaxon
else return 'không tìm ra xếp hạng'
end
end

-- nth
External utility function primarily intended for use in checking and debugging.
Returns the nth level above a taxon in a taxonomic hierarchy, where the taxon
itself is counted as the first level.
Usage:
=============================================================================
function p.nth(frame)
local currTaxon = frame.args[1] or
if currTaxon == then return 'LỖI: không có taxon nào được cung cấp' end
local n = tonumber(frame.args['n'] or 1)
if n > MaxSearchLevels then
return 'Đã vượt quá tối đa số lượng của các cấp được phép (' .. MaxSearchLevels .. ')'
end
local i = 1
local inHierarchy = true -- still in the taxonomic hierarchy or off the top?
while i ', ) -- strip all tags used to format the link text
linkText, dummy = mw.ustring.gsub(linkText, '"', ) -- remove any occurrences of " in the link text
local baseTaxon, disambig, subgenus, qualifier = l.splitTaxonName(taxon) -- split up the taxon name
baseTaxon, dummy = mw.ustring.gsub(linkText, '"', ) -- remove any occurrences of " in the base taxon name
local match = linkText baseTaxon or
linkText subgenus or
linkText baseTaxon .. ' (' .. subgenus .. ')' or
linkText mw.ustring.sub(baseTaxon, 1, 1) .. '. (' .. subgenus .. ')' or
baseTaxon 'Incertae sedis' or
rankBelowGenus and linkText mw.ustring.gsub(baseTaxon, '([A-Z]).- (.)', '%1. %2') or
mw.ustring.find(qualifier, '?', 1, true) and mw.ustring.find(linkText, baseTaxon, 1, true) == 1
return match
end

--[[= = = = = = = = = = = = = makeTable = = = = = = = = = = = = = = = = = = =
Internal utility function to return a table (array) constructed from a
taxonomic hierarchy stored in "Bản mẫu:Taxonomy/..." templates.
TABLE.n holds the total number of taxa; TABLE[1]..TABLE[TABLE.n] the taxon
names.
The last taxon in the table will either (a) have a taxonomy template but with
no parent given (e.g. 'Life') or (b) not have a taxonomy template.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
function l.makeTable(frame, currTaxon)
local taxonTable = {}
local taxonRankTable = {}
local ok, rank, parent
local i = 1
local topReached = false -- reached the top of the taxonomic hierarchy?
repeat
taxonTable[i] = currTaxon
ok, rank = p.getTaxonInfoItem(frame, currTaxon, 'rank')
if ok then taxonRankTable[i] = string.lower(rank) else taxonRankTable[i] = end
ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent')
if ok and parent ~= then
currTaxon = parent
i = i + 1
else
topReached = true -- reached the top of the hierarchy or tried to use a non-existent taxonomy template
end
until topReached or i > MaxSearchLevels
taxonTable.n = math.min(i, MaxSearchLevels)
return taxonTable, taxonRankTable
end

--= = = = = = = = = = = = getRankTable = = = = = = = = = = = = = = = = = =
Internal utility function to set up a table of numerical values corresponding
to 'Linnaean' ranks, with upper ranks having higher values. In a valid
taxonomic hierarchy, a lower rank should never have a higher value than a
higher rank. The actual numerical values are arbitrary so long as they are
ordered.
The ranks should correspond to those in Bản mẫu:Anglicise ranks.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function l.getRankTable()
return {
classis = 1400,
cohort = 1100,
divisio = 1500,
domain = 1700,
epifamilia = 802,
familia = 800,
forma = 100,
genus = 600,
grandordo = 1005,
['grandordo-mb'] = 1002,
hyperfamilia = 805;
infraclassis = 1397,
infralegio = 1197,
infraordo = 997,
infraphylum = 1497,
infraregnum = 1597,
infratribus = 697,
legio = 1200,
magnordo = 1006,
microphylum = 1495,
micrordo = 995,
mirordo = 1004,
['mirordo-mb'] = 1001,
nanophylum = 1494,
nanordo = 994,
ordo = 1000,
parafamilia = 800,
parvclassis = 1396; -- same as subterclassis
parvordo = 996,
phylum = 1500,
regnum = 1600,
sectio = 500,
--series = 400, used too inconsistently to check
species = 300,
subclassis = 1398,
subcohort = 1098,
subdivisio = 1498,
subfamilia = 798,
subgenus = 598,
sublegio = 1198,
subordo = 998,
subphylum = 1498,
subregnum = 1598,
subsectio = 498,
subspecies = 298,
subterclassis = 1396; -- same as parvclassis
subtribus = 698,
superclassis = 1403,
supercohort = 1103,
superdivisio = 1503,
superdomain = 1703,
superfamilia = 803,
superlegio = 1203,
superordo = 1003,
superphylum = 1503,
superregnum = 1603,
supersectio = 503,
supertribus = 703,
tribus = 700,
varietas = 200,
zoodivisio = 1300,
zoosectio = 900,
zoosubdivisio = 1298,
zoosubsectio = 898,
}
end

--= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
Function to look up the arbitrary numerical value of a rank in a rank value
table. "Ichno" and "oo" ranks are not stored separately, so if present the
prefix is removed.
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function l.lookupRankVal(rankValTable, rank)
local rankVal = rankValTable[rank]
if not rankVal then
-- may be an "ichno" or "oo" rank; try removing "ichno-" or "oo-"
local baseRank = mw.ustring.gsub(mw.ustring.gsub(rank, '^ichno', ), '^oo', )
if baseRank == 'rdo' then baseRank = 'ordo' end
-- if an "ichno" or "oo" rank, lower rank value slightly so it is ok below the base rank
rankVal = rankValTable[baseRank]
if rankVal then
rankVal = rankVal - 0.1
end
end
return rankVal
end

--= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
function l.tableCell(arg1, arg2)
local text, style
if arg2 then
style = arg1
text = arg2
else
style =
text = arg1
end
local res = '|'
if style ~= then
res = res .. style .. '|'
end
return res .. text .. '\n'
end

return p