Module:Taxonavigation

From Wikimedia Commons, the free media repository
Jump to navigation Jump to search
Lua

CodeDiscussionEditHistoryLinksLink count Subpages:DocumentationTestsResultsSandboxLive code All modules

Usage
This module is used to display Navigation bar for taxa (called taxonavigation) by {{Taxonavigation}}, {{TaxonavigationIncluded}} and {{TaxonavigationIncluded2}}
Functions and their usage
How to improve and test this module
  1. develop your modification in Module:Taxonavigation/sandbox, the sandbox of this module
    which is used in the sandbox templates {{Taxonavigation/sandbox}}
  2. verify your changes in Module_talk:Taxonavigation/sandbox/testcases
  3. if needed improve the testcases Module:Taxonavigation/sandbox/testcases
  4. ask an administrator to report your modifications in Module:Taxonavigation
  5. verify again your changes in Module_talk:Taxonavigation/testcases
  6. if needed improve the testcases Module:Taxonavigation/testcases
See also

Code

-- require('strict')

local getArgs = require('Module:Arguments').getArgs

-- The default language object of pagenames on the local wiki (not the current user language), for lc, uc, ucfirst.
local lang = mw.language.getContentLanguage()

local _common = require('Module:Biology')

----------------------------------------------------------------------------------------------------
---------- Exists utilities ------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------

local function articleExists(articleName)
	local page = mw.title.new(articleName, nil )
	if page then
--		_common.addDebug('articleExists(“' .. articleName .. '”)', tostring(page.exists))
		return page.exists
	else
		return false
	end
end

local function categoryExists(categoryName)
	if not string.startsWith(lang:lc(categoryName), 'category:') then
		categoryName = 'Category:' .. categoryName
	end
	local category = mw.title.new(categoryName, nil)
	if category then
--		_common.addDebug('categoryExists(“' .. categoryName .. '”)', tostring(category.exists))
		return category.exists
	else
		return false
	end
end

----------------------------------------------------------------------------------------------------
---------- Rank utilities --------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------

local _singularLatinRanksNeedingItalic = {
	cladus = false,
	subtaxon = false,
	['???'] = false,
	['(unranked)'] = false,
	['⇒'] = false,

	supergroup = false,
	group = false,
	['informal group'] = false,

	empire = false,
	kingdom = false,

	superdomain = false,
	domain = false,
	subdomain = false,

	realm = false,
	subrealm = false,

	superregnum = false,
	regnum = false,
	subregnum = false,
	infraregnum = false,

	superdivisio = false,
	divisio = false,
	subdivisio = false,
	infradivisio = false,

	superphylum = false,
	phylum = false,
	subphylum = false,
	infraphylum = false,

	superclassis = false,
	classis = false,
	subclassis = false,
	parvclassis = false,
	infraclassis = false,

	megacohors = false,
	supercohors = false,
	cohors = false,
	subcohors = false,
	infracohors = false,

	superordo = false,
	ordo = false,
	subordo = false,
	parvordo = false,
	infraordo = false,
	microordo = false,

	superfamilia = false,
	familia = false,
	subfamilia = false,

	supertribus = false,
	tribus = false,
	subtribus = false,
	infratribus = false,

	['genus group'] = true,
	genus = true,
	nothogenus = true,
	subgenus = true,

--[====[
Here we manage botanic section (between species/series and genus)
and zoology section (between family and ordo).
--]====]

	supersectio = true,
	sectio = true,
	subsectio = true,

	series = true,
	subseries = true,

	species = true,
	hybrid = true,
	nothospecies = true,
	subspecies = true,
	nothosubspecies = true,

	varietas = true,
	cultivar = true,

	forma = true,
	subforma = true,
}

--[====[
true  means must contain a space.
false means must not contain a space.
nil   means it depends.
--]====]
local _singularLatinRanksNeedingMultiWordTaxonName = {

--	cladus = false,
--	subtaxon = false,
--	['???'] = false,
--	['(unranked)'] = false,
--	['⇒'] = false,

--	supergroup = false,
--	group = false,
--	['informal group'] = false,

--	empire = false,
--	kingdom = false,

--	superdomain = false,
--	domain = false,
--	subdomain = false,

	superregnum = false,
	regnum = false,
	subregnum = false,
	infraregnum = false,

	superdivisio = false,
	divisio = false,
	subdivisio = false,
	infradivisio = false,

	superphylum = false,
	phylum = false,
	subphylum = false,
	infraphylum = false,

	superclassis = false,
	classis = false,
	subclassis = false,
	parvclassis = false,
	infraclassis = false,

	megacohors = false,
	supercohors = false,
	cohors = false,
	subcohors = false,
	infracohors = false,

	superordo = false,
	ordo = false,
	subordo = false,
	parvordo = false,
	infraordo = false,
	microordo = false,

	suprafamilia = false,
	superfamilia = false,
	familia = false,
	subfamilia = false,

	supertribus = false,
	tribus = false,
	subtribus = false,
	infratribus = false,

	['genus group'] = true, -- 'Campiglossa group'
	genus = false,
	nothogenus = true, -- 'x Rhododendron'
	subgenus = true, -- 'Rhododendron subg. Hymenanthes' or 'Rhododendron (Hymenanthes)'

--[====[
Here we manage botanic section (between species/series and genus) and zoology section (between family and ordo).
--]====]

--	supersectio = false,
--	sectio = false, -- I don't know 'Rhododendron sect. Pontica'
--	subsectio = false, -- I don't know 'Rhododendron subsect. Taliensia'

--	series = false, -- I don't know 'Liatris ser. Scariosae'
--	subseries = false, -- I don't know 'Liatris ser. Scariosae'

	species = true,
	hybrid = true, -- 'Rhododendron x Hymenanthes'
	nothospecies = true, -- 'Rhododendron x Hymenanthes'

	subspecies = true,
	nothosubspecies = true,

	varietas = true,
	cultivar = true,

	forma = true,
	subforma = true,
}

local _singularLatinRanksThatCanHaveX = {
	nothogenus = true, -- 'x Rhododendron'

	nothospecies = true, -- 'Rhododendron x Hymenanthes'
	nothosubspecies = true,

	varietas = true,
	cultivar = true,
}

--[====[
Must be similar to 'c:Template:CheckSingularLatinRank'.
--]====]
function doesSingularLatinRankExist(singularLatinRank)
	local singularLatinRankLC = lang:lc(singularLatinRank)
	local needItalic = _singularLatinRanksNeedingItalic[singularLatinRankLC]
	return not (needItalic == nil)
