Difference between revisions of "Module:Autotaxobox"

From Eat Every Plant
Jump to navigation Jump to search
(cp from wikipedia)
 
m (1 revision: Nauclea)
 
(14 intermediate revisions by 2 users not shown)
Line 1: Line 1:
--[[
+
--[[*************************************************************************
 
This module provides support to the automated taxobox system – the templates
 
This module provides support to the automated taxobox system – the templates
 
Automatic taxobox, Speciesbox, Subspeciesbox, Infraspeciesbox, etc.
 
Automatic taxobox, Speciesbox, Subspeciesbox, Infraspeciesbox, etc.
Line 5: Line 5:
 
In particular it provides a way of traversing the taxonomic hierarchy encoded
 
In particular it provides a way of traversing the taxonomic hierarchy encoded
 
in taxonomy templates (templates with names of the form
 
in taxonomy templates (templates with names of the form
"Template:Taxonomy/TAXON_NAME") without causing template expansion depth errors.
+
"Template:Taxonomy/TAXON_NAME") without causing template expansion depth
]]
+
errors.
 +
*****************************************************************************]]
  
local p = {}
+
require('Module:No globals')
 +
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
  
 
--[[=========================================================================
 
--[[=========================================================================
Line 14: Line 21:
 
avoids excessive processing time and protects against incorrectly set up
 
avoids excessive processing time and protects against incorrectly set up
 
hierarchies, e.g. loops.
 
hierarchies, e.g. loops.
 +
The value can be obtained externally via
 +
{{#invoke:Autotaxobox|getMaxSearchLevels}}
 
=============================================================================]]
 
=============================================================================]]
 
local MaxSearchLevels = 100
 
local MaxSearchLevels = 100
Line 32: Line 41:
 
=============================================================================]]
 
=============================================================================]]
 
function p.taxoboxColour(frame)
 
function p.taxoboxColour(frame)
local currTaxon = frame.args[1] or ''
+
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 i = 1 -- count levels processed
 
local searching = currTaxon ~= '' -- still searching for a colour?
 
local searching = currTaxon ~= '' -- still searching for a colour?
 
local foundICTaxon = false -- record whether 'incertae sedis' found
 
local foundICTaxon = false -- record whether 'incertae sedis' found
local colour = '' -- default is no colour
 
 
while searching and i <= MaxSearchLevels do
 
while searching and i <= MaxSearchLevels do
local plainCurrTaxon = p.stripExtra(currTaxon) -- remove trailing text after /
+
local plainCurrTaxon, dummy = l.stripExtra(currTaxon) -- remove trailing text after /
 
if string.lower(plainCurrTaxon) == 'incertae sedis' then
 
if string.lower(plainCurrTaxon) == 'incertae sedis' then
 
foundICTaxon = true
 
foundICTaxon = true
Line 58: Line 70:
 
end
 
end
 
end
 
end
if colour ~= '' then
+
if colour == '' then
return colour
+
if foundICTaxon then
elseif foundICTaxon then
+
colour = frame:expandTemplate{ title = 'Template:Taxobox colour', args = { 'incertae sedis' } }
return frame:expandTemplate{ title = 'Template:Taxobox colour', args = { 'incertae sedis' } }
+
elseif searching then
elseif searching then
+
-- hierarchy exceeds MaxSearchLevels levels
-- hierarchy exceeds MaxSearchLevels levels
+
colour = frame:expandTemplate{ title = 'Template:Taxobox/Error colour', args = { } }
return frame:expandTemplate{ title = 'Template:Taxobox/Error colour', args = { } }
+
else
else
+
colour = 'transparent'
return 'transparent'
+
end
 
end
 
end
 +
return colour
 +
end
 +
 +
--[[= = = = = = = = = = = = =  topLevelTaxon  = = = = = = = = = = = = = = = =
 +
Defines the correct top level taxa, one of which should terminate every
 +
taxonomic hierarchy encoded in taxonomy templates.
 +
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
 +
function l.topLevelTaxon(taxon)
 +
return  taxon == 'Life' or taxon == 'Veterovata' or taxon == 'Ichnos'
 
end
 
end
  
Line 82: Line 103:
 
|ggparent_authority = taxonomic authority for TAXON's greatgreatgrandparent
 
|ggparent_authority = taxonomic authority for TAXON's greatgreatgrandparent
 
|bold_first = 'bold' to bold TAXON in its row
 
|bold_first = 'bold' to bold TAXON in its row
 +
|virus = 'yes' to apply virus taxa italicization standards
 
}}
 
}}
 
=============================================================================]]
 
=============================================================================]]
 
function p.taxoboxList(frame)
 
function p.taxoboxList(frame)
 
local currTaxon = frame.args[1] or ''
 
local currTaxon = frame.args[1] or ''
 +
if currTaxon == '' then return '' end
 
local displayN = (tonumber(frame.args['display_taxa']) or 1) + 1
 
local displayN = (tonumber(frame.args['display_taxa']) or 1) + 1
local auth = frame.args['authority'] or ''
+
local authTable = {}
local parentAuth = frame.args['parent_authority'] or ''
+
authTable[1] = frame.args['authority'] or ''
local gParentAuth = frame.args['gparent_authority'] or ''
+
authTable[2] = frame.args['parent_authority'] or ''
local ggParentAuth = frame.args['ggparent_authority'] or ''
+
authTable[3] = frame.args['gparent_authority'] or ''
local gggParentAuth = frame.args['gggparent_authority'] or ''
+
authTable[4] = frame.args['ggparent_authority'] or ''
 +
authTable[5] = frame.args['gggparent_authority'] or ''
 
local boldFirst = frame.args['bold_first'] or 'link' -- values 'link' or 'bold'
 
local boldFirst = frame.args['bold_first'] or 'link' -- values 'link' or 'bold'
local taxonTable = p.makeTable(frame, currTaxon)
+
local virus = frame.args['virus'] or 'no' -- values 'yes' or 'no'
 +
local offset = tonumber(frame.args['offset'] or 0)
 +
-- adjust the authority table if 'authority' refers to a rank lower than the target taxon
 +
if offset ~= 0 then
 +
for i = 1, 5 do
 +
local j = i + offset
 +
if j <= 5 then
 +
authTable[i] = authTable[j]
 +
else
 +
authTable[i] = ''
 +
end
 +
end
 +
end
 +
local taxonTable, taxonRankTable = l.makeTable(frame, currTaxon)
 
local res = ''
 
local res = ''
-- display all taxa above possible greatgreatgrandparent
+
local topTaxonN = taxonTable.n
for i = taxonTable.n, 6, -1 do
+
-- display all taxa above possible greatgreatgrandparent, without authority
res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[i], fc = tostring(displayN >= i) } }
+
for i = topTaxonN, 6, -1 do
 +
