Module:Infobox
From TechInfoDepot
Jump to navigationJump to search
![]() | This template is used on 14128 pages, and changes to it will be widely noticed. Please test any changes in the template's /sandbox or /testcases subpages, or in a user subpage, and consider discussing changes at the talk page before implementing them. |
![]() | This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
![]() | Uses Lua: |
![]() | This module uses TemplateStyles: |
Module:Infobox is a module that implements the {{Infobox}} template. Please see the template page for usage instructions.
Tracking categories
- Category:Pages using infobox templates with ignored data cells (0)
- Category:Articles using infobox templates with no data rows (0)
- Category:Pages using embedded infobox templates with the title parameter (0)
The above documentation is transcluded from Module:Infobox/doc. (edit | history) Editors can experiment in this module's sandbox (create | mirror) and testcases (create) pages. Subpages of this module. |
1 --
2 -- This module implements {{Infobox}}
3 --
4
5 local p = {}
6
7 local args = {}
8 local origArgs
9 local root
10
11 local function union(t1, t2)
12 -- Returns the union of the values of two tables, as a sequence.
13 local vals = {}
14 for k, v in pairs(t1) do
15 vals[v] = true
16 end
17 for k, v in pairs(t2) do
18 vals[v] = true
19 end
20 local ret = {}
21 for k, v in pairs(vals) do
22 table.insert(ret, k)
23 end
24 return ret
25 end
26
27 local function getArgNums(prefix)
28 -- Returns a table containing the numbers of the arguments that exist
29 -- for the specified prefix. For example, if the prefix was 'data', and
30 -- 'data1', 'data2', and 'data5' exist, it would return {1, 2, 5}.
31 local nums = {}
32 for k, v in pairs(args) do
33 local num = tostring(k):match('^' .. prefix .. '([1-9]%d*)$')
34 if num then table.insert(nums, tonumber(num)) end
35 end
36 table.sort(nums)
37 return nums
38 end
39
40 local function addRow(rowArgs)
41 -- Adds a row to the infobox, with either a header cell
42 -- or a label/data cell combination.
43 if rowArgs.header then
44 root
45 :tag('tr')
46 :addClass(rowArgs.rowclass)
47 :cssText(rowArgs.rowstyle)
48 :attr('id', rowArgs.rowid)
49 :tag('th')
50 :attr('colspan', 2)
51 :attr('id', rowArgs.headerid)
52 :addClass(rowArgs.class)
53 :addClass(args.headerclass)
54 :css('text-align', 'center')
55 :cssText(args.headerstyle)
56 :wikitext(rowArgs.header)
57 elseif rowArgs.data then
58 local row = root:tag('tr')
59 row:addClass(rowArgs.rowclass)
60 row:cssText(rowArgs.rowstyle)
61 row:attr('id', rowArgs.rowid)
62 if rowArgs.label then
63 row
64 :tag('th')
65 :attr('scope', 'row')
66 :attr('id', rowArgs.labelid)
67 :css('text-align', 'left')
68 :cssText(args.labelstyle)
69 :wikitext(rowArgs.label)
70 :done()
71 end
72
73 local dataCell = row:tag('td')
74 if not rowArgs.label then
75 dataCell
76 :attr('colspan', 2)
77 :css('text-align', 'center')
78 end
79 dataCell
80 :attr('id', rowArgs.dataid)
81 :addClass(rowArgs.class)
82 :cssText(rowArgs.datastyle)
83 :newline()
84 :wikitext(rowArgs.data)
85 end
86 end
87
88 local function renderTitle()
89 if not args.title then return end
90
91 root
92 :tag('caption')
93 :addClass(args.titleclass)
94 :cssText(args.titlestyle)
95 :wikitext(args.title)
96 end
97
98 local function renderAboveRow()
99 if not args.above then return end
100
101 root
102 :tag('tr')
103 :tag('th')
104 :attr('colspan', 2)
105 :addClass(args.aboveclass)
106 :css('text-align', 'center')
107 :css('font-size', '125%')
108 :css('font-weight', 'bold')
109 :cssText(args.abovestyle)
110 :wikitext(args.above)
111 end
112
113 local function renderBelowRow()
114 if not args.below then return end
115
116 root
117 :tag('tr')
118 :tag('td')
119 :attr('colspan', '2')
120 :addClass(args.belowclass)
121 :css('text-align', 'center')
122 :cssText(args.belowstyle)
123 :newline()
124 :wikitext(args.below)
125 end
126
127 local function renderSubheaders()
128 if args.subheader then
129 args.subheader1 = args.subheader
130 end
131 if args.subheaderrowclass then
132 args.subheaderrowclass1 = args.subheaderrowclass
133 end
134 local subheadernums = getArgNums('subheader')
135 for k, num in ipairs(subheadernums) do
136 addRow({
137 data = args['subheader' .. tostring(num)],
138 datastyle = args.subheaderstyle or args['subheaderstyle' .. tostring(num)],
139 class = args.subheaderclass,
140 rowclass = args['subheaderrowclass' .. tostring(num)]
141 })
142 end
143 end
144
145 local function renderImages()
146 if args.image then
147 args.image1 = args.image
148 end
149 if args.caption then
150 args.caption1 = args.caption
151 end
152 local imagenums = getArgNums('image')
153 for k, num in ipairs(imagenums) do
154 local caption = args['caption' .. tostring(num)]
155 local data = mw.html.create():wikitext(args['image' .. tostring(num)])
156 if caption then
157 data
158 :tag('div')
159 :cssText(args.captionstyle)
160 :wikitext(caption)
161 end
162 addRow({
163 data = tostring(data),
164 datastyle = args.imagestyle,
165 class = args.imageclass,
166 rowclass = args['imagerowclass' .. tostring(num)]
167 })
168 end
169 end
170
171 local function renderRows()
172 -- Gets the union of the header and data argument numbers,
173 -- and renders them all in order using addRow.
174 local rownums = union(getArgNums('header'), getArgNums('data'))
175 table.sort(rownums)
176 for k, num in ipairs(rownums) do
177 addRow({
178 header = args['header' .. tostring(num)],
179 label = args['label' .. tostring(num)],
180 data = args['data' .. tostring(num)],
181 datastyle = args.datastyle,
182 class = args['class' .. tostring(num)],
183 rowclass = args['rowclass' .. tostring(num)],
184 rowstyle = args['rowstyle' .. tostring(num)],
185 dataid = args['dataid' .. tostring(num)],
186 labelid = args['labelid' .. tostring(num)],
187 headerid = args['headerid' .. tostring(num)],
188 rowid = args['rowid' .. tostring(num)]
189 })
190 end
191 end
192
193 local function renderNavBar()
194 if not args.name then return end
195
196 root
197 :tag('tr')
198 :tag('td')
199 :attr('colspan', '2')
200 :css('text-align', 'right')
201 :wikitext(mw.getCurrentFrame():expandTemplate({
202 title = 'navbar',
203 args = { args.name, mini = 1 }
204 }))
205 end
206
207 local function renderItalicTitle()
208 local italicTitle = args['italic title'] and mw.ustring.lower(args['italic title'])
209 if italicTitle == '' or italicTitle == 'force' or italicTitle == 'yes' then
210 root:wikitext(mw.getCurrentFrame():expandTemplate({title = 'italic title'}))
211 end
212 end
213
214 local function renderTrackingCategories()
215 if args.decat ~= 'yes' then
216 if #(getArgNums('data')) == 0 and mw.title.getCurrentTitle().namespace == 0 then
217 root:wikitext('[[Category:Articles which use infobox templates with no data rows]]')
218 end
219 if args.child == 'yes' and args.title then
220 root:wikitext('[[Category:Articles which use embedded infobox templates with the title parameter]]')
221 end
222 end
223 end
224
225 local function _infobox()
226 -- Specify the overall layout of the infobox, with special settings
227 -- if the infobox is used as a 'child' inside another infobox.
228 if args.child ~= 'yes' then
229 root = mw.html.create('table')
230
231 root
232 :addClass('infobox')
233 :addClass(args.bodyclass)
234
235 if args.subbox == 'yes' then
236 root
237 :css('padding', '0')
238 :css('border', 'none')
239 :css('margin', '-3px')
240 :css('width', 'auto')
241 :css('min-width', '100%')
242 :css('font-size', '100%')
243 :css('clear', 'none')
244 :css('float', 'none')
245 :css('background-color', 'transparent')
246 else
247 root
248 :css('width', '22em')
249 end
250 root
251 :cssText(args.bodystyle)
252
253 renderTitle()
254 renderAboveRow()
255 else
256 root = mw.html.create()
257
258 root
259 :wikitext(args.title)
260 end
261
262 renderSubheaders()
263 renderImages()
264 renderRows()
265 renderBelowRow()
266 renderNavBar()
267 renderItalicTitle()
268 renderTrackingCategories()
269
270 return tostring(root)
271 end
272
273 local function preprocessSingleArg(argName)
274 -- If the argument exists and isn't blank, add it to the argument table.
275 -- Blank arguments are treated as nil to match the behaviour of ParserFunctions.
276 if origArgs[argName] and origArgs[argName] ~= '' then
277 args[argName] = origArgs[argName]
278 end
279 end
280
281 local function preprocessArgs(prefixTable, step)
282 -- Assign the parameters with the given prefixes to the args table, in order, in batches
283 -- of the step size specified. This is to prevent references etc. from appearing in the
284 -- wrong order. The prefixTable should be an array containing tables, each of which has
285 -- two possible fields, a "prefix" string and a "depend" table. The function always parses
286 -- parameters containing the "prefix" string, but only parses parameters in the "depend"
287 -- table if the prefix parameter is present and non-blank.
288 if type(prefixTable) ~= 'table' then
289 error("Non-table value detected for the prefix table", 2)
290 end
291 if type(step) ~= 'number' then
292 error("Invalid step value detected", 2)
293 end
294
295 -- Get arguments without a number suffix, and check for bad input.
296 for i,v in ipairs(prefixTable) do
297 if type(v) ~= 'table' or type(v.prefix) ~= "string" or (v.depend and type(v.depend) ~= 'table') then
298 error('Invalid input detected to preprocessArgs prefix table', 2)
299 end
300 preprocessSingleArg(v.prefix)
301 -- Only parse the depend parameter if the prefix parameter is present and not blank.
302 if args[v.prefix] and v.depend then
303 for j, dependValue in ipairs(v.depend) do
304 if type(dependValue) ~= 'string' then
305 error('Invalid "depend" parameter value detected in preprocessArgs')
306 end
307 preprocessSingleArg(dependValue)
308 end
309 end
310 end
311
312 -- Get arguments with number suffixes.
313 local a = 1 -- Counter variable.
314 local moreArgumentsExist = true
315 while moreArgumentsExist == true do
316 moreArgumentsExist = false
317 for i = a, a + step - 1 do
318 for j,v in ipairs(prefixTable) do
319 local prefixArgName = v.prefix .. tostring(i)
320 if origArgs[prefixArgName] then
321 moreArgumentsExist = true -- Do another loop if any arguments are found, even blank ones.
322 preprocessSingleArg(prefixArgName)
323 end
324 -- Process the depend table if the prefix argument is present and not blank, or
325 -- we are processing "prefix1" and "prefix" is present and not blank, and
326 -- if the depend table is present.
327 if v.depend and (args[prefixArgName] or (i == 1 and args[v.prefix])) then
328 for j,dependValue in ipairs(v.depend) do
329 local dependArgName = dependValue .. tostring(i)
330 preprocessSingleArg(dependArgName)
331 end
332 end
333 end
334 end
335 a = a + step
336 end
337 end
338
339 function p.infobox(frame)
340 -- If called via #invoke, use the args passed into the invoking template.
341 -- Otherwise, for testing purposes, assume args are being passed directly in.
342 if frame == mw.getCurrentFrame() then
343 origArgs = frame:getParent().args
344 else
345 origArgs = frame
346 end
347
348 -- Parse the data parameters in the same order that the old {{infobox}} did, so that
349 -- references etc. will display in the expected places. Parameters that depend on
350 -- another parameter are only processed if that parameter is present, to avoid
351 -- phantom references appearing in article reference lists.
352 preprocessSingleArg('child')
353 preprocessSingleArg('bodyclass')
354 preprocessSingleArg('subbox')
355 preprocessSingleArg('bodystyle')
356 preprocessSingleArg('title')
357 preprocessSingleArg('titleclass')
358 preprocessSingleArg('titlestyle')
359 preprocessSingleArg('above')
360 preprocessSingleArg('aboveclass')
361 preprocessSingleArg('abovestyle')
362 preprocessArgs({
363 {prefix = 'subheader', depend = {'subheaderstyle', 'subheaderrowclass'}}
364 }, 10)
365 preprocessSingleArg('subheaderstyle')
366 preprocessSingleArg('subheaderclass')
367 preprocessArgs({
368 {prefix = 'image', depend = {'caption', 'imagerowclass'}}
369 }, 10)
370 preprocessSingleArg('captionstyle')
371 preprocessSingleArg('imagestyle')
372 preprocessSingleArg('imageclass')
373 preprocessArgs({
374 {prefix = 'header'},
375 {prefix = 'data', depend = {'label'}},
376 {prefix = 'rowclass'},
377 {prefix = 'rowstyle'},
378 {prefix = 'class'},
379 {prefix = 'dataid'},
380 {prefix = 'labelid'},
381 {prefix = 'headerid'},
382 {prefix = 'rowid'}
383 }, 50)
384 preprocessSingleArg('headerclass')
385 preprocessSingleArg('headerstyle')
386 preprocessSingleArg('labelstyle')
387 preprocessSingleArg('datastyle')
388 preprocessSingleArg('below')
389 preprocessSingleArg('belowclass')
390 preprocessSingleArg('belowstyle')
391 preprocessSingleArg('name')
392 args['italic title'] = origArgs['italic title'] -- different behaviour if blank or absent
393 preprocessSingleArg('decat')
394
395 return _infobox()
396 end
397
398 return p