FANDOM


--[=[
-- For documentation, see [[Template:Module:Infobox/doc|Module:Infobox/doc]]
--]=]
 
-- <pre>
local Infobox = {}
Infobox.__index = Infobox
Infobox.__tostring = Infobox.tostring
 
-- Edit button for unknown params
local editbutton = require('Module:Edit button')
local edit = editbutton("'''?''' (edit)")
 
-- Page title
local pagename = mw.title.getCurrentTitle().fullText
 
-- map of flags to html tags used by Infobox.addRow()
-- let's only define it once, since :addRow() is used multiple times per module
local tagmap = {
	tr = 'tr',
	th = 'th',
	td = 'td',
	argh = 'th',
	argd = 'td'
}
 
--[=[
-- Standardized functions
-- called as string with defineParams
--]=]
 
-- Standardized "has content" function
function hasContent(arg, default)
	-- Return arg if any non-whitespace character is found
	return string.match(arg or '','%S') and arg or default
end
 
 
-- Standardized "name" function
function subjectName(arg)
	return string.match(arg or '','%S') and arg or nil
end
 
-- Create a standardized release function, since so many pages use it
-- Turns release and update into a single parameter
function releaseUpdate(release,update)
	-- "release ([[update|Update]])" or
	-- "release (update unknown)" or
	-- unknown
	if release and release:find('%S') then
		if update and update:find('%S') then
			return string.format('%s ([[Update:%s|Update]])',release,update)
		else
			return string.format('%s (Update unknown)',release)
		end
	else
		return nil
	end
end
 
-- Standardized image function
function image(img)
	if img and img:find('%S') then
		return img
	else
		return nil
	end
end
 
-- Standardized numbers
function numbers(num)
	num = string.gsub(num or '',',','')
	return tonumber(num)
end
 
-- map of names to pre-defined functions, used by Infobox:defineParams
local func_map = {
	name = subjectName,
	release = { name = releaseUpdate, params = { 'release', 'update' }, flag = 'p' },
	removal = { name = releaseUpdate, params = { 'removal', 'removalupdate' }, flag = 'p' },
	has_content = hasContent,
	hasContent = hasContent,
	image = image,
	numbers = numbers,
}
 
-- used to fill nil params in switching sections
-- this message isn't kidding
-- If you see this message anywhere outside of this code
-- (including inside switchfo box data)
-- report it
local nil_param = 'UH OH YOU SHOULDN\'T SEE THIS!'
 
-- In case the nil_param is needed outside of this module
-- give it an easy way to be accessed
function Infobox.nilParam()
	return nil_param
end
 
-- quick test to see if a value is considered nil
function Infobox.isDefined(arg)
	if arg == nil then
		return false
	end
 
	if type(arg) == 'string' then
		if arg == nil_param then
			return false
		elseif arg:find('%S') then
			if arg:find('action=edit') then
				return false
			else
				return true
			end
		else
			return false
		end
	end
 
	return true
end
 
 
--[[
	Infobox class
	-- args : parameters from frame to pass through
	-- Sets a meta table and creates a <div> tag wrapper
	-- other fields are initialized in other functions
--]]
function Infobox.new(args)
	local rdiv = mw.html.create('div')
					:addClass('infobox-wrapper')
 
	local obj = setmetatable({
				args = args, -- parameters (uncleaned)
				rargs = {}, -- parameters (cleaned)
				params = {}, -- parameters mapped to functions
				paramnames = {}, -- parameter names
				dupeable = {}, -- parameters that are allowed to have duplicated switch data
				switchfo = false, -- switch infobox? or not?
				switchfoattr = {}, -- switch data class changes
				maxbuttons = 5, -- maximum number of buttons before switching becomes a menu
				rdiv = rdiv, -- returned div wrapper
				rtable = nil, -- returned infobox table
				labels = nil, -- returned labels
				_smw = { -- semantic mediawiki data
						members = 'Is members only',
						release = 'Release date'
				},
				versions = -1, -- number of switch versions (-1 is uncalculated)
				infoboxname = nil, -- template name
				bottomlinks = { -- template bottom links
						{ 'Template talk:%s', 'talk' },
						{ 'Template:%s', 'view' },
					},
				catdata = {}, -- meta category data
				catlist = {}, -- defined table of category names (strings)
				__finished = false, -- infobox status
				},
			Infobox)
	return obj
end
 
