Module:Infobox

From TechInfoDepot
Jump to navigationJump to search
Documentation icon Module documentation[view] [edit] [history] [purge]

Module:Infobox is a module that implements the {{Infobox}} template. Please see the template page for usage instructions.

Tracking categories

  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