res = res .. l.showTaxon(frame, taxonTable[i], taxonRankTable[i], topTaxonN==i, '', displayN >= i, '', virus)
 
end
 
end
-- display greatgreatgrandparent, if it exists
+
-- display all taxa above possible parent, with authority if given
if taxonTable.n >= 5 then
+
for i = math.min(topTaxonN, 5), 2, -1 do
res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[5], authority = gggParentAuth, fc = tostring(displayN >= 5) } }
+
res = res .. l.showTaxon(frame, taxonTable[i], taxonRankTable[i], topTaxonN==i, authTable[i], displayN >= i, '', virus)
 
end
 
end
-- display greatgrandparent, if it exists; force the display if an infrataxon is below
+
-- display target taxon, always displayed and emboldened
if taxonTable.n >= 4 then
+
res = res .. l.showTaxon(frame, taxonTable[1], taxonRankTable[1], topTaxonN==1, authTable[1], true, boldFirst, virus)
local force = tostring(displayN >= 4) or
+
return res
              frame.expandTemplate{ title = 'Template:Infrataxon()', args = { taxonTable[3] } } == 'true' or
+
end
              frame.expandTemplate{ title = 'Template:Infrataxon()', args = { taxonTable[2] } } == 'true'
+
 
res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[4], authority = ggParentAuth, fc = tostring(force) } }
+
--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
end
+
Show one taxon row in a taxobox.
-- display grandparent, if it exists; force the display if an infrataxon is below
+
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
if taxonTable.n >= 3 then
+
function l.showTaxon(frame, taxon, rank, isTopTaxon, auth, force, boldFirst, virus)
local force = tostring(displayN >= 3) or
+
-- it's an error if this is the top taxon and it's not a top level taxon (e.g. "Life")
                      frame.expandTemplate{ title = 'Template:Infrataxon()', args = { taxonTable[2] } } == 'true'
+
if isTopTaxon then
        res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[3], authority = gParentAuth, fc = tostring(force) } }
+
if l.topLevelTaxon(taxon) then
end
+
return '' -- don't display a top level taxon
-- display parent, if it exists
+
elseif mw.title.new('Taxonomy/'..taxon, 'Template').exists then
if taxonTable.n >= 2 then
+
-- taxonomy template for this taxon has no parent specified
res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[2], authority = parentAuth, fc = tostring(displayN >= 2) } }
+
return frame:expandTemplate{ title = 'Template: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 = 'Template: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 = 'Template: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 = 'Template:Anglicise rank', args = {rank} } .. ':')
 +
local bold = 'no'
 +
if boldFirst == 'bold' then bold = 'yes' end
 +
if auth ~= '' then
 +
auth = '<br><small>' .. auth .. '</small>'
 +
end
 +
local res = res .. l.tableCell(l.getTaxonLink(frame, taxon, rank, bold, '', '', virus) .. auth) -- italic, abbreviated
 +
return res .. TableRow
 +
else
 +
return ''
 +
end
 
end
 
end
-- display target taxon
 
res = res .. frame:expandTemplate{ title = 'Template:Taxobox/showtaxon', args = { taxonTable[1], authority = auth, fc = 'true', format = boldFirst  } }
 
return res
 
 
end
 
end
  
 
--[[========================== taxonomyList =================================
 
--[[========================== taxonomyList =================================
Returns the cells of the taxonomy table displayed on "Template:Taxonomy...."
+
Returns the cells of the taxonomy table displayed on the right hand side of
pages.
+
"Template:Taxonomy...." pages.
 
Usage: {{#invoke:Autotaxobox|taxonomyList|TAXON}}
 
Usage: {{#invoke:Autotaxobox|taxonomyList|TAXON}}
 
=============================================================================]]
 
=============================================================================]]
 
function p.taxonomyList(frame)
 
function p.taxonomyList(frame)
 
local currTaxon = frame.args[1] or ''
 
local currTaxon = frame.args[1] or ''
if currTaxon == '' then return '| ||ERROR: no taxon supplied\n|-' end
+
if currTaxon == '' then
local taxonTable = p.makeTable(frame, currTaxon)
+
return '{|class="infobox biota"\n' .. TableRow .. l.tableCell('') .. l.tableCell('ERROR: no taxon supplied') .. TableEnd
local rankTable = p.getRankTable()
+
end
 +
local taxonTable, taxonRankTable = l.makeTable(frame, currTaxon)
 +
local rankValTable = l.getRankTable()
 
local lastRankVal = 1000000
 
local lastRankVal = 1000000
local orderOk
+
local orderOk = true
local res = ''
+
-- check whether the taxonomy is for viruses; use already determined taxon colour if possible
for i = taxonTable.n, 1, -1 do
+
local virus = 'no'
 +
local taxoColour = colour
 +
if taxoColour == '' then
 +
if  taxonTable[taxonTable.n] == 'Ichnos' or taxonTable[taxonTable.n] == 'Veterovata' then
 +
taxoColour = frame:expandTemplate{ title = 'Template:Taxobox colour', args = { taxonTable[taxonTable.n] } }
 +
else
 +
taxoColour = frame:expandTemplate{ title = 'Template:Taxobox colour', args = { taxonTable[taxonTable.n - 1] } }
 +
end
 +
end
 +
if taxoColour == frame:expandTemplate{ title = 'Template:Taxobox colour', args = { 'virus' } } then
 +
virus = 'yes'
 +
end
 +
-- add information message
 +
local res = '<p style="float:right">Bold ranks show taxa that will be shown in taxoboxes<br>because rank is principal or <code>always_display=yes</code>.</p>\n'
 +
 
 +
-- start table
 +
res =  res .. '{| class="infobox biota" style="text-align: left; font-size:100%"\n' .. TableRow .. '! colspan=4 style="text-align: center; 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 = 'Template: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
 
-- check ranks are in right order in the hierarchy
local ok, rank = p.getTaxonInfoItem(frame, taxonTable[i], 'rank')
+
taxon = taxonTable[i]
local currRankVal = rankTable[rank]
+
local rank = taxonRankTable[i]
 +
currRankVal = l.lookupRankVal(rankValTable, rank)
 
if currRankVal then
 
if currRankVal then
 
orderOk = currRankVal < lastRankVal
 
orderOk = currRankVal < lastRankVal
Line 148: Line 241:
 
orderOk = true
 
orderOk = true
 
end
 
end
-- now return a row of the taxonomy table with anomalous ranks marked
+
-- see if the row will be displayed in a taxobox; bold the rank if so
if orderOk then
+
local boldRank = false
res = res .. frame:expandTemplate{ title = 'Template:Taxonomy links', args = { taxonTable[i] } }
+
local ok, alwaysDisplay = p.getTaxonInfoItem(frame, taxon, 'always_display')
 +
if ok and (alwaysDisplay == 'yes' or alwaysDisplay == 'true') then
 +
boldRank = true
 
else
 
else
if i ~= 1 then
+
boldRank = frame:expandTemplate{ title = 'Template:Principal rank', args = {rank} } == 'yes'
res = res .. frame:expandTemplate{ title = 'Template:Taxonomy links', args = { taxonTable[i], error = 'true' } }
 
else
 
res = res .. frame:expandTemplate{ title = 'Template:Taxonomy links', args = { taxonTable[i], error = 'true', last_error = 'true' } }
 
end
 
 
end
 
end
 +
-- now return a row of the taxonomy table with anomalous ranks marked
 +
local errorStr = ''
 +
if not orderOk then errorStr = 'yes' end
 +
local link = l.getTaxonLink(frame, taxon, rank, '', '', '', virus) -- bold, italic, abbreviated
 +
res = res .. l.taxonomyListRow(frame, taxon, rank, link, boldRank, errorStr)
 +
end
 +
-- close table
 +
res = res .. TableEnd
 +
-- error-tracking for taxonomy templates
 +
-- if the last row has an anomalous rank, put the page in an error-tracking category
 +
local errCat1 = ''
 +
if not orderOk then
 +
errCat1 = '[[Category:Taxonomy templates showing anomalous ranks]]\n'
 +
end
 +
-- if the last row has a taxon name in the page name that does not match the link text,
 +
-- put the taxonomy template in a tracking category
 +
local dummy, linkText = p.getTaxonInfoItem(frame, taxon, 'link_text')
 +
local match = l.matchTaxonLink(taxon, linkText, currRankVal and currRankVal < rankValTable['genus'])
 +
local errCat2 = ''
 +
if not match then
 +
errCat2 = '[[Category:Taxonomy templates with name and link text not matching|' .. taxon .. ']]\n'
 +
end
 +
if errCat1..errCat2 ~= '' then
 +
res = res .. frame:expandTemplate{ title = 'Template other', args = { errCat1..errCat2} }
 
end
 
end
 +
return res
 +
end
 +
 +
--[[ = = = = = = = = = = = = = = taxonomyListRow  = = = = = = = = = = = = = =
 +
Returns a single row of the taxonomy table displayed on the right hand side
 +
of "Template:Taxonomy...." pages.
 +
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
 +
function l.taxonomyListRow(frame, taxon, rank, link, boldRank, error)
 +
local res = ''
 +
if taxon == '' or rank == '' then return res end
 +
local baseTaxon, qualifier = l.stripExtra(taxon)
 +
-- if appropriate, make it clear that some taxa have been skipped via a ... row
 +
if qualifier == '/skip' then
 +
res = res .. TableRow .. l.tableCell('.....') .. l.tableCell('.....')
 +
end
 +
-- now generate a row of the table
 +
res = res .. TableRow
 +
local cellContent = ''
 +
local anglicizedRank = frame:expandTemplate{ title = 'Template:Anglicise rank', args = { rank } }
 +
if boldRank then
 +
cellContent = cellContent .. '<b>' .. anglicizedRank .. '</b>:'
 +
else
 +
cellContent = cellContent .. anglicizedRank .. ':'
 +
end
 +
if error == 'yes' then
 +
cellContent = '<span style="background-color:#FDD">' .. cellContent .. '</span>'
 +
end
 +
res = res .. l.tableCell(cellContent)
 +
          .. l.tableCell('<span style="white-space:nowrap;">' .. link .. '</span>')
 +
          .. l.tableCell('<span style="font-size:smaller;">' .. qualifier  .. '</span>')
 +
          .. l.tableCell('<span style="white-space:nowrap;">' .. frame:expandTemplate{ title = 'Template:Edit a taxon', args = { taxon } } .. '</span>')
 
return res
 
return res
 
end
 
end
Line 177: Line 323:
 
}}
 
}}
 
=============================================================================]]
 
=============================================================================]]
local SAME_AS = 7
 
 
local PARENT = 1
 