--[[
	Creates an infobox
	-- If Infobox:maxVersions() has not been run, it will be run here
	-- If the infobox should be a switch infobox, all labels will be added
	-- Creates a wikitable that will be the infobox
	THIS SHOULD BE DONE AFTER ADDING AND CLEANING PARAMETERS
--]]
function Infobox:create()
	-- Run to find if this is a switch infobox and if so, how many boxes
	if self.versions == -1 then
		self:maxVersion()
	end
	-- Run if switch infobox
	if self.switchfo then
		-- Buttons wrapper
		-- Hidden by default, unhidden by javascript
		local btns = self.rdiv:tag('div')
					:addClass('infobox-buttons')
					:addClass('hidden')
 
		-- default version to immediately switch to via js
		local defv = tonumber(self.args.defver)
		if defv and defv <= self.versions then -- you troll, don't try to show something that isn't there
			btns:attr('data-default-version',defv)
		end
 
		-- Used by JavaScript to turn the buttons into a menu list if too many variants
		if self.versions > self.maxbuttons then
			btns:addClass('infobox-buttons-select')
		end
 
		-- Add individual buttons to the wrapper
		local halfv = self.versions/2
		for i=1,self.versions do
			btns:tag('span')
					:attr('data-switch-index',tostring(i))
					-- space to underscore
					:attr('data-switch-anchor','#'..string.gsub(self.labels[i] or i,' ','_'))
					:addClass('button')
					:wikitext(self.labels[i] or i)
				:done()
			if self.versions >= 5 and (i==halfv or i==(halfv+.5)) then
				btns:tag('br',{selfClosing=true})
			end
		end
		btns:done()
	end
 
	-- Create infobox table
	self.rtable = self.rdiv:tag('table')
					:addClass('wikitable')
					:addClass('infobox')
					:addClass('plainlinks')
	-- Add necessary class if switch infobox
	if self.switchfo then
		self.rtable:addClass('infobox-switch')
	end
 
end
 
-- Defines an infobox name ({{Template:arg}})
-- Used to create a link at the bottom of pages
function Infobox:defineName(arg)
	self.infoboxname = arg
end
 
