Editing Module:Infobox

Jump to navigation Jump to search
Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.

Latest revision Your text
Line 4: Line 4:


-- <nowiki>
-- <nowiki>
require('strict')
local editbutton = require('Module:Edit button')
local smwutils = require('Module:SMW Utils')

local Infobox = {}
local Infobox = {}
Infobox.__index = 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)
if not Infobox.isDefined(release) then
return nil
end
if string.lower(release) == 'no' then
return 'N/A'
end
if not Infobox.isDefined(update) then
return string.format('%s (Update unknown)',release)
end
if string.lower(update) == 'no' then
return release
end
return string.format('%s ([[Update:%s|Update]])', release, update)
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,
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 = 'INFOBOX MODULE ERROR, PLEASE REPORT TO WIKI ADMIN'

-- 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

-- switch infobox globals
local LINE_WIDTH = 300
local MAX_LINES = 2
local DEFAULT_MAX_BUTTONS = 6
-- calculate with width of a switch infobox button
-- potential @TODO: rework to use actual character widths
function button_width(label)
local PX_PER_CHAR = 6
local PX_PAD_MAR = 24
return string.len(label) * PX_PER_CHAR + PX_PAD_MAR
end

Infobox.splitpoint = '&&SPLITPOINT&&'

-- 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
Infobox class
-- args : parameters from frame to pass through
-- config: table containing configuration parameters
-- Sets a meta table and creates a <div> tag wrapper
-- params : definitions for each used value
-- other fields are initialized in other functions
-- args : values passed from the Template
---- the following values are treated specially: default_version, version, version1, version2...
--]]
--]]
function Infobox.new(config, params, args)
function Infobox.new(args)
local obj = setmetatable({
local obj = setmetatable({
args_raw = args, -- parameters (uncleaned)
args = args, -- parameters (uncleaned)
args_parsed = {}, -- parsed parameters
rargs = {}, -- parameters (cleaned)
args_smw = {}, -- parameters parsed for smw
params = {}, -- parameters mapped to functions
infobox_name = nil, -- template name
paramnames = {}, -- parameter names
classes = {}, -- list of classes to add, using the default_version
dupeable = {}, -- parameters that are allowed to have duplicated switch data
add_infobox_class = true, -- if true, will add the class 'infobox'
param_names = {}, -- ordered param list
params = {}, -- param definitions
switchfo = false, -- switch infobox? or not?
switchfoattr = {}, -- switch data class changes
max_buttons = 6, -- If there are more buttons than the max, the buttons will become a dropdown menu
maxbuttons = DEFAULT_MAX_BUTTONS, -- maximum number of buttons before switching becomes a menu
default_version = 1, -- default version to populate the infobox
versions = nil, -- number of versions
switch_tag = '', -- switchfo data
switch_buttons_tag = '', -- switchfo buttons
version_names = {}, -- title of each version (for selection and SMW)
custom_buttons = false,
rtable = nil, -- infobox table to return at the end
smw_error_tag = '',
switch_datatable = '', -- datatable for javascript for switch infoboxes
categories = {}, -- set of categories
rtable = nil, -- returned infobox table
errors = {}, -- list of errors
labels = nil, -- returned labels
__smw_debug = {}, -- debug data for smw subobjects
_smw = {}, -- semantic mediawiki data
_smwOne = {}, -- semantic mediawiki data part 2
_smwSubobject = {}, -- semantic mediawiki data part 3
_smwSubobjectAppliesTo = nil, -- semantic mediawiki data part 3.5
_smwElement = {}, -- semantic mediawiki data part 4
setSMWElement = true,
versions = -1, -- number of switch versions (-1 is uncalculated)
infoboxname = nil, -- template name
appendStrs = {},
bottomlinks = { -- template bottom links
links = {
{ 'Template talk:%s', 'talk' },
{ 'Template:%s', 'view' }
},
colspan = 2
},
catdata = {}, -- meta category data
catlist = {}, -- defined table of category names (strings)
__finished = false, -- infobox status
},
},
Infobox)
Infobox)
-- Step 1, setup config vars and count the versions
obj:config(config)
obj:parse_versions()
-- Step 2, process the params
obj:define_params(params)
obj:parse_params()
-- Table
obj:table_header()
obj:buttons()
-- Misc
obj:store_smw()
obj:parse_categories()
return obj
return obj
end
end



--[[
--[[
Removes the addition of infobox class
Refers to a param after being processed by the validating func
use before :create()
Used in add_row(), define_params() and is_param_defined()
--]]
--]]
function Infobox.param(param_name)
function Infobox:removeClassInfobox()
self.add_infobox_class = false
local param = {
property = 'args_parsed',
param_name = param_name,
}
return param
end
end



