Modiwl:Taxobox

Oddi ar Wicipedia

Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value). Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value).

This module is still unstable. Use with your own caution and report bugs and feature requests at Module talk:Taxobox or Wikidata talk:WikiProject_Taxonomy.

Taxobox.lua is a lua module which can automatically generate taxonomy infobox and is overwritable by classic taxobox parameters like species, unranked_ordo etc.

This module infobox is designed to be a replacement of Wikipedia's Taxobox. It provides configuration options which can control hypernym paths, show or hide certain ranks, specify content language and its configs, and make a callback and pass parameters to "classic" taxobox.

The following code

{{Taxobox
  | qid=Q464424
}}

creates the taxobox on the right hand side. The item to show is given with qid.

If you want to have this taxobox show up on each Wikidata taxon item: [1]

Internal[golygu cod]

Method taxobox[golygu cod]

The taxobox method provides function runs the above example. The method itself can be invoked with

{{#invoke: Taxobox
| taxobox
| qid=Q464424
| config[count]=10
}}

The number of parent taxons to show is given by config[count].

Method callback[golygu cod]

The callback method provides function to retrieve all internal parameters and pass them to an external template. The method can be invoked with like this

{{#invoke: Taxobox
| callback
| qid=Q464424
| template=OtherTaxobox
| config[count]=10
}}

callback accepts all arguments that taxobox accepted. It also accepts an extra argument template to specify name of the template to be expanded.

I18n[golygu cod]

Change the i18n messages in Module:I18n/taxobox. I18n also specifys some format strings which can be use to customize the infobox output of certain language.

message description example(s)
rank-format Format of instances of rheng tacson (Q427626), or cytras (Q713623). Will be passed into 2 named arguments when rendering ranks:
{link}
Wiki link to the rank
{label}
Display title of the rank
  • "[[{link}|{label}]]": display name and give a hyperlink to the rank, or
  • "{label}" only display name of the rank
rank-format-<latinrank> This argument is similar to rank-format but can be use to specify the format of certain rank. The "<latinrank>" is a latin name, can be a instance of rheng tacson (Q427626), or cytras (Q713623). For example "rank-format-cladus" for cytras (Q713623). It accepts same 2 named arguments like rank-format
  • "": don't display anything at this rank. Can be used to hide the rank name when the taxon is a clade
  • "<i>(clade)</i>": use italic style
  • "[[{link}|<span style=\"color:gray\">{label}</span>]]": use link and change color
item-format-current-with-vernacular-name Format of instances of tacson (Q16521) or tacson un eitem (Q310890). current means either the item is the main taxon (specified by module argument qid), or the item and all taxa between the item and main taxon (if any) are all tacson un eitem (Q310890). with-vernacular-name means the item's or label exists and is different from .

4 named arguments will be passed on redering:

{link}
Wiki link to the taxon item
{vernacular}
The "common name" (the value of or item label) of taxon item in current language
{scientific}
Full scientific name
{scientificshort}
Short scientific name, for example "P. leo" of Llew (Q140)

Note: Don't use italic style here on scientific names. This style can be set by scientific-name-pattern, scientific-name-repl and other related messages.

  • "<b>{scientificshort}</b>": only display the short scientific name in bold weight
  • "<b>{vernacular} {scientificshort}</b>": display both vernacular and short scientific name in bold weight
  • "[[{link}|{vernacular}]]": display a hyperlink to target taxon with its vernacular name as label
item-format-current-with-vernacular-name Format of instances of tacson (Q16521) or tacson un eitem (Q310890). without-vernacular-name means either both and label (in specified language) are empty, or the vernacular name is the same as .

4 named arguments will be passed on rendering, and they are same to item-format-current-with-vernacular-name.

  • "[[{link}|{scientificshort}]]": display a hyperlink to target taxon with its short scientific name as label
  • "[[{link}|{vernacular}]] ({scientficshort})": display both vernacular and short scientific name, also link to target taxon on from vernacular name
scientific-name-pattern, scientific-name-repl Pattern replacement to generate full scientific name. Can be override per rank by scientific-name-pattern-<latinrank> and scientific-name-repl-<latinrank>.

Not the format of scientific-name-pattern and other *pattern* messages are Lua's patterns, which are similar to (Q125267) but not identical. Read the manual to know how to write ones.

  • (pattern) "^.+$": match all non-empty strings
    • (repl) "%0": do nothing for replacement, or
    • (repl) "<i>%0</i>": put the scientific name in <i> tag so it can be rendered as italic
short-scientific-name-pattern, short-scientific-name-repl Pattern replacement to generate short scientific name. Can be override per rank by short-scientific-name-pattern-<latinrank> and short-scientific-name-repl-<latinrank>.
scientific-name-pattern-<latinrank>, scientific-name-repl-<latinrank> Pattern replcacement to generate full scientific name per rank. The "<latinrank>" is a latin name, can be a instance of rheng tacson (Q427626), or cytras (Q713623). For example "scientific-name-pattern-species" for rhywogaeth (Q7432).
  • (scientific-name-pattern-genus) nil: fallback to scientific-name-pattern's "^.+$"
    • (scientific-name-repl-genus) "<i>%0</i>": italicize full scientific name if its rank is genws (Q34740)
short-scientific-name-pattern-<latinrank>, short-scientific-name-repl-<latinrank> Pattern replcacement to generate short scientific name per rank. The "<latinrank>" is a latin name, can be a instance of rheng tacson (Q427626), or cytras (Q713623). For example "short-scientific-name-pattern-species" for rhywogaeth (Q7432).
  • (short-scientific-name-pattern-species) "^(%w)%w+ (%w+)$": pattern to fetch the first letter of genus name (as %1) and the whole epithet (as %2) from the binomial of a species.
    • (short-scientific-name-repl-species) "<i>%1. %2</i>": for example, the result for Llew (Q140) is "P. leo"
scientific-name-replaces or short-scientific-name-replaces Pattern replacements to apply for all full (or short) scientific names, after the name has been processed by pattern-repl pair described above.

The value for each of two messages is not a string but a table. The table contains multiple pattern-repl pairs which will be applied to scientific names.

Note: Lua's table object doesn't sort, so the replacement sequence CAN NOT be guaranteed. DON'T DEPEND ON THE SEQUENCE YOU SAW!

Input Parameters[golygu cod]

The "<latinrank>" below is a latin name, can be an instance of rheng tacson (Q427626), or cytras (Q713623). For example "display[cladus]".

