Module:Autocat

From blackwiki
Jump to navigation Jump to search

It is the module that runs Template:T template.

How it works

  1. First, it catches any category page title, e.g. 2000 births.
  2. Later, this title is split into parts. Takes all the numbers from the beginning and make them the year parameter; because for this type of categories year always is at the beginning (for now, until there would be cases in the future that disproves this situation). After the year, if there are year-based suffixes, like in 2000s there is the "s", or, 21st-century there is the "st-century", clean those. Finally tha all latter word(s) role as category name. In this case the year is 2000, and the category name is births.
  3. Now, we need to access some sort of data. Module:Autocat/data/people contains the births object (of course, there would be different data modules, the module detects it).
  4. At the top of the category page, from the year that the category is in, 5 upwards and 5 downwards category links are added in a header bar. Also, there is a parent bar right below the main bar. In this one, the upper stage categories are listed, for example in 2000 births category, which is a year category, there should be 2000s births type of categories right below in the parent bar. But this parent bar only shows up if the parent category exists. The pattern continues through all stages, in the 2000s births category, the parent bar now contains 21st century births type of categories, since we are now in a decade category. Module always tries to find the parent category and put a parent bar. There can only be two bars in one category pages, and those are one header bar and one parent bar.
  5. Right below these bars, there are descriptions. If the object has a special description text specified in the data, it can be shown in the page.
  6. On the up right corner of the page, there can be a Commons link template. This module, finds the wikidata item of the current page, and checks if the item has a commons category, if so, it automatically inserts a commons link.
  7. And lastly the category is added to all parents which are extracted from the data, all with their sort keys as well.

Structure of objects

Structure of a simple object (each object can have similar labels):

k["births"] = {
	parents = {"beginnings", "people"},
	sort_keys = {"B", "B"},
	stage = 4,
	description = "'''People born''' {{{hide|in the}}} {{{type}}} {{{year}}}",
	see = {"deaths"},
	byCat = "up",
}

parents=

This is the list of the parent categories' category names. In the example above, the first parent would be beginnings. This means, the module needs to take the year parameter from the category page title, and add before this parent's category name. The year is 2000. So the result should be 2000 beginnings. In this case, the module goes to the parent category's object:

k["beginnings"] = {
	parents = {"categories"},
	sort_keys = {"B"},
	stage = 4,
	see = {"endings"},
}

For the second parent people, the same functions repeat.

Later on, the category needs to be added to the upper stage category, 2000 is a year so the upper stage for this should be a decade, but which decade? The module detects this through a function and finds the result 2000s, in the end the category name would be 2000s births. For each object, the upper stage is automatically detected. Which means, if the template is used in 2000s births, the module understands that this is a decade category and the upper stage should be a century, but only the topic for the category is sorted in that upper stage on Wikipedia. For an object, how many stages there are is explained in the stage= title below.

sort_keys=

This is a table type. In this table, there are sort keys for all the parents. A sort key specifies the sorting letter in the parent category. For example in the [[Category:2020 in art|E]] code, E is a sort key. Each category, can be added to their parents with different sort keys. The important thing is that the parents and the sort keys should be in the same order.

stage=

It specifies in which periods of time that the topic are categorised on Wikipedia at the time. In the example above, the stage value is 4. Which means, births type categories are sorted by year, decade, century, and, millennium:

But on the other hand, compilation albums has this value of 2:

k["compilation albums"] = {
	parents = {"albums"},
	sort_keys = {"C"},
	stage = 2,
}

It is clear that, the stage number 2, means that compilation albums has categories by year or decade. This stage value can be changed later.

description=

If the object has a special description text specified in the data, it can be shown in the page.

  • {{{hide|}}} is used to hide any text in a "by category", it is common to hide prefixes or suffixes that normally go with the years.
  • {{{type}}} is for the stage of time, could be year, decade, century, millennium or date or period or time.
  • {{{year}}} the year parameter for the page, for "by categories" this parameter becomes the type.

see=

If there should be category links at the top with Template:T, they can be listed on this label. In the example {"deaths"} code means to go look for the deaths object. This value is always a table, inside there could be "deaths" and other object names. So, births object provides us a link in the end.

portals=

If there should be portal links at the top with Template:T, they can be listed on this label as a table.

