Moduuli:Eras

Jedipediasta, vapaasta Tähtien sota-tietosanakirjasta tänään, 28. maaliskuuta 2024
Siirry navigaatioonSiirry hakuun

Tämän moduulin ohjeistuksen voi tehdä sivulle Moduuli:Eras/ohje

-- <nowiki>

-------------------------------------------------------------------------------
--                              Module:Eras
--
-- This module renders the icons in the top-right corner of articles, as well
-- as the Canon and Legends tabs for pages with a Canon/Legends counterpart.
-- It also formats the page title with {{DISPLAYTITLE}}. It is a rewrite of
-- [[Template:Eras]].
-------------------------------------------------------------------------------

local DEBUG_MODE = false -- if true, errors are not caught

-------------------------------------------------------------------------------
-- Icon data
-------------------------------------------------------------------------------

--[[
-- This table stores data for all the icons displayed in the top-right. It can
-- have the following fields:
-- * image - the icon image name, minus any "File:" prefix (required).
-- * tooltip - the icon tooltip (optional).
-- * link - the page to link from the icon (optional).
-- * category - a category to go with the icon, minus any "Category:" prefix
--     (optional).
-- * protectionAction - for protection icons, an action such as "edit" or "move"
--     to check (optional).
-- * protectionLevel - for protection icons, the protection level to check,
--     such as "sysop". If the page doesn't have the right protection level
--     it is put in a tracking category and the icon is not displayed
--     (optional).
-- Note: this is just a convenient place to store the data. The subtables are
-- accessed from the code manually, so adding new subtables won't automatically
-- add new icons, and removing subtables may break things.
--]]

local iconData = {
	can = {
		image = 'Eras-canon-edit.png',
		tooltip = 'Tämän artikkelin aihe kuuluu kaanoniin.',
		link = 'Kaanon'
	},
	leg = {
		image = 'LegendsBanner.png',
		tooltip = 'Tämän artikkelin aihe kuuluu Legendsiin.',
		link = 'Star Wars Legends'
	},
	pre = {
		image = "30px-Era-pre2.png",
		tooltip = "Tämän artikkelin aihe esiintyi Tasavaltaa edeltävällä aikakaudella.",
		link = "Tasavaltaa edeltävä aikakausi"
	},
	btr = {
		image = "30px-Era-pre2.png",
		tooltip = "Tämän artikkelin aihe esiintyi Tasavaltaa edeltävällä aikakaudella.",
		link = "Tasavaltaa edeltävä aikakausi"
	},
	old = {
		image = "30px-Era-old.png",
		tooltip = "Tämän artikkelin aihe esiintyi Vanhan Tasavallan aikakaudella.",
		link = "Vanhan Tasavallan aikakausi"
	},
	imp = {
		image = "30px-Era-imp.png",
		tooltip = "Tämän artikkelin aihe esiintyi Imperiumin nousun aikakaudella.",
		link = "Imperiumin nousun aikakausi"
	},
	reb = {
		image = "30px-Era-reb.png",
		tooltip = "Tämän artikkelin aihe esiintyi kapinallisten aikakaudella.",
		link = "Kapinallisten aikakausi"
	},
	new = {
		image = "30px-Era-new.png",
		tooltip = "Tämän artikkelin aihe esiintyi Uuden tasavallan aikakaudella.",
		link = "Uuden tasavallan aikakausi (tosimaailma)"
	},
	njo = {
		image = "30px-Era-njo.png",
		tooltip = "Tämän artikkelin aihe esiintyi uuden jediritarikunnan aikakaudella.",
		link = "Uuden jediritarikunnan aikakausi"
	},
	lgc = {
		image = "30px-Era-leg.png",
		tooltip = "Tämän artikkelin aihe esiintyi perinnön aikakaudella.",
		link = "Perinnön aikakausi"
	},
	inf = {
		image = "30px-Era-inf.png",
		tooltip = "Tämän artikkelin aihe on osa Star Wars Infinitiesiä.",
		link = "Infinities"
	},
	real = {
		image = "30px-Era-real.png",
		tooltip = "Tämä artikkeli kertoo todellisesta asiasta tai henkilöstä.",
		link = "Luokka:Tosimaailma"
	},
	ss = {
		image = "30px-FeaturedIcon.png",
		tooltip = "Tämä artikkeli on suositeltu sivu.",
		link = "Jedipedia:Suositeltu sivu",
		category = "Suositellut sivut"
	},
	ess = {
		image = "30px-DefeaturedIcon.png",
		tooltip = "Tämä artikkeli on entinen suositeltu sivu.",
		link = "Jedipedia:Suositeltu sivu"
	},
	ha = {
		image = "30px-GoodIcon.png",
		tooltip = "Tämä artikkeli on hyvä artikkeli.",
		link = "Jedipedia:Hyvä artikkeli",
		category = "Hyvät artikkelit"
	},
	eha = {
		image = "30px-FormerGAicon.png",
		tooltip = "Tämä artikkeli on entinen hyvä artikkeli.",
		link = "Jedipedia:Hyvä artikkeli"
	},
	vkp = {
		image = "Queicon.png",
		tooltip = "Tällä artikkelin nimellä ei ole virallista käännöstä.",
		link = "Jedipedia:Virallisen käännöksen puuttuminen",
		category = "Virallisten käännösten puutteessa olevat artikkelit"
	},
	fprot = {
		protectionAction = "edit",
		protectionLevel = "sysop",
		image = "30px-Era-Fprotect.png",
		tooltip = "Tämä sivu on suojattu muokkauksilta.",
		link = "Jedipedia:Suojauskäytäntö"
	},
	sprot = {
		protectionAction = "edit",
		protectionLevel = "autoconfirmed",
		image = "30px-Era-Sprotect.png",
		tooltip = "Tämä sivu on osittaissuojattu muokkauksilta.",
		link = "Jedipedia:Suojauskäytäntö"
	},
	mprot = {
		protectionAction = "move",
		protectionLevel = "sysop",
		image = "30px-Era-Mprotect.png",
		tooltip = "Tämä sivu on suojattu siirrolta.",
		link = "Jedipedia:Suojauskäytäntö"
	},
	uprot = {
		protectionAction = "upload",
		protectionLevel = "sysop",
		image = "30px-Era-Uprotect.png",
		tooltip = "Tämä tiedosto on suojattu tallennuksilta.",
		link = "Jedipedia:Suojauskäytäntö"
	}
}