end

--[====[
Must be similar to 'c:Template:RankNeedsItalic'.
--]====]
function rankNeedsItalic(singularLatinRank)
	local singularLatinRankLC = lang:lc(singularLatinRank)
	local needItalic = _singularLatinRanksNeedingItalic[singularLatinRankLC]
	if needItalic == nil then
		_common.addDebug('rankNeedsItalic(“' .. singularLatinRankLC .. '”)', '“' .. tostring(needItalic) .. '”')
		return true
	else
		return needItalic
	end
end

--[====[
Will be used to get parameter 'categorizeTribesIn'.
--]====]
local _pluralEnglishRankUPFBySingularLatinRankLC = {
	species = 'Species',
	genus = 'Genera',

	subtribus = 'Subtribes',
	tribus = 'Tribes',

	subfamilia = 'Subfamilies',
	familia = 'Families',
	superfamilia = 'Superfamilies',

	subordo = 'Suborders',
	ordo = 'Orders',
}

--[====[
Will be used to create 'Lauraceae by subtribe'.
--]====]
local _singularEnglishRankLCBySingularLatinRankLC = {
	species = 'species',
	genus = 'genus',

	subtribus = 'subtribe',
	tribus = 'tribe',

	subfamilia = 'subfamily',
	familia = 'family',
	superfamilia = 'superfamily',

	infraordo = 'infraorder',
	subordo = 'suborder',
	ordo = 'order',
}

--[====[
Returns '', if everything in 'taxonName' looks coherent to its rank.
Returns an error + category otherwise.
--]====]
function checkTaxonNameForARank(singularLatinRank, taxonName)
	local singularLatinRankLC = lang:lc(singularLatinRank)

	-- Check 1: some ranks require taxonName with space, others require taxonName without space.
	local needsMultiWordTaxonName = _singularLatinRanksNeedingMultiWordTaxonName[singularLatinRankLC]
	if needsMultiWordTaxonName == nil then
		-- No way to determine if the taxon name needs a space.
	else
		if string.find(taxonName, 'Virus', 1, true)	-- like ?
		or string.find(taxonName, 'virus', 1, true)	-- like 'Coxsackievirus'
		or string.find(taxonName, '-', 1, true)		-- like 'HIV-1'
		then
			-- Viruses have their own classification usage => no check can be done.
		else
			local containsSpace = string.find(taxonName, ' ', 1, true)
			if containsSpace and not needsMultiWordTaxonName then
				-- More complex because there are disambiguation like 'genus (familia)'.
				local taxonNameLC = lang:lc(taxonName)
				if string.find(taxonNameLC,' (', 1, true)				-- Disambiguation like 'genus (familia)' => no check can be done.
				or string.find(taxonNameLC, 'incertae sedis', 1, true)	-- IncertaeSedis like 'Euheterodonta incertae sedis' => no check can be done.
				or string.find(taxonNameLC, 'unassigned', 1, true)		-- IncertaeSedis like 'Unassigned Cyprinidae' => no check can be done Unassigned.
				or string.find(taxonNameLC, 'unplaced', 1, true)	    -- IncertaeSedis like 'Elapidae unplaced' => no check can be done Unassigned.
				or string.find(taxonNameLC, 'bacter', 1, true)			-- Bacteria like 'Gamma Proteobacteria' or 'Candidatus Liberibacter' have strange naming => no check can be done.
				or string.find(taxonNameLC, 'fossil', 1, true)			-- Fossils like 'Diadectes fossils' or 'Fossil Diadectes' => no check can be done.
				then
					-- Some taxonName cannot be checked.
				else
					return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect “' .. singularLatinRankLC .. '” with space.', 'Taxonavigation')
				end
			elseif needsMultiWordTaxonName and not containsSpace then
				return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect “' .. singularLatinRankLC .. '” without space.', 'Taxonavigation')
			end
		end
	end

	-- Check 2: taxonName should not contain “ X ” or “ x ”, nor start by “X ” or “x ”.
	if string.startsWith(taxonName, 'X ')
	or string.find(taxonName,' X ', 1, true) then
		return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect taxonName “' .. taxonName .. '” (and category name) containing “ X ” instead of “ × ”.', 'Taxonavigation')
	elseif string.startsWith(taxonName, 'x ')
	or string.find(taxonName,' x ', 1, true) then
		return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect taxonName “' .. taxonName .. '” (and category name) containing “ x ” instead of “ × ”.', 'Taxonavigation')
	end

	local xpos = mw.ustring.find(taxonName, '×', 1, true)
	if xpos then
		-- Check 2bis: taxonName should not contain “×” not followed by space.
		_common.addDebug('checkTaxonNameForARank', 'string.len(taxonName)=' .. tostring(mw.ustring.len(taxonName)) .. ', xpos=' .. tostring(xpos))
		if mw.ustring.len(taxonName) == xpos then
			return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect taxonName “' .. taxonName .. '” ending with “×”.', 'Taxonavigation')
		end
		local nextCar = mw.ustring.sub(taxonName, xpos + 1, xpos + 1)
		_common.addDebug('checkTaxonNameForARank', 'nextCar=“' .. tostring(nextCar) .. '”')
		if nextCar ~= ' ' then
			return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect taxonName “' .. taxonName .. '” containing “×” not followed by space.', 'Taxonavigation')
		end

		-- Check 3: taxonName containing “×” should be “nothoXXX”.
		local canHaveX = _singularLatinRanksThatCanHaveX[singularLatinRankLC]
		if not canHaveX then
			return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect rank “' .. singularLatinRankLC .. '” for a taxon containing “×” (maybe it is a “notho' .. singularLatinRankLC .. '”).', 'Taxonavigation')
		end
	end
	return ''
end