byCat=

This label is for "by categories" only. Values are:

  • up: It is used to indicate that this topic hasn't got any "... by date", "... by time", "... by period" category.
  • no: This means that in any "... by date", "... by time", "... by period" category, the category has no main category, therefore should not be in one.
  • any other title: If any "... by date", "... by time", "... by period" category, has a main category with a different name, for example in Buildings and structures by millennium of completion category, the main category is not "Buildings and structures by date of completion" but Buildings and structures by date. It is a different pattern.

If this value is a table, the first value of the table should be either "up" or "no" (it is the usage as above), and the second one is "any other title" (it is the same usage as above).

Data modules

If there is so many data on a page, the pages can be divided by their main categories.

List

All the data:

data
The sample year is 2020.
Period Year Suffix
beginnings
beginnings by period

parents: categories

see also: endings

disestablishments
disestablishments by period

parents: endings

description: This category is for organizations, places or other things abolished or disestablished {{{hide|in the}}} {{{type}}} {{{year}}}

see also: establishments

endings
endings by period

parents: categories

see also: beginnings

establishments
establishments by period

parents: beginnings

description: This category is for organizations, places or other things founded or established {{{hide|in the}}} {{{type}}} {{{year}}}

see also: disestablishments

data/people
The sample year is 2020.
Period Year Suffix
births

parents: beginnings, people

description: People born {{{hide|in the}}} {{{type}}} {{{year}}}

see also: deaths

deaths
deaths by period

parents: endings, people

description: People who died {{{hide|in the}}} {{{type}}} {{{year}}}

see also: births

people
people by period

parents: categories

see also: births, deaths

data/arts
The sample year is 2020.
Period Year Suffix
albums
albums by period

parents: in music, works

compilation albums
compilation albums by period

parents: albums

in music
music by period

parents: in the arts

in the arts
the arts by period

parents: categories

works
works by period

parents: categories, in the arts

description: Works published, created or produced {{{hide|in the}}} {{{type}}} {{{year}}}


--by HastaLaVi2

---*---*---*---*---*---*---*---*---*---*---*---*---
-- CATEGORY OBJECT
---*---*---*---*---*---*---*---*---*---*---*---*---
local Category = {}


function Category:getName()
	return self._name
end


function Category:getCategoryName()
	local year = self._year
	local name = self:getName()
	
	local function check(year, name)
		return mw.title.new('Category:' .. fixBC(year) .. " " .. name).exists and fixBC(year) .. " " .. name
	end
	
	if self._type == "year" then
		return fixBC(year) .. " " .. name
	elseif self._type == "decade" then
		return fixBC(year.."s") .. " " .. name
	elseif self._type == "century" then
		return check(suffixToYear(year).." century", name) or fixBC(suffixToYear(year).."-century") .. " " .. name
	elseif self._type == "millennium" then
		return check(suffixToYear(year).." millennium", name) or fixBC(suffixToYear(year).."-millennium") .. " " .. name
	else
		return name
	end
end


function Category:getBarName()
	return self._type == "year" and fixBC(self._year)
		or (self._type == "decade" and fixBC(self._year.."s"))
		or (self._type == "century" and fixBC(suffixToYear(self._year)))
		or (self._type == "millennium" and fixBC(suffixToYear(self._year)))
end


function Category:getYear()
	return self._year
end


function Category:byCat()
	return self._rawData.byCat
end


function Category:stage()
	return self._rawData.stage
end


function Category:getUpYear()
	return self._type == "year" and getDecade(self._year) or
		(self._type == "decade" and getCentury(self._year) or
		(self._type == "century" and getMillennium(self._year) ))
end


function Category:getWikidata()
	return mw.wikibase.getEntity(mw.wikibase.getEntityIdForCurrentPage())
end