Config Options[golygu cod]

  • config[lang]: content language (default: en).
  • config[count]: maximum count of taxon to be recursively iterated (default: 10).
  • config[references]: a space-separated list of item ids. The references to favor in case of alternative claims. Optional.
  • config[usetaxa]: a space-separated list of item ids. The taxa to favor in case of alternative claims. Optional.
  • config[link]: if the value is "sitelink" it will use local wiki site links instead of wikidata item links.
  • config[dryun]: used for callback method. Displaying a <pre> block contains wikitext instead of expanding and rendering the template. Dryrun can be used to find parameters to be overrided.
Examples[golygu cod]

Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value).

{{#invoke: taxobox
| taxobox
| qid = Q140
| config[usetaxa] = Q27379
|config[count] = 7
}}

Display Options[golygu cod]

display[<latinrank>]: if the value is "n", "no", "false" or "hide", the specified rank (in latin name or QID) will be hide. Otherwise the rank will display.

For example, to hide all clades: Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value).

{{#invoke:taxobox
|taxobox
|qid=Q140
|display[cladus]=hide
}}


Classic Parameters[golygu cod]

If there are more than one clades between two taxon ranks, you can override them by appending [<number>] index to the unranked_<latinrank> parameter.

For example, there are 3 clades between rank genus and rank species. You can override them like this:

{{#invoke:taxobox
|taxobox
|...
|unranked_species[3] = Cladus closest to genus rank
|unranked_species[2] = The middle clade
|unranked_species[1] = Cladus closest to species rank
|...
}}

Output Parameters[golygu cod]

The best way to see all output parameters is to use the config[dryrun] parameter:

{{#invoke:taxobox
|callback
|qid=Q140
|config[dryrun]=yes
}}

The result:

{{Taxobox
|code = 13011
|color = #d3d3a4
|config[dryrun] = yes
|image = 002 The lion king Snyggve in the Serengeti National Park Photo by Giles Laurent.jpg
|iucn_status[id] = 278113
|iucn_status[image] = Status iucn3.1 VU.svg
|iucn_status[label] = Vulnerable
|iucn_status[references] = Q115962546
|name = Lion
|qid = Q140
|range_map = Lion distribution.png
|rank[1][id] = 5868144
|rank[1][is_extinct] = no
|rank[1][is_monotypic] = no
|rank[1][is_subject] = no
|rank[1][latin] = superordo
|rank[1][link] = Q27379
|rank[1][raw_scientific] = Laurasiatheria
|rank[1][scientific] = Laurasiatheria
|rank[1][taxon] = [[Q27379|Laurasiatheria]]
|rank[2][id] = 713623
|rank[2][is_extinct] = no
|rank[2][is_monotypic] = no
|rank[2][is_subject] = no
|rank[2][latin] = cladus
|rank[2][link] = Q7439311
|rank[2][raw_scientific] = Scrotifera
|rank[2][scientific] = Scrotifera
|rank[2][taxon] = [[Q7439311|Scrotifera]]
|rank[3][id] = 713623
|rank[3][is_extinct] = no
|rank[3][is_monotypic] = no
|rank[3][is_subject] = no
|rank[3][latin] = cladus
|rank[3][link] = Q5444079
|rank[3][raw_scientific] = Fereuungulata
|rank[3][scientific] = Fereuungulata
|rank[3][taxon] = [[Q5444079|Fereuungulata]]
|rank[4][id] = 6462265
|rank[4][is_extinct] = no
|rank[4][is_monotypic] = no
|rank[4][is_subject] = no
|rank[4][latin] = grandordo
|rank[4][link] = Q20868
|rank[4][raw_scientific] = Ferae
|rank[4][scientific] = Ferae
|rank[4][taxon] = [[Q20868|Ferae]]
|rank[5][id] = 36602
|rank[5][is_extinct] = no
|rank[5][is_monotypic] = no
|rank[5][is_subject] = no
|rank[5][latin] = ordo
|rank[5][link] = Q25306
|rank[5][raw_scientific] = Carnivora
|rank[5][scientific] = Carnivora
|rank[5][taxon] = [[Q25306|Carnivora]]
|rank[6][id] = 5867959
|rank[6][is_extinct] = no
|rank[6][is_monotypic] = no
|rank[6][is_subject] = no
|rank[6][latin] = subordo
|rank[6][link] = Q27070
|rank[6][raw_scientific] = Feliformia
|rank[6][scientific] = Feliformia
|rank[6][taxon] = [[Q27070|Feliformia]]
|rank[7][id] = 35409
|rank[7][is_extinct] = no
|rank[7][is_monotypic] = no
|rank[7][is_subject] = no
|rank[7][latin] = familia
|rank[7][link] = Q25265
|rank[7][raw_scientific] = Felidae
|rank[7][scientific] = Felidae
|rank[7][taxon] = [[Q25265|Felidae]]
|rank[8][id] = 164280
|rank[8][is_extinct] = no
|rank[8][is_monotypic] = no
|rank[8][is_subject] = no
|rank[8][latin] = subfamilia
|rank[8][link] = Q230177
|rank[8][raw_scientific] = Pantherinae
|rank[8][scientific] = Pantherinae
|rank[8][taxon] = [[Q230177|Pantherinae]]
|rank[9][id] = 34740
|rank[9][is_extinct] = no
|rank[9][is_monotypic] = no
|rank[9][is_subject] = no
|rank[9][latin] = genus
|rank[9][link] = Q127960
|rank[9][raw_scientific] = Panthera
|rank[9][scientific] = <i>Panthera</i>
|rank[9][taxon] = [[Q127960|<i>Panthera</i>]]
|rank[9][vernacular] = Big cats
|rank[10][authority] = [[Q1043|Linnaeus]], 1758
|rank[10][id] = 7432
|rank[10][is_extinct] = no
|rank[10][is_monotypic] = no
|rank[10][is_subject] = yes
|rank[10][latin] = species
|rank[10][link] = Q140
|rank[10][raw_scientific] = Panthera leo
|rank[10][references] = Q1538807
|rank[10][scientific] = <i>Panthera leo</i>
|rank[10][taxon] = <b><i>P. leo</i></b>
|rank[10][vernacular] = Lion
|rank[references] = Q30135917 Q1538807 Q17073815 Q22814967 Q30136284 Q22826076 Q30136117 Q30135809 Q19302303 Q33883775 Q50754531 Q28191107 Q796451 Q82575
|rank[size] = 10
}}

All output parameters can be overrided by specifying same name input parameters. For example this will replace genws (Q34740) to "Foo" and Isdeulu (Q164280) "Bar":

