Permanently protected module
From Wikipedia, the free encyclopedia


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.claimsproperty == 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 > 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'

				elseif db == 'fossilworks' then 

					link = 'https://paleobiodb.org/classic/basicTaxonInfo?taxon_no=$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. "Elaeagnus x ebbingei"

			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]?)://', '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 = '<span class="external">[['..link..'|'..val..']]</span>'

	end

	return returnVal

end



local function createRow( id, label, rawValue, link, withUid )

	if link then

		local outStr = '*<span style="white-space:nowrap;">'..label..' <span'

		if withUid then outStr = outStr..' class="uid"' end

		return outStr..'>'..link..'</span></span>\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 outTablecopyTable(key)] = copyTable(value) end

	return outTable

end



local p = {}



--[[==========================================================================]]

--[[                                   Main                                   ]]

--[[==========================================================================]]



function p.authorityControlTaxon( frame )

	local resolveEntity = require( 'Module:ResolveEntityId' )

	local whitelist = require( 'Module:Taxonbar/whitelist' ).whitelist --to create acceptableInstanceOf_Strict & acceptableInstanceOf_All

	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 & QID]]

		'', -- [9] placeholder for [[Category:Taxonbars with manual taxon IDs]]

		'', --[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]]

		'', --[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 species]]

		'', --[18] placeholder for [[Category:Taxonbars with automatically added monotypic genera]]

		'', --[19] placeholder for [[Category:Taxonbars of monotypic species missing genera]]

		'', --[20] placeholder for [[Category:Taxonbars of monotypic genera missing species]]

		'', --[21] placeholder for [[Category:Taxonbars without primary Wikidata taxon IDs]]

		'', --[22] placeholder for [[Category:Taxonbars without secondary Wikidata taxon IDs]]

		'', --[23] placeholder for [[Category:Taxonbars with 20–24 taxon IDs]]

		'', --[24] placeholder for [[Category:Taxonbars with 25–29 taxon IDs]]

		'', --[25] placeholder for [[Category:Taxonbars with 30–34 taxon IDs]]

		'', --[26] placeholder for [[Category:Taxonbars with 35–39 taxon IDs]]

		'', --[27] placeholder for [[Category:Taxonbars with 40–44 taxon IDs]]

		'', --[28] placeholder for [[Category:Taxonbars with 45+ taxon IDs]]

	}

	local acceptableInstanceOf_Strict = whitelist{ args = { 'strict' } }

	local acceptableInstanceOf_All = whitelist{ args = { 'all' } }

	

	--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

		tCats3 = '[[Category:Taxonbar pages requiring a Wikidata item]]'

	end

	if currentItem then

		tCats4 = '[[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_AllinstanceOf then

				tCats4 = ''

				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( parentArgslowerk ) then

				parentArgsk = nil

				parentArgslowerk = v

			end

			--remap abc to abc1

			if mw.ustring.find(lowerk,'%d$') == nil then --if no number at end of param

				if isNilOrEmpty( parentArgslowerk..'1' ) then

					parentArgslowerk = nil

					lowerk = lowerk..'1'

					parentArgslowerk = 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( parentArgsforTitle ) then

						parentArgslowerk = nil

						lowerk = forTitle

						parentArgslowerk = 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 >= fromTitleCount then fromTitleCount = fromNumber end

					--look for duplicate froms while we're here

					if mw.ustring.find(v, '^Q%d') then

						if tFromsv then

							tCats6 = '[[Category:Taxonbars with duplicate from parameters]]'

							tFromsv = tFromsv + 1

						else

							tFromsv = 1

							iFroms = iFroms + 1

						end

						if iFroms == 2 then

							tCats14 = '[[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 >= fromTitleCount then fromTitleCount = titleNumber end

				elseif mw.ustring.lower(v) ~= 'no' and  mw.ustring.lower(v) ~= 'yes' then

					stringArgs = true

					tCats9 = '[[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 d1 ~= 'Wikidata' then --made obsolete by from

			acceptableArgsmw.ustring.lower(d1])] = true

		end

	end

	for _, a in pairs( conf.aliases ) do

		acceptableArgsmw.ustring.lower(a1])] = 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]*$', '')

			baseParentArgsbase = true

		elseif type(k) == 'number' then

			tCats13 = '[[Category:Taxonbars with unnamed parameters|'..k..']]'

		end

	end

	--compare lists and spit out unknowns

	local unknownParams = {}

	for k, _ in pairs( baseParentArgs ) do

		if acceptableArgsk == nil then

			tCats12 = '[[Category:Taxonbars with unknown parameters|'..k..']]'

			unknownParams#unknownParams + 1 = k

		end

	end

	--warn if unknown(s) present

	if #unknownParams > 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 <code>%s</code>. Please correct %s or consider adding %s to Wikidata.',

				plural,

				table.concat(unknownParams, '</code>, <code>'),

				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 tFromsbasionymId == 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_StrictinstanceOf then

							--housekeeping

							tFromsbasionymId = 1

							iFroms = iFroms + 1

							fromTitleCount = fromTitleCount + 1

							--append basionym & track

							parentArgs'from'..fromTitleCount = basionymId

							tCats15 = '[[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 tFromsorcoId == 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_StrictinstanceOf then

							--housekeeping

							tFromsorcoId = 1

							iFroms = iFroms + 1

							fromTitleCount = fromTitleCount + 1

							--append orco & track

							parentArgs'from'..fromTitleCount = orcoId

							tCats16 = '[[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 childItem = nil

			local childTaxon = nil

			local childTaxonRank = nil

			local childMonoSpecies = 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 & 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 tFromsparentMonoGenus == nil then

									--housekeeping

									tFromsparentMonoGenus = 1

									iFroms = iFroms + 1

									fromTitleCount = fromTitleCount + 1

									--append monotypic genus & track

									parentArgs'from'..fromTitleCount = parentMonoGenus

									tCats18 = '[[Category:Taxonbars with automatically added monotypic genera]]'

									break

					end	end	end	end

					if parentMonoGenus == nil or tFromsparentMonoGenus == nil then

						tCats19 = '[[Category:Taxonbars of monotypic species missing genera]]'

						break

					end

					

				elseif taxonRank and taxonRank == 'Q34740' then --genus

					--is monotypic genus; add species

					--(https://www.wikidata.org/wiki/Wikidata:Property_proposal/child_monotypic_taxon unnecessary thanks to P427!)

					local childTaxonState = currentItem:getBestStatements('P427')[1 --taxonomic type

					if childTaxonState then childTaxon = childTaxonState.mainsnak.datavalue.value.id end

					--confirm child taxon rank == species & monotypic

					if childTaxon and resolveEntity._id(childTaxon) then

						childItem = mw.wikibase.getEntity(childTaxon)

						if childItem then

							local childTaxonRankState = childItem:getBestStatements('P105')[1 --taxon rank

							if childTaxonRankState then childTaxonRank = childTaxonRankState.mainsnak.datavalue.value.id end

							if childTaxonRank and childTaxonRank == 'Q7432' then --child == species

								for _, childInstanceOfState in pairs ( childItem:getBestStatements('P31') ) do --instance of

									local childInstanceOf = childInstanceOfState.mainsnak.datavalue.value.id 

									if childInstanceOf and

									  (childInstanceOf == 'Q310890' or childInstanceOf == 'Q47487597') then --monotypic/fossil taxon

										childMonoSpecies = childTaxon --confirmed

										break

									end

								end

								if childMonoSpecies and tFromschildMonoSpecies == nil then

									--housekeeping

									tFromschildMonoSpecies = 1

									iFroms = iFroms + 1

									fromTitleCount = fromTitleCount + 1

									--append monotypic species & track

									parentArgs'from'..fromTitleCount = childMonoSpecies

									tCats17 = '[[Category:Taxonbars with automatically added monotypic species]]'

									break

					end	end	end	end

					if childMonoSpecies == nil or tFromschildMonoSpecies == nil then

						tCats20 = '[[Category:Taxonbars of monotypic genera missing species]]'

						break

					end

				end --monotype searches

				

			end --monotype handling

		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(a1]), mw.ustring.lower(a2])

			if parentArgsalias..f and parentArgsname..f == nil then

				parentArgsname..f = parentArgsalias..f

				parentArgsalias..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

				tCats1 = ''

				tCats5 = '[[Category:Taxonbars with invalid from parameters]]'

				errors = errors..mw.text.tag('strong', {class='error'}, 'Error: "'..

				         parentArgs'from'..f..'" is not a valid Wikidata entity ID.<br />')				

			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

					params1 = mw.ustring.lower(params1])

					local propId = params3

					--Wikidata fallback if requested

					if (item and item.claims) and

					   (type(propId) == 'string' or (type(propId) == 'number' and propId > 0)) then

						local wikidataId = getIdFromWikidata( item, 'P'..propId )

						local v = parentArgsparams1..f

						if wikidataId then

							if isNilOrEmpty(v) then

								parentArgsparams1..f = wikidataId

							else

								if v and v ~= 'no' and v ~= wikidataId then

									tCats11 = '[[Category:Taxonbars with manual taxon IDs differing from Wikidata]]'

								elseif v and v == wikidataId then

									tCats10 = '[[Category:Taxonbars with manual taxon IDs identical to Wikidata]]'

								end

							end

						end

					end

					if (item and item.claims) and

					   ( (type(propId) == 'number' and propId < 0)) then

						local wikidataId = getIdFromWikidata( item, 'P'..-propId )

						--mw.addWarning ("propId=" .. tostring(propId) .. "; wikidata=" .. tostring(wikidataId))

						local v = parentArgsparams1..f

						if v == 'yes' then

					    	if wikidataId then

								parentArgsparams1..f = wikidataId

							else

								parentArgsparams1..f = nil  -- don't want to use 'yes' as id

							end

						end

					end

					local val = parentArgsparams1..f

					if val and val ~= '' and mw.ustring.lower(val) ~= 'no' then

						if type(propId) == 'number' then

							if propId < 0 then propId = -propId end --allow link

							if propId > 0 then --link

								table.insert( elements, createRow( params1], params2..':', val, getLink( propId, params1], val ).text, true ) )

								if params1 == 'fossilworks' then -- fossilworks being use to link to PBDB

									if (elements#elements == elements#elements-1]) then -- check if identical PBDB entry already set

									    --mw.addWarning("item has both Fossilworks and PBDB identifers:" .. val)

									    elements#elements = nil

								    end

								end

							else --propId == 0; no link

								table.insert( elements, createRow( params1], params2..':', val, val, true ) )

							end

						else

							table.insert( elements, createRow( params1], params2..':', val, getLink( propId, params1], val ).text, true ) )

						end

						if params1 ~= 'wikidata' and params1 ~= 'wikispecies' then

							sourceCount = sourceCount + 1

						end

					end

				end --for

				

				if     sourceCount >= 45 then tCats28 = '[[Category:Taxonbars with 45+ taxon IDs]]'

				elseif sourceCount >= 40 then tCats27 = '[[Category:Taxonbars with 40–44 taxon IDs]]' --endashes

				elseif sourceCount >= 35 then tCats26 = '[[Category:Taxonbars with 35–39 taxon IDs]]'

				elseif sourceCount >= 30 then tCats25 = '[[Category:Taxonbars with 30–34 taxon IDs]]'

				elseif sourceCount >= 25 then tCats24 = '[[Category:Taxonbars with 25–29 taxon IDs]]'

				elseif sourceCount >= 20 then tCats23 = '[[Category:Taxonbars with 20–24 taxon IDs]]'

				end

				

				--Generate navbox title

				if sourceCount > 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

					tCats21 = '[[Category:Taxonbars without primary Wikidata taxon IDs]]'

				else

					tCats22 = '[[Category:Taxonbars without secondary Wikidata taxon IDs]]'

				end

				

				--Categorize

				if not isNilOrEmpty( parentArgs'from'..f ) then

					tCats1 = '' --blank "missing from" if 'from' exists

					if parentArgs'from'..f == currentEntityId then

						tCats2 = '' --blank "desynced" if 'from' matches current page

					end

				end

				if tCats1 ~= '' then

					tCats2 = '' --cannot be "desynced" if no 'from' params

				end

			end --if title.namespace == 0 or stringArgs

		end --if title

	end --for f = 1, fromTitleCount, 1

	

	if rowCount > 0 then

		local Navbox = require('Module:Navbox')

		if rowCount > 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 rowIDsparentArgs'wikidata'..f]] then --remove duplicate

						navboxParams'group'..f = nil

						navboxParams'list'..f = nil

					else

						rowIDsparentArgs'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

					tCats8 = '[[Category:Taxonbars with from2 matching article title & QID]]'

				else

					tCats7 = '[[Category:Taxonbars with from2 matching article title]]'

				end

			end

			--adjust navbox for number of rows

			navboxParams'title' = '[[Help:Taxon identifiers|Taxon identifiers]]'

			if rowCount >= 4 then

				navboxParams'navbar' = 'plain'

			else

				navboxParams'state' = 'off'

				navboxParams'navbar' = 'off'

			end

		elseif parentArgs'noTitle'..firstRow then --show title & taxon for 1-row taxonbars, per talk

			navboxParams'title' = '[[Help:Taxon identifiers|Taxon identifiers]]'

			navboxParams'state' = 'off'

			navboxParams'navbar' = 'off'

		else

			navboxParams'group'..firstRow = '[[Help:Taxon identifiers|Taxon identifiers]]<br />'..navboxParams'group'..firstRow

		end

		

		--return navbox

		outString = Navbox._navbox(navboxParams)

	end --if rowCount > 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))..'<br />'

	elseif currentTitle.namespace == 0 then

		outString = outString..table.concat(tCats)

	end

	

	return outString..errors