function Category:getParents()
	local parents = {}

	--[[
		if there is already a specified "by category" in parents table,
		we do not need to add a new one, but by default this boolean should always be true
	--]]
	local byCat = true

	--list of the parents
	local chain = self._rawData.parents or {}
	local _type = self._type
	
	--call objects for each parent
	for _,parent in ipairs(chain) do
		if parent == "categories" and _type ~= "byCat" then parent = "" end
		if parent == "years" or parent == "decades" or parent == "centuries" or parent == "millennia" then _type = "up" end
		parent = mw.ustring.gsub(parent, "{{{type}}}", _type)
		table.insert(parents, getByName(parent, self._year, _type))
		
		--and here we detect if one of the parents is a "by category"
		if mw.ustring.find(parent, "%sby%s") then
			byCat = nil
		end
	end
	
	--time to call the upper parent
	if self:getUpType() then
		table.insert(parents, getByName(self:getName(), self:getUpYear(), self:getUpType()))
	end

	--[[
		when we call the parents table, we should also call each of them as objects of their owns,
		but, other than normal parent listings on the data, each category has an "upper" category,
		which means for "2010 in art", the upper category is "2010s in art", and for "2010s in art"
		the upper category is "21st century in art", and so on
		
		but to detect the upper category we need to find which decade, century or millennium
		in the year we are currently (current for the page) in for the category
	--]]
	if self._name ~= "" and self._type ~= "byCat" and byCat then
		local name =  mw.ustring.gsub(self._name, "^in%sthe%s", "")
		name =  mw.ustring.gsub(name, "^in%s", "")
		table.insert(parents, getByName(name.." by "..self._type, self._year, self._type))
	end
	
	--export the objects
	return parents or {}
end


function Category:getPortals()
	return self._rawData.portals or {}
end


function Category:getSeeCat()
	local see = self._rawData.see or {}
	local last = {}
	
	if self._type == "byCat" then
		for _,b in ipairs(see) do
			table.insert(last, mw.getContentLanguage():ucfirst(b).." "..self._year)
		end
	else
		for _,b in ipairs(see) do
			table.insert(last, getByName(b, self._year, self._type):getCategoryName())
		end
	end
	
	return last
end


function Category:getDescription()
	if self._rawData.description then
		return mw.getContentLanguage():ucfirst(handleParams(self._rawData.description, self._year, self:getType())) .. "."
	else
		return "This category contains "..mw.getContentLanguage():lcfirst(mw.title.getCurrentTitle().text).."."
	end
end


function Category:getType()
	return self._type
end


function Category:getUpType()
	if self._type == "year" and self._rawData.stage and self._rawData.stage > 1 then
		return "decade"
	elseif self._type == "decade" and self._rawData.stage and self._rawData.stage > 2 then
		return "century"
	elseif self._type == "century" and self._rawData.stage and self._rawData.stage > 3 then
		return "millennium"
	end
end


function Category:getDataModule()
	return self._module
end


function Category:getSortKeys()
	local chain = {}
	local sortKeys = {}
	
	--years and their equivalent sort keys
	local keys = {["year"]="YLAST", ["decade"]="YLASTTWO", ["century"]="FOUR", ["millennium"]="FOUR"}
	
	for _,a in ipairs(self._rawData.sort_keys or {}) do table.insert(chain, a) end
	
	if self:getUpType() then
		table.insert(chain, keys[self._type])
	end
	
	table.insert(chain, "DORTLU")

	for _,key in ipairs(chain) do
		if key == "YLAST" then key = mw.ustring.sub( self._year, -1 )
		elseif key == "YLASTTWO" then key = mw.ustring.sub( self._year, -2 )
		elseif key == "FOUR" then key = mw.ustring.find(self._year, "-") and "-"..tostring(9999+tonumber(self._year))
			or (string.len(self._year) == 1 and "000"..self._year
			or (string.len(self._year) == 2 and "00"..self._year
			or (string.len(self._year) == 3 and "0"..self._year
			or (string.len(self._year) == 4 and self._year)))) end
		table.insert(sortKeys, key)
	end

	return sortKeys or {}
end


function Category:toJSON()
	local ret = {
		parents = self:getParents(),
		description = self:getDescription(),
		name = self:getName(),
		year = _year,
		type = _type,
		sort_keys = self:getSortKeys(),
		}
	
	return require("Modül:JSON").toJSON(ret)
end


function Category:getRawData()
	return self._rawData
end


Category.__index = Category


function createObject(name, data, year, type, module)
	return data and
		setmetatable({ _rawData = data, _name = name, _year = year, _type = type, _module = module }
			, Category)or nil
end