-------------------------------------------------------------------------------
-- Helper functions
-------------------------------------------------------------------------------

-- Find whether the specified page exists. We use pcall to catch errors if we
-- are over the expensive parser function count limit, or a number of other
-- juicy errors. This function increases the expensive parser function count
-- for every new page called.
local function exists(page)
	local success, title = pcall(mw.title.new, page)
	return success and title and title.exists or false
end

local function isRedirect(page)
	local success, title = pcall(mw.title.new, page)
	return success and title and title.isRedirect or false
end

-------------------------------------------------------------------------------
-- Eras class
-------------------------------------------------------------------------------

-- The eras class does all of the heavy lifting in the module. We use a class
-- rather than normal functions so that we can avoid passing lots of different
-- values around for each different function.

local Eras = {}
Eras.__index = Eras -- Set up inheritance for tables that use Eras as a metatable.

-- This function makes a new eras object. Here we set all the values from the
-- arguments and do any preprocessing that we need.
function Eras.new(args, title)
	local obj = setmetatable({}, Eras) -- Make our object inherit from Eras.
	obj.title = title or mw.title.getCurrentTitle()

	-- Set object structure
	obj.categories = {}

	-- Näytettävän otsikon parametrit
	obj.noDisplayTitle = args.notitle
	if args.title or args.title2 then
		obj.displayTitleBase = args.title
		obj.displayTitleParen = args.title2
	else
		obj.displayTitleBase = args.otsikko
		obj.displayTitleParen = args.otsikko2
	end

	-- Set hidden status
	obj.isHidden = args.hide

	-- Set notoc value
	obj.hideToc = args.notoc

	-- Jatkumon määrittäminen (parametri "tyyppi")
	if args.tyyppi then
		local override = args.tyyppi:lower()
		if override ~= 'kaanon' and override ~= 'legends' then
			obj:raiseError("jos parametri 'tyyppi' on määritetty, " ..
				"sen on oltava 'kaanon' tai 'legends'")
		end
		obj.continuityOverride = override
	end

	-- Kaanon- ja Legends-nimet artikkelille
	obj.legendsArticle = args.legends
	obj.canonArticle = args.kaanon

	-- Kuvakkeiden tiedot.
	do
		local icons = {}
		for _, v in ipairs(args) do
			local t = iconData[string.lower(v)]
			if t then
				icons[string.lower(v)] = t
			else
				-- Annettua kuvaketta ei ole tiedoissa, joten
				-- lisätään virheluokka.
				obj.hasBadParameter = true
			end
		end
		obj.icons = icons
	end

	return obj
