Module:Infobox

-- local Infobox = {} Infobox.__index = Infobox

local editbutton = require('Module:Edit button') local edit = editbutton("? (edit)")

-- map of flags to html tags used by Infobox.addrow local tagmap = { tr = 'tr', th = 'th', td = 'th', argh = 'th', argd = 'td' }

-- map of flags to functionality used by Infobox:param local flagmap = { r = 'r', l = 'r', d = 'd', p = 'p' }

-- Standardized "has content" function function has_content(arg) -- Return true if any non-whitespace character is found return string.match(arg or '','%S') end

-- Create a standardized release function, since so many pages use it -- Turns release and update into a single parameter function release_update(release,update) if release and release:find('%S') then update = (update and update:find('%S')) and (string.format('(Update)',update)) or						'(Update unknown)' return release..' '..update else return nil end end

-- map of names to pre-defined functions, used by Infobox:define_params local func_map = { release = { name = release_update, params = { 'release', 'update' } }, removal = { name = release_update, params = { 'removal', 'removalupdate' } }, has_content = has_content }

-- used to fill nil params in switching sections local nil_param = 'UH OH YOU SHOULDN\'T SEE THIS!'

--	Infobox class	-- args : parameters from frame to pass through	-- Sets a meta table and creates a 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,				rargs = {},				params = {},				paramnames = {},				switchfo = false,				rdiv = rdiv,				rtable = nil,				labels = nil,				versions = -1,				infoboxname = nil,				catdata = {},				__finished = false,				__tostring = self:tostring,				},			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') -- Used by JavaScript to turn the buttons into a menu list if too many variants if self.versions > 5 then btns:addClass('infobox-buttons-select') end -- Add individual buttons to the wrapper for i=1,self.versions do			btns:tag('span') :attr('data-switch-index',tostring(i)) :attr('data-switch-anchor','#'..string.gsub(self.labels[i] or '',' ','_')) :addClass('button') :wikitext(self.labels[i] or '') :done 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 -- Used to create a link at the bottom of pages function Infobox:defineName(arg) self.infoboxname = arg end

--[[	Add parameters functions	All parameters should be tables	The first parameter defines the type of cell to create		-- th : 		-- td : 		-- argh : 		-- argd : 	The second parameter defines what is inside the tag		-- th | th : text passed		-- argh | argd : parameter with the name passed	The third parameter can be used to add any styling or attributes		-- attr : mw.html:attr		-- css : mw.html:css		-- class : mw.html:addClass	Example:	ipsobox:addrow( { 'th', 'argh' },					{ 'Header', 'arg1' },					{ { attr = { title='Title' },						{ class = 'parameter' } } )	produces:	 Header args.arg1 	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 --]] function Infobox.addrow(box, ...) -- New row to add local _row = box.rtable:tag('tr') -- For each member of tags -- Assume all tables are the same size for i, v in ipairs(...) do		-- map tag name to appropriate tag, default to 		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 -- 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 argumnt with name passed elseif v.tag == 'argh' or v.tag == 'argd' then local content = box.rargs[v.content] if not content then content = '' elseif content.switches then if content.switches[1] ~= nil_param then content = content.switches[1] or '' else content = content.d or '' end 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

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) local caption = box.rtable:tag('caption') :wikitext(box.rargs.name and box.rargs.name.d or pagename)

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 -- function Infobox.attr(box, arg) box.rtable.attr(arg) return box end

function Infobox.css(box, arg) box.rtable:css(arg) return box end

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

--	Finishing function	-- Finishes the return, adding necessary final tags -- function Infobox:finish -- 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 -- Parameter data wrapper local res_span = res_tag:tag('span') :attr('data-attr-param',v) -- Child for default value res_span:tag('span') :attr('data-attr-index',0) :wikitext(tostring(param.d or edit)) :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 res_span:tag('span') :attr('data-attr-index',i) :wikitext(tostring(w)) :done end end res_span:done end end res_tag:done end

-- Add view and talk links to infobox -- Only done if a name is defined if self.infoboxname then self.rdiv:tag('span') :addClass('infobox-bottom-links') :wikitext(string.format('&#91;view&#93; &bull; '.. '&#91;talk&#93;',self.infoboxname,self.infoboxname)) :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:define_params(...) 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 has_content -- Everything else just looks for blanks else self.params[v.name] = has_content end -- Create a list of all param names table.insert(self.paramnames,v.name) end end end

--	-- Calculates the max version	-- Adds labels	-- Sees if this needs to be a switch infobox -- function Infobox:maxversion -- Only allowed to run once if self.versions ~= -1 then return end self.labels = {} self.versions = 0 -- Look for up to 125 variants, defined in order for i=1, 125 do		-- If variant# exists if self.args['version'..i] then -- Increase version count self.versions = self.versions + 1 -- Add its label table.insert(self.labels,self.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 self.versions > 0 then self.switchfo = true end end

-- #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:clean_params -- For all parameters 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 -- 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,_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 then _add.d = edit 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 -- Check for references 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 -- Check if this particular parameter is defined in any way elseif has_content(self.args[v..i]) then -- 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 -- 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].switches and self.rargs[w].switches[i]) or self.args[w..i] 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..i] 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 -- 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 else if type(self.params[v]) == 'table' then -- Find the functions name local func = self.params[v].name -- 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].switches and self.rargs[w].switches[i]) or self.args[w..i] 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..i] 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 _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

-- Parameter cleaning finished, add to table of cleaned args self.rargs[v] = _add -- Category metadata self.catdata[v] = catdata end end

--[==========================================[ -- Functions for accessing parameters easily --]==========================================] -- #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

-- Override tostring function Infobox:tostring -- If not finished, finish if not self.__finished then self:finish end

-- Make entire html wrapper a string and return it	return tostring(self.rdiv) end

function Infobox:categorydata return self.catdata end

return Infobox