function getByName(name, year, _type)
	local data_pages = {
		"data",
		"data/people",
		"data/arts",
	}
	
	local data_module
	
	for _, page in ipairs(data_pages) do
		data_module = "Module:Autocat/" .. page
		if require(data_module)[name] then
			name = type(require(data_module)[name]) == "string" and require(data_module)[name] or name
			break
		elseif require(data_module)["in the "..name] then
			name = "in the "..name
			break
		elseif require(data_module)["in "..name] then
			name = "in "..name
			break
		end
	end
	
	local data = mw.loadData(data_module)
	
	if not data[name] then
		data = {[name] = {}}
		if name == "" then
			local sp = {["century"]="centuries", ["millennium"]="millennia"}
			data[""] = {
				parents = {(sp[_type] or _type).."s"},
				sort_keys = {"FOUR"},
				stage = 4,
			}
		end
	end
	
	if mw.ustring.find(name, "%sby%s") then _type = "byCat" end

	return createObject(name, data[name], year, _type, data_module)
end

---*---*---*---*---*---*---*---*---*---*---*---*---
-- REFLECTING FUNCTIONS
---*---*---*---*---*---*---*---*---*---*---*---*---

--find and replace function
local function findandrep(text, one, two)
	return mw.ustring.sub( mw.ustring.gsub(text, one, two), 1, -1 )
end

--find function
local findIn = mw.ustring.find

local function editLink(category)
	--her kategoride "veriyi düzenle" bağlantısını eklemeye yarayan fonksiyon
	return "<div class=\"toccolours hlist plainlinks\" style=\"float: right; margin: 0.5em 0 0.5em 1em; font-weight: bold;\">[" ..
		mw.getCurrentFrame():callParserFunction{name = "fullurl", args = {category, action = "edit"}} ..
		" Edit category data]</div>"
end

--function to find the decade for a year
--for example if the year 2019, the result would be 2010
--and for 1888, the result is 1880
function getDecade(year)
	local dash = findIn(year, "^-") and "-" or nil
	local decade = findandrep(year, "^-", "")
	local result = string.len(decade) == 1 and "0" or mw.ustring.sub(decade, 1, -2) .. 0
	return (dash and dash or "") .. result
end

function getCentury(year)
	local dash = findIn(year, "^-") and "-" or nil
	local century = findandrep(year, "^-", "")
	local result = (string.len(century) == 1 or string.len(century) == 2) and 1 or tonumber(mw.ustring.sub(century, 1, -3)+1)
	return (dash and dash or "") .. tostring(result)
end

function getMillennium(year)
	local dash = findIn(year, "^-") and "-" or nil
	local mill = findandrep(year, "^-", "")
	local first = mw.ustring.sub(mill, 1, 1)
	local last = mw.ustring.sub(mill, -1)
	
	local result = (string.len(mill) ~= 1 and last ~= "0") and tonumber(first)+1 or tonumber(last)
	result = string.len(mill) == 1 and 1 or result
	
	return (dash and dash or "") .. tostring(result)
end

function yearPlus(year, number)
	local result = tonumber(year) + number
	return tostring(result)
end

function fixBC(year)
	local result = findandrep(year, ".*%-", "")
	local function check(y,v) return findIn(y, "%s"..v) and " "..v or (findIn(y, "-"..v) and "-"..v) end
	local suffix = check(year,"century") or check(year,"millennium")
	result = mw.ustring.gsub(result, "%s.*", "") .. (suffix or "")
	return findIn(result, "%d") and findandrep(year, "%-"..result, result.." BC") or year
end

function handleParams(name, year, _type)
	local result = name
	local century = _type == "century" and year or (tonumber(year) and getCentury(year) or year)
	local mill = _type == "millennium" and year or (tonumber(year) and getMillennium(getCentury(year)) or year)
	
	local lYear = _type == "year" and year
		or (_type == "decade" and year.."s")
		or (_type == "century" and suffixToYear(year) .. "-century")
		or (_type == "millennium" and suffixToYear(year) .. "-millennium")
		or year
	
	_type = (_type == "byCat" or _type == "century" or _type == "millennium") and "" or _type
	
	result = findandrep(result, "{{{year}}}", lYear)
	result = findandrep(result, "{{{type}}}", _type)
	
	--hidden items for by categories
	if _type == "byCat" and findIn(result, '%{%{%{hiden%|([^%}%}%}]+)') then
		result = findandrep(result, '%{%{%{hide%|([^%}%}%}]+)', '')
	end
	
	result = findandrep(result, '%{%{%{hide%|', '')
	result = findandrep(result, '%}%}%}', '')

	return fixBC(result)
end

--header bar for the next and the previous 5 stages
function headerBar(category)
	--we will collect all the data inside this table
	local result = {}
	
	--[[
		:getName() value, is the part after the numbers in a
		category name, so for "2010s works", this value
		would be "works"
	--]]
	local suffix = category:getName()
	
	--getting the year for the category
	local year = category:getYear()

	local function repeatF(y, _type, name)
		local function ifExists(page)
			return mw.title.new('Category:' .. page).exists
		end
		--we need to call 5 down-level and 5 upper-level categories
		for i = -5, 5 do
			--this will give us the next year value
			--each time the loop starts
			local nextYear = yearPlus(y, i)
			if _type == "year" or _type == "century" or _type == "millennium" then
				last = getByName(name, nextYear, _type)
			elseif _type == "decade" then 
				nextYear = yearPlus(getDecade(y), i..0)
				last = getByName(name, nextYear, "decade")
			end
			--let us check the pages
			
			--special case for 0s BC
			if _type == "decade" and last:getBarName() == "0s" then
				if ifExists(findandrep(last:getCategoryName(), "0s", "0s BC")) then
					table.insert(result, '\n*[[:Category:' .. findandrep(last:getCategoryName(), "0s", "0s BC") .. "|" .. last:getBarName() .. " BC]]")
				else
					table.insert(result, '\n*<span style="color:#888">' .. last:getBarName() .. " BC</span>")
				end
			end
			
			--if category exists
			if ifExists(last:getCategoryName()) then
				table.insert(result, '\n*[[:Category:' .. last:getCategoryName() .. "|" .. last:getBarName() .. "]]")
			else
				--and if not
				table.insert(result, '\n*<span style="color:#888">' .. last:getBarName() .. "</span>")
			end
		end
	end
	
	local function checkParent(category, result, _type)
		if category:getUpType() and category:getUpType() == _type then
			table.insert(result, '\n|}')
			table.insert(result, '\n{| class="toccolours hlist" style="text-align: center; margin: auto; border: none; background: transparent;"'
				.. '\n|')
		end
	end
	
	--start of the main header bar
	table.insert(result, '<div style="padding-bottom: 10px;">\n{| class="toccolours hlist" style="text-align: center; margin: auto;"'
		.. '\n|')
	
	--check for the years
	if category:getType() == "year" then
		repeatF(year, "year", suffix)
	end
	
	--[[
		if we are in a decade categıry, the getCentury() function
	    would find the right century for that decade
	    
	    but if we are already in a century or in a millennium category
	    no need to run these functions, otherwise the results will be wrong
	--]]
	if category:getType() == "century" then
		function getCentury(year) return year end
	elseif category:getType() == "millennium" then
		function getMillennium(year) return year end
	end
	
	--DECADE
	checkParent(category, result, "decade")
	if (category:getUpType() and category:getUpType() == "decade") or category:getType() == "decade" then
		repeatF(year, "decade", suffix)
	end
	
	--CENTURY
	checkParent(category, result, "century")
	if (category:getUpType() and category:getUpType() == "century") or category:getType() == "century" then
		repeatF(getCentury(year), "century", suffix)
	end
	
	--MILLENNIUM
	checkParent(category, result, "millennium")
	if (category:getUpType() and category:getUpType() == "millennium") or category:getType() == "millennium" then
		repeatF(getMillennium(year), "millennium", suffix)
	end
	
	--the end of the header bar
	table.insert(result, '\n|}\n</div>')
	
	--export all data
	return table.concat(result)
end

--this function handles the year suffixes
--for example: if it gets the number "1",
--it exports the result "1st", "2nd" for "2" and "3rd" for "3" etc...
function suffixToYear(year)
	--detect the last digit
	local lastDigit = mw.ustring.sub( tostring(year), -1 )
	
	--special cases for 1, 2, 3
	local specials = {"st", "nd", "rd"}
	
	--if the last digit is 1, 2, or 3 get the special case or the default
	local suffix = specials[tonumber(lastDigit)] and specials[tonumber(lastDigit)] or "th"
	
	return tostring(year) .. suffix
end

--[[
	this function is the core to this module,
	it takes the page name automatically,
	and detects if the page is a year, decade, century, or a millennium category
	also, it splits the title into parts, so that we know
	which category objects to call
	
	for example, "2017 web series debuts";
	
	it is a year category
	and the year is: 2017
	category prefix is: web series debuts
	
	NOTE: the result type is always a table containing these data
--]]
function splitTitle(name)
	--start the year parameter
	local year = ""
	local _type
	
	--is this a BC category?
	if findIn(name, "%sBC") then
		name = findandrep( name, "%sBC", " " )
		BC = true
	end
	
	--if the category name has numbers as prefix
	--this means we can easily detach the numbers
	--from the beginning
	while findIn(name, "^[%d]") do
		year = year .. mw.ustring.sub(name, 1, 1)
		name = findandrep(name, "^[%d]", "")
		--do we have a valid year now?
		doWeHaveYear = true
	end
	
	--for century or millennium categories
	--there could be "st, nd, rd, or th" letters
	--after the number
	name = findandrep(name, "^st", "")
	name = findandrep(name, "^nd", "")
	name = findandrep(name, "^rd", "")
	name = findandrep(name, "^th", "")
	
	--if we have a valid year
	if doWeHaveYear then
		--now it is time to detect the category type
		--year, decade, century or millennium
		if findIn(name, "^s") then
			name = findandrep(name, "^s", "")
			_type = "decade"
		elseif findIn(name, "[%-%s]century") then
			name = findandrep(name, "[%-%s]century", "")
			_type = "century"
		elseif findIn(name, "[%-%s]millennium") then
			name = findandrep(name, "[%-%s]millennium", "")
			_type = "millennium"
		else
			_type = "year"
		end
		name = findandrep(name, "^%s", "")
		--the result table
		return {(BC and "-" or "") .. year, name, _type}
	else
		return {nil, name}
	end
end

function _main(frame)
	local category = getByName("births", "2000", "year")
	
	return fixBC("-1st century")
end

function main(frame)
	--our main parameter or the page name
	local name = frame:getParent().args[1] or mw.title.getCurrentTitle()["text"]
	
	--for all the categories
	local categories = {}
	--for all the display items
	local display = {}
	
	--our year parameter
	local year = splitTitle(name)[1]
	--the category object for the page
	local category = getByName(splitTitle(name)[2], year, splitTitle(name)[3])
	--parents table
	local parents = category:getParents()
	
	table.insert(display, headerBar(category))
	table.insert(display, editLink(category:getDataModule()))

	if category:getWikidata() and category:getWikidata().claims and category:getWikidata().claims["P373"] then
		table.insert(display, mw.getCurrentFrame():expandTemplate{
			title = "Commons category",
			args = {category:getWikidata():formatPropertyValues("P373").value}})
	end
	
	if mw.title.new(name).exists then
		table.insert(display, mw.getCurrentFrame():expandTemplate{
			title = "Main",
			args = {name}})
	end
	
	if category:getPortals()[1] then
		table.insert(display, mw.getCurrentFrame():expandTemplate{
			title = "Portal",
			args = {category:getPortals()[1], category:getPortals()[2], category:getPortals()[3], category:getPortals()[4]}})
	end
	
	if category:getSeeCat()[1] then
		table.insert(display, mw.getCurrentFrame():expandTemplate{
			title = "Category see also",
			args = {category:getSeeCat()[1], category:getSeeCat()[2], category:getSeeCat()[3], category:getSeeCat()[4]}})
	end
	
	if category:getDescription() then
		table.insert(display, category:getDescription())
	end
	
	--CatAutoTOC
	table.insert(display, mw.getCurrentFrame():expandTemplate{title = "CatAutoTOC",args = {}})
	
	--insert the category to each parent categories
	for key,parent in ipairs(parents) do
		table.insert(categories, "[[Category:" .. parent:getCategoryName() .. "|" .. category:getSortKeys()[key] .. "]]")
	end
	
	--is this category empty?
	if mw.site.stats.pagesInCategory(mw.title.getCurrentTitle().text, "all") == 0 then
		table.insert(categories, "[[Category:Empty categories]]")
	end
	
	return table.concat(display) .. table.concat(categories)