Lua error in Modiwl:Cite at line 166: attempt to concatenate local 'journaltitle' (a nil value).


{{#invoke:taxobox
|taxobox
|qid=Q140
|subfamilia=<strong style="color: green">Bar</strong>
|rank[9][taxon]=<strong style="color: red">Foo</strong>
}}

Supported properties[golygu cod]

The taxobox currently supports:

  • for taxon images and red list status
  • : for tacson un eitem (Q310890) and (Q14594740)
  • also mark the taxon extinct if the value is difodwyd (Q237350)
  • for references
  • also mark the taxon extinct if present
  • mark the taxon extinct if present
  • if ICNafp applies, otherwise:
  • , otherwise the last name of English language (en) is shown.
  • for authority string format and color.
  • , common name of a language to override the item label of the language

Wikipedia use[golygu cod]

This module is designed to be a replacement for Wikipedia taxoboxes. However, it is still unstable and need plenty extra template works to allow a classic Nodyn:Blwch tacson (Q52496) to accept the new callback parameters. Suggestion and bug reports are welcome at Module talk:Taxobox or Wikidata talk:WikiProject_Taxonomy.

References[golygu cod]

  1. Integrated Taxonomic Information System
  2. Martin Schorr, Dennis R. Paulson, Klaas-Douwe B. Dijkstra, Cyrille Deliry and Federico Lozano, World Odonata List, University of Alabama
  3. National Center for Biotechnology Information (ed.), Q13711410 (no title property provided!)
  4. Fossilworks
  5. Klaas-Douwe B. Dijkstra, Günter Bechly, Seth M. Bybee, Rory A. Dow, Henri J. Dumont, Günther Fleck, Rosser W. Garrison, Matti Hämäläinen, Vincent J. Kalkman, Haruki Karube, Michael L. May, Albert G. Orr, Dennis R. Paulson, Andrew C. Rehn, Günther Theischinger, John W. H. Trueman, Jan van Tol, Natalia von Ellenrieder and Jessica Ware, "The classification and diversity of dragonflies and damselflies (Odonata)", [[Q18642812|Zootaxa]], vol. 3703, 1, , doi: 10.11646/ZOOTAXA.3703.1.9

-- vim: set noexpandtab ft=lua ts=4 sw=4:

local config = {}  -- TODO find out what this is supposed to be and fix it!
local P_AUDIO = '?'	-- TODO find out what this is supposed to be and fix it!

local ENABLE_DEBUG = true

local Cite = require('Modiwl:Cite')
local fb = require('Modiwl:Fallback')

local p = {}	-- module exports
local L = {}	-- alias to local functions
				-- (so it can be iterated by p in debug mode)
local _linkconfig	-- use links from content language Wikipedia
					-- or from Wikidata, default to Wikidata
local _contentlang
local usereferences -- array of references to be prefered in the given order
local usetaxa -- array of taxa to be preferred in the given order
local hideranks -- array of ranks to be show or hide from display
local code = false
local visited = {}
-- background colors for each code
local colors = {
	[false] = '#d3d3d3',
	[13011] = '#d3d3a4',	-- ICZN
	[693148] = '#9bcd9b',	-- ICNafp
	[764] = '#a4d3d3',		-- ICNCP
	[743780] = '#d3a4d3',	-- BC/ICNP
	[14920640] = '#2f6fab',	-- ICVCN
}

local i18nmessages = require("Modiwl:I18n/taxobox")

-- readable taxon properties
local P_IMAGE = "P18"
local P_INSTANCE_OF = "P31"
local P_TAXON_RANK = "P105"
local P_IUCN_STATUS = "P141"
local P_TAXON_PARENT = "P171"
local P_SPREAD_MAP = "P181"
local P_TAXON_NAME = "P225"
local P_STATED_IN = "P248"
local P_AUTHOR = "P405"
local P_AUTHOR_ABBR_IPNI = "P428"
local P_ERA_START = "P523"
local P_ERA_END = "P524"
local P_BASINYM= "P566"
local P_TAXON_YEAR = "P574"
local P_END_TIME = "P582"
local P_EX_AUTHOR = "P697"
local P_AUTHOR_ABBR_ZOOLOGY = "P835"
local P_NOMENCLATURE_CODE = "P944"
local P_COMMON_NAME = "P1843"

-- readable item
local CLADE = 713623
local GENUS = 34740
local SUBGENUS = 3238261
local RED_DATA_LIST = 32059
local MONOTYPIC_TAXON = 310890
local GEOLOGICAL_ERA = 630830
local SYSTEMATICS = 3516404
local RECOMBINATION = 14594740
local EXTINCT = 237350


local function capitalize(text)
	return mw.ustring.gsub(text, "^%l", mw.ustring.upper)
end