--[[
--[[
Creates an infobox
Refers to a param in raw form as passed from the Template
-- If Infobox:maxVersions() has not been run, it will be run here
Used in add_row(), define_params() and is_param_defined()
-- 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.raw_param(param_name)
function Infobox:create()
-- Run to find if this is a switch infobox and if so, how many boxes
local param = {
if self.versions == -1 then
property = 'args_raw',
self:maxVersion()
param_name = param_name,
}
end
-- Run if switch infobox
return param
if self.switchfo then
end
-- Buttons wrapper
-- Hidden by default, unhidden by javascript
self.switch_buttons_tag = mw.html.create('div')
:addClass('infobox-buttons')


-- 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
self.switch_buttons_tag:attr('data-default-version',defv)
end

local numlines = 1
local width_working = 0
local total_width = 0
local buttons = {}
-- Add individual buttons to the wrapper
for i=1,self.versions do
local wid = button_width(self.labels[i] or i)
width_working = width_working + wid
total_width = total_width + wid
if width_working > LINE_WIDTH then
numlines = numlines + 1
width_working = wid
end
local b = mw.html.create('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)
table.insert(buttons, {b, wid})
end
local best = {-1, 100000}
if (numlines > 1) and (numlines <= MAX_LINES) then
-- attempt to balance line widths
local w_s, w_e = 0,total_width
for i = 1,#buttons-1 do
w_s = w_s + buttons[i][2]
w_e = w_e - buttons[i][2]
if w_s > LINE_WIDTH then
-- w_s only increases, so we're done once it exceeds the width
break
end
if w_e <= LINE_WIDTH then
-- w_e only decreases, so just continue if it exceeds line
local diff = math.abs(w_s - w_e)
if diff < best[2] then
best = { i, diff }
end
end
end
if best[1] == -1 then
best = { math.floor(#buttons/2), 100000 }
end
end
for i,v in ipairs(buttons) do
self.switch_buttons_tag:node(v[1])
if i == best[1] then
self.switch_buttons_tag:tag('br')
end
end
-- Used by JavaScript to turn the buttons into a menu list if too many variants
if self.versions > self.maxbuttons or numlines > MAX_LINES then
self.switch_buttons_tag:addClass('infobox-buttons-select')
end
self.switch_buttons_tag:done()
end

-- Create infobox table
self.rtable = mw.html.create('table')
:addClass('plainlinks')
if self.add_infobox_class then
self.rtable:addClass('infobox')
end
-- Add necessary class if switch infobox
if self.switchfo then
self.rtable:addClass('infobox-switch')
end


--[[
Refers to a param after being processed by the validating smw_func
Used in add_row(), define_params() and is_param_defined()
--]]
function Infobox.smw_param(param_name)
local param = {
property = 'args_smw',
param_name = param_name,
}
return param
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
Checks to see if a param is defined.
-- {
-- param: Infobox.param(name), Infobox.raw_param(name) or Infobox.smw_param(name)
-- { 'link', 'label },
Returns 0 if param is never defined
-- ...
1 if param is defined for a fraction of the versions
-- }
2 if param is defined for all versions
-- 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:
function Infobox:is_param_defined(param)
-- { ...
local undefined = 0
-- { 'Template:%s/doc', 'doc' },
local defined = 0
-- ... }
for version = 1, self.versions do
-- The template's name can only be called 5 times
local value = self:get_param(param, version)
function Infobox:defineLinks(arg)
if value ~= nil then
if type(arg) == 'table' then
defined = 1
if arg.colspan then
else
self.bottomlinks.colspan = arg.colspan
undefined = 1
end
if arg.links then
if type(arg.links) == 'table' then
self.bottomlinks.links = arg.links
end
end
if arg.hide then
self.bottomlinks.hide = arg.hide
end
end
end
end
return 1 + defined - undefined
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 DEFAULT_MAX_BUTTONS
end


--[[
--[[
Add parameters functions
Adds a row to the infobox table
All parameters should be tables
Parameter should be a table of cells, where each cell is a table with the following arguments:
The first parameter defines the type of cell to create
-- tag : 'td' or 'th'
-- th : <th>
-- content : a string or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name)
-- td : <td>
-- attr (optional) : mw.html:attr({ arg1 = '1', ... })
-- argh : <th>
-- css (optional) : mw.html:css({ arg1 = '1', ...)
-- argd : <td>
-- class (optional) : mw.html:addClass('arg')
The second parameter defines what is inside the tag
---- class also supports a table of values, even though mw.html:addClass() does not
-- th | th : text passed
---- common classes: infobox-subheader
-- argh | argd : parameter with the name passed
-- rowspan (optional) : mw.html:attr('rowspan',arg)
Additional named parameters can be used to add any styling or attributes
-- colspan (optional) : mw.html:attr('colspan',arg)
-- title (optional) : mw.html:attr('title',arg)
-- 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
The row itself may be assigned a single class by setting the value of the key addClass

-- addClass : mw.html:addClass('arg')
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
-- this function currently only supports a single string
--]]
--]]
function Infobox:add_row(...)
function Infobox.addRow(box, ...)
-- New row to add
local args = ...
local args = ...
local _row = self.rtable:tag('tr')
local _row = box.rtable:tag('tr')
-- For each cell
-- For each member of tags
for _, v in ipairs(args) do
for i, v in ipairs(args) do
-- map tag name to appropriate tag, default to <td>
local _cell = _row:tag(v.tag)
local _cell = _row:tag(tagmap[v.tag] or 'td')
-- Optional parameters

-- 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
if v.attr then
_cell:attr(v.attr)
_cell:attr(v.attr)
Line 155: Line 391:
_cell:css(v.css)
_cell:css(v.css)
end
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 v.class then
if type(v.class) == 'string' then
if type(v.class) == 'string' then
_cell:addClass(v.class)
_cell:addClass(v.class)
-- mw.html:addClass() doesn't function with tables, add in a loop
elseif type(v.class) == 'table' then
elseif type(v.class) == 'table' then
for _, w in ipairs(v.class) do
for _, w in ipairs(v.class) do
Line 165: Line 405:
end
end
end
end

-- Populate the cell contents
-- if the cell is a normal th or td, add the exact argument passed
local content = self:get_param(v.content, self.default_version)
if content == nil then
if v.tag == 'th' or v.tag == 'td' then
content = self.params[v.content.param_name].empty
_cell:wikitext(v.content)
-- if defined with "arg", add the argument with name passed
end
elseif v.tag == 'argh' or v.tag == 'argd' then
_cell:wikitext(content)
local content = box.rargs[v.content]
-- Add the switch data if multiple values exist
-- if the requested parameter doesn't exist whatsoever, just return a blank string
local data_attr_param = self:add_switch_data(v.content)
if data_attr_param then
if not content then
content = ''
_cell:attr('data-attr-param', data_attr_param)
-- 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
end
end

-- not that meta
-- allow classes to be defined on the whole row
-- allow classes to be defined on the whole row
-- okay, sort of meta
if args.addClass then
if args.meta then
_row:addClass(args.addClass)
if args.meta.addClass then
_row:addClass(args.meta.addClass)
end
end
end

return self
return box
end
end


function Infobox.customButtonPlacement(box,arg)
box.custom_buttons = arg
return box
end

function Infobox.addButtonsRow(box, args)
if box.switchfo then
box.custom_buttons = true
local _row = box.rtable:tag('tr')
:addClass('rsw-infobox-switch-buttons-row')
:tag('td')
:addClass('rsw-infobox-switch-buttons')
:attr('colspan', args.colspan)
:node(box.switch_buttons_tag)
end
return box
end
function Infobox.addButtonsCaption(box)
if box.switchfo then
box.custom_buttons = true
local _row = box.rtable:tag('caption')
:addClass('rsw-infobox-switch-buttons-caption')
:node(box.switch_buttons_tag)
end
return box
end


--[[
--[[
Adds a blank row of padding spanning the given number of columns
-- adds a blank row of padding spanning the given number of columns
--]]
--]]
function Infobox:pad(colspan, class)
function Infobox.pad(box, colspan, class)
local tr = self.rtable:tag('tr'):tag('td'):attr('colspan', colspan or 1):addClass('infobox-padding')
local tr = box:tag('tr')
:tag('td'):attr('colspan', colspan or 1):addClass('infobox-padding')
:done()
if class then
if class then
tr:addClass(class)
tr:addClass(class)
end
end
tr:done()
return self
return box
end
end


--[[
--[[
-- functions the same as mw.html:wikitext() on the wrapper
Adds a class to the table as a whole
-- Should only be used for categories really
--]]
--]]
function Infobox:addClass(arg)
function Infobox.wikitext(box, arg)
self.rtable:addClass(arg)
box.rtable:wikitext(arg)
return self
return box
end
end


--[[
-- Adds the specified item(s) to the end of the infobox, outside of the table
-- items are concatenated together with an empty space
--]]
function Infobox.append(box, ...)
for i,v in ipairs({...}) do
table.insert(box.appendStrs, v)
end
return box
end


--[[
--[[
-- Adds a caption to the infobox
Setup config values
-- defaults to the pagename
-- config: table containing configuration parameters
-- or the default argument if defined
---- infobox_name = mandatory unique identifier for this infobox, used for css
---- max_buttons = max number of switch buttons before using a dropdown list instead
--]]
--]]
function Infobox:config(config)
function Infobox.caption(box)
-- default to the article's name
for k, v in pairs(config) do
local name = pagename
if k == 'infobox_name' then
-- first see if the name parameter exists
self.infobox_name = mw.ustring.gsub(v, '%s', '_')
if box.rargs.name then
elseif k == 'max_buttons' then
-- then try the default
self.max_buttons = tonumber(v)
elseif k == 'class' then
if box.rargs.name.d then
name = box.rargs.name.d
if type(v) == 'string' then
-- then look for swithes
self.classes = {v}
elseif type(v) == 'table' then
elseif box.rargs.name.switches then
-- then look at version 1
self.classes = v
if box.rargs.name.switches[1] ~= nil_param then
name = box.rargs.name.switches[1]
end
end
end
end
end
end

if self.infobox_name == nil then
local caption = box.rtable:tag('caption')
table.insert(self.errors, 'infobox_name needs to be defined in Infobox.new()\'s config!')
:wikitext(name)

-- add necessary attribute for switch infoboxes
if box.switchfo then
caption:attr('data-attr-param','name')
end
end
return self
end


return box
end


--[[
--[[
Counts the number of versions in the infobox
-- Functions for styling the infobox
-- works the same as the respective mw.html functions
Populates Infobox.version_names
Sets Infobox.default_version
--]]
--]]
-- attr
function Infobox:parse_versions()
local function insert_version_name(version_name)
function Infobox.attr(box, arg)
box.rtable:attr(arg)
table.insert(self.version_names, version_name)
return box
if smwutils.valid_subobject_name(version_name) == false then
end
table.insert(self.errors, 'Illegal version value: must not be "0" nor contain a "." in the first five characters')

end
-- css
end
function Infobox.float(box,float)
box.rtable:css('float',float)
-- Count the versions and setup self.version_names
return box
local i = 1
end
while self.args_raw['version'..i] do

insert_version_name(self.args_raw['version'..i])
function Infobox.css(box, ...)
i = i + 1
box.rtable:css(...)
end
return box
self.versions = i - 1
end
-- Should either have 0 or 2+ versions

if self.versions == 1 then
-- addClass
table.insert(self.errors, 'There should be multiple versions or no versions. If defining a custom version name for a single entry, use "version=Name" instead of "version1=Name".')
function Infobox.addClass(box, arg)
end
box.rtable:addClass(arg)
-- Handle the no version case - check for a custom version_name
return box
if self.versions == 0 then
end
insert_version_name(self.args_raw['version'] or 'DEFAULT')

self.versions = 1
-- Much like Infobox.addClass, but adds multiple classes
end
function Infobox.addClasses(box, ...)
-- Check for a default_version
for _, v in ipairs(...) do
if self.args_raw['default_version'] then
box.rtable:addClass(box)
self.default_version = tonumber(self.args_raw['default_version'])
if self.default_version > self.versions then -- Make sure the default version exists
self.default_version = 1
end
end
end
return box
end
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
Define all used parameters (except for default_version/version)
Pass a table to this function to map parameter names to properties
-- name : parameter name as used in the Template

-- func : function to validate and process the Template argument
Calling syntax:
-- If func is a function, will call func(Infobox.raw_param(name))
-- {{#show:page|?property}}:
-- If func is a table, contains the following key-value pairs:
-- "<property>" - unqualified and without a number will display the default value
---- name : function
-- "<property#>" - with a number will show the switch data from that index
---- params : list of arguments to pass to the function (use Infobox.raw_param(),
-- "all <property>" - adding all will display every unique value in a comma separated list
---- Infobox.param() and Infobox.smw_param() to use arguments)

-- empty (optional) : text to display in the infobox if func returns nil, defaults to "? (edit)"
Properties initiated in Infobox:finish()
-- category_never : category to add if func returns nil for all versions
-- category_partial : category to add if func returns nil for some versions, but a value for other versions
-- category_incomplete : category to add if func returns nil for at least 1 version (i.e. category_never and category_partial combined)
-- category_complete : category to add if func returns a value for all versions
-- smw_property (optional) : if this string is defined, the parameter will be saved into smw
-- smw_func (optional) : function to validate and process the Template argument to save into smw
-- func is used by default if smw_func is not defined
--]]
--]]
function Infobox:define_params(...)
function Infobox:useSMW(arg)
if type(arg) == 'table' then
-- For every parameter, store its corresponding function to self.params
for _, v in ipairs(...) do
for w, v in pairs(arg) do
self._smw[w] = v
if v.name then
local param = {}
-- Copy the function
if type(v.func) == 'function' or type(v.func) == 'table' then
param.func = v.func
end
-- If smw_property is defined, then use smw_func, or default to func if it is not defined
if v.smw_property then
param.smw_property = v.smw_property
if type(v.smw_func) == 'function' or type(v.smw_func) == 'table' then
param.smw_func = v.smw_func
else
param.smw_func = param.func
end
end
-- If empty is not defined, default message is "? (edit)"
param.empty = v.empty or editbutton("'''?''' (edit)")
-- Get the category names
param.category_never = v.category_never
param.category_partial = v.category_partial
param.category_incomplete = v.category_incomplete
param.category_complete = v.category_complete
-- Store the param
self.params[v.name] = param
table.insert(self.param_names, v.name)
end
end
end
end
return self
end
end


--[[
--[[
As above, but only assigns to "<property>", which will act like "all <property>" - "<property>#" not present
Fetches a param value. If the value is nil, will return the default value instead

-- arg: a non-table value (string, number, etc), or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name)
Properties initiated in Infobox:finish()
-- version: 0/'' for default, or else a number
Returns arg if a constant
param value (or default value) if Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name)
--]]
--]]
function Infobox:get_param(arg, version)
function Infobox:useSMWOne(arg)
if version == 0 then
if type(arg) == 'table' then
for w, v in pairs(arg) do
version = ''
self._smwOne[w] = v
end
end
end
end
-- Handle Infobox.param(), Infobox.raw_param(), Infobox.smw_param()
--[[
Set up the infobox to set properties in a SMW subobject. This will create a subobject for each version
- if there is only one version, it will put the properties directly on to the page, like useSMWOne

Properties initiated in Infobox:finish()
--]]
function Infobox:useSMWSubobject(arg)
if type(arg) == 'table' then
if type(arg) == 'table' then
for w, v in pairs(arg) do
local value = self[arg.property][arg.param_name..version]
self._smwSubobject[w] = v
-- If nil, grab default value (which could be nil as well)
if value == nil then
value = self[arg.property][arg.param_name]
end
end
return value
end
end
-- Everything else passes through unmodified (string, number, etc)
return arg
end
end


function Infobox:useSMWElement(arg)

if type(arg) == 'table' then
for w, v in pairs(arg) do
self._smwElement[w] = v
end
self.setSMWElement = true
end
end
--[[
--[[
Finishing function
Calculates the parsed value of a param
-- Finishes the return, adding necessary final tags
-- param_name : param_name as a string
-- version : 0/'' for default, or else a number
-- smw : boolean, will use smw_func if true, or func is false
--]]
--]]
function Infobox:parse_param(param_name, version, smw)
function Infobox:finish()
local onmain = mw.title.getCurrentTitle().namespace == 0
if version == 0 then
-- Don't finish twice
version = ''
if self.__finished then
return
end
end
-- use func or smw_func depending on smw argument
-- Add switch infobox resources
local param = self.params[param_name]
--if self.switchfo then
local func = smw and param.smw_func or param.func
self.rtable:attr('data-resource-class', '.infobox-resources-'..string.gsub(tostring(self.infoboxname), ' ', '_'))
-- call functions by passing the param_name
-- Wrapper tag, hidden
if type(func) == 'function' then
self.switch_tag = mw.html.create('div')
return func(self:get_param(self.raw_param(param_name), version))
:addClass('infobox-switch-resources')
-- call tables by grabbing the function and reading the param arguments
:addClass('infobox-resources-'..string.gsub(tostring(self.infoboxname), ' ', '_'))
elseif type(func) == 'table' then
:addClass('hidden')
local func_name = func.name

local func_params = func.params
for _, v in ipairs(self.paramnames) do
local func_fetched_params = {}
local i = 1
local param = self.rargs[v]
local default_value = param.d or edit
for _, func_param in ipairs(func_params) do
-- Parameters may not have any switches data, those are ignored
func_fetched_params[i] = self:get_param(func_param, version)
local switchattr = self.switchfoattr[v]
i = i + 1
-- Parameter data wrapper
local res_span = self.switch_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(default_value))

-- Switch classes
if switchattr then
def:attr('data-addclass',switchattr.d)
end

def:done()

if param.switches then
-- Add all switches, ignore those defined as nil
for i, w in ipairs(param.switches) do
if w ~= nil_param and w ~= nil and w ~= default_value 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
end

return func_name(unpack(func_fetched_params))
-- Add a tracking category for mainspace pages that have more than 1 version
else
if onmain then
table.insert(self.errors, 'Invalid param definition for '..param_name)
if self.versions > 1 then
-- version count data
self.switch_tag:tag('span')
:wikitext(string.format('Versions: [[Version count::%s]]',self.versions))
:done()
self.switch_tag:wikitext('[[Category:Pages that contain switch infobox data]]')
end
end

self.switch_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)%]%]')
if _d == nil then
return nil
end
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,
removal = smwRelease,
default = smwDefault
}
local smw_data_arr = {}

