--= = = = = = = = = = = = = = 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 = '<b>' .. linkText .. '</b>'
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 = '<span>†</span>' .. link
end
if string.sub(taxon, -2) == '/?' and not string.find(link, '?', 1, true) then
link = link .. '<span>&nbsp;(?)</span>'
end
return link
end  
--========================== showRankTable ================================
Returns a wikitable showing the ranks and their values as set up by
getRankTable.
Usage:
=============================================================================
function p.showRankTable
local rankTable = l.getRankTable
local res = '{|\n|+ Ranks checked in taxonomy templates\n! Rank !! Shown as !! Value\n'
for k, v in pairs(rankTable) do
local rankShown = :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:
=============================================================================
function p.find
local currTaxon = .args[1] or ''
if currTaxon == '' then return '<span>no taxon supplied</span>' end
local rank = .args[2] or ''
if rank == '' then return '<span>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(, currTaxon, 'parent')
if ok and parent ~= '' then
currTaxon = parent
local ok, currRank = p.getTaxonInfoItem(, 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>rank not found</span>'
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
local currTaxon = .args[1] or ''
if currTaxon == '' then return 'ERROR: no taxon supplied' end
local n = tonumber(.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(, 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:
=============================================================================
function p.nLevels
local currTaxon = .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(, 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:
=============================================================================
function p.listAll
local currTaxon = .args[1] or ''
if currTaxon == '' then return 'ERROR: no taxon supplied' end
return l.doListAll(l.makeTable(, 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  
--=========================== removeQualifier ================================
External utility function to remove a qualifier (any part after a "/") from a
taxon name.
Usage:
=============================================================================
function p.removeQualifier
local baseName, qualifier = l.stripExtra(.args[1])
return baseName
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]