local PARENT = 1
 
local RANK = 2
 
local RANK = 2
Line 184: Line 329:
 
local ALWAYS_DISPLAY = 5
 
local ALWAYS_DISPLAY = 5
 
local EXTINCT = 6
 
local EXTINCT = 6
 +
local SAME_AS = 7
 
local REFS = 8
 
local REFS = 8
  
 
function p.callTaxonomyKey(frame)
 
function p.callTaxonomyKey(frame)
 +
local taxon = frame.args['taxon'] or ''
 
local parent = frame.args['parent'] or ''
 
local parent = frame.args['parent'] or ''
 
local rank = frame.args['rank'] or ''
 
local rank = frame.args['rank'] or ''
Line 218: Line 365:
 
local link = linkTarget
 
local link = linkTarget
 
if linkText ~= '' and linkText ~= linkTarget then link = link .. "|" .. linkText end
 
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 = 'Template:Taxonomy key',
 
return frame:expandTemplate{ title = 'Template:Taxonomy key',
args = {parent=parent, rank=rank, extinct=extinct, always_display=alwaysDisplay, link_target=linkTarget, link=link, refs=refs, same_as=sameAsTaxon} }
+
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: {{#invoke:Autotaxobox|showRefs|TAXON|REFS}}
 +
=============================================================================]]
 +
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 (<i>incertae sedis</i>)'
 +
elseif refs == '' then
 +
refs = '–'
 +
end
 +
return refs
 
end
 
end
  
Line 226: Line 402:
 
one 'same as' link if required.
 
one 'same as' link if required.
 