end

-- Ilmoittaa virheestä. Jos DEBUG_MODE on false, tässä olevat virheet
-- huomataan funktiossa p._main.
function Eras:raiseError(msg)
	local level
	if DEBUG_MODE then
		level = nil
	else
		level = 0 -- Suppress module name and line number in the error message.
	end
	error(msg, level)
end

-- Add a category, to be rendered at the very end of the template output.
function Eras:addCategory(cat, sort)
	table.insert(self.categories, {category = cat, sortKey = sort})
end

-- Shortcut method for getting an icon data subtable.
function Eras:getIconData(code)
	return self.icons[code]
end

-- Päättyykö nykyinen otsikko /Kaanon.
function Eras:hasCanonTitle()
	return self.title.text:find('/Kaanon$')
end

-- Päättyykö nykyinen otsikko /Legends.
function Eras:hasLegendsTitle()
	return self.title.text:find('/Legends$')
end

-- Returns a boolean showing whether any of the icons were specified by the
-- user.
function Eras:hasAnyOfIcons(...)
	for i = 1, select('#', ...) do
		if self:getIconData(select(i, ...)) then
			return true
		end
	end
	return false
end	

-- Analysoi sivun nimen ja asettaa {{DISPLAYTITLE}}:n.
function Eras:renderDisplayTitle()
	local pagename = self.title.text

	-- Exit if we have been told not to set a title or if the title begins with
	-- an opening parenthesis.
	if self.noDisplayTitle or pagename:find('^%(') then
		return nil
	end

	-- Find the display base and the display parentheses.
	local dBase = self.displayTitleBase
	local dParen = self.displayTitleParen
	if not dBase or not dParen then
		-- Analyse the pagename to find base part and any ending parentheses.
		-- /Canon is removed, and parentheses are only recognised if they are
		-- at the end of the pagename.
		local trimmedPagename = pagename:gsub('/Kaanon$', '')
		trimmedPagename = trimmedPagename:gsub('/Legends$', '')
		local base, paren = trimmedPagename:match('^(.*)%s*%((.-)%)$')
		if not base then
			base = trimmedPagename
		end
		-- Use the values we found, but only if a value has not already been
		-- specified.
		dBase = dBase or base
		dParen = dParen or paren
	end

	-- Build the display string
	local display
	if dParen then
		display = string.format('%s <small>(%s)</small>', dBase, dParen)
	else
		display = dBase
	end
	if self.title.namespace ~= 0 then
		display = mw.site.namespaces[self.title.namespace].name .. ':' .. display
	end

	-- Return the expanded DISPLAYTITLE parser function.
	return mw.getCurrentFrame():callParserFunction('DISPLAYTITLE', display)
end