end

function byCat(frame)
	local args = frame:getParent().args
	
	local categories = {}
	local display = {}
	
	local year = args[1]
	local _type = args[2]
	local category = getByName(_type, "by "..year, "byCat")
	
	local by = type(category:byCat()) == "table" and category:byCat()[1] or category:byCat()

	if by and by ~= "no" and by ~= "up" then
		yukari = type(category:byCat()) == "table" and category:byCat()[2] or category:byCat()
	end
	
	local function catExists(cat) if mw.title.new('Category:'..cat).exists then return cat end end
	local perDateTime = catExists(_type..' by date') or catExists(_type..' by time') or catExists(_type..' by period')
	if category:stage() and category:stage() > 1 and by ~= "up" then
		if perDateTime then
			table.insert(display, '\n<div style="text-align:center;">'
				..'[[:Category:' .. perDateTime .. "|"..perDateTime.."]]</div>")
		end
		
		table.insert(display, '<div style="padding-bottom: 10px;">'
		.. '\n{| class="toccolours hlist" style="text-align: center; margin: auto;"'
		.. '\n|')
		
		if catExists(_type..' by year') then
			table.insert(display, "\n*[[:Category:" .. _type.." by year|by year]]")
		end
		if catExists(_type..' by decade') then
			table.insert(display, "\n*[[:Category:" .. _type.." by decade|by decade]]")
		end
		if catExists(_type..' by century') then
			table.insert(display, "\n*[[:Category:" .. _type.." by century|by century]]")
		end
		if catExists(_type..' by millennium') then
			table.insert(display, "\n*[[:Category:" .. _type.." by millennium|by millennium]]")
		end
		
		table.insert(display, '\n|}\n</div>')
	end

	table.insert(display, editLink(category:getDataModule()))
	
	if category:getWikidata() and category:getWikidata().claims and category:getWikidata().claims["P373"] then
		table.insert(display, mw.getCurrentFrame():expandTemplate{
			title = "Commons category",
			args = {category:getWikidata():formatPropertyValues("P373").value}})
	end
	
	if category:getPortals()[1] then
		table.insert(display, mw.getCurrentFrame():expandTemplate{
			title = "Portal",
			args = {category:getPortals()[1], category:getPortals()[2], category:getPortals()[3], category:getPortals()[4]}})
	end
	
	if category:getSeeCat()[1] then
		table.insert(display, mw.getCurrentFrame():expandTemplate{
			title = "Category see also",
			args = {category:getSeeCat()[1], category:getSeeCat()[2], category:getSeeCat()[3], category:getSeeCat()[4]}})
	end
	
	if category:getDescription() then
		table.insert(display, category:getDescription())
	end
	
	local periodCheck = year == "period" or year == "date" or year == "time"
	if periodCheck and by == "no" then
	elseif by == "up" or periodCheck then
		_type = up or _type
		table.insert(categories, "[[Category:" .. mw.getContentLanguage():ucfirst(_type) .. "|+]]")
	elseif findIn(category:getName(), "and "..year) then else
		if perDateTime then
			table.insert(categories, "[[Category:" .. perDateTime .. "| ]]")
		end
	end
	
	if category:getParents() then
		for k,parent in ipairs(category:getParents()) do
			local e =  mw.ustring.gsub(parent:getName(), "^in%sthe%s", "")
			local e =  mw.ustring.gsub(parent:getName(), "^in%s", "")
			if periodCheck then
				umbrella = catExists(e .. " by date") or catExists(e .. " by time") or e .. " by period"
			else
				umbrella = not findIn(category:getName(), "and "..year)
					and catExists(e .. " by type and " .. year) or e .. " by " .. year
			end
			table.insert(categories, "[[Category:" .. umbrella .. "|"
				.. (mw.site.stats.pagesInCategory(umbrella, "subcats") > 200
				and mw.ustring.char(0x0020) or category:getSortKeys()[k]) .. "]]")
		end
	end
	
	return table.concat(display) .. table.concat(categories)
end

return {main = main, byCat = byCat}