Usage: {{#invoke:Autotaxobox|taxonInfo|TAXON|ITEM}}
 
Usage: {{#invoke:Autotaxobox|taxonInfo|TAXON|ITEM}}
ITEM is one of: 'parent', 'rank', 'link target', 'link text', 'link', 'extinct',
+
ITEM is one of: 'parent', 'rank', 'link target', 'link text', 'extinct',
 
'always display', 'refs', 'same as' or 'all'.
 
'always display', 'refs', 'same as' or 'all'.
 
If ITEM is not specified, the default is 'all' – all values in a single string
 
If ITEM is not specified, the default is 'all' – all values in a single string
Line 237: Line 413:
 
local ok, info = p.getTaxonInfoItem(frame, taxon, item)
 
local ok, info = p.getTaxonInfoItem(frame, taxon, item)
 
return info
 
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 = 'Template:Taxonomy/' .. taxon, args = {['machine code'] = item } })
 +
if ok then
 +
if info == '' then
 +
-- try 'same as'
 +
local sameAsTaxon = frame:expandTemplate{ title = 'Template:Taxonomy/' .. taxon, args = {['machine code'] = 'same as' } }
 +
if sameAsTaxon ~= '' then
 +
ok, info = pcall(frame.expandTemplate, frame, { title = 'Template: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 .. '<span style="font-style:normal;font-weight:normal;"> (?)</span>'
 +
end
 +
end
 +
else
 +
info = '[[Template:Taxonomy/' .. taxon .. ']]' --error indicator in code before conversion to Lua
 +
end
 +
return ok, info
 
end
 
end
  
Line 249: Line 469:
 
|italic=          : 'yes' makes the core output italic
 
|italic=          : 'yes' makes the core output italic
 
|link_target=    : target for the wikilink
 
|link_target=    : target for the wikilink
link_text=        : text of the wikilink (may be same as link_target), without †, italics, etc.
+
|link_text=        : text of the wikilink (may be same as link_target), without †, italics, etc.
 
}}
 
}}
 
=============================================================================]]
 
=============================================================================]]
 
function p.taxonLink(frame)
 
function p.taxonLink(frame)
 
local taxon = frame.args['taxon'] or ''
 
local taxon = frame.args['taxon'] or ''
local extinct = string.lower(frame.args['extinct']) or ''
+
local extinct = string.lower(frame.args['extinct'] or '')
 
local bold = frame.args['bold'] or ''
 
local bold = frame.args['bold'] or ''
 
local italic = frame.args['italic'] or ''
 
local italic = frame.args['italic'] or ''
 +
local abbreviated = frame.args['abbreviated'] or ''
 
local linkTarget = frame.args['link_target'] 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
 
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 = 'Template: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 link text is missing, try to find a replacement
 
if linkText == '' then
 
if linkText == '' then
Line 265: Line 507:
 
linkTarget = 'Incertae sedis'
 
linkTarget = 'Incertae sedis'
 
else
 
else
linkText = p.stripExtra(taxon)
+
linkText, dummy = l.stripExtra(taxon)
 
end
 
end
 
end
 
end
 +
if linkTarget == '' then linkTarget = linkText end
 +
if italic == 'yes' then linkText = TaxonItalics.italicizeTaxonName(linkText, false, abbreviated=='yes') end
 
local link = ''
 
local link = ''
 
if bold == 'yes' then link = '<b>' .. linkText .. '</b>'
 
if bold == 'yes' then link = '<b>' .. linkText .. '</b>'
 
else
 
else
if linkTarget == linkText or linkTarget == '' then link = linkText
+
if linkTarget == linkText then link = linkText
 
else link = linkTarget .. '|' .. linkText
 
else link = linkTarget .. '|' .. linkText
 
end
 
end
 
link = '[[' .. link .. ']]'
 
link = '[[' .. link .. ']]'
 
end
 
end
if italic == 'yes' and not string.find(link, "''", 1, true) then link = '<i>' .. link .. '</i>' end
 
 
if (extinct == 'yes' or extinct == 'true') and not string.find(link, '†', 1, true) then
 
if (extinct == 'yes' or extinct == 'true') and not string.find(link, '†', 1, true) then
 
link = '<span style="font-style:normal;font-weight:normal;">†</span>' .. link
 
link = '<span style="font-style:normal;font-weight:normal;">†</span>' .. link
Line 286: Line 529:
 
end
 
end
  
--[[========================== showRankTable ================================
+
--[[========================== show ================================
 
Returns a wikitable showing the ranks and their values as set up by
 
Returns a wikitable showing the ranks and their values as set up by
 
getRankTable().
 
getRankTable().
 
Usage: {{#invoke:Autotaxobox|showRankTable}}
 
Usage: {{#invoke:Autotaxobox|showRankTable}}
 
=============================================================================]]
 
=============================================================================]]
 
 
function p.showRankTable(frame)
 
function p.showRankTable(frame)
local rankTable = p.getRankTable()
+
local rankTable = l.getRankTable()
 
local res = '{| class="wikitable sortable"\n|+ Ranks checked in taxonomy templates\n! Rank !! Shown as !! Value\n'
 
local res = '{| class="wikitable sortable"\n|+ Ranks checked in taxonomy templates\n! Rank !! Shown as !! Value\n'
 
for k, v in pairs(rankTable) do
 
for k, v in pairs(rankTable) do
 
local rankShown = frame:expandTemplate{ title = 'Template:Anglicise rank', args = { k } }
 
local rankShown = frame:expandTemplate{ title = 'Template:Anglicise rank', args = { k } }
res = res .. '|-\n|' .. k .. '||' .. rankShown .. '||' .. v .. '\n'
+
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: {{#invoke:Autotaxobox|find|TAXON|RANK}}
 +
=============================================================================]]
 +
function p.find(frame)
 +
local currTaxon = frame.args[1] or ''
 +
if currTaxon == '' then return '<span class="error">no taxon supplied</span>' end
 +
local rank = frame.args[2] or ''
 +
if rank == '' then return '<span class="error">no rank supplied</span>' 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 '<span class="error">rank not found</span>'
 
end
 
end
return res .. '|}\n'
 
 
end
 
end
  
Line 365: Line 635:
 
local currTaxon = frame.args[1] or ''
 
local currTaxon = frame.args[1] or ''
 
if currTaxon == '' then return 'ERROR: no taxon supplied' end
 
if currTaxon == '' then return 'ERROR: no taxon supplied' end
return p.listTaxa(p.makeTable(frame, currTaxon))
+
return l.doListAll(l.makeTable(frame, currTaxon))
 +
end
 +
 
 +
function l.doListAll(taxonTable, taxonRankTable)
 +
local lst = taxonTable[1] .. '-' .. tostring(taxonRankTable[1])
 +
for i = 2, taxonTable.n, 1 do
 +
lst = lst .. ', ' .. taxonTable[i] .. '-' .. taxonRankTable[i]
 +
end
 +
return lst
 
end
 
end
  
Line 372: Line 650:
 
=============================================================================]]
 
=============================================================================]]
  