--[====[
Calculates a category/gallery's title out of the taxonavigation parameters.
We need to find the category/article's taxonName in the taxonavigation parameters
to ensure that its associated rank requires italic.
--]====]
function calcItalicTitle(taxonName)
	-- Is it a category/article with disambiguation?
	local taxonNameSecondPart = ''
	if string.endsWith(taxonName,')') then
		-- “Bombus (disamb)” but also subgenera like “Bombus (Psithyrus)” but NOT species like “genus (subgenus) species”.
		local parenthesisStart = string.find(taxonName,' (', 1, true)
		if parenthesisStart then
			taxonNameSecondPart = ' <small>' .. string.sub(taxonName,parenthesisStart + 1) .. '</small>'
			taxonName = string.sub(taxonName, 1, parenthesisStart - 1)
		end
	end
	
	taxonName = string.gsub(taxonName, " subsp%. ", "''%0''")
	
	local namespace = mw.title.getCurrentTitle().nsText
	if not string.isNilOrEmpty(namespace) then
		namespace = namespace .. ':' -- Like “Category:”.
	end
	return namespace .. "''" .. taxonName .. "''" .. taxonNameSecondPart
end

--[====[
Returns:
	- ' • Genus<b>: <i>[[:Category:Cinnamomum|Cinnamomum]]</i></b>'
out of:
	- rank='Genus',
	- taxonName='Cinnamomum'.
The returned string is to be added to a Taxonavigation.
--]====]
function addRankAndTaxonName(frame, namespace, pagename, firstTaxon, singularLatinRank, taxonName)
	-- Separator 1.
	local taxonavigationStr = not firstTaxon and '&nbsp;• ' or ''

	-- Check singularLatinRank.
	if not doesSingularLatinRankExist(singularLatinRank) then
		taxonavigationStr = taxonavigationStr .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect rank “' .. singularLatinRank .. '”.', 'Taxonavigation')
	end
	
	-- Detect extinct sign: |Eohiodon†| or |†Eohiodon| (must be before calc of currentTaxonNameIsPagename).
	local rankNeedsItalic = rankNeedsItalic(singularLatinRank)
	local extinctCross = ''
	if string.find(taxonName,'†',1,true) then
		if rankNeedsItalic then
			extinctCross = "</i>†<i>"
		else
			extinctCross = '†'
		end
		taxonName = string.gsub(taxonName, '†', '', 5)
		taxonName = mw.text.trim(taxonName)
	end

	-- Add rank	+ open bold.
	local currentTaxonNameIsPagename = (lang:ucfirst(taxonName) == lang:ucfirst(pagename))
	singularLatinRank = lang:ucfirst(singularLatinRank)
	if currentTaxonNameIsPagename then
		if not string.isNilOrEmpty(extinctCross) then
			-- we need to change the cross because it must be more visible
			if rankNeedsItalic then
				extinctCross = "</i><big>†</big><i>"
			else
				extinctCross = '<big>†</big>'
			end
		end
		taxonavigationStr = taxonavigationStr .. '<b>' .. singularLatinRank .. ':&nbsp;'
	else
		taxonavigationStr = taxonavigationStr .. singularLatinRank .. '<b>:&nbsp;'
	end

	-- Open italic.
	local openItalicIfNeeded = ''
	local closeItalicIfNeeded = ''
	if rankNeedsItalic then
		openItalicIfNeeded = '<i>'
		closeItalicIfNeeded = '</i>'
	end
	taxonavigationStr = taxonavigationStr .. openItalicIfNeeded
	
	-- Check that taxonName knowing its ranks.
	taxonavigationStr = taxonavigationStr .. checkTaxonNameForARank(singularLatinRank, taxonName)

	-- Add taxonName.
	local taxonNameToDisplay = nil
	if taxonName == 'Incertae Sedis'
	or taxonName == 'Incertae sedis'
	or taxonName == 'incertae sedis'
	or taxonName == '?' then
		taxonNameToDisplay = "''Incertae sedis''"
	else
		-- Disambiguation: '|Ibicella (Martyniaceae)|'.
		local taxonDisplayName = taxonName
		if string.endsWith(taxonName, ')') then
			-- "Bombus (disamb)" but also subgenera like "Bombus (Psithyrus)" but NOT species like "genus (subgenus) species"
			local parenthesisStart = string.find(taxonName,' (', 1, true)
			if parenthesisStart then
				taxonDisplayName = string.sub(taxonName, 1, parenthesisStart - 1) ..
					'<small style="font-style:normal">&nbsp;' ..
					string.sub(taxonName, parenthesisStart + 1) ..
					'</small>'
			end
		end
		taxonDisplayName = string.gsub(taxonDisplayName, ' ', '&nbsp;')
		taxonDisplayName = string.gsub(taxonDisplayName, '<small&nbsp;style="font%-style:normal;?">', '<small style="font-style:normal">')

		if namespace == 0 then
			-- article/gallery
			if (not currentTaxonNameIsPagename) and articleExists(taxonName) then
				-- Higher taxon => link to existing articles
				taxonNameToDisplay = '[[' .. taxonName ..'|<span style="color:#005896">' .. taxonDisplayName ..'</span>]]'
			end
		elseif namespace == mw.site.namespaces.Category.id then
			-- category
			if currentTaxonNameIsPagename and articleExists(taxonName) then
				-- article's taxon => link to existing article
				taxonNameToDisplay = '[[' .. taxonName ..'|<span style="color:#005896">' .. taxonDisplayName ..'</span>]]'
			end
		end
		if not taxonNameToDisplay then
			-- general case
			taxonNameToDisplay = '[[:Category:' .. taxonName ..'|' .. taxonDisplayName ..']]'
		end
	end
	taxonavigationStr = taxonavigationStr .. extinctCross .. taxonNameToDisplay
	
	-- Close italic.
	taxonavigationStr = taxonavigationStr .. closeItalicIfNeeded

	-- Close bold.
	taxonavigationStr = taxonavigationStr .. '</b>'

	-- Change title.
	if currentTaxonNameIsPagename and rankNeedsItalic then
		local title = calcItalicTitle(taxonName)
		if title then
			_common.addDebug('addRankAndTaxonName', 'displaytitle=“' .. title .. '”')
			taxonavigationStr = taxonavigationStr .. ' ' .. frame:preprocess('{{DISPLAYTITLE:' .. title .. '}}') .. ' '
		end
	end
	return taxonavigationStr
end

