Module:Infobox: Difference between revisions
(Simplify the table_class param) |
(When a parameter is nil, make sure the function is called with it being set to nil (previously, all other parameters would be shifted left one)) |
||
(44 intermediate revisions by 6 users not shown) | |||
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 |
||
-- config: table containing configuration parameters |
|||
-- args : parameters from frame to pass through |
|||
-- params : definitions for each used value |
|||
-- Sets a meta table and creates a <div> tag wrapper |
|||
-- args : values passed from the Template |
|||
-- other fields are initialized in other functions |
|||
---- the following values are treated specially: default_version, version, version1, version2... |
|||
--]] |
--]] |
||
function Infobox.new(args) |
function Infobox.new(config, params, args) |
||
local obj = setmetatable({ |
local obj = setmetatable({ |
||
args_raw = args, -- parameters (uncleaned) |
|||
args_parsed = {}, -- parsed parameters |
|||
args_smw = {}, -- parameters parsed for smw |
|||
infobox_name = nil, -- template name |
|||
classes = {}, -- list of classes to add, using the default_version |
|||
param_names = {}, -- ordered param list |
|||
params = {}, -- param definitions |
|||
max_buttons = 6, -- If there are more buttons than the max, the buttons will become a dropdown menu |
|||
switchfoattr = {}, -- switch data class changes |
|||
default_version = 1, -- default version to populate the infobox |
|||
maxbuttons = DEFAULT_MAX_BUTTONS, -- maximum number of buttons before switching becomes a menu |
|||
versions = nil, -- number of versions |
|||
version_names = {}, -- title of each version (for selection and SMW) |
|||
switch_buttons_tag = '', -- switchfo buttons |
|||
rtable = nil, -- infobox table to return at the end |
|||
custom_buttons = false, |
|||
switch_datatable = '', -- datatable for javascript for switch infoboxes |
|||
smw_error_tag = '', |
|||
categories = {}, -- set of categories |
|||
errors = {}, -- list of errors |
|||
__smw_debug = {}, -- debug data for smw subobjects |
|||
_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 |
||
--[[ |
--[[ |
||
Refers to a param after being processed by the validating func |
|||
Changes the class of the Infobox from 'infobox' to something else |
|||
Used in add_row(), define_params() and is_param_defined() |
|||
use before :create() |
|||
--]] |
--]] |
||
function Infobox |
function Infobox.param(param_name) |
||
local param = { |
|||
if type(arg) == 'string' then |
|||
property = 'args_parsed', |
|||
self.table_class = new_class |
|||
param_name = param_name, |
|||
end |
|||
} |
|||
return param |
|||
end |
end |
||
--[[ |
--[[ |
||
Refers to a param in raw form as passed from the Template |
|||
Creates an infobox |
|||
Used in add_row(), define_params() and is_param_defined() |
|||
-- 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 |
function Infobox.raw_param(param_name) |
||
local param = { |
|||
-- Run to find if this is a switch infobox and if so, how many boxes |
|||
property = 'args_raw', |
|||
if self.versions == -1 then |
|||
param_name = param_name, |
|||
self:maxVersion() |
|||
} |
|||
return param |
|||
-- Run if switch infobox |
|||
end |
|||
if self.switchfo then |
|||
-- 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') |
|||
:addClass(table_class) |
|||
-- 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 and for css |
|||
function Infobox:defineName(arg) |
|||
self.infoboxname = arg |
|||
end |
|||
--[[ |
|||
-- Defines the bottom links of the infobox |
|||
Checks to see if a param is defined. |
|||
-- pass a table whose elements are tables that define a link and a label |
|||
-- param: Infobox.param(name), Infobox.raw_param(name) or Infobox.smw_param(name) |
|||
-- { |
|||
Returns 0 if param is never defined |
|||
-- { 'link', 'label }, |
|||
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' |
|||
function Infobox:is_param_defined(param) |
|||
-- If we wanted Template:InFooBar to link to it's /doc page with a "doc" label: |
|||
local undefined = 0 |
|||
-- { ... |
|||
local defined = 0 |
|||
-- { 'Template:%s/doc', 'doc' }, |
|||
for version = 1, self.versions do |
|||
-- ... } |
|||
local value = self:get_param(param, version) |
|||
-- The template's name can only be called 5 times |
|||
if value ~= nil then |
|||
function Infobox:defineLinks(arg) |
|||
defined = 1 |
|||
if type(arg) == 'table' then |
|||
else |
|||
if arg.colspan then |
|||
undefined = 1 |
|||
self.bottomlinks.colspan = arg.colspan |
|||
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 |
|||
--[[ |
--[[ |
||
Adds a row to the infobox table |
|||
Add parameters functions |
|||
Parameter should be a table of cells, where each cell is a table with the following arguments: |
|||
All parameters should be tables |
|||
-- tag : 'td' or 'th' |
|||
The first parameter defines the type of cell to create |
|||
-- content : a string or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name) |
|||
-- th : <th> |
|||
-- attr (optional) : mw.html:attr({ arg1 = '1', ... }) |
|||
-- td : <td> |
|||
-- css (optional) : mw.html:css({ arg1 = '1', ...) |
|||
-- argh : <th> |
|||
-- class (optional) : mw.html:addClass('arg') |
|||
-- argd : <td> |
|||
---- class also supports a table of values, even though mw.html:addClass() does not |
|||
The second parameter defines what is inside the tag |
|||
---- common classes: infobox-subheader |
|||
-- th | th : text passed |
|||
-- rowspan (optional) : mw.html:attr('rowspan',arg) |
|||
-- argh | argd : parameter with the name passed |
|||
-- colspan (optional) : mw.html:attr('colspan',arg) |
|||
Additional named parameters can be used to add any styling or attributes |
|||
-- title (optional) : mw.html:attr('title',arg) |
|||
-- 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> |
|||
The row itself may be assigned a single class by setting the value of the key addClass |
|||
adding it to the infobox table of ipsobox |
|||
-- 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 |
function Infobox:add_row(...) |
||
-- New row to add |
|||
local args = ... |
local args = ... |
||
local _row = |
local _row = self.rtable:tag('tr') |
||
-- For each |
-- For each cell |
||
for |
for _, v in ipairs(args) do |
||
local _cell = _row:tag(v.tag) |
|||
-- map tag name to appropriate tag, default to <td> |
|||
-- Optional parameters |
|||
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 |
if v.attr then |
||
_cell:attr(v.attr) |
_cell:attr(v.attr) |
||
Line 391: | Line 155: | ||
_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 405: | Line 165: | ||
end |
end |
||
end |
end |
||
-- Populate the cell contents |
|||
local content = self:get_param(v.content, self.default_version) |
|||
-- if the cell is a normal th or td, add the exact argument passed |
|||
if |
if content == nil then |
||
content = self.params[v.content.param_name].empty |
|||
end |
|||
-- if defined with "arg", add the argument with name passed |
|||
_cell:wikitext(content) |
|||
elseif v.tag == 'argh' or v.tag == 'argd' then |
|||
-- Add the switch data if multiple values exist |
|||
local content = box.rargs[v.content] |
|||
local data_attr_param = self:add_switch_data(v.content) |
|||
-- if the requested parameter doesn't exist whatsoever, just return a blank string |
|||
if data_attr_param then |
|||
_cell:attr('data-attr-param', data_attr_param) |
|||
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 |
||
end |
end |
||
-- not that meta |
|||
-- allow classes to be defined on the whole row |
-- allow classes to be defined on the whole row |
||
if args.addClass then |
|||
-- okay, sort of meta |
|||
_row:addClass(args.addClass) |
|||
if args.meta then |
|||
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 |
|||
--]] |
--]] |
||
function Infobox |
function Infobox:pad(colspan, class) |
||
local tr = |
local tr = self.rtable:tag('tr'):tag('td'):attr('colspan', colspan or 1):addClass('infobox-padding') |
||
: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 |
||
return self |
|||
tr:done() |
|||
return box |
|||
end |
end |
||
--[[ |
--[[ |
||
Adds a class to the table as a whole |
|||
-- functions the same as mw.html:wikitext() on the wrapper |
|||
-- Should only be used for categories really |
|||
--]] |
--]] |
||
function Infobox |
function Infobox:addClass(arg) |
||
self.rtable:addClass(arg) |
|||
return |
return self |
||
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 |
|||
--[[ |
--[[ |
||
Setup config values |
|||
-- Adds a caption to the infobox |
|||
-- config: table containing configuration parameters |
|||
-- defaults to the pagename |
|||
---- infobox_name = mandatory unique identifier for this infobox, used for css |
|||
-- or the default argument if defined |
|||
---- max_buttons = max number of switch buttons before using a dropdown list instead |
|||
--]] |
--]] |
||
function Infobox |
function Infobox:config(config) |
||
for k, v in pairs(config) do |
|||
-- default to the article's name |
|||
if k == 'infobox_name' then |
|||
local name = pagename |
|||
self.infobox_name = mw.ustring.gsub(v, '%s', '_') |
|||
-- first see if the name parameter exists |
|||
elseif k == 'max_buttons' then |
|||
if box.rargs.name then |
|||
self.max_buttons = tonumber(v) |
|||
-- then try the default |
|||
elseif k == 'class' then |
|||
if type(v) == 'string' then |
|||
name = box.rargs.name.d |
|||
self.classes = {v} |
|||
-- then look for swithes |
|||
elseif |
elseif type(v) == 'table' then |
||
self.classes = v |
|||
-- then look at version 1 |
|||
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 |
|||
table.insert(self.errors, 'infobox_name needs to be defined in Infobox.new()\'s config!') |
|||
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 |
end |
||
return self |
|||
return box |
|||
end |
end |
||
--[[ |
--[[ |
||
Counts the number of versions in the infobox |
|||
Populates Infobox.version_names |
|||
-- works the same as the respective mw.html functions |
|||
Sets Infobox.default_version |
|||
--]] |
--]] |
||
function Infobox:parse_versions() |
|||
-- attr |
|||
function |
local function insert_version_name(version_name) |
||
table.insert(self.version_names, version_name) |
|||
box.rtable:attr(arg) |
|||
if smwutils.valid_subobject_name(version_name) == false then |
|||
return box |
|||
table.insert(self.errors, 'Illegal version value: must not be "0" nor contain a "." in the first five characters') |
|||
end |
|||
end |
|||
end |
|||
-- css |
|||
function Infobox.float(box,float) |
|||
-- Count the versions and setup self.version_names |
|||
box.rtable:css('float',float) |
|||
local i = 1 |
|||
return box |
|||
while self.args_raw['version'..i] do |
|||
end |
|||
insert_version_name(self.args_raw['version'..i]) |
|||
i = i + 1 |
|||
function Infobox.css(box, ...) |
|||
end |
|||
box.rtable:css(...) |
|||
self.versions = i - 1 |
|||
return box |
|||
-- Should either have 0 or 2+ versions |
|||
end |
|||
if self.versions == 1 then |
|||
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".') |
|||
-- addClass |
|||
end |
|||
function Infobox.addClass(box, arg) |
|||
-- Handle the no version case - check for a custom version_name |
|||
box.rtable:addClass(arg) |
|||
if self.versions == 0 then |
|||
return box |
|||
insert_version_name(self.args_raw['version'] or 'DEFAULT') |
|||
end |
|||
self.versions = 1 |
|||
end |
|||
-- Much like Infobox.addClass, but adds multiple classes |
|||
-- Check for a default_version |
|||
function Infobox.addClasses(box, ...) |
|||
if self.args_raw['default_version'] then |
|||
for _, v in ipairs(...) do |
|||
self.default_version = tonumber(self.args_raw['default_version']) |
|||
box.rtable:addClass(box) |
|||
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 |
|||
--[[ |
--[[ |
||
Define all used parameters (except for default_version/version) |
|||
Allows the infobox to use Semantic Media Wiki and give parameters properties |
|||
-- name : parameter name as used in the Template |
|||
Pass a table to this function to map parameter names to properties |
|||
-- func : function to validate and process the Template argument |
|||
-- If func is a function, will call func(Infobox.raw_param(name)) |
|||
Calling syntax: |
|||
-- If func is a table, contains the following key-value pairs: |
|||
-- {{#show:page|?property}}: |
|||
---- name : function |
|||
-- "<property>" - unqualified and without a number will display the default value |
|||
---- params : list of arguments to pass to the function (use Infobox.raw_param(), |
|||
-- "<property#>" - with a number will show the switch data from that index |
|||
---- Infobox.param() and Infobox.smw_param() to use arguments) |
|||
-- "all <property>" - adding all will display every unique value in a comma separated list |
|||
-- empty (optional) : text to display in the infobox if func returns nil, defaults to "? (edit)" |
|||
-- category_never : category to add if func returns nil for all versions |
|||
Properties initiated in Infobox:finish() |
|||
-- 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: |
function Infobox:define_params(...) |
||
-- For every parameter, store its corresponding function to self.params |
|||
if type(arg) == 'table' then |
|||
for _, v in ipairs(...) do |
|||
if v.name then |
|||
self._smw[w] = v |
|||
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 |
||
--[[ |
--[[ |
||
Fetches a param value. If the value is nil, will return the default value instead |
|||
As above, but only assigns to "<property>", which will act like "all <property>" - "<property>#" not present |
|||
-- arg: a non-table value (string, number, etc), or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name) |
|||
-- version: 0/'' for default, or else a number |
|||
Properties initiated in Infobox:finish() |
|||
Returns arg if a constant |
|||
param value (or default value) if Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name) |
|||
--]] |
--]] |
||
function Infobox: |
function Infobox:get_param(arg, version) |
||
if |
if version == 0 then |
||
version = '' |
|||
for w, v in pairs(arg) do |
|||
self._smwOne[w] = v |
|||
end |
|||
end |
end |
||
-- Handle Infobox.param(), Infobox.raw_param(), Infobox.smw_param() |
|||
end |
|||
--[[ |
|||
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 |
||
local value = self[arg.property][arg.param_name..version] |
|||
for w, v in pairs(arg) do |
|||
-- If nil, grab default value (which could be nil as well) |
|||
self._smwSubobject[w] = v |
|||
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 |
|||
--[[ |
--[[ |
||
Calculates the parsed value of a param |
|||
Finishing function |
|||
-- param_name : param_name as a string |
|||
-- Finishes the return, adding necessary final tags |
|||
-- version : 0/'' for default, or else a number |
|||
-- smw : boolean, will use smw_func if true, or func is false |
|||
--]] |
--]] |
||
function Infobox: |
function Infobox:parse_param(param_name, version, smw) |
||
if version == 0 then |
|||
local onmain = mw.title.getCurrentTitle().namespace == 0 |
|||
version = '' |
|||
-- Don't finish twice |
|||
if self.__finished then |
|||
return |
|||
end |
end |
||
-- use func or smw_func depending on smw argument |
|||
local param = self.params[param_name] |
|||
-- Add switch infobox resources |
|||
local func = smw and param.smw_func or param.func |
|||
--if self.switchfo then |
|||
-- call functions by passing the param_name |
|||
self.rtable:attr('data-resource-class', '.infobox-resources-'..string.gsub(tostring(self.infoboxname), ' ', '_')) |
|||
if type(func) == 'function' then |
|||
-- Wrapper tag, hidden |
|||
return func(self:get_param(self.raw_param(param_name), version)) |
|||
self.switch_tag = mw.html.create('div') |
|||
-- call tables by grabbing the function and reading the param arguments |
|||
:addClass('infobox-switch-resources') |
|||
elseif type(func) == 'table' then |
|||
:addClass('infobox-resources-'..string.gsub(tostring(self.infoboxname), ' ', '_')) |
|||
local func_name = func.name |
|||
:addClass('hidden') |
|||
local func_params = func.params |
|||
local func_fetched_params = {} |
|||
for _, v in ipairs(self.paramnames) do |
|||
local i = 1 |
|||
for _, func_param in ipairs(func_params) do |
|||
local default_value = param.d or edit |
|||
func_fetched_params[i] = self:get_param(func_param, version) |
|||
-- Parameters may not have any switches data, those are ignored |
|||
i = i + 1 |
|||
local switchattr = self.switchfoattr[v] |
|||
-- 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)) |
|||
else |
|||
-- Add a tracking category for mainspace pages that have more than 1 version |
|||
table.insert(self.errors, 'Invalid param definition for '..param_name) |
|||
if onmain then |
|||
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,' • ') |
|||
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 |
||
--[[ |
--[[ |
||
Parses all the param values (args_parsed and args_smw) |
|||
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: |
function Infobox:parse_params() |
||
-- Calculate the param value for all params and all versions |
|||
for _, v in ipairs(...) do |
|||
for _, param_name in ipairs(self.param_names) do |
|||
-- For every parameter, store its corresponding function to self.params |
|||
for version=0, self.versions do |
|||
if v.name then |
|||
if version == 0 then |
|||
-- If the value is a function or a table (which should define a function) |
|||
version = '' |
|||
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 |
end |
||
-- |
-- get the parsed value |
||
self.args_parsed[param_name..version] = self:parse_param(param_name, version, false) |
|||
table.insert(self.paramnames,v.name) |
|||
-- Only get the smw value if smw_property is defined |
|||
-- function to allow duplicated values |
|||
if |
if self.params[param_name].smw_property then |
||
self. |
self.args_smw[param_name..version] = self:parse_param(param_name, version, true) |
||
end |
end |
||
end |
end |
||
end |
end |
||
end |
end |
||
--[[ |
--[[ |
||
Creates the table header |
|||
-- Forces an infobox to only use 1 variant |
|||
-- Mainly used by lite infoboxes |
|||
-- This should be run before creation |
|||
--]] |
--]] |
||
function Infobox: |
function Infobox:table_header() |
||
-- Create infobox table |
|||
self.versions = 1 |
|||
self. |
self.rtable = mw.html.create('table') |
||
: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 |
||
--[[ |
--[[ |
||
If multiple versions exist, creates a set of buttons for switching |
|||
-- Calculates the max version |
|||
-- self.max_buttons : if the number of versions exceeds this value, a dropdown will be created instead |
|||
-- Adds labels |
|||
-- Sees if this needs to be a switch infobox |
|||
-- Returns extra version count (even if already run) |
|||
-- Sets the smw subobject names from version or smwname |
|||
--]] |
--]] |
||
function Infobox |
function Infobox:buttons() |
||
if self.versions < 2 then |
|||
-- Only allowed to run once |
|||
return |
|||
if box.versions ~= -1 then |
|||
return box.versions |
|||
end |
end |
||
-- Button caption |
|||
local buttons = self.rtable:tag('caption') |
|||
box.labels = {} |
|||
:tag('div') |
|||
box.versions = 0 |
|||
:addClass('infobox-buttons') |
|||
local smwappliesto = {} |
|||
:attr('data-default-index', self.default_version) |
|||
-- Dropdown list instead of buttons if too many versions |
|||
-- Look for up to 125 variants, defined in order |
|||
if self.versions > self.max_buttons then |
|||
for i=1, 125 do |
|||
buttons:addClass('infobox-buttons-select') |
|||
-- If variant# exists |
|||
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] then |
|||
table.insert(smwappliesto, mw.text.split(box.args['smwname'..i], '¦')) |
|||
else |
|||
table.insert(smwappliesto, {label}) |
|||
end |
|||
else |
|||
-- Stop if it doesn't exist |
|||
break |
|||
end |
|||
end |
end |
||
-- Create all the buttons |
|||
for version=1, self.versions do |
|||
-- Define self as a switch infobox if at least 1 other version is found |
|||
local button = buttons:tag('span') |
|||
if box.versions > 0 then |
|||
:attr('data-switch-index', version) |
|||
box.switchfo = true |
|||
:attr('data-switch-anchor', '#'..self.version_names[version]) |
|||
box._smwSubobjectAppliesTo = smwappliesto |
|||
:addClass('button') |
|||
else |
|||
:wikitext(self.version_names[version]) |
|||
-- single version, check for appliesto |
|||
-- 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 |
|||
if box.args['smwname'] then |
|||
if self.versions > self.max_buttons then |
|||
box._smwSubobjectAppliesTo = {mw.text.split(box.args['smwname'], '¦')} |
|||
button:addClass('hidden') |
|||
end |
end |
||
end |
end |
||
-- versions calculated |
|||
return box.versions |
|||
end |
end |
||
--[[ |
--[[ |
||
For each version, stores a subobject for all params that have smw_property defined |
|||
-- 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: |
function Infobox:store_smw() |
||
for version=1, self.versions do |
|||
-- map of flags to functionality |
|||
-- Generate a subobject name |
|||
local flagmap = { |
|||
-- Reminder - subobject name cannot have a . in the first 5 characters |
|||
r = 'r', |
|||
local subobject_name = self.version_names[version] |
|||
l = 'r', |
|||
-- Store each param that has smw_property defined and is not nil |
|||
d = 'd', |
|||
local subobject = {Infobox = self.infobox_name} -- Also store the current Infobox name |
|||
p = 'p' |
|||
for _, param_name in ipairs(self.param_names) do |
|||
} |
|||
local property = self.params[param_name].smw_property |
|||
-- For all parameters named |
|||
if property then |
|||
for _, v in ipairs(self.paramnames) do |
|||
local value = self:get_param(self.smw_param(param_name), version) |
|||
-- Parameter to add |
|||
if value ~= nil then |
|||
subobject[property] = value |
|||
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 |
|||
-- 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 |
|||
self.__smw_debug[subobject_name] = subobject |
|||
if _add.d == nil or _add.d == nil_param then |
|||
-- Save subobjects if not in mainspace |
|||
-- have to do pagename defaults here to prevent weird behaviour with switch values |
|||
if mw.title.getCurrentTitle():inNamespace(0) then |
|||
if v == 'name' then |
|||
local result = true |
|||
if self.versions == 1 then |
|||
result = mw.smw.set(subobject) |
|||
else |
else |
||
result = mw.smw.subobject(subobject, subobject_name) |
|||
_add.d = edit |
|||
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 |
|||
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 |
|||
Automatically add categories for each param as defined in define_params() |
|||
func = func_map[func] |
|||
--]] |
|||
end |
|||
function Infobox:parse_categories() |
|||
for _, param_name in ipairs(self.param_names) do |
|||
-- catch all |
|||
local param = self.params[param_name] |
|||
if type(func) ~= 'function' then |
|||
local defined = self:is_param_defined(Infobox.param(param_name)) |
|||
func = has_content |
|||
if defined == 0 and param.category_never then |
|||
end |
|||
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 |
|||
self.categories[param.category_partial] = 1 |
|||
-- 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 |
end |
||
if defined < 2 and param.category_incomplete then |
|||
self.categories[param.category_incomplete] = 1 |
|||
-- 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 |
|||
catdata.all_defined = false |
|||
break |
|||
end |
|||
end |
|||
end |
|||
end |
|||
end |
end |
||
if defined == 2 and param.category_complete then |
|||
self. |
self.categories[param.category_complete] = 1 |
||
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 |
|||
--]==========================================] |
|||
--[[ |
--[[ |
||
When a parameter is added into the table, add the alternative versions' data into the switch datatable |
|||
Access the param |
|||
-- content : a non-table value (string, number, etc), or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name) |
|||
-- arg : param name |
|||
Returns the name of the parameter in the tag 'data-attr-param' |
|||
-- 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: |
function Infobox:add_switch_data(content) |
||
if self.versions < 2 then |
|||
-- All parameters |
|||
return false |
|||
if arg == 'all' then |
|||
return self.rargs |
|||
end |
end |
||
-- Only needed for non-constant params |
|||
-- case-insensitive flagging |
|||
if type(content) ~= 'table' then |
|||
retp = tostring(retp):lower() |
|||
return false |
|||
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 |
end |
||
-- Only needed if the param changes value between different versions |
|||
local first_value = self:get_param(content, 1) |
|||
-- Fetch parameter |
|||
local |
local all_same = true |
||
for version=2, self.versions do |
|||
-- Return nil if no table found |
|||
if first_value ~= self:get_param(content, version) then |
|||
if not param then return nil end |
|||
all_same = false |
|||
break |
|||
-- Return default |
|||
end |
|||
if ret_func == 'd' then |
|||
return param.d |
|||
end |
end |
||
if all_same then |
|||
return false |
|||
-- Return full table |
|||
if ret_func == 'f' then |
|||
return param |
|||
end |
end |
||
-- Let's build the datatable |
|||
-- datum name: Prepend raw__ or smw__ if not a parsed argument |
|||
-- Return switch table |
|||
local name = content.param_name |
|||
if ret_func == 's' then |
|||
if content.property == 'args_raw' then |
|||
return param.switches |
|||
name = 'raw__' + name |
|||
end |
end |
||
if content.property == 'args_smw' then |
|||
name = 'smw__' + name |
|||
-- 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 |
end |
||
local data_param = self.switch_datatable:tag('span'):attr('data-attr-param', name) |
|||
-- |
-- Add each version to the datatable |
||
for version=1, self.versions do |
|||
if ret_func == 's2' then |
|||
local text = self:get_param(content, version) |
|||
-- no switches |
|||
if |
if text == nil then |
||
text = self.params[content.param_name].empty |
|||
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 |
||
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 |
|||
--[[ |
--[[ |
||
Override tostring |
|||
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: |
function Infobox:__tostring() |
||
-- Create error messages |
|||
local arg = self.rargs[param] |
|||
local error_text = '' |
|||
-- if no parameters, return nil |
|||
for _, v in ipairs(self.errors) do |
|||
if not arg then |
|||
error_text = error_text..'<span class="mw-message-box-error">'..v..'</span>' |
|||
return nil |
|||
end |
end |
||
-- Create categories |
|||
local |
local category_text = '' |
||
if mw.title.getCurrentTitle():inNamespace(0) then |
|||
local valtype = type(val) |
|||
for key, _ in pairs(self.categories) do |
|||
-- start with the default value |
|||
category_text = category_text..'[[Category:'..key..']]' |
|||
-- 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 |
||
end |
end |
||
local dump = '' |
|||
if self.args_raw.__dump then |
|||
-- switch cases |
|||
setmetatable(self, nil) |
|||
-- more organized putting them here |
|||
dump = '<nowiki>'..mw.dumpObject(self)..'</nowiki>' |
|||
if arg.switches then |
|||
setmetatable(self, Infobox) |
|||
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 |
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 |
|||
Debug function |
|||
local valtype = type(val) |
|||
--]] |
|||
-- start with the default value |
|||
function Infobox:dump() |
|||
-- Temporarily detach the metatable so we don't override tostring anymore, allowing us to use dumpObject |
|||
if valtype == 'function' then |
|||
setmetatable(self, nil) |
|||
ret = val(arg.d) |
|||
mw.log(mw.dumpObject(self)) |
|||
setmetatable(self, Infobox) |
|||
-- true means it matched |
|||
mw.log(tostring(self)) |
|||
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 |
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> |
Latest revision as of 12:28, 26 November 2024
Creating a template step-by-step
Import Module:Infobox and Module:Param Parse
local Infobox = require('Module:Infobox')
local parse = require('Module:Param Parse')
Unpack the frame arguments from the Template
local p = {}
function p.main(frame)
local args = frame:getParent().args
...
Setup the Module config settings
local config = {
infobox_name = 'Scenery',
class = {Infobox.smw_param('episode')}, -- Add css class with episode name to colorize Infobox
}
Map your arguments to parsing functions
Use the params in Module:Param Parse to validate and format the data - feel free to create your own new params in Module:Param Parse.
local params = {
parse.name,
parse.image,
{name = 'description', func = parse.has_content, smw_property = 'Description'}, -- Custom param
parse.episode,
...
}
Define an Infobox object
local infobox = Infobox.new(config, params, args)
Create your table
infobox
:add_row{
{tag='th', content=Infobox.param('name'), class='infobox-header', colspan='20'},
}
:add_row{
{tag='td', content=Infobox.param('image'), class='infobox-image', colspan='20'},
}
:pad("20")
:add_row{
{tag='td', content='Info', class='infobox-subheader', colspan='20'},
}
:pad("20")
:add_row{
{tag='th', content='Description', colspan="6"},
{tag='td', content=Infobox.param('description'), colspan="14"},
}
:add_row{
{tag='th', content='[[Episode]]', colspan="6"},
{tag='td', content=Infobox.param('episode'), colspan="14"},
}
...
You're done!
return infobox
end
return p
Functions
Special params
You don't need to do anything about these special parameters, but they may be used as parameters within the Template:
param | explanation |
---|---|
version1, version2, version3 | Button label and SMW name for each switch version of the infobox |
default_version | The default version to display when the page is loaded |
version | If there is only a single version, you can use version to set the SMW name (default SMW name is "DEFAULT") |
Referring to params
Each parameter can have a different value for each version. In addition, there are 3 different representations of each value. Therefore, a parameter must be accessed via one of the 3 helper functions:
helper function | example | explanation |
---|---|---|
Infobox.raw_param(name) | "1000" | Raw value as passed by the Template |
Infobox.param(name) | "1,000" | Value formatted for display in the Infobox |
Infobox.smw_param(name) | 1000 | Value formatted to be saved as an SMW property |
Infobox.new(config, params, args)
Creates a new infobox. Automatically parses the arguments, creates SMW subobjects and adds categories.
local infobox = Infobox.new(config, params, args)
config
There are only 3 parameters for config
local config = {
infobox_name = 'Scenery', -- mandatory unique identifier for css
class = {'CustomClass', Infobox.smw_param('episode')} -- optional, defaults to {}. Adds css classes to infobox table: {'infobox-CustomClass', 'infobox-[default version parameter's value]'}
max_buttons = 6, -- optional, defaults to 6, max number of switch buttons before using a dropdown list instead
}
params
A list of parameters to be processed by the Infobox module
local params = {
{ name = <param>, func = <func>, ... },
...
}
key | value | ||||
---|---|---|---|---|---|
name | parameter name as used in the Template | ||||
func | A function in Module:Param Parse to validate and process the Template argument. You can also use a local function, but this is not recommended.
If func is a function, will call func(Infobox.raw_param(name)): {name = <param>, func = <func>, ... },
If func is a table, it takes the following parameters: {name = <param>, func = { name = <func>, params = <func_params>}, ... },
| ||||
empty (optional) | text to display in the infobox if func returns nil; defaults to "? (edit)" | ||||
category_never (optional) | category to add if func returns nil for all versions | ||||
category_partial (optional) | category to add if func returns nil for some versions, but a value for other versions | ||||
category_incomplete (optional) | category to add if func returns nil for at least 1 version (i.e. category_never and category_partial combined) | ||||
category_complete (optional) | 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 |
args
Arguments passed via the Template
local args = frame:getParent().args
infobox:is_param_defined(param)
Used to conditionally display a line in the infobox
infobox:add_row{
{tag='th', content=Infobox.param('name'), class='infobox.subheader', colspan='2'},
}
if infobox:is_param_defined(Infobox.param('owner')) > 0 then
infobox:add_row{
{tag='td', content='[[Owner]]'},
{tag='td', content=Infobox.param('owner')},
}
end
param | a parameter referenced via Infobox.raw_param(name), Infobox.param(name) or Infobox.smw_param(name) |
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 |
Infobox:add_row(...)
Adds a row to the infobox table. Parameter should be a table of cells:
infobox:add_row{
{tag='td', content='[[Cell1]]', ...},
{tag='td', content=Infobox.param('param'), ...},
{tag='td', content='[[Cell3]]', ...},
...
addClass = 'row-class'
}
Each cell should have a set of key-values, of which only the tag and content are mandatory:
key | value |
---|---|
tag | 'td' or 'th' |
content | a string or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name) |
attr (optional) | a table of attributes to add to the cell
mw.html:attr({ arg1 = '1', ... }) |
css (optional) | a table of css to add to the cell
mw.html:css({ arg1 = '1', ... }) |
class (optional) | a string with a class to add, or a table of classes to add. infobox-header, infobox-subheader and infobox-image are commonly used.
mw.html:addClass(...) |
rowspan (optional) | Set the cell rowspan
mw.html:attr('rowspan',arg) |
rowspan (optional) | Set the cell colspan
mw.html:attr('colspan',arg) |
title (optional) | Set the cell title
mw.html:attr('title',arg) |
Infobox:pad(colspan, class)
Adds a blank row of padding spanning the given number of columns. Will always add the class infobox-padding, but can optionally add another class:
infobox:pad("10", class=<class>)
Infobox:addClass(class)
Adds a class to the entire table
infobox:addClass(class)
Infobox:dump()
Logs all the values into the Debug console for debugging purposes. You can also dump all the values in an Infobox template by setting a template parameter "__dump = Yes".
--[=[
-- For documentation, see [[Module:Infobox/doc]]
--]=]
-- <nowiki>
require('strict')
local editbutton = require('Module:Edit button')
local smwutils = require('Module:SMW Utils')
local Infobox = {}
Infobox.__index = Infobox
--[[
Infobox class
-- config: table containing configuration parameters
-- params : definitions for each used value
-- args : values passed from the Template
---- the following values are treated specially: default_version, version, version1, version2...
--]]
function Infobox.new(config, params, args)
local obj = setmetatable({
args_raw = args, -- parameters (uncleaned)
args_parsed = {}, -- parsed parameters
args_smw = {}, -- parameters parsed for smw
infobox_name = nil, -- template name
classes = {}, -- list of classes to add, using the default_version
param_names = {}, -- ordered param list
params = {}, -- param definitions
max_buttons = 6, -- If there are more buttons than the max, the buttons will become a dropdown menu
default_version = 1, -- default version to populate the infobox
versions = nil, -- number of versions
version_names = {}, -- title of each version (for selection and SMW)
rtable = nil, -- infobox table to return at the end
switch_datatable = '', -- datatable for javascript for switch infoboxes
categories = {}, -- set of categories
errors = {}, -- list of errors
__smw_debug = {}, -- debug data for smw subobjects
},
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
end
--[[
Refers to a param after being processed by the validating func
Used in add_row(), define_params() and is_param_defined()
--]]
function Infobox.param(param_name)
local param = {
property = 'args_parsed',
param_name = param_name,
}
return param
end
--[[
Refers to a param in raw form as passed from the Template
Used in add_row(), define_params() and is_param_defined()
--]]
function Infobox.raw_param(param_name)
local param = {
property = 'args_raw',
param_name = param_name,
}
return param
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
--[[
Checks to see if a param is defined.
-- param: Infobox.param(name), Infobox.raw_param(name) or Infobox.smw_param(name)
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
]]
function Infobox:is_param_defined(param)
local undefined = 0
local defined = 0
for version = 1, self.versions do
local value = self:get_param(param, version)
if value ~= nil then
defined = 1
else
undefined = 1
end
end
return 1 + defined - undefined
end
--[[
Adds a row to the infobox table
Parameter should be a table of cells, where each cell is a table with the following arguments:
-- tag : 'td' or 'th'
-- content : a string or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name)
-- attr (optional) : mw.html:attr({ arg1 = '1', ... })
-- css (optional) : mw.html:css({ arg1 = '1', ...)
-- class (optional) : mw.html:addClass('arg')
---- class also supports a table of values, even though mw.html:addClass() does not
---- common classes: infobox-subheader
-- rowspan (optional) : mw.html:attr('rowspan',arg)
-- colspan (optional) : mw.html:attr('colspan',arg)
-- title (optional) : mw.html:attr('title',arg)
The row itself may be assigned a single class by setting the value of the key addClass
-- addClass : mw.html:addClass('arg')
-- this function currently only supports a single string
--]]
function Infobox:add_row(...)
local args = ...
local _row = self.rtable:tag('tr')
-- For each cell
for _, v in ipairs(args) do
local _cell = _row:tag(v.tag)
-- Optional parameters
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 v.class then
if type(v.class) == 'string' then
_cell:addClass(v.class)
-- mw.html:addClass() doesn't function with tables, add in a loop
elseif type(v.class) == 'table' then
for _, w in ipairs(v.class) do
_cell:addClass(w)
end
end
end
-- Populate the cell contents
local content = self:get_param(v.content, self.default_version)
if content == nil then
content = self.params[v.content.param_name].empty
end
_cell:wikitext(content)
-- Add the switch data if multiple values exist
local data_attr_param = self:add_switch_data(v.content)
if data_attr_param then
_cell:attr('data-attr-param', data_attr_param)
end
end
-- allow classes to be defined on the whole row
if args.addClass then
_row:addClass(args.addClass)
end
return self
end
--[[
Adds a blank row of padding spanning the given number of columns
--]]
function Infobox:pad(colspan, class)
local tr = self.rtable:tag('tr'):tag('td'):attr('colspan', colspan or 1):addClass('infobox-padding')
if class then
tr:addClass(class)
end
return self
end
--[[
Adds a class to the table as a whole
--]]
function Infobox:addClass(arg)
self.rtable:addClass(arg)
return self
end
--[[
Setup config values
-- config: table containing configuration parameters
---- 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)
for k, v in pairs(config) do
if k == 'infobox_name' then
self.infobox_name = mw.ustring.gsub(v, '%s', '_')
elseif k == 'max_buttons' then
self.max_buttons = tonumber(v)
elseif k == 'class' then
if type(v) == 'string' then
self.classes = {v}
elseif type(v) == 'table' then
self.classes = v
end
end
end
if self.infobox_name == nil then
table.insert(self.errors, 'infobox_name needs to be defined in Infobox.new()\'s config!')
end
return self
end
--[[
Counts the number of versions in the infobox
Populates Infobox.version_names
Sets Infobox.default_version
--]]
function Infobox:parse_versions()
local function insert_version_name(version_name)
table.insert(self.version_names, version_name)
if smwutils.valid_subobject_name(version_name) == false then
table.insert(self.errors, 'Illegal version value: must not be "0" nor contain a "." in the first five characters')
end
end
-- Count the versions and setup self.version_names
local i = 1
while self.args_raw['version'..i] do
insert_version_name(self.args_raw['version'..i])
i = i + 1
end
self.versions = i - 1
-- Should either have 0 or 2+ versions
if self.versions == 1 then
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".')
end
-- Handle the no version case - check for a custom version_name
if self.versions == 0 then
insert_version_name(self.args_raw['version'] or 'DEFAULT')
self.versions = 1
end
-- Check for a default_version
if self.args_raw['default_version'] then
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
--[[
Define all used parameters (except for default_version/version)
-- name : parameter name as used in the Template
-- func : function to validate and process the Template argument
-- If func is a function, will call func(Infobox.raw_param(name))
-- If func is a table, contains the following key-value pairs:
---- name : function
---- params : list of arguments to pass to the function (use Infobox.raw_param(),
---- Infobox.param() and Infobox.smw_param() to use arguments)
-- empty (optional) : text to display in the infobox if func returns nil, defaults to "? (edit)"
-- 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(...)
-- For every parameter, store its corresponding function to self.params
for _, v in ipairs(...) do
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
return self
end
--[[
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)
-- 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)
if version == 0 then
version = ''
end
-- Handle Infobox.param(), Infobox.raw_param(), Infobox.smw_param()
if type(arg) == 'table' then
local value = self[arg.property][arg.param_name..version]
-- If nil, grab default value (which could be nil as well)
if value == nil then
value = self[arg.property][arg.param_name]
end
return value
end
-- Everything else passes through unmodified (string, number, etc)
return arg
end
--[[
Calculates the parsed value of a param
-- 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)
if version == 0 then
version = ''
end
-- use func or smw_func depending on smw argument
local param = self.params[param_name]
local func = smw and param.smw_func or param.func
-- call functions by passing the param_name
if type(func) == 'function' then
return func(self:get_param(self.raw_param(param_name), version))
-- call tables by grabbing the function and reading the param arguments
elseif type(func) == 'table' then
local func_name = func.name
local func_params = func.params
local func_fetched_params = {}
local i = 1
for _, func_param in ipairs(func_params) do
func_fetched_params[i] = self:get_param(func_param, version)
i = i + 1
end
return func_name(unpack(func_fetched_params))
else
table.insert(self.errors, 'Invalid param definition for '..param_name)
end
end
--[[
Parses all the param values (args_parsed and args_smw)
--]]
function Infobox:parse_params()
-- Calculate the param value for all params and all versions
for _, param_name in ipairs(self.param_names) do
for version=0, self.versions do
if version == 0 then
version = ''
end
-- get the parsed value
self.args_parsed[param_name..version] = self:parse_param(param_name, version, false)
-- Only get the smw value if smw_property is defined
if self.params[param_name].smw_property then
self.args_smw[param_name..version] = self:parse_param(param_name, version, true)
end
end
end
end
--[[
Creates the table header
--]]
function Infobox:table_header()
-- Create infobox table
self.rtable = mw.html.create('table')
: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
--[[
If multiple versions exist, creates a set of buttons for switching
-- self.max_buttons : if the number of versions exceeds this value, a dropdown will be created instead
--]]
function Infobox:buttons()
if self.versions < 2 then
return
end
-- Button caption
local buttons = self.rtable:tag('caption')
:tag('div')
:addClass('infobox-buttons')
:attr('data-default-index', self.default_version)
-- Dropdown list instead of buttons if too many versions
if self.versions > self.max_buttons then
buttons:addClass('infobox-buttons-select')
end
-- Create all the buttons
for version=1, self.versions do
local button = buttons:tag('span')
:attr('data-switch-index', version)
:attr('data-switch-anchor', '#'..self.version_names[version])
:addClass('button')
:wikitext(self.version_names[version])
-- 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
if self.versions > self.max_buttons then
button:addClass('hidden')
end
end
end
--[[
For each version, stores a subobject for all params that have smw_property defined
--]]
function Infobox:store_smw()
for version=1, self.versions do
-- Generate a subobject name
-- Reminder - subobject name cannot have a . in the first 5 characters
local subobject_name = self.version_names[version]
-- Store each param that has smw_property defined and is not nil
local subobject = {Infobox = self.infobox_name} -- Also store the current Infobox name
for _, param_name in ipairs(self.param_names) do
local property = self.params[param_name].smw_property
if property then
local value = self:get_param(self.smw_param(param_name), version)
if value ~= nil then
subobject[property] = value
end
end
end
-- Keep a copy for debugging
self.__smw_debug[subobject_name] = subobject
-- Save subobjects if not in mainspace
if mw.title.getCurrentTitle():inNamespace(0) then
local result = true
if self.versions == 1 then
result = mw.smw.set(subobject)
else
result = mw.smw.subobject(subobject, subobject_name)
end
if result ~= true then
table.insert(self.errors, 'SMW error: '..result.error)
end
end
end
end
--[[
Automatically add categories for each param as defined in define_params()
--]]
function Infobox:parse_categories()
for _, param_name in ipairs(self.param_names) do
local param = self.params[param_name]
local defined = self:is_param_defined(Infobox.param(param_name))
if defined == 0 and param.category_never then
self.categories[param.category_never] = 1
end
if defined == 1 and param.category_partial then
self.categories[param.category_partial] = 1
end
if defined < 2 and param.category_incomplete then
self.categories[param.category_incomplete] = 1
end
if defined == 2 and param.category_complete then
self.categories[param.category_complete] = 1
end
end
end
--[[
When a parameter is added into the table, add the alternative versions' data into the switch datatable
-- content : a non-table value (string, number, etc), or Infobox.param(name), Infobox.raw_param(name), Infobox.smw_param(name)
Returns the name of the parameter in the tag 'data-attr-param'
--]]
function Infobox:add_switch_data(content)
if self.versions < 2 then
return false
end
-- Only needed for non-constant params
if type(content) ~= 'table' then
return false
end
-- Only needed if the param changes value between different versions
local first_value = self:get_param(content, 1)
local all_same = true
for version=2, self.versions do
if first_value ~= self:get_param(content, version) then
all_same = false
break
end
end
if all_same then
return false
end
-- Let's build the datatable
-- datum name: Prepend raw__ or smw__ if not a parsed argument
local name = content.param_name
if content.property == 'args_raw' then
name = 'raw__' + name
end
if content.property == 'args_smw' then
name = 'smw__' + name
end
local data_param = self.switch_datatable:tag('span'):attr('data-attr-param', name)
-- Add each version to the datatable
for version=1, self.versions do
local text = self:get_param(content, version)
if text == nil then
text = self.params[content.param_name].empty
end
data_param:tag('span'):attr('data-attr-index', version):wikitext(text)
end
-- return the 'data-attr-param' name
return name
end
--[[
Override tostring
--]]
function Infobox:__tostring()
-- Create error messages
local error_text = ''
for _, v in ipairs(self.errors) do
error_text = error_text..'<span class="mw-message-box-error">'..v..'</span>'
end
-- Create categories
local category_text = ''
if mw.title.getCurrentTitle():inNamespace(0) then
for key, _ in pairs(self.categories) do
category_text = category_text..'[[Category:'..key..']]'
end
end
local dump = ''
if self.args_raw.__dump then
setmetatable(self, nil)
dump = '<nowiki>'..mw.dumpObject(self)..'</nowiki>'
setmetatable(self, Infobox)
end
return tostring(self.rtable) .. tostring(self.switch_datatable) .. error_text .. category_text .. dump
end
--[[
Debug function
--]]
function Infobox:dump()
-- Temporarily detach the metatable so we don't override tostring anymore, allowing us to use dumpObject
setmetatable(self, nil)
mw.log(mw.dumpObject(self))
setmetatable(self, Infobox)
mw.log(tostring(self))
end
return Infobox