Module:List

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