local function mergeTable(a, b)
	for _, value in ipairs(b) do
		a[#a + 1] = value
	end
	return a
end
L.mergeTable = mergeTable


-- credit to http://lua-users.org/wiki/StringInterpolation
local function namedStringFormat(str, vars)
	-- Allow replace_vars{str, vars} syntax as well as
	-- replace_vars(str, {vars})
	if not vars then
		vars = str
		str = vars[1]
	end
	return (string.gsub(str, "({([^}]+)})",
		function(whole,i)
			return vars[i] or whole
		end))
end
L.namedStringFormat = namedStringFormat


local function setLang(contentlang)
	_contentlang = contentlang or "en"
end
L.setLang = setLang


local function getLang()
	return _contentlang
end
L.getLang = getLang


local function i18n(str)
	local message = i18nmessages[str]
	if type(message) == 'string' then
		return message
	end
	return fb._langSwitch(message, getLang())
end
L.i18n = i18n


-- parse item-ids like argument (like config[references]) which is a space
-- separated list of item numbers like "Q1 Q2 Q3"
local function parseItemIds(itemids)
	local items = {}
	local priority = 0
	if itemids then
		for word in string.gmatch(itemids, "%w+") do
			priority = priority + 1
			local item = "Q" .. tonumber(string.sub(word, 2))
			items[item] = priority
		end
	end
	items.size = priority
	return items
end
L.parseItemIds = parseItemIds


-- parse config arguments passed by #invode:taxobox. below are all we
-- support currently:
-- - config[lang]: set content language (default: en)
-- - config[count]: maximum count of taxon to be recursively iterated
-- - config[references]: references to be preffered in the given order
-- - config[dryrun]: generate <pre> block instead of expanding template
-- - config[link]: local or wikidata
local function parseConfig(args)
	setLang(args["config[lang]"])  -- TODO what?
	local count = tonumber(args["config[count]"]) or 10
	if count > 25 then
		-- roughly about 100 expensive parser function calls
		error(i18n("taxon-count-too-high"))
	end
	usereferences = parseItemIds(args["config[references]"])
	usetaxa = parseItemIds(args["config[usetaxa]"])

	hideranks = {}
	local displaypattern = "^display%[([^]]+)%]$"
	local qidpattern = "^Q?(%d+)$"
	for k, v in pairs(args) do
		v = mw.ustring.lower(v)
		if string.match(k, displaypattern) then
			k = string.gsub(k, displaypattern, "%1")
			if string.match(k, qidpattern) then
				k = string.gsub(k, qidpattern, "%1")
				k = tonumber(k)
			end
			-- TODO: i18n?
			if ({n=true, no=true, ["false"]=true, hide=true})[v] then
				hideranks[k] = true
			end
		end
	end

	_linkconfig = "wikidata"
	if args["config[link]"] and
		mw.ustring.lower(args["config[link]"]) == "sitelink" then
		_linkconfig = "sitelink"
	end

	return {
		["count"] = count,
		-- ["lang"] = lang,  -- this fails with "Tried to read nil global lang" from Module:No_globals required by Module:Wikidata
		["lang"] = config['lang'],  -- TODO this is a quick guess at what might have been intended
		["dryrun"] = args["config[dryrun]"],
		["link"] = _linkconfig
	}
end


-- check if International Code of Nomenclature for algae, fungi, and plants (ICNafp) applies
local function icnafpApplies()
	return code == 693148
end
L.icnafpApplies = icnafpApplies


-- the label of the item if present in the specified language or 'no label'
-- TODO: merge with getLabel
local function getItemLabel(item, lang)
	local label = i18n('no-label')
	if item then
		label = item:getLabel(lang or getLang()) or label
	end
	return label
end
L.getItemLabel = getItemLabel


local function getLabel(id)
	if type(id) == "number" then
		id = "Q" .. id
	end
	return getItemLabel(mw.wikibase.getEntity(id))
end
L.getLabel = getLabel


local function getLink(id, label, format, named)
	if type(id) == "number" then
		id = "Q" .. id
	end
	local link = id
	if _linkconfig == "sitelink" then
		link = mw.wikibase.sitelink(id)
	end
	label = label or getItemLabel(mw.wikibase.getEntity(id))
	format = format or "[[%s|%s]]"
	if named then
		return namedStringFormat{format, link=link, label=label}
	else
		return string.format(format, link, label)
	end
end
L.getLink = getLink


-- Collect all claims of the given property of the item
-- Returns all claims and their references in tables combined by the claims' rank.
-- result.preferred[target id of claim] = [target id of P248 reference]
-- use only if the data type of the property is item
local function targetIds(item, property)
	local claims = {preferred = {}, normal = {}, deprecated = {}}
	if item and item.claims and item.claims[property] then
		for _,claim in pairs(item.claims[property]) do
				if claim.mainsnak.datavalue and claim.mainsnak.datavalue.value then
					local valueid = claim.mainsnak.datavalue.value['numeric-id']

					local refids = {}
					if claim.references then
						for _,ref in pairs(claim.references) do
							for prop, refclaim in pairs(ref.snaks[P_STATED_IN] or {}) do
								refids[tostring('Q' .. refclaim.datavalue.value['numeric-id'])] = true
							end
						end
					end
					claims[claim.rank][valueid] = refids
				else
					claims[claim.rank]['novalue'] = true -- snaktype not value
				end
		end
	end
	return claims
end
L.targetIds = targetIds


-- Gives the first highest ranked claim and its references.
-- use only if the data type of the property is item
local function targetId(item, property)
	local claims = targetIds(item, property)
	if next(claims.preferred) then
		return claims.preferred
	end
	if next(claims.normal) then
		return claims.normal
	end
	return claims.deprecated
end
L.targetId = targetId


-- Collect all claims of the given property of the item
-- Returns a triple of claims, their qualifiers, and their references in tables combined by the claims' rank.
-- Use only if the data type of the property is string
local function targetStrs(item, property)
	local choosenclaim = {preferred = {}, normal = {}, deprecated = {}}
	local choosenqualifiers = {preferred = {}, normal = {}, deprecated = {}}
	local choosenreferences = {preferred = {}, normal = {}, deprecated = {}}
	if item and item.claims and item.claims[property] then
		for _,claim in pairs(item.claims[property]) do
			if claim.mainsnak and claim.mainsnak.datavalue then
				local index = #choosenclaim[claim.rank] + 1
				mw.log(index, claim.mainsnak.datavalue.value)

				local refids = {}
				if claim.references then
					for _,ref in pairs(claim.references) do
						for prop, refclaim in pairs(ref.snaks[P_STATED_IN] or {}) do
							refids['Q' .. refclaim.datavalue.value['numeric-id']] = true
							mw.log('string ref', refclaim.datavalue.value['numeric-id'])
						end
					end
				end
				if claim.mainsnak.datatype == 'monolingualtext' then
					choosenclaim[claim.rank][index] = tostring(claim.mainsnak.datavalue.value)
				else
					choosenclaim[claim.rank][index] = tostring(claim.mainsnak.datavalue.value)
				end
				choosenqualifiers[claim.rank][index] = claim.qualifiers
				choosenreferences[claim.rank][index] = refids
			end
		end
	end

	return choosenclaim, choosenqualifiers, choosenreferences
end
L.targetStrs = targetStrs


-- Gives the first highest ranked claim and its qualifiers and references.
-- Use only if the data type of the property is string
local function targetStr(item, property)
	local choosenclaim, choosenqualifiers, choosenreferences = targetStrs(item, property)

	for _, priority in pairs({"preferred", "normal", "deprecated"}) do
		local index = next(choosenclaim[priority])
		if index then
			return choosenclaim[priority][index],
				choosenqualifiers[priority][index],
				choosenreferences[priority][index]
		end
	end
	return
end
L.targetStr = targetStr


-- helper function to merge all claims, regardless of rank
local function mergeClaims(claims, qualifiers, references)
	local c = {}
	local q = {}
	local r = {}
	for _, priority in pairs({"preferred", "normal", "deprecated"}) do
		mergeTable(c, claims[priority] or {})
		mergeTable(q, qualifiers[priority] or {})
		mergeTable(r, references[priority] or {})
	end
	return c, q, r
end
L.mergeClaims = mergeClaims


-- Use only if the data type of the property is time
local function targetTime(item, property)
	if item and item.claims and item.claims[property] then
		for _,claim in pairs(item.claims[property]) do
			if claim.mainsnak and claim.mainsnak.datavalue then
				return claim.mainsnak.datavalue.value.time
			end
		end
	end
end
L.targetTime = targetTime


-- same as targetId but for qualifiers
-- TODO merge
local function qualifierTargetId(qualifiers, property)
	local claims = {}
	if qualifiers and qualifiers[property] then
		for _,claim in pairs(qualifiers[property]) do
			local valueid = claim.datavalue.value['numeric-id']
			table.insert(claims, valueid)
		end
	end
	return claims
end
L.qualifierTargetId = qualifierTargetId


-- same as targetTime but for qualifiers
-- TODO merge
local function qualifierTargetTime(qualifiers, property)
	local claims = {}
	if qualifiers and qualifiers[property] then
		for _,claim in pairs(qualifiers[property]) do
			if claim.datavalue then
				mw.log('time qualifier', property, claim.datavalue.value.time)
				return claim.datavalue.value.time
			end
		end
	end
end
L.qualifierTargetTime = qualifierTargetTime


-- takes a list of item ids (the values of the given table) and creates wikilinks based on their labels
local function createLinks(list, authorAbbreviation)
	local authors = {}
	for _,authorid in pairs(list) do
		if authorid then
			local author = mw.wikibase.getEntity('Q' .. authorid)
			if author then
				local label
				if authorAbbreviation then
					if icnafpApplies() then
						-- get author abbrieviation per IPNI set
						if targetStr(author, P_AUTHOR_ABBR_IPNI) then
							label = targetStr(author, P_AUTHOR_ABBR_IPNI)
						end
					elseif targetStr(author, P_AUTHOR_ABBR_ZOOLOGY) then
						-- get zoologist author citation set
						label = targetStr(author, P_AUTHOR_ABBR_ZOOLOGY)
					end
					if not label then
						-- use the "last" name if no abbreviation found
						-- also don't use the translated name
						label = getItemLabel(author, "en")
						if label ~= i18n('no-label') then
							local _
							_, _, label = mw.ustring.find(label, "(%w+)$")
						end
					end
				end
				table.insert(authors, getLink(authorid, label))
			end
		end
	end
	return authors
end
L.createLinks = createLinks


local function vernacularName(item)
	local vernacularname
	-- select vernacular name for current language
	if item.claims and item.claims[P_COMMON_NAME] then
		for _, claim in pairs(item.claims[P_COMMON_NAME]) do
			if claim.mainsnak and claim.mainsnak.datavalue and
				claim.mainsnak.datavalue.type == "monolingualtext" and
				claim.mainsnak.datavalue.value.language == getLang() then
				vernacularname = claim.mainsnak.datavalue.value.text
				break
			end
		end
		if vernacularname == '' then
			vernacularname = nil
		end
	end

	if not vernacularname then
		-- test if item label is not one of the scientific names
		vernacularname = getItemLabel(item)
		local scnames = mergeClaims(targetStrs(item, P_TAXON_NAME))
		for _, n in pairs(scnames) do
			if vernacularname == n then
				return
			end
		end
	end
	if vernacularname == i18n("no-label") then
		return
	end
	return capitalize(vernacularname)
end
L.vernacularName = vernacularName


local function authorString(item, namequalifiers, pid)
	pid = pid or P_AUTHOR -- set default property
	local concatstr = ', '
	local authorids = qualifierTargetId(namequalifiers, pid) -- get qualifiers
	if not next(authorids) then -- no qualifiers found, check properties
		local authorset = targetId(item, pid)
		local authors = {}
		if authorset then -- create list from set
			authorids = {}
			for author,_ in pairs(authorset) do
				table.insert(authorids, author)
			end
		end
	end
	local authors = createLinks(authorids, true)
	if next(authors) then
		local last = table.remove(authors)
		local rest = ''
		if #authors > 0 then
			rest = table.concat(authors, concatstr) .. ' & '
		end
		return rest .. last
	end
end
L.authorString = authorString


-- create the taxon authors string, including year, ex authors and authors of the basionym
local function createAllAuthorsStr(item, namequalifiers, year)
	local authors = authorString(item, namequalifiers)
	local authorsstr = ''
	if authors or not year == '????' then
		if icnafpApplies() then
			-- check for basionym
			local basionymids = targetId(item, P_BASINYM)
			local basionymstr = ''
			if next(basionymids) then
				local basionym = mw.wikibase.getEntity('Q' .. next(basionymids))
				local _,basionymnamequalifiers = targetStr(basionym, P_TAXON_NAME)
				basionymstr = createAllAuthorsStr(basionym, basionymnamequalifiers)
				if basionymstr ~= '' then
					basionymstr = '(' .. basionymstr .. ') '
				else
					-- indicate missing basionym author
					basionymstr = '(????) '
				end
			end

			-- check ex-authors
			local exauthors = authorString(nil, namequalifiers, P_EX_AUTHOR)
			local exauthorsstr = ''
			mw.log(exauthors)
			if exauthors then
				exauthorsstr = exauthors .. ' ex '
			end
			authorsstr = basionymstr .. exauthorsstr .. authors
			if year then
				authorsstr = authorsstr .. ' (' .. year .. ')'
			end
		else
			authorsstr = authors .. ', ' .. year

			-- parentheses needed if instance of recombination
			local recombination = false
			for _,tid in pairs(qualifierTargetId(namequalifiers, P_INSTANCE_OF)) do
				if tid == RECOMBINATION then
					recombination = true
				end
			end

			if recombination then
				authorsstr = '(' .. authorsstr .. ')'
			end
		end
	end
	return authorsstr
end
L.createAllAuthorsStr = createAllAuthorsStr

-- show the stratigraphic range in which an extinct fossil existed
local function fossilParams(item, params)
	local era1, era1references = next(targetId(item, P_ERA_START))
	local era2, era2references = next(targetId(item, P_ERA_END))
	if era1 and era2 and not (era1 == 'novalue' or era2 == 'novalue') then
		local era1item = mw.wikibase.getEntity("Q" .. era1)
		if era1item then
			params["era[1][id]"] = era1
			params["era[1][label]"] = getItemLabel(era1item)
			if not (era1 == era2) then
				local era2item = mw.wikibase.getEntity("Q" .. era2)
				if era2item then
					params["era[2][id]"] = era2
					params["era[2][label]"] = getItemLabel(era2item)
				end
			end

			-- merge references from era2 to era1, only show once
			for a, b in pairs(era2references) do
				era1references[a] = b
			end

			-- TODO: return data structure instead of pure str here
			params["era[references]"] = era1references
		end
	end
end
L.fossilParams = fossilParams


-- returns html for the given refids set
-- parameters:
-- refids: list of integer ID to create a list of <ref>-references
local function references(refids)
	local frame = mw.getCurrentFrame()
	local refstr = ''
	if refids then
		for id,_ in pairs(refids) do
			local ref = Cite.citeitem(id, getLang()) or 'Error during creation of citation. Please report [[' .. id .. ']] at [[Sgwrs modiwl:Cite]]'
			mw.log('refstr for ', id, ref)
			refstr = refstr .. frame:extensionTag('ref', ref, {name=id})
		end
	end
	return refstr
end
L.references = references


local function i18nByLatin(ranklatin, str, default)
	local suc, format = pcall(i18n, str .. "-" .. ranklatin)
	if not suc then
		format = default
	end
	return format
end
L.i18nByLatin = i18nByLatin


local function formatScientificName(ranklatin, scientific, short)
	local pf = "scientific-name"
	if short then
		pf = "short-" .. pf
	end
	local scipattern = i18nByLatin(
		ranklatin, pf .. "-pattern", i18n(pf .. "-pattern"))
	local scirepl = i18nByLatin(
		ranklatin, pf .. "-repl", i18n(pf .. "-repl"))
	scientific = string.gsub(scientific, scipattern, scirepl)
	for scipattern, scirepl in pairs(
		i18nByLatin(ranklatin, pf .. "-replaces", i18n(pf .. "-replaces"))) do
		scientific = string.gsub(scientific, scipattern, scirepl)
	end
	return scientific
end
L.formatScientificName = formatScientificName


local function renderTableHead(text, color)
	local bgcolor = ""
	if color then
		bgcolor = bgcolor .. " background-color: " .. color .. ";"
	end
	return mw.text.tag('tr', {}, mw.text.tag('th', {
		colspan='2',
		style="text-align: center;" .. bgcolor
	}, text))
end
L.renderTableHead = renderTableHead


local function renderTableRow(text, extra_css)
	local css = "text-align: center;"
	if extra_css then
		css = css .. " " .. extra_css
	end
	return mw.text.tag('tr', {}, mw.text.tag('td',
		{colspan='2', style=css}, text))
end
L.renderTableRow = renderTableRow


local function renderFossilEra(params)
	local eralink = {}
	local refstr = references(params["era[references]"])
	for i = 1, 2 do
		local eraid = params[string.format("era[%d][id]", i)]
		if eraid then
			eralink[#eralink + 1] = getLink(eraid)
		end
	end
	return renderTableHead(getLink(GEOLOGICAL_ERA) .. refstr, params.color) ..
		renderTableRow(table.concat(eralink, '&mdash;'))
end
L.renderFossilEra = renderFossilEra


local function renderIUCNStatus(params)
	local r = {}
	local refstr = references(params["iucn_status[references]"])
	r[#r + 1] = renderTableHead(
		getLink(RED_DATA_LIST, getLabel(P_IUCN_STATUS)) .. refstr, params.color)
	-- TODO: support SVG systemLanguage here
	r[#r + 1] = renderTableRow(
		"[[File:" .. params["iucn_status[image]"] ..
		"|220px|" .. params["iucn_status[label]"] .. "]]")
	return table.concat(r)
end
L.renderIUCNStatus = renderIUCNStatus


local function formatTaxon(
	latin, qid, scientific, vernacular, is_subject, is_extinct)
	local nameformat
	scientific = scientific or i18n("no-scientific-name")
	local scientificshort = scientific
	if latin then
		scientificshort = formatScientificName(latin, scientific, true)
		scientific = formatScientificName(latin, scientific)
	end
	local nf = "item-format-parent"
	if is_subject then
		nf = "item-format-current"
	end
	if vernacular then
		nameformat = i18n(nf .. "-with-vernacular-name")
	else
		nameformat = i18n(nf .. "-without-vernacular-name")
	end
	local link = qid
	if _linkconfig == "sitelink" then
		link = mw.wikibase.sitelink(qid)
	end
	if is_extinct then
		nameformat = i18n("extinct-mark") .. nameformat
	end
	return namedStringFormat{
		nameformat, link=link, vernacular=vernacular,
		scientific=scientific, scientificshort=scientificshort},
		scientific
end
L.formatTaxon = formatTaxon


local function renderRank(i, params)
	local row
	local nameformat
	local detailrows = {}
	local pf = string.format("rank[%d]", i)
	local ranklink = i18n("unknown-rank")
	local rankid = params[pf .. "[id]"]
	local ranklatin = params[pf .. "[latin]"]
	local is_subject = params[pf .. "[is_subject]"]
	local scientific = params[pf .. "[scientific]"]
	local formatted = params[pf .. "[taxon]"]
	if rankid then
		local linkformat = i18nByLatin(
			ranklatin, "rank-format", i18n("rank-format"))
		ranklink = getLink(rankid, nil, linkformat, true)
	end
	row = mw.text.tag(
		'tr', {},
		mw.text.tag('td', {}, ranklink) ..
		mw.text.tag('td', {}, formatted))
	if is_subject then
		local refstr = references(params[pf .. "[references]"])
		local authority = params[pf .. "[authority]"]
		detailrows = {
			renderTableHead(
				string.format(i18n("scientific-name-of-taxon"), getLabel(rankid))
				.. refstr, params.color
			),
			renderTableRow(scientific),
			renderTableRow(authority, "font-variant: small-caps;")
		}
	end
	return row, detailrows
end
L.renderRank = renderRank


-- in case of more than one parent taxa: choose target according to the
-- references selected by usereferences
local function chooseParent(item)
	local cand
	local nextparent = {}
	for id,refs in pairs(targetId(item, P_TAXON_PARENT)) do

		-- some taxon, like Q2382443, the parent taxon is null
		local novalue = id == "novalue"

		-- try to find match from usetaxa
		if not novalue and usetaxa["Q" .. id] then
			table.insert(nextparent, {usetaxa["Q" .. id], id, refs})
		end

		-- or according to usereferences
		if refs and type(refs) ~= "boolean"  then
			for r, i in pairs(usereferences) do
				if refs[r] then
					table.insert(nextparent, {i + usetaxa.size, id, refs})
				end
			end
		end

		if not novalue and not cand then -- if no item had references yet
			cand = {nil, id, refs} -- use this
		end
	end
	-- nextparent is not sorted, so sort it
	table.sort(nextparent, function(a, b)
		return a[1] < b[1]
	end)

	if next(nextparent) then
		local _
		_, cand = next(nextparent)
	end

	if cand and cand[1] == nil and cand[3] then
		for targetid, _ in pairs(cand[3]) do
			usereferences.size = usereferences.size + 1
			usereferences[targetid] = usereferences.size
		end
	end
	if cand then
		return cand[2], cand[3] or {}
	else
		return nil, {}
	end
end
L.chooseParent = chooseParent


-- Find out if this taxon is extinct already
local function isExtinct(item)

	-- check IUCN status
	local statusid, _ = next(targetId(item, P_IUCN_STATUS))
	if statusid == EXTINCT then
		return true
	end

	-- check temporal range end
	local eraend, _ = next(targetId(item, P_ERA_END))
	if eraend then
		return true
	end

	-- check end time
	local endtime = targetTime(item, P_END_TIME)
	if endtime then
		return true
	end

	return false
end
L.isExtinct = isExtinct


-- Find out if the item is a monotypic taxon
local function isMonotypic(item)
	return next(targetId(item, P_INSTANCE_OF)) == MONOTYPIC_TAXON
end
L.isMonotypic = isMonotypic


local function taxonParams(qid, item, params, fetch_detail, is_parent_extinct)
	local rankid
	local ranklatin
	local is_subject
	local level = params["rank[size]"] + 1
	local pf = string.format("rank[%d]", level)
	local name, namequalifiers, namereferences = targetStr(item, P_TAXON_NAME)
	local vernacular = vernacularName(item)
	local is_extinct = is_parent_extinct or isExtinct(item)

	if rankid == SUBGENUS and
		string.match(name, "^%w+$") and
		params[string.format("rank[%d][id]", level - 1)] == GENUS then
		-- follow ICZN to prepend genus name in front of subgenus name
		name = string.format("%s (%s)",
			params[string.format("rank[%d][raw_scientific]", level - 1)],
			mw.ustring.lower(name))
	end

	rankid = next(targetId(item, P_TAXON_RANK))
	if rankid == "novalue" then
		rankid = CLADE
	end

	if rankid then
		local rankitem = mw.wikibase.getEntity('Q' .. rankid)
		ranklatin = rankitem:getLabel('la')
	end
	if ranklatin then
		ranklatin = mw.ustring.lower(ranklatin)
	end

	if (hideranks[rankid] or hideranks[ranklatin]) and not usetaxa[qid] then
		-- interrupt since this rank has been hided from display
		return params, is_extinct
	end

	name = capitalize(name)
	params["rank[size]"] = level
	params[pf .. "[id]"] = rankid
	params[pf .. "[link]"] = qid
	params[pf .. "[is_monotypic]"] = isMonotypic(item)
	params[pf .. "[vernacular]"] = vernacular
	params[pf .. "[raw_scientific]"] = name
	params[pf .. "[latin]"] = ranklatin
	params[pf .. "[is_extinct]"] = is_extinct

	if fetch_detail then
		-- get detail
		is_subject = true
		params[pf .. "[references]"] = namereferences
		local year = qualifierTargetTime(namequalifiers, P_TAXON_YEAR) or
			targetTime(item, P_TAXON_YEAR)
		if year then
			-- access year in time representation "+1758-00-00T00:00:00Z"
			year = string.sub(year, 2, 5)
		else
			year = '????'
		end
		local authorsstr = createAllAuthorsStr(item, namequalifiers, year)
		params[pf .. "[authority]"] = authorsstr
	else
		is_subject = false
	end

	local formatted, sciname = formatTaxon(
		ranklatin, qid, name, vernacular, is_subject, is_extinct)

	params[pf .. "[scientific]"] = sciname
	params[pf .. "[is_subject]"] = is_subject
	params[pf .. "[taxon]"] = formatted

	return params, is_extinct
end
L.taxonParams = taxonParams


-- performs the loop up the hierarchy using P_TAXON_PARENT
local function iterateRanks(
		qid, count, fetch_detail, child_detailed, child_extinct, params)
	local inneritem
	local params = params or {["rank[size]"] = 0}
	local item = mw.wikibase.getEntity(qid)

	local name = targetStr(item, P_TAXON_NAME)
	if name == 'nil' then
		return params, {}, item
	end

	local nextid, references = chooseParent(item)
	mw.log('nextid', nextid)
	if not code then
		local codeid = next(targetId(item, P_NOMENCLATURE_CODE))
		if codeid and colors[codeid] then
			code = codeid
		end
	end

	if visited[nextid] then -- loop detection
		return params, {}, item
	elseif nextid then
		visited[nextid] = true
	end

	-- Monotypic taxon can contain extinct taxa,
	-- should not fetch detail in such circumstances
	-- for example: [[Q7105303]]
	local fetch_detail =
		fetch_detail or
		(child_detailed and not child_extinct and isMonotypic(item))
	local is_extinct, is_parent_extinct

	if nextid and (not code or count > 0) then
		local refs, _
		params, refs, _, is_parent_extinct = iterateRanks(
			'Q' .. nextid, count - 1, false,
			fetch_detail, isExtinct(item), params)
		for ref, _ in pairs(refs) do
			references[ref] = true
		end
	end
	if count > 0 then
		params, is_extinct = taxonParams(
			qid, item, params, fetch_detail, is_parent_extinct)
	end
	params["code"] = code
	params["color"] = colors[code]
	return params, references, item, is_extinct
end
L.iterateRanks = iterateRanks

-- use arguments from second table to override the first table
-- support classical {{taxobox}} parameters like "species", "unranked_ordo"
local function overrideParams(params, overrides)
	overrides = overrides or {}

	for key, val in pairs(overrides) do
		params[key] = overrides[key] or params[key]
	end

	-- classical taxonomic rank params
	local unranked = {}
	for i = 1, params["rank[size]"] do
		local pf = string.format("rank[%d]", i)
		local latin = params[pf .. "[latin]"]
		if latin == "clade" then
			unranked[#unranked + 1] = i
		else
			local txarg = pf .. "[taxon]"
			local atarg = pf .. "[authority]"
			params[txarg] = overrides[latin] or params[txarg]
			params[atarg] = overrides[latin .. "_authority"] or params[atarg]
			for j = #unranked, 1, -1 do
				local txarg = string.format("rank[%d][taxon]", unranked[j])
				local atarg = string.format("rank[%d][authority]", unranked[j])
				local argname = string.format("unranked_%s", latin)
				if j == #unranked then
					-- TODO Changed "override" to "overrides" twice, but that just a guess.
					params[txarg] = overrides[argname] or
						overrides[argname .. "1"] or params[txarg]
					params[atarg] = overrides[argname .. "_authority"] or
						overrides[argname .. "1_authority"] or params[atarg]
				else
					params[txarg] = overrides[string.format('%s%d',
						argname, #unranked - j + 1)] or params[txarg]
					params[atarg] = overrides[string.format('%s%d_authority',
						argname, #unranked - j + 1)] or params[atarg]
				end
			end
			unranked = {}
		end
		if latin == "species" then
			local scarg = pf .. "[scientific]" 
			params[scarg] = overrides["binomial"] or params[scarg]
		elseif ({subspecies=true, varietas=true, forma=true})[latin] then
			local scarg = pf .. "[scientific]" 
			params[scarg] = overrides["trinomial"] or params[scarg]
		end
	end

	return params
end
L.overrideParams = overrideParams


-- fetch params should passed to taxobox for the given qid (e.g., qid=Q729412
-- for Heloderma) and count higher levels of the taxon hierarchy.
-- developers: use this method for tests in the debug console, e.g.,
-- p.localFunction("getTaxoboxParams")('Q729412', 5)
local function getTaxoboxParams(qid, count)
	visited = {}
	local params, references, item = iterateRanks(qid, count, true)
	if params["rank[size]"] == 0 then
		return {}
	end

	local scarg = string.format("rank[%d][scientific]", params["rank[size]"])
	local vnarg = string.format("rank[%d][vernacular]", params["rank[size]"])
	params["name"] = params[vnarg] or params[scarg]

	capitalize(getItemLabel(item))

	local image = targetStr(item, P_IMAGE)
	params["image"] = image

	fossilParams(item, params)

	params["rank[references]"] = references

	local map = targetStr(item, P_SPREAD_MAP)
	params["range_map"] = map

	local statusid, statusreferences = next(targetId(item, P_IUCN_STATUS))
	params["iucn_status[id]"] = statusid
	if statusid then
		local status = mw.wikibase.getEntity("Q" .. statusid)
		local img, imgqualifiers, imgreferences = targetStr(status, P_IMAGE)
		if img then
			params["iucn_status[label]"] = getItemLabel(status)
			params["iucn_status[references]"] = statusreferences
			params["iucn_status[image]"] = img
		end
	end

	local audio, audioreferences = targetStr(item, P_AUDIO)
	params["audio"] = audio

	return params
end
L.getTaxoboxParams = getTaxoboxParams


local function callbackTaxobox(template, params, overrides, dryrun)
	local content = {}
	local frame = mw.getCurrentFrame()
	params = overrideParams(params, overrides)

	for key, val in pairs(params) do
		if type(val) == "boolean" then
			val = val and "yes" or "no"
		elseif type(val) == "table" and
			string.match(key, "%[references%]$") then
			local refs = {}
			for r, _ in pairs(val) do
				table.insert(refs, r)
			end
			val = table.concat(refs, " ")
		end
		if dryrun then
			content[#content + 1] = string.format(
				"|%s = %s", key, val)
		else
			params[key] = val
		end
	end

	if dryrun then
		table.sort(content, function(a, b)
			a = string.gsub(a, "%[(%d+)%]", function(i)
				return "[" .. string.rep("0", 3 - #i) .. i .. "]"
			end)
			b = string.gsub(b, "%[(%d+)%]", function(i)
				return "[" .. string.rep("0", 3 - #i) .. i .. "]"
			end)
			return a < b
		end)
		content = "{{" .. template .. "\n" ..
			table.concat(content, "\n") .. "\n}}\n"
		content = frame:callParserFunction("#tag", "nowiki", content)
		return mw.text.tag("pre", {}, content)
	else
		return frame:expandTemplate{title=template, args=params}
	end
end
L.callbackTaxobox = callbackTaxobox


-- creates the taxobox from giving params
local function renderTaxobox(params, overrides)
	local content = {}
	params = overrideParams(params, overrides)

	local color = params.color

	-- title
	content[#content + 1] = renderTableHead(params.name, color)

	-- image
	if params.image then
		content[#content + 1] = renderTableRow(
			"[[File:" .. params.image .. "|220px|.]]")
	end

	-- fossil era
	if params["era[1][id]"] then
		content[#content + 1] = renderFossilEra(params)
	end

	-- systematics
	local refstr = references(params["rank[references]"])
	content[#content + 1] = renderTableHead(
		getLink(SYSTEMATICS) .. refstr, color)

	-- ranks
	if params["rank[size]"] > 0 then
		local taxondetails = {}
		for i = 1, params["rank[size]"] do
			local row, detailrows = renderRank(i, params)
			content[#content + 1] = row
			taxondetails[#taxondetails + 1] = table.concat(detailrows)
		end
		content[#content + 1] = table.concat(taxondetails)
	end

	-- range map
	if params.range_map then
		content[#content + 1] = renderTableHead(i18n('range-map'), color)
		content[#content + 1] = renderTableRow(
			"[[File:" .. params.range_map .. "|220px|.]]")
	end

	-- iucn status
	if params["iucn_status[id]"] then
		content[#content + 1] = renderIUCNStatus(params)
	end

	-- audio
	if params.audio then
		content[#content + 1] = renderTableHead(getLink(P_AUDIO), color)
		content[#content + 1] =
			renderTableRow("[[File:" .. params.audio .. "]]")
	end

	return mw.text.tag('table', {
		style = [[
			width: 200px; border-width: 1px; float: right;
			border-style: solid; background-color: #f9f9f9;
		]]
	}, table.concat(content))
end
L.renderTaxobox = renderTaxobox


if debug then
	function p.debugParams(params)
		mw.log("Start of logging params")
		mw.log(string.rep("=", 20))
		for k, v in pairs(params) do
			mw.log(k, v)
		end
		mw.log("End of logging params")
	end

	function p.localFunction(name)
		return L[name]
	end
end


function p.taxobox(frame)
	local config = parseConfig(frame.args)
	local qid = frame.args.qid
	local params = getTaxoboxParams(qid, config.count)
	return renderTaxobox(params, frame.args)
end


function p.callback(frame)
	local config = parseConfig(frame.args)
	local qid = frame.args.qid
	local template = frame.args.template or "Taxobox"
	local params = getTaxoboxParams(qid, config.count)
	return callbackTaxobox(template, params, frame.args, config.dryrun)
end

return p