-- Renders an eras icon from the given icon data. It deals with the image,
-- tooltip, link, and the category, but not the protection fields.
function Eras:renderIcon(data)
	-- Render the category at the end if it exists.
	if data.category then
		self:addCategory(data.category)
	end
	-- Render the icon and return it.
	local ret = {}
	ret[#ret + 1] = '[[Tiedosto:'
	ret[#ret + 1] = data.image
	if data.tooltip then
		ret[#ret + 1] = '|'
		ret[#ret + 1] = data.tooltip
	end
	if data.link then
		ret[#ret + 1] = '|link='
		ret[#ret + 1] = data.link
	end
	ret[#ret + 1] = ']]'
	return table.concat(ret)
end

-- Renders a protection eras icon from the given data. If the page doesn't have
-- the specified protection level, returns nil and adds a flag to add a
-- tracking category later on in processing.
function Eras:renderProtectionIcon(data)
	if not data.protectionAction then
		return self:renderIcon(data)
	end
	local protectionLevel = self.title.protectionLevels[data.protectionAction]
	protectionLevel = protectionLevel and protectionLevel[1]
	if protectionLevel and protectionLevel == data.protectionLevel then
		return self:renderIcon(data)
	else
		self.hasIncorrectProtectionIcon = true
	end
end

-- Renders the first icon, either Canon or Legends, or nil if the continuity
-- couldn't be determined. This is equivalent to the previous {{Eraicon/canon}}
-- template.
function Eras:renderContinuityIcon()
	-- First, find what continuity to use, if any.
	local continuity, isUsingCategory
	if self.continuityOverride == 'kaanon' or self:hasAnyOfIcons('can') then
		continuity = 'canon'
		if not self:hasAnyOfIcons('real') then
			isUsingCategory = true
		end
	elseif self.continuityOverride == 'legends' or self:hasAnyOfIcons('leg') then
		continuity = 'legends'
		if not self:hasAnyOfIcons('real') then
			isUsingCategory = true
		end
	elseif self.title.namespace == 0 then
		if self:hasLegendsTitle() then
			continuity = 'legends'
			isUsingCategory = true
		elseif self:hasCanonTitle() then
			continuity = 'canon'
			isUsingCategory = true
		elseif self.legendsArticle then
			continuity = 'canon'
			isUsingCategory = true
		elseif self.canonArticle then
			continuity = 'legends'
			isUsingCategory = true
		elseif exists(self.title.text .. '/Legends') then
			continuity = 'canon'
			isUsingCategory = true
		elseif exists(self.title.text .. '/Kaanon') and not isRedirect(self.title.text .. '/Kaanon') then
			continuity = 'legends'
			isUsingCategory = true
		elseif self:hasAnyOfIcons('pre', 'btr', 'old', 'imp', 'reb', 'new',
			'njo', 'lgc')
		then
			continuity = 'legends'
			if self:hasAnyOfIcons('real') then
				isUsingCategory = false
			else
				isUsingCategory = true
			end
		elseif self:hasAnyOfIcons('inf') then
			continuity = 'legends'
			isUsingCategory = false
		elseif self:hasAnyOfIcons('real') then
			isUsingCategory = false
		else
			continuity = 'canon'
			isUsingCategory = true
		end
	end

	-- Generate the icon data and make the icon.
	if continuity == 'canon' then
		local data = iconData['can']
		if isUsingCategory then
			data.category = 'Kaanonartikkelit'
		end
		return self:renderIcon(data)
	elseif continuity == 'legends' then
		local data = iconData['leg']
		if isUsingCategory then
			data.category = 'Legends-artikkelit'
		end
		return self:renderIcon(data)
	end
end

-- Renders the icons that respond to a publishing era.
function Eras:renderPublishingIcons()
	if self.continuityOverride ~= 'kaanon'
		and not self.legendsArticle
		and not self:hasCanonTitle()
	then
		local ret = {}
		local codes = {'pre', 'btr', 'old', 'imp', 'reb', 'new', 'njo', 'lgc', 'inf'}
		for _, code in ipairs(codes) do
			local data = self:getIconData(code)
			if data then
				ret[#ret + 1] = self:renderIcon(data)
			end
		end
		return table.concat(ret)
	end
end

-- Renders other icons, e.g. featured article status and protection status.
function Eras:renderNonPublishingIcons()
	local ret = {}
	local codes = {'real', 'ss', 'ess', 'ha', 'eha', 'vkp'}
	for _, code in ipairs(codes) do
		local data = self:getIconData(code)
		if data then
			ret[#ret + 1] = self:renderIcon(data)
		end
	end
	local protectionCodes = {'fprot', 'sprot', 'mprot', 'uprot'}
	for _, code in ipairs(protectionCodes) do
		local data = self:getIconData(code)
		if data then
			ret[#ret + 1] = self:renderProtectionIcon(data)
		end
	end
	return table.concat(ret)
end

-- Render all the icons and eclose them in a surrounding div tag.
function Eras:renderIcons()
	local icons = {}
	icons[#icons + 1] = self:renderContinuityIcon()
	icons[#icons + 1] = self:renderPublishingIcons()
	icons[#icons + 1] = self:renderNonPublishingIcons()
	icons = table.concat(icons)

	local root = mw.getCurrentFrame():extensionTag{ name = "indicator", content = icons, args = { name = "eras"} }

	return tostring(root)
end

-- Renders the Canon and Legends tabs for articles that are in both
-- continuities.
function Eras:renderCanonTab()
	-- Tarkkailuluokat sivujen siirtoa varten
	if self:hasCanonTitle() then
		self:addCategory('Siirrettävät kaanonartikkelit')
	elseif exists(self.title.text .. '/Kaanon') and not isRedirect(self.title.text .. '/Kaanon') then
		self:addCategory('Siirrettävät Legends-artikkelit')
	end

	if self.isHidden or self.title.namespace ~= 0 then
		-- Exit if we have been explicitly hidden or if we are not in the main
		-- namespace.
		return nil
	end

	-- Find the page type, canon title, and legends title.
	local pageType, canonTitle, legendsTitle
	if self.legendsArticle then
		pageType = 'canon'
		canonTitle = self.title.text
		legendsTitle = self.legendsArticle
	elseif self.canonArticle then
		pageType = 'legends'
		canonTitle = self.canonArticle
		legendsTitle = self.title.text
	elseif self:hasCanonTitle() then
		pageType = 'canon'
		canonTitle = self.title.text
		legendsTitle = canonTitle:match('^(.*)/Kaanon$') or canonTitle
	elseif self:hasLegendsTitle() then
		pageType = 'legends'
		legendsTitle = self.title.text
		canonTitle = legendsTitle:match('^(.*)/Legends$') or legendsTitle
	elseif exists(self.title.text .. '/Legends') then
		pageType = 'canon'
		legendsTitle = self.title.text .. '/Legends'
		canonTitle = self.title.text
	elseif exists(self.title.text .. '/Kaanon') and not isRedirect(self.title.text .. '/Kaanon') then
		pageType = 'legends'
		canonTitle = self.title.text .. '/Kaanon'
		legendsTitle = self.title.text
	else
		-- Could not determine that the article has both a Canon and a Legends
		-- version, so exit.
		return nil
	end

	-- Add categories.
	if pageType == 'canon' then
		if self:hasCanonTitle() and exists(legendsTitle) == false then
			self:addCategory('Kaanonartikkelit, joiden Legends-vastine puuttuu')
			return nil
		else
			self:addCategory('Kaanonartikkelit, joista on vastine Legendsissä')
		end
	elseif pageType == 'legends' then
		if self:hasLegendsTitle() and exists(canonTitle) == false then
			self:addCategory('Legends-artikkelit, joiden kaanonvastine puuttuu')
			return nil
		else
			self:addCategory('Legends-artikkelit, joista on vastine kaanonissa')
		end
	else
		self:addCategory('Outliers')
	end

	-- Make the table root.
	local root = mw.html.create('table')
	root
		:attr('id', 'canontab')
		:css('text-align', 'center')
		:css('padding', '0')
		:css('margin', '0 0 5px 0')
		:css('border-left', '0')
		:css('border-right', '0')
		:css('border-top', '0')
		:css('border-bottom', '7px solid #002e54')
		:css('border-spacing', '0')
		:css('border-collapse', 'collapse')
		:css('width', '100%')
		:css('vertical-align', 'top')

	local row = root:tag('tr')

	-- This makes one Canon/Legends cell. Having this as a function rather than
	-- doing it with chaining allows us to avoid putting the same code in twice.
	local function makeCell(id, color, image, link, tooltip)
		local cell = mw.html.create('td')
		cell
			:attr('id', id)
			:css('padding', '5px 0 5px 0')
			:css('background-color', color)
			:css('line-height', '0.95em')
			:css('font-size', '150%')
			:css('font-weight', 'bold')
			:css('width', '20px')
			:css('vertical-align', 'top')
			:css('border-top-left-radius', '5px')
			:css('border-top-right-radius', '5px')
			:wikitext(string.format(
				' [[Tiedosto:%s|link=%s|%s]]',
				image, link, tooltip
			))
		return cell
	end

	local foregroundColor = '#002e54'
	local backgroundColor = '#d8e9fc'

	-- Make the canon cell.
	do
		local id = 'canontab-canon'
		local link = canonTitle
		local color, image, tooltip
		if pageType == 'canon' then
			color = foregroundColor
			image = 'Tab-canon-white-edit.png'
			tooltip = 'Tämä on artikkelin kaanonversio.'
		else
			color = backgroundColor
			image = 'Tab-canon-black-edit.png'
			tooltip = "Paina tästä lukeaksesi artikkelin kaanonversiota."
		end
		row:node(makeCell(id, color, image, link, tooltip))
	end

	-- First separator cell
	row:tag('td')
		:attr('id', 'canontab-separator1')
		:css('width', '3px')
		:wikitext('&nbsp;')
	
	-- Make the legends cell
	do
		local id = 'canontab-legends'
		local link = legendsTitle
		local color, image, tooltip
		if pageType ~= 'canon' then -- is a Legends page
			color = foregroundColor
			image = 'Tab-legends-white.png'
			tooltip = 'Tämä on artikkelin Legends-versio.'
		else -- is a Canon page
			color = backgroundColor
			image = 'Tab-legends-black.png'
			tooltip = "Paina tästä lukeaksesi artikkelin Legends-versiota."
		end
		row:node(makeCell(id, color, image, link, tooltip))
	end
	
	-- Second separator cell
	row:tag('td')
		:attr('id', 'canontab-separator2')
		:css('width', '3000px')
		:wikitext('&nbsp;')
	
	return tostring(root)
end

-- Render all the categories that were specified using Eras:addCategory or with
-- category flags.
function Eras:renderCategories()
	local fullPagename = self.title.prefixedText
	if fullPagename == 'Malline:Eras' or fullPagename == 'Malline:Eraicon' then
		-- Exit if we are on a blacklisted page.
		return nil
	end

	local pagename = self.title.text

	-- Lisää yhden luokan.
	local function renderCategory(cat, sort)
		return string.format(
			'[[%s:%s|%s]]', 'Luokka', cat, sort or pagename
		)
	end

	local ret = {}

	-- Render categories from Eras:addCategory
	for i, t in ipairs(self.categories) do
		ret[i] = renderCategory(t.category, t.sortKey)
	end

	-- Render categories from category flags.
	if self.hasBadParameter then
		ret[#ret + 1] = renderCategory(
			'Sivut, joissa on virheellisiä parametrejä mallineessa Eras'
		)
	end
	if self.hasIncorrectProtectionIcon then
		ret[#ret + 1] = renderCategory(
			'Sivut, joissa on virheellisiä suojausmerkintöjä'
		)
	end

	return table.concat(ret)
end

-- Lisää __NOTOC__ tarvittaessa
function Eras:renderNotoc()
	if self.hideToc then
		return '__NOTOC__'
	end
	return nil
end

-- This method is called when the tostring function is used on the Eras object.
-- (This works in a similar fashion to Eras.__index above.) It calls all the
-- top-level render methods and returns the final output.
function Eras:__tostring()
	local ret = {}
	ret[#ret + 1] = self:renderDisplayTitle()
	ret[#ret + 1] = self:renderIcons()
	ret[#ret + 1] = self:renderCanonTab()
	ret[#ret + 1] = self:renderCategories()
	ret[#ret + 1] = self:renderNotoc()
	return table.concat(ret)
end

-------------------------------------------------------------------------------
-- Exports
-------------------------------------------------------------------------------

local p = {}

-- This function is the entry point from other Lua modules.
function p._main(args)
	-- Define a function to call from pcall so that we can catch any errors
	-- and display them to the user rather than the cryptic "Script error".
	-- (It's not so cryptic if you click on it to see the error message, but
	-- not so many users know to do that.)
	local function getErasResult ()
		local erasObj = Eras.new(args)
		return tostring(erasObj)
	end

	-- Get the result. We only catch errors if debug mode is set to false.
	local success, result
	if DEBUG_MODE then
		success = true
		result = getErasResult()
	else
		success, result = pcall(getErasResult)
	end

	-- Return the result if there were no errors, and a formatted error message 
	-- if there were.
	if success then
		return result
	else
		return string.format(
			'<strong class="error">[[Malline:Eras]]-virhe: %s.</strong>' ..
			'[[Luokka:Sivut, joissa on virheellisiä parametrejä mallineessa Eras]]',
			result -- this is the error message
		)
	end
end

-- This is the function accessed from wikitext. It must be accessed through
-- a template. The template should transclude only the text
-- "{{#invoke:Eras|main}}", and then when that template is used, any arguments
-- passed to it are magically sent through to the module.
function p.main(frame)
	local args = {}
	for k, v in pairs(frame:getParent().args) do
		v = v:match('^%s*(.-)%s*$') -- trim whitespace
		if v ~= '' then
			args[k] = v
		end
	end
	return p._main(args)
end

return p

-- </nowiki>
-- [[Luokka:Ulkoasumallineet|{{PAGENAME}}]]