Module:List
Jump to navigation
Jump to search
Documentation for this module may be created at Module:List/doc
1 -- This module outputs different kinds of lists. At the moment, bulleted,
2 -- unbulleted, horizontal, ordered, and horizontal ordered lists are supported.
3
4 local libUtil = require('libraryUtil')
5 local checkType = libUtil.checkType
6 local mTableTools = require('Module:TableTools')
7
8 local p = {}
9
10 local listTypes = {
11 ['bulleted'] = true,
12 ['unbulleted'] = true,
13 ['horizontal'] = true,
14 ['ordered'] = true,
15 ['horizontal_ordered'] = true
16 }
17
18 function p.makeListData(listType, args)
19 -- Constructs a data table to be passed to p.renderList.
20 local data = {}
21
22 -- Classes
23 data.classes = {}
24 if listType == 'horizontal' or listType == 'horizontal_ordered' then
25 table.insert(data.classes, 'hlist hlist-separated')
26 elseif listType == 'unbulleted' then
27 table.insert(data.classes, 'plainlist')
28 end
29 table.insert(data.classes, args.class)
30
31 -- Main div style
32 data.style = args.style
33
34 -- Indent for horizontal lists
35 if listType == 'horizontal' or listType == 'horizontal_ordered' then
36 local indent = tonumber(args.indent)
37 indent = indent and indent * 1.6 or 0
38 if indent > 0 then
39 data.marginLeft = indent .. 'em'
40 end
41 end
42
43 -- List style types for ordered lists
44 -- This could be "1, 2, 3", "a, b, c", or a number of others. The list style
45 -- type is either set by the "type" attribute or the "list-style-type" CSS
46 -- property.
47 if listType == 'ordered' or listType == 'horizontal_ordered' then
48 data.listStyleType = args.list_style_type or args['list-style-type']
49 data.type = args['type']
50
51 -- Detect invalid type attributes and attempt to convert them to
52 -- list-style-type CSS properties.
53 if data.type
54 and not data.listStyleType
55 and not tostring(data.type):find('^%s*[1AaIi]%s*$')
56 then
57 data.listStyleType = data.type
58 data.type = nil
59 end
60 end
61
62 -- List tag type
63 if listType == 'ordered' or listType == 'horizontal_ordered' then
64 data.listTag = 'ol'
65 else
66 data.listTag = 'ul'
67 end
68
69 -- Start number for ordered lists
70 data.start = args.start
71 if listType == 'horizontal_ordered' then
72 -- Apply fix to get start numbers working with horizontal ordered lists.
73 local startNum = tonumber(data.start)
74 if startNum then
75 data.counterReset = 'listitem ' .. tostring(startNum - 1)
76 end
77 end
78
79 -- List style
80 -- ul_style and ol_style are included for backwards compatibility. No
81 -- distinction is made for ordered or unordered lists.
82 data.listStyle = args.list_style
83
84 -- List items
85 -- li_style is included for backwards compatibility. item_style was included
86 -- to be easier to understand for non-coders.
87 data.itemStyle = args.item_style or args.li_style
88 data.items = {}
89 for i, num in ipairs(mTableTools.numKeys(args)) do
90 local item = {}
91 item.content = args[num]
92 item.style = args['item' .. tostring(num) .. '_style']
93 or args['item_style' .. tostring(num)]
94 item.value = args['item' .. tostring(num) .. '_value']
95 or args['item_value' .. tostring(num)]
96 table.insert(data.items, item)
97 end
98
99 return data
100 end
101
102 function p.renderList(data)
103 -- Renders the list HTML.
104
105 -- Return the blank string if there are no list items.
106 if type(data.items) ~= 'table' or #data.items < 1 then
107 return ''
108 end
109
110 -- Render the main div tag.
111 local root = mw.html.create('div')
112 for i, class in ipairs(data.classes or {}) do
113 root:addClass(class)
114 end
115 root:css{['margin-left'] = data.marginLeft}
116 if data.style then
117 root:cssText(data.style)
118 end
119
120 -- Render the list tag.
121 local list = root:tag(data.listTag or 'ul')
122 list
123 :attr{start = data.start, type = data.type}
124 :css{
125 ['counter-reset'] = data.counterReset,
126 ['list-style-type'] = data.listStyleType
127 }
128 if data.listStyle then
129 list:cssText(data.listStyle)
130 end
131
132 -- Render the list items
133 for i, t in ipairs(data.items or {}) do
134 local item = list:tag('li')
135 if data.itemStyle then
136 item:cssText(data.itemStyle)
137 end
138 if t.style then
139 item:cssText(t.style)
140 end
141 item
142 :attr{value = t.value}
143 :wikitext(t.content)
144 end
145
146 return tostring(root)
147 end
148
149 function p.renderTrackingCategories(args)
150 local isDeprecated = false -- Tracks deprecated parameters.
151 for k, v in pairs(args) do
152 k = tostring(k)
153 if k:find('^item_style%d+$') or k:find('^item_value%d+$') then
154 isDeprecated = true
155 break
156 end
157 end
158 local ret = ''
159 if isDeprecated then
160 ret = ret .. '[[Category:List templates with deprecated parameters]]'
161 end
162 return ret
163 end
164
165 function p.makeList(listType, args)
166 if not listType or not listTypes[listType] then
167 error(string.format(
168 "bad argument #1 to 'makeList' ('%s' is not a valid list type)",
169 tostring(listType)
170 ), 2)
171 end
172 checkType('makeList', 2, args, 'table')
173 local data = p.makeListData(listType, args)
174 local list = p.renderList(data)
175 local trackingCategories = p.renderTrackingCategories(args)
176 return list .. trackingCategories
177 end
178
179 for listType in pairs(listTypes) do
180 p[listType] = function (frame)
181 local mArguments = require('Module:Arguments')
182 local origArgs = mArguments.getArgs(frame, {
183 valueFunc = function (key, value)
184 if not value or not mw.ustring.find(value, '%S') then return nil end
185 if mw.ustring.find(value, '^%s*[%*#;:]') then
186 return value
187 else
188 return value:match('^%s*(.-)%s*$')
189 end
190 return nil
191 end
192 })
193 -- Copy all the arguments to a new table, for faster indexing.
194 local args = {}
195 for k, v in pairs(origArgs) do
196 args[k] = v
197 end
198 return p.makeList(listType, args)
199 end
200 end
201
202 return p