-- 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 _x = mw.text.split(tostring(_arg), Infobox.splitpoint, true)
if not smw_data_arr[v] then
smw_data_arr[v] = {}
end
if not smw_data_arr['All '..v] then
smw_data_arr['All '..v] = {}
end
for _,y in ipairs(_x) do
local temp_smw_data = smwfunc(y)
table.insert(smw_data_arr[v], temp_smw_data)
table.insert(smw_data_arr['All '..v], temp_smw_data)
end
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 _x = mw.text.split(tostring(x), Infobox.splitpoint, true)
if not smw_data_arr[v..i] then
smw_data_arr[v..i] = {}
end
if not smw_data_arr['All '..v] then
smw_data_arr['All '..v] = {}
end
for _,y in ipairs(_x) do
local temp_smw_data = smwfunc(y)
table.insert(smw_data_arr[v..i], temp_smw_data)
table.insert(smw_data_arr['All '..v], temp_smw_data)
end
end
end
end
end
-- if one version, put smwSubobject into smwOne and just do that
if self.versions < 2 and not self._smwSubobjectAppliesTo then
for w,v in pairs(self._smwSubobject) do
if not self._smwOne[w] then
self._smwOne[w] = v
elseif type(self._smwOne[w]) == 'table' then
table.insert(self._smwOne[w], v)
else
self._smwOne[w] = { self._smwOne[w], v }
end
end
end
for w, _v in pairs(self._smwOne) 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'
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 _x = mw.text.split(tostring(x), Infobox.splitpoint, true)
for _,y in ipairs(_x) do
local temp_smw_data = smwfunc(y)
for _,v in ipairs(type(_v) == 'table' and _v or {_v}) do
if not smw_data_arr[v] then
smw_data_arr[v] = {}
end
table.insert(smw_data_arr[v], temp_smw_data)
end
end
end
else
if Infobox.isDefined(_arg) then
local _targ = mw.text.split(tostring(_arg), Infobox.splitpoint, true)
for _,y in ipairs(_targ) do
local temp_smw_data = smwfunc(y)
for _,v in ipairs(type(_v) == 'table' and _v or {_v}) do
if not smw_data_arr[v] then
smw_data_arr[v] = {}
end
table.insert(smw_data_arr[v], temp_smw_data)
end
end
end
end
end
end
local smw_data_arr_elem = {}
for w, v in pairs(self._smwElement) 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'
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
if Infobox.isDefined(x) then
local _x = mw.text.split(tostring(x), Infobox.splitpoint, true)
for _,y in ipairs(_x) do
local temp_smw_data = smwfunc(y)
if not smw_data_arr_elem[v] then
smw_data_arr_elem[v] = {}
end
table.insert(smw_data_arr_elem[v], temp_smw_data)
end
end
end
else
if Infobox.isDefined(_arg) then
local _targ = mw.text.split(tostring(_arg), Infobox.splitpoint, true)
for _,y in ipairs(_targ) do
local temp_smw_data = smwfunc(y)
if not smw_data_arr_elem[v] then
smw_data_arr_elem[v] = {}
end
table.insert(smw_data_arr_elem[v], temp_smw_data)
end
end
end
end
end
-- if is a switchfo, setup for subobjects
local smw_data_arr_subobj = {}
if self._smwSubobjectAppliesTo then
for i,k in ipairs(self._smwSubobjectAppliesTo) do
local subobj_data = {
['Is variant of'] = {pagename},
}
for w,v in pairs(self._smwSubobject) do
local smwfunc = smw_to_func[w] or smw_to_func.default
local curarg = self.rargs[w]
if curarg then
local argval = curarg.d
if curarg.switches then
argval = curarg.switches[i]
if not Infobox.isDefined(argval) then
argval = curarg.d
end
end
if Infobox.isDefined(argval) then
local _x = mw.text.split(tostring(argval), Infobox.splitpoint, true)
for _, _x1 in ipairs(_x) do
_x1 = smwfunc(_x1)
if _x1 ~= 'unknown' then
if not subobj_data[v] then
subobj_data[v] = {}
end
table.insert(subobj_data[v], _x1)
end
end
end
end
end
for w,v in ipairs(k) do
local subobj_name = v
-- can't have a . in the first 5 characters of a subobject identifier
if mw.ustring.find(mw.ustring.sub(subobj_name,0,5), '%.') then
self:wikitext('[[Category:Pages with an invalid subobject name]]')
subobj_name = mw.ustring.gsub(subobj_name, '%.', '')
end
if subobj_name == '0' or subobj_name == '' then
self:wikitext('[[Category:Pages with an invalid subobject name]]')
subobj_name = 'v_'..subobj_name
end
smw_data_arr_subobj[subobj_name] = subobj_data
end
end
end
local res = mw.smw.set(smw_data_arr)
local res_subobj = true
for w,v in pairs(smw_data_arr_subobj) do
res_subobj = mw.smw.subobject(v, w)
if not res_subobj == true then
break
end
end
if not (res == true and res_subobj == true) then
self.smw_error_tag = mw.html.create('div'):css('display','none'):addClass('infobox-smw-data')
if not res == true then
self.smw_error_tag:tag('div'):wikitext('Error setting SMW properties: '+res.error):done()
end
if not res_subobj == true then
self.smw_error_tag:tag('div'):wikitext('Error setting SMW subobject properties: '+res_subobj.error):done()
end
end
if self.setSMWElement then
if self.smw_error_tag == '' then
self.smw_error_tag = mw.html.create('div'):css('display','none'):addClass('infobox-smw-data')
end
for i,v in pairs(smw_data_arr_elem) do
for j,k in ipairs(v) do
if k ~= 'unknown' then
self.smw_error_tag:tag('span'):wikitext(mw.ustring.format('%s: [[%s::%s]]', i,i,k ))
end
end
end
end
if self._smwSubobjectAppliesTo then
if self.smw_error_tag == '' then
self.smw_error_tag = mw.html.create('div'):css('display','none'):addClass('infobox-smw-data')
end
for w,v in pairs(smw_data_arr_subobj) do
local subobjdiv = self.smw_error_tag:tag('div')
subobjdiv:tag('span'):wikitext('SMW Subobject for '..w)
for j,k in pairs(v) do
subobjdiv:tag('span'):wikitext(mw.ustring.format('%s: %s', j, table.concat(k, ', ')))
end
end
end
end