-- Defines the bottom links of the infobox
-- pass a table whose elements are tables that define a link and a label
-- {
--	{ 'link', 'label },
--	...
-- }
-- The template name can be substituted into the tables using '%s'
-- If we wanted Template:InFooBar to link to it's /doc page with a "doc" label:
-- { ...
--	{ 'Template:%s/doc', 'doc' },
-- ... }
-- The template's name can only be called 5 times
function Infobox:defineLinks(arg)
	if type(arg) == 'table' then
		self.bottomlinks = arg
	end
end
 
-- Change max number of buttons before switching to menu
-- defaults to 5
-- MUST BE RUN BEFORE :create()
function Infobox:setMaxButtons(arg)
	-- if not a number, just go back to default
	self.maxbuttons = tonumber(arg) or 5
end
 
--[[
	Add parameters functions
	All parameters should be tables
	The first parameter defines the type of cell to create
		-- th : <th>
		-- td : <td>
		-- argh : <th>
		-- argd : <td>
	The second parameter defines what is inside the tag
		-- th | th : text passed
		-- argh | argd : parameter with the name passed
	Additional named parameters can be used to add any styling or attributes
		-- attr : mw.html:attr({ arg1 = '1', ... })
		-- css : mw.html:css({ arg1 = '1', ...)
		-- class : mw.html:addClass('arg')
		---- class also supports a table of values, even though mw.html:addClass() does not
		-- rowspan : mw.html:attr('rowspan',arg)
		-- colspan : mw.html:attr('colspan',arg)
		-- title : mw.html:attr('title',arg)
	Example:
		ipsobox:addRow( { 'th' , 'Header', title = 'Title' },
				{ 'argh', 'arg1', class = 'parameter' } })
	produces:
		<tr><th title="Title">Header</th><th class="parameter">args.arg1</th></tr>
 
	adding it to the infobox table of ipsobox
 
	Cells defined as 'argh' and 'argd' will automatically have data-attr-param="" added, and defined as the passed argument if the infobox in creation is defined as a switch infobox
 
	The row itself may be modified with metadata using the named index at "meta"
		-- meta.addClass : mw.html:addClass('arg')
		-- this function currently only supports a single string
--]]
function Infobox.addRow(box, ...)
	-- New row to add
	local args = ...
	local _row = box.rtable:tag('tr')
	-- For each member of tags
	for i, v in ipairs(args) do
		-- map tag name to appropriate tag, default to <td>
		local _cell = _row:tag(tagmap[v.tag] or 'td')
 
		-- mw.html:attr() and mw.html:css() both accept table input
		-- colspan, rowspan, title will be quick ways to access attr
		-- these functions also do all the necessary work
		if v.attr then
			_cell:attr(v.attr)
		end
		if v.colspan then
			_cell:attr('colspan',v.colspan)
		end
		if v.rowspan then
			_cell:attr('rowspan',v.rowspan)
		end
		if v.title then
			_cell:attr('title',v.title)
		end
		if v.css then
			_cell:css(v.css)
		end
 
		-- if class is a string, it can be added directly
		-- if a table, add every value
		-- mw.html:addClass() doesn't function with tables
		-- so iterate over the class names here and add them individually
		if v.class then
			if type(v.class) == 'string' then
				_cell:addClass(v.class)
			elseif type(v.class) == 'table' then
				for _, w in ipairs(v.class) do
					_cell:addClass(w)
				end
			end
		end
 
		-- if the cell is a normal th or td, add the exact argument passed
		if v.tag == 'th' or v.tag == 'td' then
			_cell:wikitext(v.content)
		-- if defined with "arg", add the argument with name passed
		elseif v.tag == 'argh' or v.tag == 'argd' then
			local content = box.rargs[v.content]
			-- if the requested parameter doesn't exist whatsoever, just return a blank string
			if not content then
				content = ''
			-- If switches exist, first attempt to use the version1 values
			elseif content.switches then
				if content.switches[1] ~= nil_param then
					content = content.switches[1] or ''
				else
					content = content.d or ''
				end
			-- fallback to default value
			else
				content = content.d or ''
			end
 
			_cell:wikitext(content)
 
			-- add necessary attribute for switch infoboxes
			if box.switchfo then
				_cell:attr('data-attr-param',v.content)
			end
		end
	end
 
	-- not that meta
	-- allow classes to be defined on the whole row
	-- okay, sort of meta
	if args.meta then
		if args.meta.addClass then
			_row:addClass(args.meta.addClass)
		end
	end
 
	return box
end
 
--[[
	-- functions the same as mw.html:wikitext() on the wrapper
	-- Should only be used for categories really
--]]
function Infobox.wikitext(box, arg)
	box.rdiv:wikitext(arg)
	return box
end
 
--[[
	-- Adds a caption to the infobox
	-- defaults to the pagename
	-- or the default argument if defined
--]]
function Infobox.caption(box)
	-- default to the article's name
	local name = pagename
	-- first see if the name parameter exists
	if box.rargs.name then
		-- then try the default
		if box.rargs.name.d then
			name = box.rargs.name.d
		-- then look for swithes
		elseif box.rargs.name.switches then
			-- then look at version 1
			if box.rargs.name.switches[1] ~= nil_param then
				name = box.rargs.name.switches[1]
			end
		end
	end
 
	local caption = box.rtable:tag('caption')
				:wikitext(name)
 
	-- add necessary attribute for switch infoboxes
	if box.switchfo then
		caption:attr('data-attr-param','name')
	end
 
	return box
end
 
--[[
	-- Functions for styling the infobox
	-- works the same as the respective mw.html functions
--]]
-- attr
function Infobox.attr(box, arg)
	box.rtable:attr(arg)
	return box
end
 
-- css
function Infobox.float(box,float)
	box.rdiv:css('float',float)
	return box
end
 
function Infobox.css(box, ...)
	box.rtable:css(...)
	return box
end
 
-- addClass
function Infobox.addClass(box, arg)
	box.rtable:addClass(arg)
	return box
end
 
-- Much like Infobox.addClass, but adds multiple classes
function Infobox.addClasses(box, ...)
	for _, v in ipairs(...) do
		box.rtable:addClass(box)
	end
	return box
end
 
--[[
	Add tags directly to the infobox table
	Use sparingly
	Returns the tag created rather than the entire box
	Which is an mw.html object
	Further uses of :tag() will be mw.html.tag, rather than Infobox.tag
	As such, Infobox:addRow() cannot be used afterwards without restating the infobox as the object
--]]
function Infobox.tag(box, arg)
	return box.rtable:tag(arg)
end
 
--[[
	Allows the infobox to use Semantic Media Wiki and give parameters properties
	Pass a table to this function to map parameter names to properties
 
	Calling syntax:
	-- {{#show:page|?property}}:
	-- "<property>" - unqualified and without a number will display the default value
	-- "<property#>" - with a number will show the switch data from that index
	-- "all <property>" - adding all will display every unique value in a comma separated list
 
	Properties initiated in Infobox:finish()
--]]
function Infobox:useSMW(arg)
	if type(arg) == 'table' or true then
		for w, v in pairs(arg) do
			self._smw[w] = v
		end
	end
end
--[[
	Finishing function
	-- Finishes the return, adding necessary final tags
--]]
function Infobox:finish()
	local onmain = mw.title.getCurrentTitle().namespace == 0
	-- Don't finish twice
	if self.__finished then
		return
	end
 
	-- Add switch infobox resources
	if self.switchfo then
		-- Wrapper tag, hidden
		local res_tag = self.rdiv:tag('div')
									:addClass('infobox-switch-resources')
									:addClass('hidden')
 
		for _, v in ipairs(self.paramnames) do
			local param = self.rargs[v]
			-- Parameters may not have any switches data, those are ignored
			if param.switches then
				local switchattr = self.switchfoattr[v]
				-- Parameter data wrapper
				local res_span = res_tag:tag('span')
											:attr('data-attr-param',v)
				-- Child for default value
				local def = res_span:tag('span')
							:attr('data-attr-index',0)
							:wikitext(tostring(param.d or edit))
 
				-- Switch classes
				if switchattr then
					def:attr('data-addclass',switchattr.d)
				end
 
				def:done()
 
				-- Add all switches, ignore those defined as nil
				for i, w in ipairs(param.switches) do
					if w ~= nil_param and w ~= nil then
						local _w = res_span:tag('span')
									:attr('data-attr-index',i)
									:wikitext(tostring(w))
						-- Switch classes
						if switchattr then
							_w:attr('data-addclass',switchattr.switches[i])
						end
 
						_w:done()
					end
				end
				res_span:done()
			end
		end
 
		-- Add a tracking category for mainspace pages that have more than 1 version
		if onmain then
			-- version count data
			res_tag:tag('span')
				:wikitext(string.format('Versions: [[Version count::%s]]',self.versions))
			:done()
			res_tag:wikitext('[[Category:Pages that contain switch infobox data]]')
		end
 
		res_tag:done()
	end
 
	-- smw data
	if onmain then
		-- members smw display, yes --> true; no --> false; other --> unknown
		local function smwMembers(smw_arg)
			local smw_argv = string.lower(smw_arg or '')
			if smw_argv == 'yes' then
				return 'true'
			elseif smw_argv == 'no' then
				return 'false'
			else
				return 'unknown'
			end
		end
 
		-- release date smw display
		local function smwRelease(smw_arg)
			local _d,_m,_y = string.match(smw_arg or '', '%[%[(%d%d?) (%a+)%]%] %[%[(%d%d%d%d)%]%]')
			return table.concat({_d,_m,_y},' ')
		end
 
		-- default, just return the text
		local function smwDefault(smw_arg)
			if smw_arg ~= nil_param and smw_arg ~= edit then
				return smw_arg
			else
				return 'unknown'
			end
		end
 
		local smw_to_func = {
			members = smwMembers,
			release = smwRelease,
			default = smwDefault
		}
		local res_tag = self.rdiv:tag('div')
								:addClass('infobox-semantics-data')
								:addClass('hidden')
								:css('display','none')
		local smw_data = res_tag:tag('pre')
					:wikitext('Defined properties:\n')
 
		-- custom properties
		for w, v in pairs(self._smw) do
			-- only needed to give special formatting to release
			-- and to make members true/false
			local smwfunc = smw_to_func[w] or smw_to_func.default
 
			local curarg = self.rargs[w]
			if curarg then
				local _arg = curarg.d
				local argdefault = _arg
				if _arg == edit then
					argdefault = 'Unknown'
				else
					local temp_smw_data = smwfunc(_arg)
					smw_data:wikitext(string.format('All %s: [[All %s::%s]]\n%s: [[%s::%s]]\n',v,v,temp_smw_data,v,v,temp_smw_data))
				end
 
				if curarg.switches then
					local _args = {}
 
					for _, x in ipairs(curarg.switches) do
						if x ~= nil_param then
							table.insert(_args,x)
						else
							table.insert(_args,argdefault or nil_param)
						end
					end
 
					for i, x in ipairs(_args) do
						local temp_smw_data = smwfunc(x)
						smw_data:wikitext(string.format('All %s: [[All %s::%s]]\n%s%s: [[%s%s::%s]]\n',v,v,temp_smw_data,v,i,v,i,temp_smw_data))
					end
				end
			end
		end
 
		smw_data:done()
		res_tag:done()
	end
 
	-- Add view and talk links to infobox
	-- Only done if a name is defined
	if self.infoboxname then
		local bottom_links = {}
		for _, v in ipairs(self.bottomlinks) do
			table.insert(bottom_links,
					string.format(
						table.concat({'&#91;[[',
								v[1],
								'|',
								v[2],
								']]&#93;'}),
						self.infoboxname,
						self.infoboxname,
						self.infoboxname,
						self.infoboxname,
						self.infoboxname)
				)
		end
 
		bottom_links = table.concat(bottom_links,' &bull; ')
		self.rdiv:tag('span')
					:addClass('infobox-bottom-links')
					:wikitext(bottom_links)
					:done()
	end
	-- Define as finished
	self.__finished = true
end
 
--[[
	Function for defining parameters
	-- name : parameter name
	-- func : function to define param, defaults to looking at blanks
	DO NOT DEFINE VERSION HERE
	USE :maxVersion()
	Can be used any number of times for efficient definition
--]]
function Infobox:defineParams(...)
	for _, v in ipairs(...) do
		-- For every parameter, store its corresponding function to self.params
		if v.name then
			-- If the value is a function or a table (which should define a function)
			if type(v.func) == 'function' or type(v.func) == 'table' then
				self.params[v.name] = v.func
			-- If the value is a string, use the predefined Infobox function of that name
			elseif type(v.func) == 'string' then
				self.params[v.name] = func_map[v.func] or hasContent
			-- Everything else just looks for blanks
			else
				self.params[v.name] = hasContent()
			end
			-- Create a list of all param names
			table.insert(self.paramnames,v.name)
			-- function to allow duplicated values
			if v.dupes then
				self.dupeable[v.name] = true
			end
		end
	end
end
 
--[[
	-- Forces an infobox to only use 1 variant
	-- Mainly used by lite infoboxes
	-- This should be run before creation
--]]
function Infobox:noSwitch()
	self.versions = 1
	self.switchfo = false
end
 
--[[
	-- Calculates the max version
	-- Adds labels
	-- Sees if this needs to be a switch infobox
	-- Returns extra version count (even if already run)
--]]
function Infobox.maxVersion(box)
	-- Only allowed to run once
	if box.versions ~= -1 then
		return box.versions
	end
 
	box.labels = {}
	box.versions = 0
 
	-- Look for up to 125 variants, defined in order
	for i=1, 125 do
		-- If variant# exists
		if box.args['version'..i] then
			-- Increase version count
			box.versions = box.versions + 1
 
			-- Add its label
			table.insert(box.labels,box.args['version'..i] or ('Version '..i))
		-- Stop if it doesn't exist
		else
			break
		end
	end
 
	-- Define self as a switch infobox if at least 1 other version is found
	if box.versions > 0 then
		box.switchfo = true
	end
 
	-- versions calculated
	return box.versions
end
 
--[[
	-- Cleans parameters as defined by the above function
	-- SHOULD NOT BE RUN UNTIL ALL THINGS ARE DEFINED
	-- Handles switches as well
	-- adds table _add to rargs, a cleaned up version of arguments
	-- d : default value
	-- switches : table of switches (in numerical order)
	-- Functions can be defined with tables
	---- name : name of function
	---- params : table of args to pass to functions
	---- flag : flags for input
				d | #default : use the cleaned parameter first, otherwise passed
				p : use the passed value of parameters
				r | l : use raw (literal) text, rather than values
		-- Defining a single flag will use that flag on all parameters
		-- Defining a table of flags will use the respective flag by position
--]]
function Infobox:cleanParams()
	-- map of flags to functionality
	local flagmap = {
		r = 'r',
		l = 'r',
		d = 'd',
		p = 'p'
	}
	-- For all parameters named
	for _, v in ipairs(self.paramnames) do
		-- Parameter to add
		local _add = {}
		local catdata = { all_defined = true, one_defined = false }
		-- If the value of params is a function
		if type(self.params[v]) == 'function' then
			-- Perform that function with the parameter
			_add.d = self.params[v](self.args[v])
		-- If it's a table, parse it into a function
		elseif type(self.params[v]) == 'table' then
			-- Find the functions name
			local func = self.params[v].name
 
			if type(func) == 'string' then
				func = func_map[func]
			end
 
			-- catch all
			if type(func) ~= 'function' then
				func = has_content
			end
 
			-- Recreate table of args and flags
			local func_args = {}
			local flag = {}
			-- If the flags are NOT a table, turn them into a table
			-- Same size as the parameter table
			-- Every flag will be the same
			if type(self.params[v].flag) ~= 'table' then
				-- Map flags, if unmapped, use default
				local _flag = flagmap[self.params[v].flag] or 'd'
				-- recreate table
				for x=1,#self.params[v].params do
					table.insert(flag,_flag)
				end
			-- If flags are already a table, recreate them in new table
			elseif type(self.params[v].flag) == 'table' then
				local _flag = self.params[v].flag
				-- recreate table
				for x=1,#self.params[v].params do
					-- Map flags, if unmapped, use default
					table.insert(flag,flagmap[_flag[x]] or 'd')
				end
			end
			-- Recreate param table, parsing flags as instructions
			for x, w in ipairs(self.params[v].params) do
				local xarg
				-- By default or defined as 'd'
				-- looks for the cleaned value of the named parameter first
				-- if it doesn't exist, look at the passed value next
				-- if that doesn't exist, use blank
				if flag[x] == 'd' then
					xarg = (self.rargs[w] and self.rargs[w].d) or self.args[w] or ''
				-- Look only at the passed value of the named parameter
				-- if that doesn't exist, use blank
				elseif flag[x] == 'p' then
					xarg = self.args[w] or ''
				-- Don't interpret value as a parameter name, and paste the as is
				elseif flag[x] == 'r' then
					xarg = w
				end
				-- Add parsed argument to table
				table.insert(func_args,xarg)
			end
			-- Run function
			_add.d = func(unpack(func_args))
		end
 
		if _add.d == nil or _add.d == nil_param then
			-- have to do pagename defaults here to prevent weird behaviour with switch values
			if v == 'name' then
				_add.d = pagename
			else
				_add.d = edit
			end
			catdata.all_defined = false
		end
		if self.switchfo then
			-- Table of switches values and count of them
			local _add_switch = {}
			local switches = 0
			-- Look for up to the maximum number
			for i=1, self.versions do
				local _addarg
				-- see if this param is allowed to have switch
				if v ~= 'image' and v ~= 'examine' and string.find(self.args[v..i] or '','%$%d') then
					local refi = string.match(self.args[v..i],'%$(%d+)')
					_addarg = _add_switch[tonumber(refi)] or nil_param
				else
					-- If the value of params is a function
					if type(self.params[v]) == 'function' then
						-- Perform that function with the parameter at that index
						_addarg = self.params[v](self.args[v..i])
					-- If it's a table, parse it into a function
					elseif type(self.params[v]) == 'table' then
						-- Find the functions name
						local func = self.params[v].name
 
 
						if type(func) == 'string' then
							func = func_map[func]
						end
 
						-- catch all
						if type(func) ~= 'function' then
							func = has_content
						end
 
						-- Recreate table of args and flags
						local func_args = {}
						local flag = {}
						-- If the flags are NOT a table, turn them into a table
						-- Same size as the parameter table
						-- Every flag will be the same
						if type(self.params[v].flag) ~= 'table' then
							-- Map flags, if unmapped, use default
							local _flag = flagmap[self.params[v].flag] or 'd'
							 -- recreate table
							for x=1,#self.params[v].params do
								table.insert(flag,_flag)
							end
						-- If flags are already a table, recreate them in new table
						elseif type(self.params[v].flag) == 'table' then
							local _flag = self.params[v].flag
							-- recreate table
							for x=1,#self.params[v].params do
								-- Map flags, if unmapped, use default
								table.insert(flag,flagmap[_flag[x]] or 'd')
							end
						end
						-- Recreate param table, parsing flags as instructions
						for x, w in ipairs(self.params[v].params) do
							local xarg
							-- By default or defined as 'd'
							-- looks for the cleaned value of the named parameter first
							-- if it doesn't exist, look at the passed value next
							-- if that doesn't exist, look at the default
							-- if that doesn't exist, use blank
							if flag[x] == 'd' then
								if self.rargs[w] then
									if self.rargs[w].switches then
										xarg = self.rargs[w].switches[i]
									else
										xarg = self.args[w..i]
									end
									if xarg == nil or xarg == nil_param then
										xarg = self.rargs[w].d
									end
								end
								-- multiple catches in a row just to cover everything
								if xarg == nil or xarg == nil_param then
									xarg = self.args[w..i]
								end
								if xarg == nil or xarg == edit or xarg == nil_param then
									xarg = self.args[w]
								end
								if xarg == nil or xarg == edit or xarg == nil_param then
									xarg = ''
								end
							-- Look only at the passed value of the named parameter
							-- if that doesn't exist, use unnumbered parameter
							-- if that doesn't exist, use blank
							elseif flag[x] == 'p' then
								xarg = self.args[w..i] or self.args[w] or ''
							-- Don't interpret value as a parameter name, and paste the as is
							elseif flag[x] == 'r' then
								xarg = w
							end
							-- Add parsed argument to table
							table.insert(func_args,xarg)
						end
						-- Run function
						_addarg = func(unpack(func_args))
					end
				end
				-- If not defined, add the nil_param value
				-- An actual nil would cause errors in placement
				-- So it needs to be filled with an actual value
				-- "nil_param" is understood as nil in other functions
				-- Include table in case parameter isn't defined by template
 
				if _addarg == nil or _addarg == nil_param then
					table.insert(_add_switch, nil_param)
				else
					switches = switches + 1
					table.insert(_add_switch, _addarg)
					catdata.one_defined = true
				end
			end
			-- If there are actually other values to switch to
			-- Define a switches subtable, otherwise ignore it
			if switches > 0 then
				_add.switches = _add_switch
			end
		end
 
		-- Quick fix for names (which defaults to pagename)
		if v == 'name' then
			if _add.d == pagename then
				if _add.switches and _add.switches[1] ~= pagename and _add.switches[1] ~= nil_param then
					_add.d = _add.switches[1]
				end
			end
		end
 
		-- Parameter cleaning finished, add to table of cleaned args
		self.rargs[v] = _add
 
		-- Category metadata
		-- If every param except default is defined, all_defined = true
		if catdata.all_defined == false then
			if _add.d == edit then
				if _add.switches then
					catdata.all_defined = true
					for _, v in ipairs(_add.switches) do
						if v == nil_param then
							all_defined = false
							break
						end
					end
				end
			end
		end
 
		self.catdata[v] = catdata
	end
 
	-- mass dupe removal
	-- this needs to be done at the end to keep dependent parameters working
	-- also removes incompatible data types
	for _, v in ipairs(self.paramnames) do
		-- not removed from dupe enabled params parameters
		if not self.dupeable[v] and self.rargs[v] and self.rargs[v].switches then
			-- tells us whether or not we'll need to remove the switch data
			-- switched to false if a switch values does not match the default
			local rmvswitch = true
			for q, z in ipairs(self.rargs[v].switches) do
				-- if it matches the default, switch it to nil
				if z == self.rargs[v].d then
					self.rargs[v].switches[q] = nil_param
 
				-- remove types that don't turn into strings properly
				elseif type(z) == 'table' or type(z) == 'function' then
					self.rargs[v].switches[q] = nil_param
 
				-- if it doesn't match and also isn't nil or an edit button
				-- change variable to keep the switch data
				elseif z ~= nil_param and z ~= edit then
					rmvswitch = false
				end
			end
			-- remove switch data if everything was a dupe
			if rmvswitch then
				self.rargs[v].switches = nil
			end
		end
	end
 
	-- Title parentheses (has to be done here, sadly)
	local _name
	if self.rargs.name then
		_name = self.rargs.name.d
		-- replace html entities to their actual character
		_name = mw.text.decode(_name)
 
		-- if the page name matches the item name, don't add parentheses
		if _name == mw.title.getCurrentTitle().fullText then
			self.rtable:addClass('no-parenthesis-style')
		end
	end	
end
 
--[[
	Function to link internal use parameters with JS class attribution
	-- self:linkParams{ { arg1, arg2 }, ... { arg1a, arg2a } }
	-- arg1: parameter name being linked
	-- arg2: parameter name that holds the classes
	-- THIS FUNCTION SHOULD BE RUN AFTER :cleanParams()
	-- THIS FUNCTION SHOULD BE RUN BEFORE :finish()
	-- The second argument's data should always contain a value (a CSS class name) at every index
	-- This function is cancelled for non switch boxes
--]]
function Infobox:linkParams(...)
	if not self.switchfo then
		return
	end
	for _, v in ipairs(...) do
		self.switchfoattr[v[1]] = self.rargs[v[2]]
	end
end
 
--[==========================================[
-- Functions for accessing parameters easily
--]==========================================]
--[[
	Access the param
	-- arg : param name
	-- retp : return type
		d | #default : self.rargs[arg].d -- Default value
		f | full : self.rargs[arg] -- Entire table
		s | switches : self.rargs[arg].switches -- Entire switch table
		s# : self.rargs[arg].switches[#] -- Single switch value at index #
		r : switches[1] or d
--]]
function Infobox:param(arg, retp)
	-- All parameters
	if arg == 'all' then
		return self.rargs
	end
	-- case-insensitive flagging
	retp = tostring(retp):lower()
	local fmap = {
		d = 'd', default = 'd', s0 = 'd', -- let s-naught count as default (since that's what it is)
		f = 'f', full = 'f',
		s = 's', switch = 's', switches = 's',
		r = 'r'
	}
	local ret_func
	-- quickly see if the parameter is a value greater than 0
	if retp:match('s[1-9]') then
		ret_func = 's2'
	else
		-- Otherwise map it to the correct flag, or the default
		ret_func = fmap[retp] or fmap.d
	end
 
	-- Fetch parameter
	local param = self.rargs[arg]
	-- Return nil if no table found
	if not param then return nil end
 
	-- Return default
	if ret_func == 'd' then
		return param.d
	end
 
	-- Return full table
	if ret_func == 'f' then
		return param
	end
 
	-- Return switch table
	if ret_func == 's' then
		return param.switches
	end
 
	-- Return the first switch, otherwise the default
	if ret_func == 'r' then
		if not param.switches then
			return param.d
		elseif param.switches[1] == nil_param then
			return param.d
		else
			return param.switches[1]
		end
	end
 
	-- If s2, reread the param
	if ret_func == 's2' then
		-- no switches
		if not param.switches then
			return nil
		end
		-- Parse index by removing the s
		local index = retp:match('s(%d+)')
		-- nil_param
		if param.switches[index] == nil_param then
			return nil
		else
			return param.switches[index]
		end
	end
end
 
--[[
	Checks if a parameter is defined and not blank
	-- arg : parameter to look at
	-- index : index of switches to look at (defaults to default param)
		-- defining 'all' will look at every possible value for the parameter
--]]
function Infobox:paramDefined(arg,index)
	-- Can use cleaned params for switches
	-- but need the passed to identify blanks in the template
	local param = self.rargs[arg]
	local _arg = self.args[arg]
	if string.find(_arg or '','%?action=edit') then
		_arg = ''
	end
	index = index or 0
	local ret
	-- create a long strong of every value to test for things if 'all'
	if string.lower(index) == 'all' then
		return self.catdata[arg] and (self.catdata[arg].one_defined or self.catdata[arg].all_defined)
	-- index to number otherwise
	else
		index = tonumber(index) or 0
		if index == 0 then
			ret = _arg
		else
			if not param.switches then
				return nil
			end
			if param.switches[index] == nil_param then
				return nil
			end
			ret = param.switches[index]
		end
	end
	return tostring(ret or ''):find('%S')
end
 
--[[
	Function to perform a search on all parameters of a defined name
	-- param: param name
	-- val: a value or function
		-- functions passed must return either true or false
		-- with true being counted as a match
--]]
function Infobox:paramGrep(param,val)
	local arg = self.rargs[param]
	-- if no parameters, return nil
	if not arg then
		return nil
	end
 
	local ret
	local valtype = type(val)
	-- start with the default value
	-- if it's a function, run it
	if valtype == 'function' then
		ret = val(arg.d)
 
		-- true means it matched
		if ret == true then
			return ret
		end
 
		-- switches up here for functions
		if arg.switches then
			for _, v in ipairs(arg.switches) do
				ret = val(v)
				if ret == true then
					return true
				end
			end
		end
 
	-- if it's just a value, compare the two
	-- only run if types match to avoid errors
	-- compare switches later
	elseif valtype == type(arg.d) then
		-- if a string, make case insensitive
		if valtype == 'string' then
			if string.lower(val) == string.lower(arg.d) then
				return true
			end
		-- everything else just test directly
		elseif val == arg.d then
			return true
		end
	end
 
	-- switch cases
	-- more organized putting them here
	if arg.switches then
		for _, v in ipairs(arg.switches) do
			if valtype == type(v) then
				if valtype == 'string' then
					if val:lower() == v:lower() then
						return true
					end
				elseif val == v then
					return true
				end
			end
		end
	end
 
	-- return false in every other case
	return false
end
------
 
function Infobox.paramRead(arg,val)
	-- if no parameters, return nil
	if not arg then
		return nil
	end
 
	local ret
	local valtype = type(val)
	-- start with the default value
	-- if it's a function, run it
	if valtype == 'function' then
		ret = val(arg.d)
 
		-- true means it matched
		if ret == true then
			return ret
		end
 
		-- switches up here for functions
		if arg.switches then
			for _, v in ipairs(arg.switches) do
				ret = val(v)
				if ret == true then
					return true
				end
			end
		end
 
	-- if it's just a value, compare the two
	-- only run if types match to avoid errors
	-- compare switches later
	elseif valtype == type(arg.d) then
		-- if a string, make case insensitive
		if valtype == 'string' then
			if string.lower(val) == string.lower(arg.d) then
				return true
			end
		-- everything else just test directly
		elseif val == arg.d then
			return true
		end
	end
 
	-- switch cases
	-- more organized putting them here
	if arg.switches then
		for _, v in ipairs(arg.switches) do
			if valtype == type(v) then
				if valtype == 'string' then
					if val:lower() == v:lower() then
						return true
					end
				elseif val == v then
					return true
				end
			end
		end
	end
 
	-- return false in every other case
	return false
end
 
----
 
-- Return collected category data
function Infobox:categoryData()
	return self.catdata
end
 
-- Override tostring
function Infobox.tostring(box)
	-- If not finished, finish
	if not box.__finished then
		box:finish()
	end
 
	-- Make entire html wrapper a string and return it
	return tostring(box.rdiv)
end
 
return Infobox

Interférence d'un bloqueur de publicité détectée !


Wikia est un site gratuit qui compte sur les revenus de la publicité. L'expérience des lecteurs utilisant des bloqueurs de publicité est différente

Wikia n'est pas accessible si vous avez fait d'autres modifications. Supprimez les règles personnalisées de votre bloqueur de publicité, et la page se chargera comme prévu.