Module:Taxonbar
require('strict')
 
 local conf = require( 'Module:Taxonbar/conf' ) --configuration module
 local TaxonItalics = require( 'Module:TaxonItalics' ) --use a function to conditionally italicize taxon names
 
 --[[==========================================================================]]
 --[[                             Local functions                              ]]
 --[[==========================================================================]]
 
 local function isNilOrEmpty( thing )
 	if thing == nil or thing == '' then
 		return true
 	end
 	return nil
 end
 
 local function getIdFromWikidata( item, property )
 	local id = nil
 	if property == 'PWikispecies:$1' then
 		local siteLinks = item.sitelinks
 		if siteLinks then
 			local speciesWiki = item.sitelinks.specieswiki
 			if speciesWiki then
 				id = speciesWiki.title
 			end
 		end
 		return id
 	elseif item.claims[property] == nil then
 		return id
 	end
 	--[[ this code picks up deprecated values on wikidata; better to use getBestStatements
 	for _, statement in pairs( item.claims[property] ) do
 		if statement.mainsnak.datavalue then
 			id = statement.mainsnak.datavalue.value
 			break
 		end
 	end
 	]]
 	local statements = item:getBestStatements(property)[1] 
 	if statements and
 	   statements.mainsnak and
 	   statements.mainsnak.datavalue and
 	   statements.mainsnak.datavalue.value
 	then
 		id = statements.mainsnak.datavalue.value
 	end
 
 	return id
 end
 
 local function getLink( property, db, val )
 	local link, returnVal = '', {}
 	
 	returnVal.isError = false
 	
 	if mw.ustring.find( val, '//' ) then
 		link = val
 	else
 		if type(property) == 'number' and property &gt; 0 then
 			local entityObject = mw.wikibase.getEntity('P'..property)
 			local dataType
 			
 			if entityObject then dataType = entityObject.datatype
 			else returnVal.isError = true end
 			
 			if dataType == 'external-id' then
 				local formatterURL = nil
 				if property == 3746 or --Wildflowers of Israel
 				   property == 3795 or --Flora of Israel Online
 				   property == 5397    --Tierstimmenarchiv
 				then
 					formatterURL = entityObject:getBestStatements('P1630')[2] --use 2nd formatterURL for English version
 				end
 				if formatterURL == nil then formatterURL = entityObject:getBestStatements('P1630')[1] end --default to [1]
 				if formatterURL then
 					if formatterURL.mainsnak.datavalue and formatterURL.mainsnak.datavalue.value then --nil check for ABA
 						link = formatterURL.mainsnak.datavalue.value
 					end
 				end
 				if db == 'bow' then -- for birds of world which uses eBird identifier
 					link = 'https://birdsoftheworld.org/bow/species/$1'
 				end
 			elseif dataType == 'url' then
 				local subjectItem = entityObject:getBestStatements('P1629')[1]
 				if subjectItem then
 					local officialWebsite = mw.wikibase.getEntity(subjectItem.mainsnak.datavalue.value.id):getBestStatements('P856')[1]
 					if officialWebsite then	link = officialWebsite.mainsnak.datavalue.value end
 				end
 			elseif dataType == 'string' then
 				local formatterURL = entityObject:getBestStatements('P1630')[1]
 				if formatterURL then
 					link = formatterURL.mainsnak.datavalue.value
 				else
 					local subjectItem = entityObject:getBestStatements('P1629')[1]
 					if subjectItem then
 						local officialWebsite = mw.wikibase.getEntity(subjectItem.mainsnak.datavalue.value.id):getBestStatements('P856')[1]
 						if officialWebsite then	link = officialWebsite.mainsnak.datavalue.value end
 					end
 				end
 			else
 				returnVal.isError = true
 			end
 		elseif type(property) == 'string' then
 			link = property
 		end
 		
 		--local valurl = val
 		local valurl = mw.uri.encode( val, 'PATH' )
 		valurl = string.gsub (valurl, '%%2F', '/') --escape '/' (e.g. issue with P5354); see wikidata T128078 and https://gerrit.wikimedia.org/r/c/mediawiki/extensions/Wikibase/+/664820/3/lib/includes/PropertyInfoSnakUrlExpander.php
 		
 		if type(property) == 'number' then
 			--doublecheck language for Wildflowers of Israel ID
 			if property == 3746 then link = mw.ustring.gsub(link, '/hebrew/', '/english/') end
 			--format spaces in PfaF binomials, e.g. &quot;Elaeagnus x ebbingei&quot;
 			if property == 4301 then valurl = mw.ustring.gsub(valurl, '%%20', '+') end
 		end
 		valurl = mw.ustring.gsub(valurl,'%%','%%%%')
 		link = mw.ustring.gsub(link, '$1', valurl)
 	end
 	
 	link = mw.ustring.gsub(link, '^[Hh][Tt][Tt][Pp]([Ss]?)&amp;#58;//', 'http%1://') --fix wikidata URL
 	val = mw.ustring.match(val, '([^=/]*)/?$') --get display name from end of URL
 	if mw.ustring.find( link, '//' ) then
 		returnVal.text = '['..link..' '..mw.text.encode(mw.uri.decode(val, 'PATH'),'%[%]')..']'
 	elseif link == '' then
 		returnVal.text = val
 	else
 		returnVal.text = '&lt;span class=&quot;external&quot;&gt;[['..link..'|'..val..']]&lt;/span&gt;'
 	end
 	return returnVal
 end
 
 local function createRow( id, label, rawValue, link, withUid )
 	if link then
 		local outStr = '*&lt;span style=&quot;white-space:nowrap;&quot;&gt;'..label..' &lt;span'
 		if withUid then outStr = outStr..' class=&quot;uid&quot;' end
 		return outStr..'&gt;'..link..'&lt;/span&gt;&lt;/span&gt;\n'
 	else
 		return '* '..mw.text.tag('span', {class='error'}, 'The identifier '..id..' '..rawValue..' is not valid.')..'\n'
 	end
 end
 
 local function copyTable(inTable)
 	if type(inTable) ~= 'table' then return inTable end
 	local outTable = setmetatable({}, getmetatable(inTable))
 	for key, value in pairs (inTable) do outTable[copyTable(key)] = copyTable(value) end
 	return outTable
 end
 
 local p = {}
 
 --[[==========================================================================]]
 --[[                                   Main                                   ]]
 --[[==========================================================================]]
 
 function p.authorityControlTaxon( frame )
 	local resolveEntity = require( 'Module:ResolveEntityId' )
 	local parentArgs = copyTable(frame:getParent().args)
 	local currentTitle = mw.title.getCurrentTitle()
 	local currentEntityId = mw.wikibase.getEntityIdForCurrentPage()
 	
 	local stringArgs = false
 	local fromTitleCount, firstRow, rowCount = 1, 0, 0
 	local outString, errors = '', ''
 	local iFroms = 0 --integer size of tFroms, b/c Lua
 	local tFroms = {} --non-sequential table of unique froms
 	local tCats = {
 		'[[Category:Taxonbars without from parameter]]',
 		'[[Category:Taxonbars desynced from Wikidata]]',
 		'', -- [3] placeholder for [[Category:Taxonbar pages requiring a Wikidata item]]
 		'', -- [4] placeholder for [[Category:Taxonbars on possible non-taxon pages]]
 		'', -- [5] placeholder for [[Category:Taxonbars with invalid from parameters]]
 		'', -- [6] placeholder for [[Category:Taxonbars with duplicate from parameters]]
 		'', -- [7] placeholder for [[Category:Taxonbars with from2 matching article title]]
 		'', -- [8] placeholder for [[Category:Taxonbars with from2 matching article title &amp; QID]]
 		'', -- [9] placeholder for [[Category:Taxonbars with manual taxon IDs]] --renamed 'using' -&gt; 'with'
 		'', --[10] placeholder for [[Category:Taxonbars with manual taxon IDs identical to Wikidata]]
 		'', --[11] placeholder for [[Category:Taxonbars with manual taxon IDs differing from Wikidata]]
 		'', --[12] placeholder for [[Category:Taxonbars with unknown parameters]]
 		'', --[13] placeholder for [[Category:Taxonbars with unnamed parameters]]
 		'', --[14] placeholder for [[Category:Taxonbars with multiple manual Wikidata items]] --renamed 'using' -&gt; 'with'
 		'', --[15] placeholder for [[Category:Taxonbars with automatically added basionyms]]
 		'', --[16] placeholder for [[Category:Taxonbars with automatically added original combinations]]
 		'', --[17] placeholder for [[Category:Taxonbars with automatically added monotypic genera]]
 		'', --[18] placeholder for [[Category:Taxonbars of monotypic species missing genera]]
 		'', --[19] placeholder for [[Category:Taxonbars without primary Wikidata taxon IDs]]
 		'', --[20] placeholder for [[Category:Taxonbars without secondary Wikidata taxon IDs]]
 		'', --[21] placeholder for [[Category:Taxonbars with 20–24 taxon IDs]]
 		'', --[22] placeholder for [[Category:Taxonbars with 25–29 taxon IDs]]
 		'', --[23] placeholder for [[Category:Taxonbars with 30–34 taxon IDs]]
 		'', --[24] placeholder for [[Category:Taxonbars with 35–39 taxon IDs]]
 		'', --[25] placeholder for [[Category:Taxonbars with 40–44 taxon IDs]] --renamed '40+' -&gt; '40–44'
 		'', --[26] placeholder for [[Category:Taxonbars with 45+ taxon IDs]] --new
 	}
 	local acceptableInstanceOf_Strict = {
 		['Q16521'] = 'taxon',                      --strict
 		['Q310890'] = 'monotypic taxon',           --strict
 		['Q98961713'] = 'extinct taxon',           --strict
 		['Q2568288'] = 'ichnotaxon',               --strict
 		['Q23038290'] = 'fossil taxon',            --strict
 		['Q47487597'] = 'monotypic fossil taxon',  --strict
 	}
 	local acceptableInstanceOf_All = {
 		['Q16521'] = 'taxon',                      --strict
 		['Q310890'] = 'monotypic taxon',           --strict
 		['Q98961713'] = 'extinct taxon',           --strict
 		['Q2568288'] = 'ichnotaxon',               --strict
 		['Q23038290'] = 'fossil taxon',            --strict
 		['Q47487597'] = 'monotypic fossil taxon',  --strict
 		['Q42621'] = 'hybrid',                     --lax
 		['Q235536'] = 'incertae sedis',            --lax
 		['Q713623'] = 'clade',                     --lax
 		['Q848328'] = 'serotype',                  --lax
 		['Q857968'] = 'candidatus',                --lax
 		['Q17487588'] = 'unavailable combination', --lax
 	}
 	
 	--Assess the page's relationship with Wikidata
 	local currentItem = nil
 	if currentTitle.namespace == 10 then --i.e. Module:Taxonbar/sandbox, Template:Taxonbar/doc, etc.
 		if resolveEntity._id(parentArgs['from']) then
 			currentItem = mw.wikibase.getEntity(parentArgs['from'])
 		end
 		if currentItem == nil then
 			if resolveEntity._id(parentArgs['from1']) then
 				currentItem = mw.wikibase.getEntity(parentArgs['from1'])
 			end
 		end
 	elseif resolveEntity._id(currentEntityId) then
 		currentItem = mw.wikibase.getEntity(currentEntityId)
 	else --currentEntityId == nil/unresolvable
 		tCats[3] = '[[Category:Taxonbar pages requiring a Wikidata item]]'
 	end
 	if currentItem then
 		tCats[4] = '[[Category:Taxonbars on possible non-taxon pages]]' --unset if acceptable found
 		for _, instanceOfState in pairs ( currentItem:getBestStatements('P31') ) do --instance of
 			local instanceOf = instanceOfState.mainsnak.datavalue.value.id
 			if acceptableInstanceOf_All[instanceOf] then
 				tCats[4] = ''
 				break
 			end
 		end
 	end
 	
 	--Cleanup args
 	for k, v in pairs( frame:getParent().args ) do
 		if type(k) == 'string' then
 			--make args case insensitive
 			local lowerk = mw.ustring.lower(k)
 			if isNilOrEmpty( parentArgs[lowerk] ) then
 				parentArgs[k] = nil
 				parentArgs[lowerk] = v
 			end
 			--remap abc to abc1
 			if mw.ustring.find(lowerk,'%d$') == nil then --if no number at end of param
 				if isNilOrEmpty( parentArgs[lowerk..'1'] ) then
 					parentArgs[lowerk] = nil
 					lowerk = lowerk..'1'
 					parentArgs[lowerk] = v
 				end
 			end
 			if v and v ~= '' then
 				--remap 'for' to 'title'
 				if mw.ustring.sub(lowerk,1,3) == 'for' then
 					local forTitle = mw.ustring.gsub(lowerk,'^for','title',1)
 					if isNilOrEmpty( parentArgs[forTitle] ) then
 						parentArgs[lowerk] = nil
 						lowerk = forTitle
 						parentArgs[lowerk] = v
 					end
 				end
 				--find highest from or title param
 				if mw.ustring.sub(lowerk,1,4) == 'from' then
 					local fromNumber = tonumber(mw.ustring.sub(lowerk,5,-1))
 					if fromNumber and fromNumber &gt;= fromTitleCount then fromTitleCount = fromNumber end
 					--look for duplicate froms while we're here
 					if mw.ustring.find(v, '^Q%d') then
 						if tFroms[v] then
 							tCats[6] = '[[Category:Taxonbars with duplicate from parameters]]'
 							tFroms[v] = tFroms[v] + 1
 						else
 							tFroms[v] = 1
 							iFroms = iFroms + 1
 						end
 						if iFroms == 2 then
 							tCats[14] = '[[Category:Taxonbars with multiple manual Wikidata items]]'
 						end
 					end
 				elseif mw.ustring.sub(lowerk,1,5) == 'title' then
 					local titleNumber = tonumber(mw.ustring.sub(lowerk,4,-1))
 					if titleNumber and titleNumber &gt;= fromTitleCount then fromTitleCount = titleNumber end
 				elseif mw.ustring.lower(v) ~= 'no' then
 					stringArgs = true
 					tCats[9] = '[[Category:Taxonbars with manual taxon IDs]]'
 				end
 			end
 		end --if type(k) == 'string'
 	end --for
 	
 	--Check for unknown parameters
 	--create knowns list
 	local acceptableArgs = { from = true, } --master list of l/c acceptable args
 	for _, d in pairs( conf.databases ) do
 		if d[1] ~= 'Wikidata' then --made obsolete by from
 			acceptableArgs[mw.ustring.lower(d[1])] = true
 		end
 	end
 	for _, a in pairs( conf.aliases ) do
 		acceptableArgs[mw.ustring.lower(a[1])] = true
 	end
 	--create trimmed parents list
 	local baseParentArgs = {} --condensed list of l/c parent args w/o trailing #s
 	for k, _ in pairs( parentArgs ) do
 		if type(k) == 'string' then
 			local lowerk = mw.ustring.lower(k)
 			local base = mw.ustring.gsub(lowerk, '[%d]*$', '')
 			baseParentArgs[base] = true
 		elseif type(k) == 'number' then
 			tCats[13] = '[[Category:Taxonbars with unnamed parameters|'..k..']]'
 		end
 	end
 	--compare lists and spit out unknowns
 	local unknownParams = {}
 	for k, _ in pairs( baseParentArgs ) do
 		if acceptableArgs[k] == nil then
 			tCats[12] = '[[Category:Taxonbars with unknown parameters|'..k..']]'
 			unknownParams[#unknownParams + 1] = k
 		end
 	end
 	--warn if unknown(s) present
 	if #unknownParams &gt; 0 then
 		local plural = 's'
 		local itthem = 'them'
 		if #unknownParams == 1 then
 			plural = ''
 			itthem = 'it'
 		end
 		errors = errors..require('Module:If preview')._warning({
 			mw.ustring.format(
 				'Unknown parameter%s &lt;code&gt;%s&lt;/code&gt;. Please correct %s or consider adding %s to Wikidata.',
 				plural,
 				table.concat(unknownParams, '&lt;/code&gt;, &lt;code&gt;'),
 				itthem,
 				itthem
 			)
 		})
 	end
 	
 	--Append basionym to arg list, if not already provided
 	if currentItem then
 		local currentBasState = currentItem:getBestStatements('P566')[1] --basionym
 		if currentBasState then
 			local basionymId = currentBasState.mainsnak.datavalue.value.id
 			if basionymId and resolveEntity._id(basionymId) and tFroms[basionymId] == nil then
 				--check that basionym is a strict instance of taxon
 				local basionymItem = mw.wikibase.getEntity(basionymId)
 				if basionymItem then
 					for _, instanceOfState in pairs ( basionymItem:getBestStatements('P31') ) do --instance of
 						local instanceOf = instanceOfState.mainsnak.datavalue.value.id
 						if acceptableInstanceOf_Strict[instanceOf] then
 							--housekeeping
 							tFroms[basionymId] = 1
 							iFroms = iFroms + 1
 							fromTitleCount = fromTitleCount + 1
 							--append basionym &amp; track
 							parentArgs['from'..fromTitleCount] = basionymId
 							tCats[15] = '[[Category:Taxonbars with automatically added basionyms]]'
 							break
 	end	end	end	end	end	end
 	
 	--Append original combination to arg list, if not already provided
 	if currentItem then
 		local currentOCState = currentItem:getBestStatements('P1403')[1] --original combination
 		if currentOCState then
 			local orcoId = currentOCState.mainsnak.datavalue.value.id
 			if orcoId and resolveEntity._id(orcoId) and tFroms[orcoId] == nil then
 				--check that orco is a strict instance of taxon
 				local orcoItem = mw.wikibase.getEntity(orcoId)
 				if orcoItem then
 					for _, instanceOfState in pairs ( orcoItem:getBestStatements('P31') ) do --instance of
 						local instanceOf = instanceOfState.mainsnak.datavalue.value.id
 						if acceptableInstanceOf_Strict[instanceOf] then
 							--housekeeping
 							tFroms[orcoId] = 1
 							iFroms = iFroms + 1
 							fromTitleCount = fromTitleCount + 1
 							--append orco &amp; track
 							parentArgs['from'..fromTitleCount] = orcoId
 							tCats[16] = '[[Category:Taxonbars with automatically added original combinations]]'
 							break
 	end	end	end	end	end	end
 	
 	--Append monotypic genus/species to arg list of monotypic species/genus, if not already provided
 	if currentItem then
 		for _, instanceOfState in pairs ( currentItem:getBestStatements('P31') ) do --instance of
 			local taxonRank = nil
 			local parentItem = nil
 			local parentTaxon = nil
 			local parentTaxonRank = nil
 			local parentMonoGenus = nil --holy grail/tbd
 			local instanceOf = instanceOfState.mainsnak.datavalue.value.id
 			if instanceOf and (instanceOf == 'Q310890' or instanceOf == 'Q47487597') then --monotypic/fossil taxon
 				local taxonRankState = currentItem:getBestStatements('P105')[1] --taxon rank
 				if taxonRankState then taxonRank = taxonRankState.mainsnak.datavalue.value.id end
 				
 				if taxonRank and taxonRank == 'Q7432' then --species
 					--is monotypic species; add genus
 					local parentTaxonState = currentItem:getBestStatements('P171')[1] --parent taxon
 					if parentTaxonState then parentTaxon = parentTaxonState.mainsnak.datavalue.value.id end
 					--confirm parent taxon rank == genus &amp; monotypic
 					if parentTaxon and resolveEntity._id(parentTaxon) then
 						parentItem = mw.wikibase.getEntity(parentTaxon)
 						if parentItem then
 							local parentTaxonRankState = parentItem:getBestStatements('P105')[1] --taxon rank
 							if parentTaxonRankState then parentTaxonRank = parentTaxonRankState.mainsnak.datavalue.value.id end
 							if parentTaxonRank and parentTaxonRank == 'Q34740' then --parent == genus
 								for _, parentInstanceOfState in pairs ( parentItem:getBestStatements('P31') ) do --instance of
 									local parentInstanceOf = parentInstanceOfState.mainsnak.datavalue.value.id 
 									if parentInstanceOf and
 									  (parentInstanceOf == 'Q310890' or parentInstanceOf == 'Q47487597') then --monotypic/fossil taxon
 										parentMonoGenus = parentTaxon --confirmed
 										break
 									end
 								end
 								if parentMonoGenus and tFroms[parentMonoGenus] == nil then
 									--housekeeping
 									tFroms[parentMonoGenus] = 1
 									iFroms = iFroms + 1
 									fromTitleCount = fromTitleCount + 1
 									--append monotypic genus &amp; track
 									parentArgs['from'..fromTitleCount] = parentMonoGenus
 									tCats[17] = '[[Category:Taxonbars with automatically added monotypic genera]]'
 									break
 								end
 							end
 						end
 					end
 					if parentMonoGenus == nil or tFroms[parentMonoGenus] == nil then
 						tCats[18] = '[[Category:Taxonbars of monotypic species missing genera]]'
 						break
 					end
 				elseif taxonRank and taxonRank == 'Q34740' then --genus
 					--TODO:
 					--is monotypic genus; add species (no way to easily find these (yet?))
 					--...
 				end
 				
 			end
 		end --for
 	end --if currentItem
 	
 	--Setup navbox
 	local navboxParams = {
 		name  = 'Taxonbar',
 		bodyclass = 'hlist',
 		listclass = '',
 		groupstyle = 'text-align: left;',
 	}
 	
 	for f = 1, fromTitleCount, 1
 	do
 		local elements, title = {}, nil
 		--cleanup parameters
 		if parentArgs['from'..f] == '' then parentArgs['from'..f] = nil end
 		if parentArgs['title'..f] == '' then parentArgs['title'..f] = nil end
 		--remap aliases
 		for _, a in pairs( conf.aliases ) do
 			local alias, name = mw.ustring.lower(a[1]), mw.ustring.lower(a[2])
 			if parentArgs[alias..f] and parentArgs[name..f] == nil then
 				parentArgs[name..f] = parentArgs[alias..f]
 				parentArgs[alias..f] = nil
 			end
 		end
 		--Fetch Wikidata item
 		local from = resolveEntity._id(parentArgs['from'..f])
 		local item = mw.wikibase.getEntity(from)
 		local label = nil
 		if type(item) == 'table' then
 			local statements = item:getBestStatements('P225')[1] --taxon name
 			if statements then
 				local datavalue = statements.mainsnak.datavalue
 				if datavalue then
 					label = datavalue.value
 				end
 			end
 			label = label or item:getLabel()
 		else
 			if parentArgs['from'..f] then
 				tCats[1] = ''
 				tCats[5] = '[[Category:Taxonbars with invalid from parameters]]'
 				errors = errors..mw.text.tag('strong', {class='error'}, 'Error: &quot;'..
 				         parentArgs['from'..f]..'&quot; is not a valid Wikidata entity ID.&lt;br /&gt;')				
 			end
 		end
 		if label and label ~= '' then
 			title = mw.title.new(label)
 		end
 		if title == nil and parentArgs['title'..f] then
 			title = mw.title.new(parentArgs['title'..f])
 		end
 		if title == nil and f == 1 then
 			title = currentTitle
 		end
 		
 		if title then
 			if isNilOrEmpty( parentArgs['wikidata'..f] ) and 
 			   (title.namespace == 0) then
 				if parentArgs['from'..f] then
 					parentArgs['wikidata'..f] = parentArgs['from'..f]
 				elseif item then
 					parentArgs['wikidata'..f] = item.id
 				end
 			end
 			if title.namespace == 0 or stringArgs then --only in mainspace or if manual overrides exist
 				local sourceCount = 0
 				for _, params in pairs( conf.databases ) do
 					params[1] = mw.ustring.lower(params[1])
 					local propId = params[3]
 					--Wikidata fallback if requested
 					if (item and item.claims) and
 					   (type(propId) == 'string' or (type(propId) == 'number' and propId &gt; 0)) then
 						local wikidataId = getIdFromWikidata( item, 'P'..propId )
 						local v = parentArgs[params[1]..f]
 						if wikidataId then
 							if isNilOrEmpty(v) then
 								parentArgs[params[1]..f] = wikidataId
 							else
 								if v and v ~= 'no' and v ~= wikidataId then
 									tCats[11] = '[[Category:Taxonbars with manual taxon IDs differing from Wikidata]]'
 								elseif v and v == wikidataId then
 									tCats[10] = '[[Category:Taxonbars with manual taxon IDs identical to Wikidata]]'
 								end
 							end
 						end
 					end
 					if (item and item.claims) and
 					   ( (type(propId) == 'number' and propId &lt; 0)) then
 						local wikidataId = getIdFromWikidata( item, 'P'..-propId )
 						--mw.addWarning (&quot;propId=&quot; .. tostring(propId) .. &quot;; wikidata=&quot; .. tostring(wikidataId))
 						local v = parentArgs[params[1]..f]
 						if v == 'yes' then
 					    	if wikidataId then
 								parentArgs[params[1]..f] = wikidataId
 							else
 								parentArgs[params[1]..f] = nil  -- don't want to use 'yes' as id
 							end
 						end
 					end
 					local val = parentArgs[params[1]..f]
 					if val and val ~= '' and mw.ustring.lower(val) ~= 'no' then
 						if type(propId) == 'number' then
 							if propId &lt; 0 then propId = -propId end --allow link
 							if propId &gt; 0 then --link
 								table.insert( elements, createRow( params[1], params[2]..':', val, getLink( propId, params[1], val ).text, true ) )
 							else --propId == 0; no link
 								table.insert( elements, createRow( params[1], params[2]..':', val, val, true ) )
 							end
 						else
 							table.insert( elements, createRow( params[1], params[2]..':', val, getLink( propId, params[1], val ).text, true ) )
 						end
 						if params[1] ~= 'wikidata' and params[1] ~= 'wikispecies' then
 							sourceCount = sourceCount + 1
 						end
 					end
 				end --for
 				
 				if     sourceCount &gt;= 45 then tCats[26] = '[[Category:Taxonbars with 45+ taxon IDs]]'
 				elseif sourceCount &gt;= 40 then tCats[25] = '[[Category:Taxonbars with 40–44 taxon IDs]]' --endashes
 				elseif sourceCount &gt;= 35 then tCats[24] = '[[Category:Taxonbars with 35–39 taxon IDs]]'
 				elseif sourceCount &gt;= 30 then tCats[23] = '[[Category:Taxonbars with 30–34 taxon IDs]]'
 				elseif sourceCount &gt;= 25 then tCats[22] = '[[Category:Taxonbars with 25–29 taxon IDs]]'
 				elseif sourceCount &gt;= 20 then tCats[21] = '[[Category:Taxonbars with 20–24 taxon IDs]]'
 				end
 				
 				--Generate navbox title
 				if sourceCount &gt; 0 then
 					rowCount = rowCount + 1
 					if firstRow == 0 then firstRow = f end
 					--set title from wikidata if it doesn't exist
 					if isNilOrEmpty( parentArgs['title'..f] ) then
 						parentArgs['noTitle'..f] = true
 						parentArgs['title'..f] = title.text
 					end
 					--if it exists now, set row heading to title
 					if not isNilOrEmpty( parentArgs['title'..f] ) then
 						navboxParams['group'..f] = TaxonItalics.italicizeTaxonName(parentArgs['title'..f], false)
 					else
 						navboxParams['group'..f] = ''
 					end
 					navboxParams['list'..f] = table.concat( elements )
 				elseif currentEntityId and (currentEntityId == parentArgs['from'..f] or fromTitleCount == 1) then
 					tCats[19] = '[[Category:Taxonbars without primary Wikidata taxon IDs]]'
 				else
 					tCats[20] = '[[Category:Taxonbars without secondary Wikidata taxon IDs]]'
 				end
 				
 				--Categorize
 				if not isNilOrEmpty( parentArgs['from'..f] ) then
 					tCats[1] = '' --blank &quot;missing from&quot; if 'from' exists
 					if parentArgs['from'..f] == currentEntityId then
 						tCats[2] = '' --blank &quot;desynced&quot; if 'from' matches current page
 					end
 				end
 				if tCats[1] ~= '' then
 					tCats[2] = '' --cannot be &quot;desynced&quot; if no 'from' params
 				end
 			end --if title.namespace == 0 or stringArgs
 		end --if title
 	end --for f = 1, fromTitleCount, 1
 	
 	if rowCount &gt; 0 then
 		local Navbox = require('Module:Navbox')
 		if rowCount &gt; 1 then
 			--remove duplicates and don't bother moving page title to top
 			local rowIDs = {}
 			for f = 1,fromTitleCount,1
 			do
 				if not isNilOrEmpty( parentArgs['title'..f] ) then
 					if rowIDs[parentArgs['wikidata'..f]] then --remove duplicate
 						navboxParams['group'..f] = nil
 						navboxParams['list'..f] = nil
 					else
 						rowIDs[parentArgs['wikidata'..f]] = true
 					end
 				end
 			end
 			if parentArgs['title'..2] and parentArgs['title'..2] == currentTitle.text then
 				if currentItem and parentArgs['from'..2] == currentItem['id'] then
 					tCats[8] = '[[Category:Taxonbars with from2 matching article title &amp; QID]]'
 				else
 					tCats[7] = '[[Category:Taxonbars with from2 matching article title]]'
 				end
 			end
 			--adjust navbox for number of rows
 			navboxParams['title'] = '[[:en:Help:Taxon identifiers|உயிரின வகைப்பாட்டு&lt;br/&gt;இனங்காட்டிகள்]]'
 			if rowCount &gt;= 4 then
 				navboxParams['navbar'] = 'plain'
 			else
 				navboxParams['state'] = 'off'
 				navboxParams['navbar'] = 'off'
 			end
 		elseif parentArgs['noTitle'..firstRow] then
 			navboxParams['group'..firstRow] = '[[:en:Help:Taxon identifiers|உயிரின வகைப்பாட்டு&lt;br/&gt;இனங்காட்டிகள்]]'
 		else
 			navboxParams['group'..firstRow] = '[[:en:Help:Taxon identifiers|உயிரின வகைப்பாட்டு&lt;br/&gt;இனங்காட்டிகள்]]&lt;br /&gt;'..navboxParams['group'..firstRow]
 		end
 		
 		--return navbox
 		outString = Navbox._navbox(navboxParams)
 	end --if rowCount &gt; 0
 	
 	--Add categories
 	if string.sub(currentTitle.subpageText,1,9) == 'testcases' then parentArgs['demo'] = true end
 	if not isNilOrEmpty( parentArgs['demo'] ) then
 		outString = outString..mw.text.nowiki(table.concat(tCats))..'&lt;br /&gt;'
 	elseif currentTitle.namespace == 0 then
 		outString = outString..table.concat(tCats)
 	end
 	
 	return outString..errors
 end