end



return p
Permanently protected module
From Wikipedia, the free encyclopedia


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.claimsproperty == 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 > 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'

				elseif db == 'fossilworks' then 

					link = 'https://paleobiodb.org/classic/basicTaxonInfo?taxon_no=$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. "Elaeagnus x ebbingei"

			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]?)&#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 = '<span class="external">[['..link..'|'..val..']]</span>'

	end

	return returnVal

end



local function createRow( id, label, rawValue, link, withUid )

	if link then

		local outStr = '*<span style="white-space:nowrap;">'..label..' <span'

		if withUid then outStr = outStr..' class="uid"' end

		return outStr..'>'..link..'</span></span>\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 outTablecopyTable(key)] = copyTable(value) end

	return outTable

end



local p = {}



--[[==========================================================================]]

--[[                                   Main                                   ]]

--[[==========================================================================]]



function p.authorityControlTaxon( frame )

	local resolveEntity = require( 'Module:ResolveEntityId' )

	local whitelist = require( 'Module:Taxonbar/whitelist' ).whitelist --to create acceptableInstanceOf_Strict & acceptableInstanceOf_All

	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 & QID]]

		'', -- [9] placeholder for [[Category:Taxonbars with manual taxon IDs]]

		'', --[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]]

		'', --[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 species]]

		'', --[18] placeholder for [[Category:Taxonbars with automatically added monotypic genera]]

		'', --[19] placeholder for [[Category:Taxonbars of monotypic species missing genera]]

		'', --[20] placeholder for [[Category:Taxonbars of monotypic genera missing species]]

		'', --[21] placeholder for [[Category:Taxonbars without primary Wikidata taxon IDs]]

		'', --[22] placeholder for [[Category:Taxonbars without secondary Wikidata taxon IDs]]

		'', --[23] placeholder for [[Category:Taxonbars with 20–24 taxon IDs]]

		'', --[24] placeholder for [[Category:Taxonbars with 25–29 taxon IDs]]

		'', --[25] placeholder for [[Category:Taxonbars with 30–34 taxon IDs]]

		'', --[26] placeholder for [[Category:Taxonbars with 35–39 taxon IDs]]

		'', --[27] placeholder for [[Category:Taxonbars with 40–44 taxon IDs]]

		'', --[28] placeholder for [[Category:Taxonbars with 45+ taxon IDs]]

	}

	local acceptableInstanceOf_Strict = whitelist{ args = { 'strict' } }

	local acceptableInstanceOf_All = whitelist{ args = { 'all' } }

	

	--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

		tCats3 = '[[Category:Taxonbar pages requiring a Wikidata item]]'

	end

	if currentItem then

		tCats4 = '[[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_AllinstanceOf then

				tCats4 = ''

				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( parentArgslowerk ) then

				parentArgsk = nil

				parentArgslowerk = v

			end

			--remap abc to abc1

			if mw.ustring.find(lowerk,'%d$') == nil then --if no number at end of param

				if isNilOrEmpty( parentArgslowerk..'1' ) then

					parentArgslowerk = nil

					lowerk = lowerk..'1'

					parentArgslowerk = 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( parentArgsforTitle ) then

						parentArgslowerk = nil

						lowerk = forTitle

						parentArgslowerk = 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 >= fromTitleCount then fromTitleCount = fromNumber end

					--look for duplicate froms while we're here

					if mw.ustring.find(v, '^Q%d') then

						if tFromsv then

							tCats6 = '[[Category:Taxonbars with duplicate from parameters]]'

							tFromsv = tFromsv + 1

						else

							tFromsv = 1

							iFroms = iFroms + 1

						end

						if iFroms == 2 then

							tCats14 = '[[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 >= fromTitleCount then fromTitleCount = titleNumber end

				elseif mw.ustring.lower(v) ~= 'no' and  mw.ustring.lower(v) ~= 'yes' then

					stringArgs = true

					tCats9 = '[[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 d1 ~= 'Wikidata' then --made obsolete by from

			acceptableArgsmw.ustring.lower(d1])] = true

		end

	end

	for _, a in pairs( conf.aliases ) do

		acceptableArgsmw.ustring.lower(a1])] = 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]*$', '')

			baseParentArgsbase = true

		elseif type(k) == 'number' then

			tCats13 = '[[Category:Taxonbars with unnamed parameters|'..k..']]'

		end

	end

	--compare lists and spit out unknowns

	local unknownParams = {}

	for k, _ in pairs( baseParentArgs ) do

		if acceptableArgsk == nil then

			tCats12 = '[[Category:Taxonbars with unknown parameters|'..k..']]'

			unknownParams#unknownParams + 1 = k

		end

	end

	--warn if unknown(s) present

	if #unknownParams > 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 <code>%s</code>. Please correct %s or consider adding %s to Wikidata.',

				plural,

				table.concat(unknownParams, '</code>, <code>'),

				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 tFromsbasionymId == 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_StrictinstanceOf then

							--housekeeping

							tFromsbasionymId = 1

							iFroms = iFroms + 1

							fromTitleCount = fromTitleCount + 1

							--append basionym & track

							parentArgs'from'..fromTitleCount = basionymId

							tCats15 = '[[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 tFromsorcoId == 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_StrictinstanceOf then

							--housekeeping

							tFromsorcoId = 1

							iFroms = iFroms + 1

							fromTitleCount = fromTitleCount + 1

							--append orco & track

							parentArgs'from'..fromTitleCount = orcoId

							tCats16 = '[[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 childItem = nil

			local childTaxon = nil

			local childTaxonRank = nil

			local childMonoSpecies = 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 & 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 tFromsparentMonoGenus == nil then

									--housekeeping

									tFromsparentMonoGenus = 1

									iFroms = iFroms + 1

									fromTitleCount = fromTitleCount + 1

									--append monotypic genus & track

									parentArgs'from'..fromTitleCount = parentMonoGenus

									tCats18 = '[[Category:Taxonbars with automatically added monotypic genera]]'

									break

					end	end	end	end

					if parentMonoGenus == nil or tFromsparentMonoGenus == nil then

						tCats19 = '[[Category:Taxonbars of monotypic species missing genera]]'

						break

					end

					

				elseif taxonRank and taxonRank == 'Q34740' then --genus

					--is monotypic genus; add species

					--(https://www.wikidata.org/wiki/Wikidata:Property_proposal/child_monotypic_taxon unnecessary thanks to P427!)

					local childTaxonState = currentItem:getBestStatements('P427')[1 --taxonomic type

					if childTaxonState then childTaxon = childTaxonState.mainsnak.datavalue.value.id end

					--confirm child taxon rank == species & monotypic

					if childTaxon and resolveEntity._id(childTaxon) then

						childItem = mw.wikibase.getEntity(childTaxon)

						if childItem then

							local childTaxonRankState = childItem:getBestStatements('P105')[1 --taxon rank

							if childTaxonRankState then childTaxonRank = childTaxonRankState.mainsnak.datavalue.value.id end

							if childTaxonRank and childTaxonRank == 'Q7432' then --child == species

								for _, childInstanceOfState in pairs ( childItem:getBestStatements('P31') ) do --instance of

									local childInstanceOf = childInstanceOfState.mainsnak.datavalue.value.id 

									if childInstanceOf and

									  (childInstanceOf == 'Q310890' or childInstanceOf == 'Q47487597') then --monotypic/fossil taxon

										childMonoSpecies = childTaxon --confirmed

										break

									end

								end

								if childMonoSpecies and tFromschildMonoSpecies == nil then

									--housekeeping

									tFromschildMonoSpecies = 1

									iFroms = iFroms + 1

									fromTitleCount = fromTitleCount + 1

									--append monotypic species & track

									parentArgs'from'..fromTitleCount = childMonoSpecies

									tCats17 = '[[Category:Taxonbars with automatically added monotypic species]]'

									break

					end	end	end	end

					if childMonoSpecies == nil or tFromschildMonoSpecies == nil then

						tCats20 = '[[Category:Taxonbars of monotypic genera missing species]]'

						break

					end

				end --monotype searches

				

			end --monotype handling

		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(a1]), mw.ustring.lower(a2])

			if parentArgsalias..f and parentArgsname..f == nil then

				parentArgsname..f = parentArgsalias..f

				parentArgsalias..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

				tCats1 = ''

				tCats5 = '[[Category:Taxonbars with invalid from parameters]]'

				errors = errors..mw.text.tag('strong', {class='error'}, 'Error: "'..

				         parentArgs'from'..f..'" is not a valid Wikidata entity ID.<br />')				

			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

					params1 = mw.ustring.lower(params1])

					local propId = params3

					--Wikidata fallback if requested

					if (item and item.claims) and

					   (type(propId) == 'string' or (type(propId) == 'number' and propId > 0)) then

						local wikidataId = getIdFromWikidata( item, 'P'..propId )

						local v = parentArgsparams1..f

						if wikidataId then

							if isNilOrEmpty(v) then

								parentArgsparams1..f = wikidataId

							else

								if v and v ~= 'no' and v ~= wikidataId then

									tCats11 = '[[Category:Taxonbars with manual taxon IDs differing from Wikidata]]'

								elseif v and v == wikidataId then

									tCats10 = '[[Category:Taxonbars with manual taxon IDs identical to Wikidata]]'

								end

							end

						end

					end

					if (item and item.claims) and

					   ( (type(propId) == 'number' and propId < 0)) then

						local wikidataId = getIdFromWikidata( item, 'P'..-propId )

						--mw.addWarning ("propId=" .. tostring(propId) .. "; wikidata=" .. tostring(wikidataId))

						local v = parentArgsparams1..f

						if v == 'yes' then

					    	if wikidataId then

								parentArgsparams1..f = wikidataId

							else

								parentArgsparams1..f = nil  -- don't want to use 'yes' as id

							end

						end

					end

					local val = parentArgsparams1..f

					if val and val ~= '' and mw.ustring.lower(val) ~= 'no' then

						if type(propId) == 'number' then

							if propId < 0 then propId = -propId end --allow link

							if propId > 0 then --link

								table.insert( elements, createRow( params1], params2..':', val, getLink( propId, params1], val ).text, true ) )

								if params1 == 'fossilworks' then -- fossilworks being use to link to PBDB

									if (elements#elements == elements#elements-1]) then -- check if identical PBDB entry already set

									    --mw.addWarning("item has both Fossilworks and PBDB identifers:" .. val)

									    elements#elements = nil

								    end

								end

							else --propId == 0; no link

								table.insert( elements, createRow( params1], params2..':', val, val, true ) )

							end

						else

							table.insert( elements, createRow( params1], params2..':', val, getLink( propId, params1], val ).text, true ) )

						end

						if params1 ~= 'wikidata' and params1 ~= 'wikispecies' then

							sourceCount = sourceCount + 1

						end

					end

				end --for

				

				if     sourceCount >= 45 then tCats28 = '[[Category:Taxonbars with 45+ taxon IDs]]'

				elseif sourceCount >= 40 then tCats27 = '[[Category:Taxonbars with 40–44 taxon IDs]]' --endashes

				elseif sourceCount >= 35 then tCats26 = '[[Category:Taxonbars with 35–39 taxon IDs]]'

				elseif sourceCount >= 30 then tCats25 = '[[Category:Taxonbars with 30–34 taxon IDs]]'

				elseif sourceCount >= 25 then tCats24 = '[[Category:Taxonbars with 25–29 taxon IDs]]'

				elseif sourceCount >= 20 then tCats23 = '[[Category:Taxonbars with 20–24 taxon IDs]]'

				end

				

				--Generate navbox title

				if sourceCount > 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

					tCats21 = '[[Category:Taxonbars without primary Wikidata taxon IDs]]'

				else

					tCats22 = '[[Category:Taxonbars without secondary Wikidata taxon IDs]]'

				end

				

				--Categorize

				if not isNilOrEmpty( parentArgs'from'..f ) then

					tCats1 = '' --blank "missing from" if 'from' exists

					if parentArgs'from'..f == currentEntityId then

						tCats2 = '' --blank "desynced" if 'from' matches current page

					end

				end

				if tCats1 ~= '' then

					tCats2 = '' --cannot be "desynced" if no 'from' params

				end

			end --if title.namespace == 0 or stringArgs

		end --if title

	end --for f = 1, fromTitleCount, 1

	

	if rowCount > 0 then

		local Navbox = require('Module:Navbox')

		if rowCount > 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 rowIDsparentArgs'wikidata'..f]] then --remove duplicate

						navboxParams'group'..f = nil

						navboxParams'list'..f = nil

					else

						rowIDsparentArgs'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

					tCats8 = '[[Category:Taxonbars with from2 matching article title & QID]]'

				else

					tCats7 = '[[Category:Taxonbars with from2 matching article title]]'

				end

			end

			--adjust navbox for number of rows

			navboxParams'title' = '[[Help:Taxon identifiers|Taxon identifiers]]'

			if rowCount >= 4 then

				navboxParams'navbar' = 'plain'

			else

				navboxParams'state' = 'off'

				navboxParams'navbar' = 'off'

			end

		elseif parentArgs'noTitle'..firstRow then --show title & taxon for 1-row taxonbars, per talk

			navboxParams'title' = '[[Help:Taxon identifiers|Taxon identifiers]]'

			navboxParams'state' = 'off'

			navboxParams'navbar' = 'off'

		else

			navboxParams'group'..firstRow = '[[Help:Taxon identifiers|Taxon identifiers]]<br />'..navboxParams'group'..firstRow

		end

		

		--return navbox

		outString = Navbox._navbox(navboxParams)

	end --if rowCount > 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))..'<br />'

	elseif currentTitle.namespace == 0 then

		outString = outString..table.concat(tCats)

	end

	

	return outString..errors

end



return p

Videos

Youtube | Vimeo | Bing

Websites

Google | Yahoo | Bing

Encyclopedia

Google | Yahoo | Bing

Facebook