-- Add view and talk links to infobox
-- Only done if a name is defined
if self.infoboxname and not self.bottomlinks.hide then
local bottom_links = {}
for _, v in ipairs(self.bottomlinks.links) do
table.insert(bottom_links,
string.format(
table.concat({'[[',
v[1],
'|',
v[2],
']]'}),
self.infoboxname,
self.infoboxname,
self.infoboxname,
self.infoboxname,
self.infoboxname)
)
end

bottom_links = table.concat(bottom_links,' &bull; ')
self.rtable:tag('tr'):tag('td')
:addClass('infobox-template-links')
:attr('colspan', self.bottomlinks.colspan)
:wikitext(bottom_links)
:done()
end
end
-- Define as finished
self.__finished = true
end
end



--[[
--[[
Function for defining parameters
Parses all the param values (args_parsed and args_smw)
-- 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:parse_params()
function Infobox:defineParams(...)
for _, v in ipairs(...) do
-- Calculate the param value for all params and all versions
-- For every parameter, store its corresponding function to self.params
for _, param_name in ipairs(self.param_names) do
if v.name then
for version=0, self.versions do
-- If the value is a function or a table (which should define a function)
if version == 0 then
if type(v.func) == 'function' or type(v.func) == 'table' then
version = ''
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
end
-- get the parsed value
-- Create a list of all param names
table.insert(self.paramnames,v.name)
self.args_parsed[param_name..version] = self:parse_param(param_name, version, false)
-- function to allow duplicated values
-- Only get the smw value if smw_property is defined
if self.params[param_name].smw_property then
if v.dupes then
self.args_smw[param_name..version] = self:parse_param(param_name, version, true)
self.dupeable[v.name] = true
end
end
end
end
end
end
end
end



--[[
--[[
-- Forces an infobox to only use 1 variant
Creates the table header
-- Mainly used by lite infoboxes
-- This should be run before creation
--]]
--]]
function Infobox:table_header()
function Infobox:noSwitch()
self.versions = 1
-- Create infobox table
self.rtable = mw.html.create('table')
self.switchfo = false
:addClass('plainlinks')
:addClass('infobox')
:addClass('infobox-'..self.infobox_name)
for _, class in ipairs(self.classes) do
local class_name = self:get_param(class, self.default_version)
if type(class_name) == 'string' then
self.rtable:addClass('infobox-'..mw.ustring.gsub(class_name, '%s', '_'))
end
end
-- Create the switch datatable if multiple versions
if self.versions > 1 then
self.rtable:addClass('infobox-switch')
self.switch_datatable = mw.html.create('div')
:addClass('infobox-switch-resources')
:addClass('infobox-resources-'..self.infobox_name)
:addClass('hidden')
end
return self
end
end



--[[
--[[
-- Calculates the max version
If multiple versions exist, creates a set of buttons for switching
-- Adds labels
-- self.max_buttons : if the number of versions exceeds this value, a dropdown will be created instead
-- Sees if this needs to be a switch infobox
-- Returns extra version count (even if already run)
--]]
--]]
function Infobox:buttons()
function Infobox.maxVersion(box)
-- Only allowed to run once
if self.versions < 2 then
if box.versions ~= -1 then
return
return box.versions
end
end

-- Button caption
box.labels = {}
local buttons = self.rtable:tag('caption')
box.versions = 0
:tag('div')
local smwappliesto = {}
:addClass('infobox-buttons')

:attr('data-default-index', self.default_version)
-- Look for up to 125 variants, defined in order
-- Dropdown list instead of buttons if too many versions
for i=1, 125 do
if self.versions > self.max_buttons then
-- If variant# exists
buttons:addClass('infobox-buttons-select')
if box.args['version'..i] then
-- Increase version count
box.versions = box.versions + 1

-- Add its label
local label = box.args['version'..i] or ('Version '..i)
table.insert(box.labels,label)
-- add to appliesto
if box.args['smwname'..i] or box.args['appliesto'..i] then
table.insert(smwappliesto, mw.text.split(box.args['smwname'..i] or box.args['appliesto'..i], '¦'))
else
table.insert(smwappliesto, {label})
end
else
-- Stop if it doesn't exist
break
end
end
end

-- Create all the buttons
-- Define self as a switch infobox if at least 1 other version is found
for version=1, self.versions do
if box.versions > 0 then
local button = buttons:tag('span')
box.switchfo = true
:attr('data-switch-index', version)
box._smwSubobjectAppliesTo = smwappliesto
:attr('data-switch-anchor', '#'..self.version_names[version])
else
:addClass('button')
-- single version, check for appliesto
:wikitext(self.version_names[version])
if box.args['smwname'] or box.args['appliesto'] then
-- In case of dropdown list, hide the buttons as the switch gadget will convert them to a dropdown list - we can't directly create the dropdown list here as the select/option tags are rejected by wikitext
box._smwSubobjectAppliesTo = {mw.text.split(box.args['smwname'] or box.args['appliesto'], '¦')}
if self.versions > self.max_buttons then
button:addClass('hidden')
end
end
end
end
end


-- versions calculated
return box.versions
end


--[[
--[[
-- Cleans parameters as defined by the above function
For each version, stores a subobject for all params that have smw_property defined
-- 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:store_smw()
function Infobox:cleanParams()
-- map of flags to functionality
for version=1, self.versions do
local flagmap = {
-- Generate a subobject name
r = 'r',
-- Reminder - subobject name cannot have a . in the first 5 characters
l = 'r',
local subobject_name = self.version_names[version]
d = 'd',
-- Store each param that has smw_property defined and is not nil
p = 'p'
local subobject = {Infobox = self.infobox_name} -- Also store the current Infobox name
}
for _, param_name in ipairs(self.param_names) do
-- For all parameters named
local property = self.params[param_name].smw_property
for _, v in ipairs(self.paramnames) do
if property then
-- Parameter to add
local value = self:get_param(self.smw_param(param_name), version)
if value ~= nil then
local _add = {}
local catdata = { all_defined = true, one_defined = false }
subobject[property] = value
-- 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
-- compare to nil explicitly because false is a valid value
if xarg == nil then
xarg = self.args[w] or ''
end
-- 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
end
-- Add parsed argument to table
table.insert(func_args,xarg)
end
end
-- Run function
_add.d = func(unpack(func_args))
end
end

-- Keep a copy for debugging
if _add.d == nil or _add.d == nil_param then
self.__smw_debug[subobject_name] = subobject
-- have to do pagename defaults here to prevent weird behaviour with switch values
-- Save subobjects if not in mainspace
if v == 'name' then
if mw.title.getCurrentTitle():inNamespace(0) then
local result = true
_add.d = pagename
if self.versions == 1 then
result = mw.smw.set(subobject)
else
else
_add.d = edit
result = mw.smw.subobject(subobject, subobject_name)
end
if result ~= true then
table.insert(self.errors, 'SMW error: '..result.error)
end
end
catdata.all_defined = false
else
--_add.d is not nil
catdata.one_defined = true
end
end
end
if self.switchfo then
end
-- 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]
Automatically add categories for each param as defined in define_params()
end
--]]

function Infobox:parse_categories()
-- catch all
for _, param_name in ipairs(self.param_names) do
if type(func) ~= 'function' then
local param = self.params[param_name]
func = has_content
local defined = self:is_param_defined(Infobox.param(param_name))
end
if defined == 0 and param.category_never then

self.categories[param.category_never] = 1
-- 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
end

if defined == 1 and param.category_partial then
-- Quick fix for names (which defaults to pagename)
self.categories[param.category_partial] = 1
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
end

if defined < 2 and param.category_incomplete then
-- Parameter cleaning finished, add to table of cleaned args
self.categories[param.category_incomplete] = 1
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
catdata.all_defined = false
break
end
end
end
end
end
end

if defined == 2 and param.category_complete then
self.categories[param.category_complete] = 1
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
-- remove types that don't turn into strings properly
if type(z) == 'table' or type(z) == 'function' then
self.rargs[v].switches[q] = nil_param

-- if it 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
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
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
When a parameter is added into the table, add the alternative versions' data into the switch datatable
-- arg : param name
-- content : a non-table value (string, number, etc), or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name)
-- retp : return type
Returns the name of the parameter in the tag 'data-attr-param'
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:add_switch_data(content)
function Infobox:param(arg, retp)
-- All parameters
if self.versions < 2 then
if arg == 'all' then
return false
return self.rargs
end
end
-- case-insensitive flagging
-- Only needed for non-constant params
retp = tostring(retp):lower()
if type(content) ~= 'table' then
local fmap = {
return false
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
end

-- Only needed if the param changes value between different versions
-- Fetch parameter
local first_value = self:get_param(content, 1)
local all_same = true
local param = self.rargs[arg]
-- Return nil if no table found
for version=2, self.versions do
if not param then return nil end
if first_value ~= self:get_param(content, version) then

all_same = false
-- Return default
break
if ret_func == 'd' then
end
return param.d
end
end

if all_same then
-- Return full table
return false
if ret_func == 'f' then
return param
end
end

-- Let's build the datatable
-- Return switch table
-- datum name: Prepend raw__ or smw__ if not a parsed argument
if ret_func == 's' then
local name = content.param_name
return param.switches
if content.property == 'args_raw' then
name = 'raw__' + name
end
end

if content.property == 'args_smw' then
-- Return the first switch, otherwise the default
name = 'smw__' + name
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
end

local data_param = self.switch_datatable:tag('span'):attr('data-attr-param', name)
-- Add each version to the datatable
-- If s2, reread the param
if ret_func == 's2' then
for version=1, self.versions do
-- no switches
local text = self:get_param(content, version)
if text == nil then
if not param.switches then
return nil
text = self.params[content.param_name].empty
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
data_param:tag('span'):attr('data-attr-index', version):wikitext(text)
end
end
-- return the 'data-attr-param' name
return name
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
if param.switches then
if Infobox.isDefined(param.switches[1]) then
ret = param.switches[1]
else
ret = _arg
end
else
ret = _arg
end
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
Override tostring
-- 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:__tostring()
function Infobox:paramGrep(param,val)
local arg = self.rargs[param]
-- Create error messages
-- if no parameters, return nil
local error_text = ''
if not arg then
for _, v in ipairs(self.errors) do
return nil
error_text = error_text..'<span class="mw-message-box-error">'..v..'</span>'
end
end

-- Create categories
local category_text = ''
local ret
local valtype = type(val)
if mw.title.getCurrentTitle():inNamespace(0) then
-- start with the default value
for key, _ in pairs(self.categories) do
-- if it's a function, run it
category_text = category_text..'[[Category:'..key..']]'
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
end
end

local dump = ''
-- switch cases
if self.args_raw.__dump then
-- more organized putting them here
setmetatable(self, nil)
if arg.switches then
dump = '<nowiki>'..mw.dumpObject(self)..'</nowiki>'
for _, v in ipairs(arg.switches) do
setmetatable(self, Infobox)
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
end

return tostring(self.rtable) .. tostring(self.switch_datatable) .. error_text .. category_text .. dump
-- return false in every other case
return false
end
end
------


function Infobox.paramRead(arg,val)
-- if no parameters, return nil
if not arg then
return nil
end


local ret
--[[
local valtype = type(val)
Debug function
-- start with the default value
--]]
function Infobox:dump()
-- if it's a function, run it
if valtype == 'function' then
-- Temporarily detach the metatable so we don't override tostring anymore, allowing us to use dumpObject
ret = val(arg.d)
setmetatable(self, nil)

mw.log(mw.dumpObject(self))
-- true means it matched
setmetatable(self, Infobox)
if ret == true then
mw.log(tostring(self))
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
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
local btns = box.switch_buttons_tag
if box.custom_buttons then
btns = ''
end
if box.args.__dump__ then
return '<' .. 'pre>'..mw.dumpObject(box) .. '</' .. 'pre>[[Category:Dumping infoboxes]]'
end
return tostring(btns) .. tostring(box.rtable) .. table.concat(box.appendStrs, '') .. tostring(box.switch_tag) .. tostring(box.smw_error_tag)
end


return Infobox
return Infobox
-- </nowiki>
Please note that all contributions to Brighter Shores Wiki are considered to be released under the CC BY-NC-SA 3.0 (see Brighter Shores:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!
Cancel Editing help (opens in new window)
Preview page with this template

This page is a member of a hidden category: