Permanently protected module
From Wikipedia, the free encyclopedia


require('strict')



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

--[[                             Local functions                              ]]

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



local function addOrd( i ) --12 -> 12th, etc.

	if tonumber(i) then

		local s = tostring(i)

		local  tens = string.match(s, '1%d$')

		local  ones = string.match(s,  '%d$')

		if     tens        then return s..'th'

		elseif ones == '1' then return s..'st'

		elseif ones == '2' then return s..'nd'

		elseif ones == '3' then return s..'rd'

		elseif ones ~= nil then return s..'th'

		end

	end

	return ''

end



local function isNilOrEmpty( thing )

	return (thing == nil or thing == '')

end



local p = {}



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

--[[                            External function                             ]]

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



function p.autodetect( frame )

	local conf = require( 'Module:Category described in year/conf' ) --configuration module

	local commonsLink = require('Module:Commons link')

	local currentTitle = mw.title.getCurrentTitle()

	local parentArg = frame:getParent().args1 --accept 1 unnamed category parameter if not in category namespace; required for testing/doc/etc. purposes

	local header = ' ' --header template(s), nav bar, and category description text; whitespace-initialized for convenience

	local nav = nil

	local portal = nil --for {{Portal|...}}

	local commons = nil --for {{Commons|...}}

	local wikispecies = nil --for {{Wikispecies|...}}

	local description = nil

	local toc = nil

	local categories = {}

	local trackingCats = {

		1 = '', --placeholder for [[Category:Described in year unknown category]]

		2 = '', --placeholder for [[Category:Described in year error]]

	}

	local outString = nil

	local bConfError = false

	

	--prelim namespace/title determination

	local currCat = nil

	local currQID = nil

	if currentTitle.namespace == 14 then --category namespace

		currCat = currentTitle.text --without namespace nor interwiki prefixes

		currQID = mw.wikibase.getEntityIdForCurrentPage()

	else

		if parentArg then

			currCat = mw.ustring.gsub(parentArg, 'Category:', '')

			currQID = mw.wikibase.getEntityIdForTitle('Category:'..currCat)

		else --currQID & currCat both nil

			if currentTitle.fullText ~= 'Template:Category described in year' then --ignore self...

				trackingCats2 = '[[Category:Described in year error|P]]' --missing a category parameter outside category namespace

			end

		end

	end

	

	--find commons & wikispecies link(s); produce {{Commons and category}} and/or {{Wikispecies}} template(s)

	if currQID then

		if commonsLink._hasGallery(currQID) or commonsLink._hasCategory(currQID) then

			commons = frame:expandTemplate{ title = 'Commons and category', args = { qid=currQID }}

		end

		local currEntity = mw.wikibase.getEntity(currQID)

		if currEntity then

			--check "Other sites" sitelinks for Wikispecies

			local currSiteLinks = currEntity.sitelinks

			if currSiteLinks then

				local currSpeciesWiki = currEntity.sitelinks.specieswiki

				if currSpeciesWiki then

					local currSpeciesWikiTitle = currSpeciesWiki.title

					if currSpeciesWikiTitle then

						wikispecies = frame:expandTemplate{ title = 'Wikispecies', args = { currSpeciesWikiTitle } }

	end	end	end	end	end

	

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

	--[[                                 Main                                 ]]

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

	if currCat then

		

		--determine current/related/adjacent cats' properties/vars/etc.

		local currGroup = mw.ustring.match(currCat, '^([%w ]+) described in') --Bacteria/Plants/etc.

		if isNilOrEmpty(currGroup) then currGroup = mw.ustring.match(currCat, '^([%w ]+) by year of formal description') end

		if confcurrGroup == nil then confcurrGroup = conf'Default' end --default to Default

		local currYDCF = nil --possible future values: year/decade/century/formal

		local currYear = mw.ustring.match(currCat, 'described in (%d%d%d%d)$')

		local currDeca = mw.ustring.match(currCat, 'described in the (%d%d%d%d)s$') --deprecated

		local currCent = mw.ustring.match(currCat, 'described in the (%d+)[snrt][tdh] century$')

		local currFrml = mw.ustring.match(currCat, 'by year of (formal) description$')

		local parentCent = nil --used with currYear

		local minYear = tonumber(confcurrGroup].minyear)

		if minYear == nil or 

		  (minYear and (minYear <= 1700 or minYear >= 2000)) then

			minYear = 1758 --default to 1758 per ICZN Art. 5

		end

		if currYear then

			currYDCF = 'year'

			if mw.ustring.match(currYear, '^%d%d00') then --1900 in 19th century

				parentCent = mw.ustring.match(currYear, '^%d%d')

			else --1901 in 20th century

				parentCent = 1 + mw.ustring.match(currYear, '^%d%d')

			end

		elseif currDeca then

			currYDCF = 'decade'

			bConfError = true

			trackingCats2 = '[[Category:Described in year error|D]]' --invalid decade-parent (deprecated)

		elseif currCent then

			currYDCF = 'century'

		elseif currFrml then

			currYDCF = 'formal'

		else

			bConfError = true

			trackingCats2 = '[[Category:Described in year error|N]]' --invalid category name

		end

		

		--conf error checkng (missing keys)

		--Numeric sortkeys are unfortunately grouped together under "0-9".

		--Check phab T203355 (Magic word to force category number headings instead of 0-9).

		if bConfError == false then

			if confcurrGroup == nil then

				bConfError = true

				trackingCats2 = '[[Category:Described in year error|1]]' --group (Bacteria/Plants/etc.) key missing from conf

			elseif confcurrGroup][currYDCF == nil then

				bConfError = true

				trackingCats2 = '[[Category:Described in year error|2]]' --year/century/formal key missing

			else

				if confcurrGroup][currYDCF].description == nil then

					bConfError = true

					trackingCats2 = '[[Category:Described in year error|3]]' --description key missing

				end

				if confcurrGroup][currYDCF].parent1 == nil then

					bConfError = true

					trackingCats2 = '[[Category:Described in year error|4]]' --parent key missing

				end

			end

		end

		

		if bConfError == false then

			--produce portal

			if currGroup == 'Fossil taxa' or currGroup == 'Fossil parataxa' then

				portal = frame:expandTemplate{ title = 'Portal', args = { 'Paleontology' } }

			end

			

			--produce description, evaluate %variables%

			description = confcurrGroup][currYDCF].description

			if mw.ustring.match(description, '%%year%%') then

				if currYear then description = mw.ustring.gsub(description, '%%year%%', currYear) --"2011"

				else description = mw.ustring.gsub(description, '%%year%%', 'this year') end

			end

			if mw.ustring.match(description, '%%century%%') then

				if currCent then description = mw.ustring.gsub(description, '%%century%%', addOrd(currCent)) --"21st"

				else description = mw.ustring.gsub(description, '%%century%%', 'this century') end

			end

			

			--produce cats & navs

			local iparent = 1

			local parenti = 'parent'..iparent

			local sortkeyi = 'sortkey'..iparent

			while confcurrGroup][currYDCF][parenti do

				local parent = confcurrGroup][currYDCF][parenti

				local sortkey = confcurrGroup][currYDCF][sortkeyi

				

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

				if currYDCF == 'year' then

					if nav == nil then

						local args = { 'min' = minYear, 'skip-gaps' = 'yes' }

						if parentArg and currentTitle.namespace ~= 14 then

							args'testcase' = parentArg

						end

						nav = frame:expandTemplate{ title = 'Category series navigation', args = args }

					end

					if parent == 'century' then

						if isNilOrEmpty(sortkey) then sortkey = currYear end --default to currYear

						categoriesiparent = '[[Category:'..currGroup..' described in the '..addOrd(parentCent)..' century|'..sortkey..']]'

						

					elseif parent == 'biology' then

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none

						else sortkey = '|'..sortkey end

						if tonumber(currYear) < 1865 then

							categoriesiparent = '[[Category:'..currYear..' in science'..sortkey..']]' --biology cat structure doesn't exist pre-1865, as of 10/2018

						else

							categoriesiparent = '[[Category:'..currYear..' in biology'..sortkey..']]' --if/when all biology cats exists, merge this elseif with 'paleontology'

						end

						

					elseif parent == 'paleontology' then

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none

						else sortkey = '|'..sortkey end

						categoriesiparent = '[[Category:'..currYear..' in '..parent..sortkey..']]'

						

					elseif parent == 'environment' then

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none

						else sortkey = '|'..sortkey end

						categoriesiparent = '[[Category:'..currYear..' in the environment'..sortkey..']]'

						

					elseif mw.ustring.match(parent, '^%u[%l ]+') then --e.g. Animals/Insects/Fossil taxa

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none

						else sortkey = '|'..sortkey end

						categoriesiparent = '[[Category:'..parent..' described in '..currYear..sortkey..']]'

						

					else

						trackingCats2 = '[[Category:Described in year error|Y]]' --invalid year-parent

					end

					

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

				elseif currYDCF == 'century' then

					if nav == nil then

						local args = {}

						if parentArg and currentTitle.namespace ~= 14 then

							args'testcase' = parentArg

						end

						nav = frame:expandTemplate{ title = 'Container category' } .. 

							  frame:expandTemplate{ title = 'Category series navigation', args = args }

					end

					if parent == 'formal' then

						if isNilOrEmpty(sortkey) then sortkey = addOrd(currCent) end --default to currCent

						categoriesiparent = '[[Category:'..currGroup..' by year of formal description|'..sortkey..']]'

						

					elseif parent == 'biology' then

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none

						else sortkey = '|'..sortkey end

						if tonumber(currCent) < 19 then

							categoriesiparent = '[[Category:'..addOrd(currCent)..' century in science'..sortkey..']]' --biology cat structure doesn't exist pre-1865, as of 10/2018

						else

							categoriesiparent = '[[Category:'..addOrd(currCent)..' century in biology'..sortkey..']]' --if/when all biology cats exists, merge this elseif with 'paleontology'

						end

						

					elseif parent == 'paleontology' then

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none

						else sortkey = '|'..sortkey end

						categoriesiparent = '[[Category:'..addOrd(currCent)..' century in '..parent..sortkey..']]'

						

					elseif parent == 'environment' then

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none

						else sortkey = '|'..sortkey end

						categoriesiparent = '[[Category:'..addOrd(currCent)..' century in the environment'..sortkey..']]'

						

					elseif mw.ustring.match(parent, '^%u[%l ]+') then --e.g. Animals/Insects/Fossil taxa

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none

						else sortkey = '|'..sortkey end

						categoriesiparent = '[[Category:'..parent..' described in the '..addOrd(currCent)..' century'..sortkey..']]'

						

					else

						trackingCats2 = '[[Category:Described in year error|C]]' --invalid century-parent

					end

					

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

				elseif currYDCF == 'formal' then

					local formalParentsDefaultSortkey_Space = {

						'Animals' = true,

						'Insects' = true,

						'Molluscs' = true,

						'Fungi' = true,

					}

					local formalParentsDefaultSortkey_None = {

						'Species' = true,

						'Taxa' = true,

						'Fossil taxa' = true,

					}

					if nav == nil then

						nav = frame:expandTemplate{ title = 'Container category' }

					end

					if parent == 'Group' then

						if isNilOrEmpty(sortkey) then sortkey = ' Year' end --default to " Year"

						categoriesiparent = '[[Category:'..currGroup..'|'..sortkey..']]'

						

					elseif parent == 'paleontology' then

						if isNilOrEmpty(sortkey) then sortkey = ' ' end --default to " "; special parent

						categoriesiparent = '[[Category:Paleontology by year|'..sortkey..']]'

						

					elseif parent then --allow freeform formal-parents, as long as they exist

						if mw.title.new( parent, 'Category' ).exists then

							if sortkey then

								categoriesiparent = '[[Category:'..parent..'|'..sortkey..']]'

							else

								categoriesiparent = '[[Category:'..parent..']]'

							end

						else

							trackingCats2 = '[[Category:Described in year error|G]]' --invalid freeform formal-parent

						end

						

					elseif formalParentsDefaultSortkey_Spaceparent then

						if isNilOrEmpty(sortkey) then sortkey = ' ' end --default to " "; normal parent

						categoriesiparent = '[[Category:'..parent..' by year of formal description|'..sortkey..']]'

						

					elseif formalParentsDefaultSortkey_Noneparent then

						if isNilOrEmpty(sortkey) then sortkey = '' --default to none; normal parent

						else sortkey = '|'..sortkey end

						categoriesiparent = '[[Category:'..parent..' by year of formal description'..sortkey..']]'

						

					else

						trackingCats2 = '[[Category:Described in year error|F]]' --invalid formal-parent

					end

					

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

				else

					trackingCats2 = '[[Category:Described in year error|U]]' --unknown configuration

				end

				

				iparent = iparent + 1

				parenti = 'parent'..iparent

				sortkeyi = 'sortkey'..iparent

			end --while conf[currGroup][currYDCF][parenti] do

		end --if bConfError == false then

		

		--check for non-existent cats

		for _, category in pairs(categories) do

			local cat = mw.ustring.match(category, '%[%[Category:([%w%s]+)')

			if mw.title.new(cat, 14).exists == false then

				trackingCats1 = '[[Category:Described in year unknown category]]'

				break

			end

		end

		

	end --if currCat then

	

	--build header & rem surrounding whitespace

	local br = '<br />'

	if nav then header = nav end

	if portal then header = header..'\n'..portal end

	if commons then header = header..'\n'..commons end

	if wikispecies then header = header..'\n'..wikispecies end

	if description and description ~= '' then

		header = header..description

	elseif portal or commons or wikispecies then 

		header = mw.ustring.gsub(header, '<br ?/?>', '')

	end

	if toc then header = header..br..toc end

	header = mw.text.trim(header)

	header = mw.ustring.gsub(header, '^<br />', '')

	header = mw.ustring.gsub(header, '<br />$', '')

	

	--append header to outString

	if outString then outString = outString..header

	else outString = header end

	

	--append cats to outString

	if currentTitle.namespace == 14 then --category namespace

		if table.maxn(categories) > 0 then outString = outString..table.concat(categories) end

		outString = outString..table.concat(trackingCats)

	else

		if table.maxn(categories) > 0 then --might be 0 if there's an error before setting cats

			outString = outString..br..mw.ustring.gsub(table.concat(categories, br), '%[%[', '[[:')

		end

		outString = outString..br..mw.ustring.gsub(table.concat(trackingCats, br), '%[%[', '[[:')

		outString = mw.ustring.gsub(outString, br..br, br) --produced by empty ('') first/consecutive tracking cat/s

		outString = mw.ustring.gsub(outString, br..br, br) --jic (use while loop if #trackingCats >= 3 or 4)

	end

	

	return outString

end



return p