--[====[
Returns:
	- '[[Category:Lauraceae by subtribe|Cinnamomum]]', or
	- '[[Category:Subtribes of Lauraceae|Cinnamomum]]',
out of:
	- categorizeIn='Lauraceae',
	- currentPageTaxonSingularEnglishRankLC='subtribe',
	- currentPageTaxonPluralEnglishRankUPF='Subtribes',
	- pagename='Cinnamomum',
in: 'Category:Cinnamomum'.
The returned string is to be added to a Taxonavigation.
It uses these parameters:
	- categorizeIn: like 'Lauraceae' or 'FAMILIA';
	- currentPageTaxonSingularEnglishRankLC: the lowercase singular English rank of the taxon described by the Taxonavigation
		(among 'species', 'genus', 'subtribe', 'tribe', 'subfamily', 'family');
	- currentPageTaxonPluralEnglishRankUPF: the uppercase-first plural English rank of the taxon described by the Taxonavigation
		(among 'Species', 'Genera', 'Subtribes', 'Tribes', 'Subfamilies', 'Families').
--]====]
function calcCategory(options, categorizeIn, pagename, currentPageTaxonSingularEnglishRankLC, currentPageTaxonPluralEnglishRankUPF)
	local categorizeInLC = lang:lc(categorizeIn)
	local cleverCategorization = doesSingularLatinRankExist(categorizeInLC)
	if cleverCategorization then
		-- categorizeIn like 'FAMILIA', so we need to find the family
		local parametersStr = options.parameters
		if not parametersStr then
			_common.addDebug('calcCategory', 'Failed to find rank “' .. categorizeInLC .. '” (unspecified parameters)')
			return nil
		end
		local parameters = mw.text.jsonDecode(parametersStr)
		if not parameters then
			_common.addDebug('calcCategory', 'Failed to find rank “' .. categorizeInLC .. '” (non-JSON parameters)')
			return nil
		end
		local realCategorizeIn = parameters[categorizeInLC]
		if string.isNilOrEmpty(realCategorizeIn) then
			_common.addDebug('calcCategory', 'Failed to find rank “' .. categorizeInLC .. '” (in ' .. tostring(table.getn(parameters)) .. ' JSON parameters)')
			return nil
		end
		realCategorizeIn = string.gsub(realCategorizeIn, '†', '', 5)
		realCategorizeIn = mw.text.trim(realCategorizeIn)

		-- categorizeIn='FAMILIA' => we replace it with the family name => categorizeIn='Lauraceae'.
		_common.addDebug('calcCategory', 'Modified categorizeIn=“' .. tostring(realCategorizeIn) .. '”')
		categorizeIn = realCategorizeIn
	end

	-- Step1: try fullCategory like 'Lauraceae by subtribe' or 'Plantae by family'.
	local fullCategory = categorizeIn .. ' by ' .. currentPageTaxonSingularEnglishRankLC
	if categoryExists(fullCategory) then
		_common.addDebug('calcCategory', 'fullCategory(first case)=“' .. fullCategory ..'”')
	else
		_common.addDebug('calcCategory', 'fullCategory(first case)=“' .. fullCategory .. '” does not exists')
		-- Step2: try fullCategory like 'Subtribes of Lauraceae'.
		fullCategory = currentPageTaxonPluralEnglishRankUPF .. ' of ' .. categorizeIn
		if cleverCategorization and not categoryExists(fullCategory) then
			_common.addDebug('calcCategory', 'fullCategory(second case+clever)=“' .. fullCategory .. '” does not exists')
			return nil
		else
			_common.addDebug('calcCategory', 'fullCategory(second case)=“' .. fullCategory .. '”')
		end
	end
	return '[[Category:' .. fullCategory .. '|' .. pagename .. ']]'
end

--[====[
Returns:
	- '[[Category:Lauraceae by subtribe|Cinnamomum]]', or
	- '[[Category:Subtribes of Lauraceae|Cinnamomum]]',
out of:
	- currentPageTaxonSingularLatinRankLC='subtribus',
	- pagename='Cinnamomum',
in: 'Category:Cinnamomum'.
The returned string is to be added to a Taxonavigation.
It uses these parameters:
	- currentPageTaxonSingularLatinRankLC: the singular lowercase Latin rank of the taxon described by the Taxonavigation (species, genus ...).
--]====]
function calcCategories(options, pagename, currentPageTaxonSingularLatinRankLC)
	if string.startsWith(currentPageTaxonSingularLatinRankLC, 'notho') then
		-- nothogenus, nothospecies, nothosubspecies should be categorizes like genus, species, subspecies
		currentPageTaxonSingularLatinRankLC = string.sub(currentPageTaxonSingularLatinRankLC, string.len('notho')+1)
		_common.addDebug('calcCategories', 'currentPageTaxonSingularLatinRankLC=“' .. tostring(currentPageTaxonSingularLatinRankLC) .. '” was a notho')
	end
	local currentPageTaxonPluralEnglishRankUPF = _pluralEnglishRankUPFBySingularLatinRankLC[currentPageTaxonSingularLatinRankLC]
	if not currentPageTaxonPluralEnglishRankUPF then
		_common.addDebug('calcCategories', 'currentPageTaxonSingularLatinRankLC=“' .. tostring(currentPageTaxonSingularLatinRankLC) .. '” seems incorrect')
		return nil
	end

	local currentPageTaxonSingularEnglishRankLC = _singularEnglishRankLCBySingularLatinRankLC[currentPageTaxonSingularLatinRankLC]
	if not currentPageTaxonSingularEnglishRankLC then
		_common.addDebug('calcCategories', 'currentPageTaxonSingularLatinRankLC=“' .. tostring(currentPageTaxonSingularLatinRankLC) .. '” seems incorrect')
		return nil
	end

	local optionBase = 'categorize' .. currentPageTaxonPluralEnglishRankUPF .. 'In'
	local categories = ''

	for optionIndex = 1, 5 do
		local optionName = optionBase
		if (optionIndex > 1) then
			optionName = optionBase .. tostring(optionIndex)
		end
		local categorizeIn = options[optionName]
		_common.addDebug('calcCategories', 'optionName=“' .. tostring(optionName) .. '”, categorizeIn=“' .. tostring(categorizeIn) .. '”')

		if not categorizeIn then
			-- categorizeSubtribesInX does not exists => stop iteration on X
			_common.addDebug('calcCategories', 'categories=“' .. categories .. '”')
			return categories -- Returns less than 5 categories
		end

		local newCategory = calcCategory(options, categorizeIn, pagename, currentPageTaxonSingularEnglishRankLC, currentPageTaxonPluralEnglishRankUPF)
		if newCategory then
			categories = categories .. newCategory
		else
			-- categorizeSubtribesInX led to nothing, let us try categorizeSubtribesInX + 1
		end
	end
	_common.addDebug('calcCategories', 'categories=“' .. categories .. '”')
	return categories -- Returns 5 categories
end

--[====[
Returns a string containing a category for include templates like Template:Angiospermes and 'Template:Lauraceae (APG)'.
You can test the result of this function in Template:Angiosperms/sandbox and 'Template:Lauraceae (APG)/sandbox'.
--]====]
function calcIncludeTemplateCategory(options, namespace, pagename)
	local categorizeTemplate = options.categorizeTemplate
	if categorizeTemplate == 'no' then
		return ''
	end
	local categoryName = 'Templates to include in Taxonavigation'	-- for templates without include using TaxonavigationIncluded
	local includeOption = options.include
	if not string.isNilOrEmpty(includeOption) then
		includeOption = string.gsub(includeOption, '/sandbox', '', 1) -- for include=Angiosperms/sandbox
		categoryName = includeOption .. ' Templates to include in Taxonavigation'	-- for templates with include using TaxonavigationIncluded2
	end

	local errorCategory = ''
	if not categoryExists(categoryName) then
		errorCategory = _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Category “' .. categoryName .. '” does not exist.', 'Taxonavigation')
	end
	return '[[Category:' .. categoryName .. ']]' .. errorCategory
end

--[====[
Returns a string containing a documentation for include templates like Template:Angiospermes and 'Template:Lauraceae (APG)'.
You can test the result of this function in Template:Angiosperms/sandbox and 'Template:Lauraceae (APG)/sandbox'.
--]====]
function calcIncludeTemplateDocumentation(options, namespace, pagename)
	if options.documentTemplate == 'no' then
		return ''
	end

	local classificationLine = ''
	local documentTemplateWithClassification = options.documentTemplateWithClassification
	if string.isNilOrEmpty(documentTemplateWithClassification) then
		local classification = options['classification']
		if string.isNilOrEmpty(classification) then
			-- Classification has not been provided by 'Template:Angiospermes' to 'Template:TaxonavigationIncluded'.
			-- => Ask the user to call '{{Taxonavigation|include=CurrentTemplate|' with 'classification='.
			-- _common.addDebug('calcIncludeTemplateDocumentation', '(case1) documentTemplateWithClassification and classification are empty => display IOC.')
			classificationLine = '<dd><kbd>|classification = </kbd>?&lt;!-- Please specify. --&gt;</dd>'
		else
			-- Classification has been provided by 'Template:Lauraceae (APG)' to 'Template:TaxonavigationIncluded2'.
			-- => Don't tell the user to call '{{Taxonavigation|include=CurrentTemplate|' with 'classification='.
			-- _common.addDebug('calcIncludeTemplateDocumentation', '(case2) documentTemplateWithClassification is empty AND classification has been provided => do not document classification.')
		end
	elseif documentTemplateWithClassification == 'none' then
		-- _common.addDebug('calcIncludeTemplateDocumentation', '(case3) documentTemplateWithClassification=“none”.')
	else
		-- _common.addDebug('calcIncludeTemplateDocumentation', '(case4) documentTemplateWithClassification displayed.')
		classificationLine = '<dd><kbd>|classification = ' .. tostring(documentTemplateWithClassification) .. '</kbd></dd>'
	end

	local taxonavigationAutoCategory = nil
	local space = string.find(pagename, ' ', 1, true)
	if space then
		local taxonName = string.sub(pagename, 1, space - 1)
		taxonavigationAutoCategory = '{{TaxonavigationAutoCategory|' .. taxonName .. '|includename=' .. pagename .. '}}'
	else
		taxonavigationAutoCategory = '{{TaxonavigationAutoCategory|' .. pagename .. '}}'
	end
	
	local doc =	[=[
<dl>
<dt>Usage</dt>
	<dd>This template should be used in categories and articles as [[:Template:Taxonavigation|{{Taxonavigation}}]]'s parameter include=</dd>
	<dd>For that reason, those kind of templates are called 'taxonavigation include templates' or simply 'include templates'.</dd>
	<dd>There are such templates for:
		<table style="text-align:left">
		<tr>
			<td><ul><li>[[:Category:Templates to include in Taxonavigation|all classes]]</li></ul></td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Insecta Templates to include in Taxonavigation|all insect orders]]</li></ul></td>
			<td>WARNING: they are named '''[[Template:Orthoptera|&lt;orderName&gt;]]''' except for '''[[Template:Coleoptera (include)|Coleoptera (include)]]''' and '''[[Template:Lepidoptera (include)|Lepidoptera (include)]]'''</td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Angiosperms Templates to include in Taxonavigation|all angiospermes families]]</li></ul></td>
			<td>WARNING: as these families template follow [[APGIII]], they are named '''[[Template:Cactaceae (APG)|&lt;familyName&gt; (APG)]]''' and contain classification=APGIII</td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Pinopsida Templates to include in Taxonavigation|all conifer families]]</li></ul></td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Pteridophyta Templates to include in Taxonavigation|all fern families]]</li></ul></td>
			<td>WARNING: as these families template follow [[Smith System]], they are named '''[[Template:Schizaeaceae (Smith)|&lt;familyName&gt; (Smith)]]''' and contain classification=Smith</td>
		</tr>
		<tr>
			<td><ul><li>[[:Category:Aves Templates to include in Taxonavigation|all bird families]]</li></ul></td>
			<td>WARNING: as these families template follow [[Template:Taxonavigation/IOC_Classification|IOC classification]], they are named '''[[Template:Diomedeidae (IOC)|&lt;familyName&gt; (IOC)]]''' and contain classification=IOC</td>
		</tr>
		</table>
	</dd>
<dt>Example</dt>
	<dd><kbd>{{Taxonavigation</kbd></dd>
	<dd><kbd>|include = ]=] .. pagename .. [=[</kbd></dd>
]=] .. classificationLine .. [=[
	<dd>...</dd>
	<dd><kbd>|authority = Linnaeus, 1758</kbd></dd>
	<dd><kbd>}}</kbd></dd>