--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
--[[= = = = = = = = = = = = stripExtra  = = = = = = = = = = = = = = = = = = =
 
Internal utility function to strip off any extra parts of a taxon name, i.e.
 
Internal utility function to strip off any extra parts of a taxon name, i.e.
anything after a '/'. Thus "Felidae/?" would be reduced to "Felidae".
+
anything after a '/'. Thus 'Felidae/?' would be split into 'Felidae' and '?'.
 
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
 
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
function p.stripExtra(taxonName)
+
function l.stripExtra(taxonName)
local i = string.find(taxonName,'/')
+
local i = mw.ustring.find(taxonName, '/', 1, true)
 
if i then
 
if i then
return string.sub(taxonName,1,i-1)
+
return mw.ustring.sub(taxonName, 1, i-1), mw.ustring.sub(taxonName, i, -1)
 
else
 
else
return taxonName
+
return taxonName, ''
 
end
 
end
 
end
 
end
  
--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
--[[= = = = = = = = = = = = splitTaxonName  = = = = = = = = = = = = = = = = =
Internal utility function to convert a taxon table to a comma-separated list.
+
Internal utility function to split a taxon name into its parts and return
 +
them. Possible formats include:
 +
* taxon
 +
* taxon (disambig)
 +
* taxon (Subgenus)
 +
* taxon/qualifier
 +
* combinations, e.g. taxon (disambig)/qualifier
 
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
 
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
function p.listTaxa(taxonTable)
+
function l.splitTaxonName(taxon)
local lst = taxonTable[1]
+
-- get any qualifier present
for i = 2, taxonTable.n, 1 do
+
local qualifier = ''
lst = lst .. ', ' .. taxonTable[i]
+
local i = mw.ustring.find(taxon, '/', 1, true)
 +
if i then
 +
qualifier = mw.ustring.sub(taxon, i+1, -1)
 +
taxon = mw.ustring.sub(taxon, 1, i-1)
 
end
 
end
return lst
+
-- get any disambiguator or subgenus
end
+
local disambig = ''
 
+
local subgenus = ''
--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
i = mw.ustring.find(taxon, ' (', 1, true)
Internal utility function to extract an item of information from a
+
if i then
taxonomy template, following one 'same as' link if required.
+
local parenTerm = mw.ustring.sub(taxon, i+2, -2)
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
+
taxon = mw.ustring.sub(taxon, 1, i-1)
function p.getTaxonInfoItem(frame, taxon, item)
+
local char1 = mw.ustring.sub(parenTerm, 1, 1)
-- item == 'link' is a special case
+
if char1 == mw.ustring.lower(char1) then
if item == 'link' then
+
disambig = parenTerm
return p.getTaxonInfoLink(frame, taxon)
+
else
end
+
subgenus = parenTerm
-- item ~= 'link'
 
local ok, info = pcall(frame.expandTemplate, frame, { title = 'Template:Taxonomy/' .. taxon, args = {['machine code'] = item } })
 
if ok then
 
if info == '' then
 
-- try 'same as'
 
local sameAsTaxon = frame:expandTemplate{ title = 'Template:Taxonomy/' .. taxon, args = {['machine code'] = 'same as' } }
 
if sameAsTaxon ~= '' then
 
ok, info = pcall(frame.expandTemplate, frame, { title = 'Template:Taxonomy/' .. sameAsTaxon, args = {['machine code'] = item } })
 
end
 
end
 
end
 
if ok then
 
-- if item is 'link_text' check whether '(?)' needs to be added
 
if item == 'link_text' and string.sub(taxon, -2) == '/?' and not string.find(info, '?', 1, true) then
 
info = info .. '<span style="font-style:normal;font-weight:normal;"> (?)</span>'
 
 
end
 
end
else
 
info = '[[Template:Taxonomy/' .. taxon .. ']]' --error indicator in code before conversion to Lua
 
 
end
 
end
return ok, info
+
return taxon, disambig, subgenus, qualifier
 
end
 
end
  
function p.getTaxonInfoLink(frame, taxon)
+
--[[= = = = = = = = = = = = matchTaxonLink  = = = = = = = = = = = = = = = = =
local ok, linkText, linkTarget
+
Function to determine whether the taxon name derived from the name of the
local link = ''
+
taxonomy template (passed in the parameter taxon) matches the link text
ok, linkText = p.getTaxonInfoItem(frame, taxon, 'link_text')
+
(passed in the parameter linkText).
if ok then
+
The taxon name may have any of the formats:
ok, linkTarget = p.getTaxonInfoItem(frame, taxon, 'link_target')
+
* baseTaxon/qualifier
if ok then
+
* baseTaxon (disambig)
if linkText == linkTarget then link = linkText
+
* baseTaxon (Subgenus) [distinguished by the capital letter]
else link = linkTarget .. '|' .. linkText
+
* a qualifier may be present after the previous two formats.
end
+
 
end
+
Examples of matches (baseTaxon ~ linkText):
end
+
* Pinus ~ Pinus
return ok, link
+
* Pinus sect. Trifoliae ~ Pinus sect. Trifoliae
 +
* Pinus sect. Trifoliae ~ ''Pinus'' sect. ''Trifoliae'' [italic markers ignored]
 +
* Pinus sect. Trifoliae ~ P. sect. Trifoliae [abbreviated genus name matches]
 +
* Bombus (Pyrobombus) ~ Bombus (Pyrobombus)
 +
* Bombus (Pyrobombus) ~ B. (Pyrobombus)
 +
* Bombus (Pyrobombus) ~ Pyrobombus [link text may just be the subgenus]
 +
* Heteractinida ~ "Heteractinida" [double-quotes are ignored in link text]
 +
* Incertae sedis ~ anything [link text is ignored for matching in this case]
 +
* Cetotheriidae with qualifier=? ~ Cetotheriidae (?)
 +
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
 +
function l.matchTaxonLink(taxon, linkText, rankBelowGenus)
 +
local dummy
 +
linkText, dummy = mw.ustring.gsub(linkText, "''", '') -- remove any italic wikitext in the link text
 +
linkText, dummy = mw.ustring.gsub(linkText, '<.->', '') -- 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
 +
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
 
end
  
--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
--[[= = = = = = = = = = = = = makeTable = = = = = = = = = = = = = = = = = = =
 
Internal utility function to return a table (array) constructed from a
 
Internal utility function to return a table (array) constructed from a
 
taxonomic hierarchy stored in "Template:Taxonomy/..." templates.
 
taxonomic hierarchy stored in "Template:Taxonomy/..." templates.
 
TABLE.n holds the total number of taxa; TABLE[1]..TABLE[TABLE.n] the taxon
 
TABLE.n holds the total number of taxa; TABLE[1]..TABLE[TABLE.n] the taxon
 
names.
 
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 p.makeTable(frame, currTaxon)
+
function l.makeTable(frame, currTaxon)
 +
local taxonTable = {}
 +
local taxonRankTable = {}
 +
local ok, rank, parent
 
local i = 1
 
local i = 1
local inHierarchy = true -- still in the taxonomic hierarchy or off the top?
+
local topReached = false -- reached the top of the taxonomic hierarchy?
local taxonTable = {}
+
repeat
taxonTable[1] = currTaxon;
+
taxonTable[i] = currTaxon
while i < MaxSearchLevels and inHierarchy do
+
ok, rank = p.getTaxonInfoItem(frame, currTaxon, 'rank')
local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent')
+
if ok then taxonRankTable[i] = string.lower(rank) else taxonRankTable[i] = '' end
 +
ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent')
 
if ok and parent ~= '' then
 
if ok and parent ~= '' then
 
currTaxon = parent
 
currTaxon = parent
 
i = i + 1
 
i = i + 1
taxonTable[i] = currTaxon
 
 
else
 
else
inHierarchy = false -- run off the top of the hierarchy or tried to use non-existent taxonomy template
+
topReached = true -- reached the top of the hierarchy or tried to use a non-existent taxonomy template
 
end
 
end
end
+
until topReached or i > MaxSearchLevels
taxonTable.n = i
+
taxonTable.n = math.min(i, MaxSearchLevels)
return taxonTable
+
return taxonTable, taxonRankTable
 
end
 
end
  
--[[= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
--[[= = = = = = = = = = = = getRankTable  = = = = = = = = = = = = = = = = = =
 
Internal utility function to set up a table of numerical values corresponding
 
Internal utility function to set up a table of numerical values corresponding
 
to 'Linnaean' ranks, with upper ranks having higher values. In a valid
 
to 'Linnaean' ranks, with upper ranks having higher values. In a valid
Line 475: Line 773:
 
The ranks should correspond to those in Template:Anglicise ranks.
 
The ranks should correspond to those in Template:Anglicise ranks.
 
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
 
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]]
function p.getRankTable()
+
function l.getRankTable()
 
return {
 
return {
 
classis = 1400,
 
classis = 1400,
Line 498: Line 796:
 
mirordo = 1004,
 
mirordo = 1004,
 
['mirordo-mb'] = 1001,
 
['mirordo-mb'] = 1001,
nanophylum = 1499,
+
nanophylum = 1494,
nanordo = 999,
+
nanordo = 994,
 
ordo = 1000,
 
ordo = 1000,
 
parafamilia = 800,
 
parafamilia = 800,
 +
parvclassis = 1396; -- same as subterclassis
 
parvordo = 996,
 
parvordo = 996,
 
phylum = 1500,
 
phylum = 1500,
Line 519: Line 818:
 
subsectio = 498,
 
subsectio = 498,
 
subspecies = 298,
 
subspecies = 298,
 +
subterclassis = 1396; -- same as parvclassis
 
subtribus = 698,
 
subtribus = 698,
 
superclassis = 1403,
 
superclassis = 1403,
Line 537: Line 837:
 
zoosubsectio = 898,
 
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" ranks
 +
rank = mw.ustring.gsub(mw.ustring.gsub(rank, '^ichno', ''), '^oo', '')
 +
if rank == 'rdo' then rank = 'ordo' end
 +
rankVal = rankValTable[rank]
 +
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
 
end
  
 
return p
 
return p

Latest revision as of 23:20, 23 March 2019

--[[************************************************************************* 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 "Template:Taxonomy/TAXON_NAME") without causing template expansion depth errors.

                                                                                                                                                          • ]]

require('Module:No globals') 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 100 =============================================================================]] 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: transparent =============================================================================]] 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 <= MaxSearchLevels do local plainCurrTaxon, dummy = l.stripExtra(currTaxon) -- remove trailing text after / if string.lower(plainCurrTaxon) == 'incertae sedis' then foundICTaxon = true else local possibleColour = frame:expandTemplate{ title = 'Template:Taxobox colour', args = { plainCurrTaxon } } if string.sub(possibleColour,1,3) == 'rgb' then colour = possibleColour searching = false end end if searching then local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent') if ok and parent ~= then currTaxon = parent i = i + 1 else searching = false -- run off the top of the hierarchy or tried to use non-existent taxonomy template end end end if colour == then if foundICTaxon then colour = frame:expandTemplate{ title = 'Template:Taxobox colour', args = { 'incertae sedis' } } elseif searching then -- hierarchy exceeds MaxSearchLevels levels colour = frame:expandTemplate{ title = 'Template:Taxobox/Error colour', args = { } } else colour = 'transparent' end end return colour end

--[[= = = = = = = = = = = = = topLevelTaxon = = = = = = = = = = = = = = = = Defines the correct top level taxa, one of which should terminate every taxonomic hierarchy encoded in taxonomy templates. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function l.topLevelTaxon(taxon) return taxon == 'Life' or taxon == 'Veterovata' or taxon == 'Ichnos' end

--[[=========================== taxoboxList ================================= Returns the rows of taxa in an automated taxobox, based on the taxonomic hierarchy for the supplied taxon. Usage: Lua error at line 156: attempt to index a nil value. =============================================================================]] function p.taxoboxList(frame) local currTaxon = frame.args[1] or if currTaxon == then return end local displayN = (tonumber(frame.args['display_taxa']) or 1) + 1 local authTable = {} authTable[1] = frame.args['authority'] or authTable[2] = frame.args['parent_authority'] or authTable[3] = frame.args['gparent_authority'] or authTable[4] = frame.args['ggparent_authority'] or authTable[5] = frame.args['gggparent_authority'] or local boldFirst = frame.args['bold_first'] or 'link' -- values 'link' or 'bold' local virus = frame.args['virus'] or 'no' -- values 'yes' or 'no' local offset = tonumber(frame.args['offset'] or 0) -- adjust the authority table if 'authority' refers to a rank lower than the target taxon if offset ~= 0 then for i = 1, 5 do local j = i + offset if j <= 5 then authTable[i] = authTable[j] else authTable[i] = end end end local taxonTable, taxonRankTable = l.makeTable(frame, currTaxon) local res = local topTaxonN = taxonTable.n -- display all taxa above possible greatgreatgrandparent, without authority for i = topTaxonN, 6, -1 do res = res .. l.showTaxon(frame, taxonTable[i], taxonRankTable[i], topTaxonN==i, , displayN >= 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').exists then -- taxonomy template for this taxon has no parent specified return frame:expandTemplate{ title = 'Template: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 = 'Template: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 = 'Template: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 = 'Template: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 "Template:Taxonomy...." pages.

Usage:

Bold ranks show taxa that will be shown in taxoboxes
because rank is principal or always_display=yes.

Ancestral taxa
Unrecognized taxon (fix): TAXON

=============================================================================]] function p.taxonomyList(frame) local currTaxon = frame.args[1] or if currTaxon == then return '{|class="infobox biota"\n' .. TableRow .. l.tableCell() .. l.tableCell('ERROR: no taxon supplied') .. 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 = 'Template:Taxobox colour', args = { taxonTable[taxonTable.n] } } else taxoColour = frame:expandTemplate{ title = 'Template:Taxobox colour', args = { taxonTable[taxonTable.n - 1] } } end end if taxoColour == frame:expandTemplate{ title = 'Template:Taxobox colour', args = { 'virus' } } then virus = 'yes' end -- add information message

local res = '

Bold ranks show taxa that will be shown in taxoboxes
because rank is principal or 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; 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 = 'Template: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 < lastRankVal if orderOk then lastRankVal = currRankVal end else orderOk = true end -- see if the row will be displayed in a taxobox; bold the rank if so local boldRank = false local ok, alwaysDisplay = p.getTaxonInfoItem(frame, taxon, 'always_display') if ok and (alwaysDisplay == 'yes' or alwaysDisplay == 'true') then boldRank = true else boldRank = frame:expandTemplate{ title = 'Template:Principal rank', args = {rank} } == 'yes' end -- now return a row of the taxonomy table with anomalous ranks marked local errorStr = if not orderOk then errorStr = 'yes' end local link = l.getTaxonLink(frame, taxon, rank, , , , virus) -- bold, italic, abbreviated res = res .. l.taxonomyListRow(frame, taxon, rank, link, boldRank, errorStr) end -- close table res = res .. TableEnd -- error-tracking for taxonomy templates -- if the last row has an anomalous rank, put the page in an error-tracking category local errCat1 = if not orderOk then errCat1 = '\n' end -- if the last row has a taxon name in the page name that does not match the link text, -- put the taxonomy template in a tracking category local dummy, linkText = p.getTaxonInfoItem(frame, taxon, 'link_text') local match = l.matchTaxonLink(taxon, linkText, currRankVal and currRankVal < rankValTable['genus']) local errCat2 = if not match then errCat2 = '\n' end if errCat1..errCat2 ~= then res = res .. frame:expandTemplate{ title = 'Template other', args = { errCat1..errCat2} } end return res end

--[[ = = = = = = = = = = = = = = taxonomyListRow = = = = = = = = = = = = = = Returns a single row of the taxonomy table displayed on the right hand side

of "Template:Taxonomy...." pages.

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function l.taxonomyListRow(frame, taxon, rank, link, boldRank, error) local res = if taxon == or rank == then return res end local baseTaxon, qualifier = l.stripExtra(taxon) -- if appropriate, make it clear that some taxa have been skipped via a ... row if qualifier == '/skip' then res = res .. TableRow .. l.tableCell('.....') .. l.tableCell('.....') end -- now generate a row of the table res = res .. TableRow local cellContent = local anglicizedRank = frame:expandTemplate{ title = 'Template:Anglicise rank', args = { rank } } if boldRank then cellContent = cellContent .. '' .. 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 = 'Template:Edit a taxon', args = { taxon } } .. '') return res end

--[[========================= callTaxonomyKey =============================== Prepares for, and then calls, Template: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:

Bold ranks show taxa that will be shown in taxoboxes
because rank is principal or always_display=yes.

Ancestral taxa
Ancestral taxa
You're currently creating: [[ ]]

Template:Error If the table below looks correct, then the necessary taxonomic information for the target taxon exists.

Template:Taxonomy key/missing template Not sure why you're here? Tell Keenan about it.

=============================================================================]] 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 = 'Template: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 'Error: attempt to follow two "same as" links: same_as = ' .. sameAsTaxon .. ', but Template:Taxonomy/' .. sameAsTaxon .. ' also has asame_as parameter.' end else return frame:expandTemplate{ title = 'Template: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 = 'Template: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: REFS =============================================================================]] 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 Template:Taxonomy/TAXON, following one 'same as' link if required. Usage: Template:Taxonomy/TAXON 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 = '†' else info = end end -- item ~= 'dagger' else ok, info = pcall(frame.expandTemplate, frame, { title = 'Template:Taxonomy/' .. taxon, args = {['machine code'] = item } }) if ok then if info == then -- try 'same as' local sameAsTaxon = frame:expandTemplate{ title = 'Template:Taxonomy/' .. taxon, args = {['machine code'] = 'same as' } } if sameAsTaxon ~= then ok, info = pcall(frame.expandTemplate, frame, { title = 'Template: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 .. ' (?)' end end else info = 'Template: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: : text of the wikilink (may be same as link_target), without †, italics, etc. =============================================================================]] 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 = 'Template: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 .. ' (?)' end return link end --[[========================== show ================================ Returns a wikitable showing the ranks and their values as set up by getRankTable(). Usage:
Parent: [Taxonomy; edit]
Rank: (displays as )
Link: value of 'link' parameter in taxonomy template|value of parameter 2 in taxonomy template(links to value of 'link' parameter in taxonomy template)
Extinct: no
Always displayed: no
Taxonomic references:
Parent's taxonomic references: Template:Taxonomy/
Ranks checked in taxonomy templates
Rank Shown as Value
infratribus Infratribe 697
infraphylum Infraphylum 1497
infraordo Infraorder 997
cohort Cohort 1100
micrordo Microrder 995
genus Genus 600
grandordo Grandorder 1005
microphylum Microphylum 1495
sublegio Sublegion 1198
zoosubsectio Subsection 898
parvclassis Parvclass 1396
supercohort Supercohort 1103
nanordo Nanorder 994
parafamilia Parafamily 800
zoosubdivisio Subdivision 1298
regnum Kingdom 1600
magnordo Magnorder 1006
mirordo Mirorder 1004
varietas Variety 200
tribus Tribe 700
superfamilia Superfamily 803
superphylum Superphylum 1503
subregnum Subkingdom 1598
infraregnum Infrakingdom 1597
grandordo-mb Grandorder 1002
supertribus Supertribe 703
zoodivisio Division 1300
superlegio Superlegion 1203
zoosectio Section 900
superdivisio Superdivision 1503
superclassis Superclass 1403
subtribus Subtribe 698
divisio Division 1500
subterclassis Subterclass 1396
classis Class 1400
subspecies Subspecies 298
infraclassis Infraclass 1397
subcohort Subcohort 1098
subfamilia Subfamily 798
subsectio Subsection 498
superregnum Superkingdom 1603
species Species 300
subphylum Subphylum 1498
subordo Suborder 998
subgenus Subgenus 598
ordo Order 1000
subdivisio Subdivision 1498
subclassis Subclass 1398
legio Legion 1200
sectio Section 500
domain Domain 1700
superdomain Superdomain 1703
superordo Superorder 1003
parvordo Parvorder 996
familia Family 800
nanophylum Nanophylum 1494
infralegio Infralegion 1197
forma Form 100
mirordo-mb Mirorder 1001
phylum Phylum 1500

=============================================================================]] function p.showRankTable(frame) local rankTable = l.getRankTable() local res = '{| class="wikitable sortable"\n|+ Ranks checked in taxonomy templates\n! Rank !! Shown as !! Value\n' for k, v in pairs(rankTable) do local rankShown = frame:expandTemplate{ title = 'Template: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: rank not found =============================================================================]] function p.find(frame) local currTaxon = frame.args[1] or if currTaxon == then return 'no taxon supplied' end local rank = frame.args[2] or if rank == then return 'no rank supplied' 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 'rank not found' 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: Lua error at line 584: attempt to compare number with nil. =============================================================================]] function p.nth(frame) local currTaxon = frame.args[1] or if currTaxon == then return 'ERROR: no taxon supplied' end local n = tonumber(frame.args['n'] or 1) if n > MaxSearchLevels then return 'Exceeded maximum number of levels allowed (' .. MaxSearchLevels .. ')' end local i = 1 local inHierarchy = true -- still in the taxonomic hierarchy or off the top? while i < n and inHierarchy do local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent') if ok and parent ~= then currTaxon = parent i = i + 1 else inHierarchy = false end end if inHierarchy then return currTaxon else return 'Level ' .. n .. ' is past the top of the taxonomic hierarchy' end end

--[[============================= nLevels =================================== External utility function primarily intended for use in checking and debugging. Returns number of levels in a taxonomic hierarchy, starting from the supplied taxon as level 1. Usage: 1 =============================================================================]] function p.nLevels(frame) local currTaxon = frame.args[1] or if currTaxon == then return 'ERROR: no taxon supplied' end local i = 1 local inHierarchy = true -- still in the taxonomic hierarchy or off the top? while inHierarchy and i < MaxSearchLevels do local ok, parent = p.getTaxonInfoItem(frame, currTaxon, 'parent') if ok and parent ~= then currTaxon = parent i = i + 1 else inHierarchy = false end end if inHierarchy then return MaxSearchLevels .. '+' else return i end end

--[[============================= listAll =================================== External utility function primarily intended for use in checking and debugging. Returns a comma separated list of a taxonomic hierarchy, starting from the supplied taxon. Usage: TAXON- =============================================================================]] function p.listAll(frame) local currTaxon = frame.args[1] or if currTaxon == then return 'ERROR: no taxon supplied' end return l.doListAll(l.makeTable(frame, currTaxon)) end

function l.doListAll(taxonTable, taxonRankTable) local lst = taxonTable[1] .. '-' .. tostring(taxonRankTable[1]) for i = 2, taxonTable.n, 1 do lst = lst .. ', ' .. taxonTable[i] .. '-' .. taxonRankTable[i] end return lst end

--[[========================================================================= Internal functions =============================================================================]]

--[[= = = = = = = = = = = = stripExtra = = = = = = = = = = = = = = = = = = = Internal utility function to strip off any extra parts of a taxon name, i.e. anything after a '/'. Thus 'Felidae/?' would be split into 'Felidae' and '?'. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function l.stripExtra(taxonName) local i = mw.ustring.find(taxonName, '/', 1, true) if i then return mw.ustring.sub(taxonName, 1, i-1), mw.ustring.sub(taxonName, i, -1) else return taxonName, end end

--[[= = = = = = = = = = = = splitTaxonName = = = = = = = = = = = = = = = = = Internal utility function to split a taxon name into its parts and return them. Possible formats include:

  • taxon
  • taxon (disambig)
  • taxon (Subgenus)
  • taxon/qualifier
  • combinations, e.g. taxon (disambig)/qualifier

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function l.splitTaxonName(taxon) -- get any qualifier present local qualifier = local i = mw.ustring.find(taxon, '/', 1, true) if i then qualifier = mw.ustring.sub(taxon, i+1, -1) taxon = mw.ustring.sub(taxon, 1, i-1) end -- get any disambiguator or subgenus local disambig = local subgenus = i = mw.ustring.find(taxon, ' (', 1, true) if i then local parenTerm = mw.ustring.sub(taxon, i+2, -2) taxon = mw.ustring.sub(taxon, 1, i-1) local char1 = mw.ustring.sub(parenTerm, 1, 1) if char1 == mw.ustring.lower(char1) then disambig = parenTerm else subgenus = parenTerm end end return taxon, disambig, subgenus, qualifier end

--[[= = = = = = = = = = = = matchTaxonLink = = = = = = = = = = = = = = = = = Function to determine whether the taxon name derived from the name of the taxonomy template (passed in the parameter taxon) matches the link text (passed in the parameter linkText). The taxon name may have any of the formats:

  • baseTaxon/qualifier
  • baseTaxon (disambig)
  • baseTaxon (Subgenus) [distinguished by the capital letter]
  • a qualifier may be present after the previous two formats.

Examples of matches (baseTaxon ~ linkText):

  • Pinus ~ Pinus
  • Pinus sect. Trifoliae ~ Pinus sect. Trifoliae
  • Pinus sect. Trifoliae ~ Pinus sect. Trifoliae [italic markers ignored]
  • Pinus sect. Trifoliae ~ P. sect. Trifoliae [abbreviated genus name matches]
  • Bombus (Pyrobombus) ~ Bombus (Pyrobombus)
  • Bombus (Pyrobombus) ~ B. (Pyrobombus)
  • Bombus (Pyrobombus) ~ Pyrobombus [link text may just be the subgenus]
  • Heteractinida ~ "Heteractinida" [double-quotes are ignored in link text]
  • Incertae sedis ~ anything [link text is ignored for matching in this case]
  • Cetotheriidae with qualifier=? ~ Cetotheriidae (?)

= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function l.matchTaxonLink(taxon, linkText, rankBelowGenus) local dummy linkText, dummy = mw.ustring.gsub(linkText, "", ) -- remove any italic wikitext in the link text linkText, dummy = mw.ustring.gsub(linkText, '<.->', ) -- 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 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 "Template: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 Template:Anglicise ranks. = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =]] function l.getRankTable() return { classis = 1400, cohort = 1100, divisio = 1500, domain = 1700, familia = 800, forma = 100, genus = 600, grandordo = 1005, ['grandordo-mb'] = 1002, 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, 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" ranks rank = mw.ustring.gsub(mw.ustring.gsub(rank, '^ichno', ), '^oo', ) if rank == 'rdo' then rank = 'ordo' end rankVal = rankValTable[rank] 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