<dt>Automatic categories</dt>]=]

	local needsParameterRanks = false
	local needsParameterParameters = false
	local categoriesOptionFound = false
	-- First iteration: we display non-clever categories, second iteration: we display clever categories.
	for clever = 0,1 do
		for singularLatinRankLC, pluralEnglishRankUPF in pairs(_pluralEnglishRankUPFBySingularLatinRankLC) do
			local optionBase = 'categorize' .. pluralEnglishRankUPF .. 'In'
			local singularEnglishRankLC = _singularEnglishRankLCBySingularLatinRankLC[singularLatinRankLC]
			for optionIndex = 1, 5 do
				local optionName = optionBase
				if optionIndex > 1 then
					optionName = optionBase .. tostring(optionIndex)
				end
				local categorizeIn = options[optionName]
				_common.addDebug('calcIncludeTemplateDocumentation', optionName .. '=“' .. tostring(categorizeIn) .. "”")
				if not categorizeIn then
					break
				end
				categoriesOptionFound = true
				local cat1 = categorizeIn .. ' by ' .. singularEnglishRankLC
				local cat2 = pluralEnglishRankUPF .. ' of ' .. categorizeIn
				local cleverCategorization = doesSingularLatinRankExist(categorizeIn)
				if not cleverCategorization and clever == 0 then
					cat1 = cat1 .. '|' .. cat1
					cat2 = cat2 .. '|' .. cat2
					doc = doc .. [=[
	<dd>As you provided parameter ]=] .. optionName .. ', [[:Category:' .. cat1 .. ']] or [[:Category:' .. cat2 .. ']] will be automatically added to ' ..
						string.lower(pluralEnglishRankUPF) .. ' using this template.</dd>'
					needsParameterRanks = true
				elseif cleverCategorization and (clever == 1) then
					-- categorizeIn like 'FAMILIA', so we need to find the family
					cat1 = cat1 .. '|<' .. categorizeIn .. '> by ' .. singularEnglishRankLC
					cat2 = cat2 .. '|' .. pluralEnglishRankUPF .. ' of <' .. categorizeIn .. '>'
					doc = doc .. [=[
	<dd>As you provided parameter ]=] .. optionName .. ', [[:Category:' .. cat1 .. ']] or [[:Category:' .. cat2 .. ']] will be automatically added to ' ..
						string.lower(pluralEnglishRankUPF) .. ' using this template.</dd>' .. [=[
		<dl>
			<dd>This automatic category should only contain <b>{{TaxonavigationAutoCategory|<]=] .. categorizeIn .. '>|includename=' .. pagename .. '}}</b></dd>' .. [=[
		</dl>]=]
					needsParameterRanks = true
					needsParameterParameters = true
				end
			end
		end
		if clever == 0 and categoriesOptionFound then
			doc = doc .. '<dd><dl><dd>These automatic category should only contain <b>' .. taxonavigationAutoCategory .. '</b></dd></dl></dd>'
		end
	end

	if categoriesOptionFound then
		local wikicode = mw.title.getCurrentTitle():getContent()

		local parameter = 'rank={{{rank|}}}'
		if needsParameterRanks and not string.find(wikicode, parameter, 1, true) then
			doc = doc .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Please add “' .. parameter .. '”.', 'Taxonavigation')
		end

		parameter = 'parameters={{{parameters|}}}'
		if needsParameterParameters and not string.find(wikicode, parameter, 1, true) then
			doc = doc .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Please add “' .. parameter .. '”.', 'Taxonavigation')
		end
	else
		doc = doc .. [=[
	<dd>You should provide the following parameters to have automatic categories added to species and genera:
		<table role="presentation" border="0" cellspacing="0" cellpadding="0">
		<tr><td><kbd>|rank={{{rank|}}}</kbd><td></tr>
		<tr><td><kbd>|categorizeSpeciesIn=</kbd><i>parentName</i></td><td>&lt;!-- Only if <i>parent</i> contains multiple genera --&gt;</td></tr>
		<tr><td><kbd>|categorizeGeneraIn=</kbd><i>parentName</i></td><td>&lt;!-- Only if <i>parent</i> contains subfamilies or tribes --&gt;</td></tr>
		<tr><td><kbd>|categorizeSubtribesIn=</kbd><i>parentName</i></td><td>&lt;!-- Only if <i>parent</i> contains tribes AND subtribes --&gt;</td></tr>
		<tr><td><kbd>|categorizeTribesIn=</kbd><i>parentName</i></td><td>&lt;!-- Only if <i>parent</i> contains subfamilies AND tribes --&gt;</td></tr>
		<tr><td><kbd>|categorizeSubfamiliesIn=</kbd><i>parentName</i></td><td>&lt;!-- Only if <i>parent</i> contains clades containing subfamilies --&gt;</td></tr>
		<tr><td><kbd>|categorizeFamiliesIn=</kbd><i>parentName</i></td><td>&lt;!-- Only if <i>parent</i> contains multiple families --&gt;</td></tr>
		</table>
	</dd>]=]
	end

	doc = doc ..[=[
<dt>See also</dt>
	<dd>
		<ul>
		<li>[[Template:Taxonavigation|{{Taxonavigation}}]] to use in categories and articles</li>
		<li>[[Template:TaxonavigationIncluded|{{TaxonavigationIncluded}}]] to use in include templates</li>
		<li>[[Template:TaxonavigationIncluded2|{{TaxonavigationIncluded2}}]] to use in include templates that themself use include=</li>
		<li>[[Template:TaxonavigationAutoCategory|{{TaxonavigationAutoCategory}}]] to use in automatic categories</li>
		<li>[[Module:Taxonavigation]] that displays this documentation and adds categories</li>
		</ul>
	</dd>
</dl>]=]
	return doc
end

local _templatesThatShouldNotBeAutoDocumented = {
	Taxonavigation = true,
	Coleoptera = true,
	['Coleoptera/sandbox'] = true,
	Coleoptera2 = true,
	Lepidoptera = true,
	Lepidoptera2 = true,
}

--[====[
Returns a string containing documentation and categories for include templates like 'Template:Angiospermes' and 'Template:Lauraceae (APG)'.
You can test the result of this function in 'Template:Angiosperms/sandbox' and 'Template:Lauraceae (APG)/sandbox'.
--]====]
function calcIncludeTemplateDocumentationAndCategory(options, namespace, pagename)
	if namespace ~= mw.site.namespaces.Template.id then
		return ''
	end
	if _templatesThatShouldNotBeAutoDocumented[pagename]
	or string.startsWith(pagename, 'Taxonavigation/')
	or string.startsWith(pagename, 'TaxonavigationIncluded')
	or string.startsWith(pagename, 'Biohist')
	then
		--[=[
		'Template:Taxonavigation', 'Template:Coleoptera*', 'Template:Lepidoptera*' have no need for documentation nor categories.
		'Template:Taxonavigation/testcases', 'Template:Taxonavigation/sandbox/testcases' have no need for documentation nor categories.
		'Template:TaxonavigationIncluded', ..., 'Template:TaxonavigationIncluded2/sandbox' have their own documentation that add their own categories.
		'Template:Biohist' and 'Template:Biohist/sandbox' have no need for documentation nor categories.
		--]=]
		return ''
	end
	return calcIncludeTemplateDocumentation(options, namespace, pagename) ..
		calcIncludeTemplateCategory(options, namespace, pagename)
end

--[====[
Returns a string that Template:Taxonavigation will display.
It uses parameters:
* indexed parameters containing Cladus,magnoliids,Ordo,Laurales,Familia,Lauraceae,Genus,Cinnamomum...
* namespaceForDebug
* pagenameForDebug
* rank (when called by TaxanavigationIncluded or TaxanavigationIncluded2)
* categorizeSpeciesIn2, categorizeSubtribesIn (when called by TaxanavigationIncluded)
--]====]
function taxonavigation(frame, options)
	local taxonavigationStr = ''
	local firstTaxon = string.isNilOrEmpty(options['include']) -- include=XXX => taxonHaveAlreadyBeenDisplayed=true.
	local currentTitle = mw.title.getCurrentTitle()
	local namespace = currentTitle.namespace
	local pagename = currentTitle.text

	-- prepare NonRegression
	local namespaceForDebug = options['namespaceForDebug']
	local pagenameForDebug = options['pagenameForDebug']
	if not string.isNilOrEmpty(namespaceForDebug) then
		namespace = tonumber(namespaceForDebug,10)
	end
	if not string.isNilOrEmpty(pagenameForDebug) then
		pagename = pagenameForDebug
	end
	local isGalleryOrCategory = (namespace == 0) or (namespace == mw.site.namespaces.Category.id)
	_common.addDebug('taxonavigation', [=[
----------------------------------------------------------
caller=“]=] .. tostring(options['caller']) .. '”'
--		.. ', namespaceForDebug=“' .. tostring(namespaceForDebug) .. '”'
--		.. ', pagenameForDebug=“' .. tostring(pagenameForDebug) .. '”'
--		.. ', namespace=“' .. tostring(namespace) .. '”'
--		.. ', pagename=“' .. tostring(pagename) .. '”'
--		.. ', isGalleryOrCategory=“' .. tostring(isGalleryOrCategory) .. '”'
--		.. ', serializeCurrentParameters=“' ..serializeCurrentParameters(options) .. '”'
		.. ', options[parameters]=“' .. tostring(options['parameters']) .. '”'
--		.. ', options[1]=“' .. tostring(options['1']) .. '”'
--		.. ', options[2]=“' .. tostring(options['2']) .. '”.'
	)

	for paramIndex = 1, 1602, 2 do
		local singularLatinRank = options[tostring(paramIndex)]
		local taxonName         = options[tostring(paramIndex+1)]
		if string.isNilOrEmpty(taxonName) then
			if string.isNilOrEmpty(singularLatinRank) then
				-- _common.addDebug('taxonavigation', 'Stopped at index ' .. tostring(paramIndex) .. ', singularLatinRank=“' .. tostring(singularLatinRank).. '”, taxonName=“' .. tostring(taxonName) .. '”.')
			else
				taxonavigationStr = taxonavigationStr .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Strange parameter “' .. singularLatinRank .. '”.', 'Taxonavigation')
			end
			break
		end
		singularLatinRank = mw.text.trim(singularLatinRank)
		taxonName = mw.text.trim(taxonName) -- Don't suppress “†”, it will be done by addRankAndTaxonName()
		if taxonName == 'NOTTOBEDISPLAYED'
		or taxonName == 'EMPTY' and singularLatinRank == 'EMPTY' then
			-- 'Template:Coleoptera' and 'Template:Lepidoptera' have strange empty lines.
		else
			taxonavigationStr = taxonavigationStr .. addRankAndTaxonName(frame, namespace, pagename, firstTaxon, singularLatinRank, taxonName)
			firstTaxon = false
		end
	end

	if isGalleryOrCategory then
		local currentPageTaxonSingularLatinRank = options['rank'] -- rank of the taxon described by the Taxonavigation
		_common.addDebug('taxonavigation', 'currentPageTaxonSingularLatinRank=“' ..tostring(currentPageTaxonSingularLatinRank) .. '” (available in include templates only).')
		if not string.isNilOrEmpty(currentPageTaxonSingularLatinRank) then
			-- Called by Template:TaxonavigationIncluded
			local categories = calcCategories(options, pagename, lang:lc(currentPageTaxonSingularLatinRank))
			if categories then
				taxonavigationStr = taxonavigationStr .. categories
			end
		end
	end

	local mustBeEmpty = options.mustBeEmpty
	if not string.isNilOrEmpty(mustBeEmpty) then
		taxonavigationStr = taxonavigationStr .. _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Parameter “mustBeEmpty” is not empty.', 'Taxonavigation')
	end

	return taxonavigationStr .. calcIncludeTemplateDocumentationAndCategory(options, currentTitle.namespace, currentTitle.text)
end

--[====[
Finds current page taxon singularLatinRank from Taxonavigation parameters. For example,
it returns: 'Genus' out of {{Taxonavigation|...|Genus|Cinnamomum|..}} in 'Category:Cinnamomum'.
--]====]
function findCurrentPageTaxonSingularLatinRank(options)
	local currentTitle = mw.title.getCurrentTitle()
	local pagename = currentTitle.text
	-- Prepare non-regression.
	local pagenameForDebug = options.pagenameForDebug
	if not string.isNilOrEmpty(pagenameForDebug) then
		pagename = pagenameForDebug
	end

	for paramIndex = 1, 1602, 2 do
		local singularLatinRank = options[tostring(paramIndex)]
		local taxonName = options[tostring(paramIndex + 1)]
		if string.isNilOrEmpty(singularLatinRank) or string.isNilOrEmpty(taxonName) then
			break
		end
		singularLatinRank = mw.text.trim(singularLatinRank)
		taxonName = mw.text.trim(string.gsub(taxonName, '†', '', 5))
		if taxonName == 'NOTTOBEDISPLAYED'
		or taxonName == 'EMPTY' and singularLatinRank == 'EMPTY' then
			-- 'Template:Coleoptera' and 'Template:Lepidoptera' have strange empty lines.
		elseif lang:ucfirst(taxonName) == lang:ucfirst(pagename) then
			return lang:ucfirst(singularLatinRank)
		end
	end
	return ''
end

--[====[
Serializes rank and taxon parameters from Taxonavigation. For example,
it returns: '{"species":"Cinnamomum camphora","CurrentPageTaxonRank":"Familia","genus":"Cinnamomum"}',
out of: {{Taxonavigation|...|Genus|Cinnamomum|Species|Cinnamomum camphora|..}} in 'Category:Cinnamomum'.
--]====]
function serializeCurrentParameters(options)
	local currentTitle = mw.title.getCurrentTitle()
	local pagename = currentTitle.text
	-- Prepare non-regression.
	local pagenameForDebug = options.pagenameForDebug
	if not string.isNilOrEmpty(pagenameForDebug) then
		pagename = pagenameForDebug
	end

	-- Parameters is the table to be returned serialized with JSON.
	local parameters = {}
	-- Provide caller for debug.
	local caller = options.caller
	if not string.isNilOrEmpty(caller) then
		parameters.caller = caller
	end
	for paramIndex = 1, 1602, 2 do
		local singularLatinRank = options[tostring(paramIndex)]
		local taxonName = options[tostring(paramIndex + 1)]
		if string.isNilOrEmpty(singularLatinRank) or string.isNilOrEmpty(taxonName) then
			break
		end
		singularLatinRank = mw.text.trim(singularLatinRank)
		taxonName = mw.text.trim(string.gsub(taxonName, '†', '', 5))
		if taxonName == 'NOTTOBEDISPLAYED'
		or taxonName == 'EMPTY' and singularLatinRank == 'EMPTY' then
			-- 'Template:Coleoptera' and 'Template:Lepidoptera' have strange empty lines.
		else
			parameters[lang:lc(singularLatinRank)] = taxonName  -- can contains extinct sign
			if lang:ucfirst(taxonName) == lang:ucfirst(pagename) then
				parameters.CurrentPageTaxonRank = lang:ucfirst(singularLatinRank)
			end
		end
	end
	return mw.text.jsonEncode(parameters)
end

local _acceptedNamedParameters = {
	authority = true,
	include = true,
	documentTemplate = true,
	documentTemplateWithClassification = true,
	categorizeTemplate = true,
	classification = true,
	parameters = true,
	namespaceForDebug = true,
	pagenameForDebug = true,
	caller = true,
	rank = true,
	mustBeEmpty = true,
	-- + categorizeFamiliesInX,
}

--[====[
Detects that incorrect parameters have been passed to {{Taxonavigation}}.
--]====]
function detectTaxonavigationBadParameters(options)
	local authority = options['authority']
	if authority and string.find(authority, '†', 1, true) then
		return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Please transfer “†” from “authority” to Taxonavigation last taxon.', 'Taxonavigation')
	end
	for index, lang in pairs(options) do
		if type(index) == 'string' then
			if _acceptedNamedParameters[index] then -- Normal argument pair: include='Angiosperms', authority='L.'.
--				_common.addDebug('detectTaxonavigationBadParameters', 'parameter “' .. index .. '” is in _acceptedNamedParameters.')
			elseif string.startsWith(index, 'categorize') then -- Normal argument pair: categorizeFamiliesIn='FAMILIA'.
--				_common.addDebug('detectTaxonavigationBadParameters', 'parameter “' .. index .. '” starts with “categorize”.')
			else -- Bad argument pair: Authority='BadOrtho', Familia='Saturniidae'.
--				_common.addDebug('detectTaxonavigationBadParameters', 'parameter “' .. index .. '” seems bad.')
--				return _common.incorrectBiologyTemplateUsage('Taxonavigation', 'Incorrect parameter [' .. index .. '].', 'Taxonavigation')
			end
		else -- Normal argument pair: 1='Familia', 2='Saturniidae'.
--			_common.addDebug('detectTaxonavigationBadParameters', 'parameter[' .. tostring(index) .. '] is not a string.')
		end
	end
	return ''
end

----------------------------------------------------------------------------------------------------
---------- PUBLIC FUNCTIONS ------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------
local p = {}

-- public version of taxonavigation
-- Used by {{Taxonavigation}}, {{TaxonavigationIncluded}} and {{TaxonavigationIncluded2}}
function p.taxonavigation(frame)
	local args = getArgs(frame)
	local taxonavigationStr = taxonavigation(frame, args)
--	_common.addDebug('p.taxonavigation', detectTaxonavigationBadParameters(args)
	return taxonavigationStr .. _common.getDebug('<BR/>') .. detectTaxonavigationBadParameters(args)
end

--[====[
Public version of findCurrentPageTaxonRank.
Used by {{Taxonavigation}} and {{TaxonavigationIncluded2}}.
--]====]
function p.findCurrentPageTaxonRank(frame)
	local args = getArgs(frame)
	return findCurrentPageTaxonSingularLatinRank(args)
end

--[====[
Public version of serializeCurrentParameters.
Used by {{Taxonavigation}}.
--]====]
function p.serializeCurrentParameters(frame)
	local args = getArgs(frame)
	return serializeCurrentParameters(args)
end

----------------------------------------------------------------------------------------------------
---------- Testcase public functions (return string) -----------------------------------------------
----------------------------------------------------------------------------------------------------

function p.testcase_stringTrim(frame)
	local args = frame.args
	return mw.text.trim(tostring(args['1']))
end

function p.testcase_doesSingularLatinRankExist(frame)
	local args = frame.args
	local singularLatinRank = args['1']
	if string.isNilOrEmpty(singularLatinRank) then
		return false
	end
	return tostring(doesSingularLatinRankExist(singularLatinRank))
end

function p.testcase_rankNeedsItalic(frame)
	local args = frame.args
	local singularLatinRank = args['1']
	if string.isNilOrEmpty(singularLatinRank) then
		return true
	else
		return tostring(rankNeedsItalic(singularLatinRank))
	end
end

function p.testcase_articleExists(frame)
	local args = frame.args
	local article = args['1']
	if string.isNilOrEmpty(article) then
		return false
	else
		return tostring(articleExists(article))
	end
end

function p.testcase_categoryExists(frame)
	local args = frame.args
	local category = args['1']
	if string.isNilOrEmpty(category) then
		return false
	else
		return tostring(categoryExists(category))
	end
end

function p.testcase_calcItalicTitle(frame)
	local args = frame.args
	return mw.text.nowiki(calcItalicTitle(args['1']))
end

return p