Module:List: Difference between revisions

    From Nonbinary Wiki
    m>Mr. Stradivarius
    (add support for start and type attributes in ordered lists)
    m (3 revisions imported from wikipedia:Module:List: see Topic:Vtixlm0q28eo6jtf)
     
    (35 intermediate revisions by 10 users not shown)
    Line 1: Line 1:
    -- This module outputs different kinds of lists. At the moment, bulleted, unbulleted, horizontal, and numbered lists are supported.
    -- This module outputs different kinds of lists. At the moment, bulleted,
    -- unbulleted, horizontal, ordered, and horizontal ordered lists are supported.
     
    local libUtil = require('libraryUtil')
    local checkType = libUtil.checkType
    local mTableTools = require('Module:TableTools')


    local p = {}
    local p = {}


    local function getListItem(data, style, itemStyle)
    local listTypes = {
    if not data then
    ['bulleted'] = true,
    return nil
    ['unbulleted'] = true,
    ['horizontal'] = true,
    ['ordered'] = true,
    ['horizontal_ordered'] = true
    }
     
    function p.makeListData(listType, args)
    -- Constructs a data table to be passed to p.renderList.
    local data = {}
     
    -- Classes
    data.classes = {}
    if listType == 'horizontal' or listType == 'horizontal_ordered' then
    table.insert(data.classes, 'hlist hlist-separated')
    elseif listType == 'unbulleted' then
    table.insert(data.classes, 'plainlist')
    end
    table.insert(data.classes, args.class)
     
    -- Main div style
    data.style = args.style
     
    -- Indent for horizontal lists
    if listType == 'horizontal' or listType == 'horizontal_ordered' then
    local indent = tonumber(args.indent)
    indent = indent and indent * 1.6 or 0
    if indent > 0 then
    data.marginLeft = indent .. 'em'
    end
    end
    end
    if style or itemStyle then
    style = style or ''
    -- List style types for ordered lists
    itemStyle = itemStyle or ''
    -- This could be "1, 2, 3", "a, b, c", or a number of others. The list style
    return mw.ustring.format(
    -- type is either set by the "type" attribute or the "list-style-type" CSS
    '<li style="%s%s">%s</li>',
    -- property.
    style, itemStyle, data
    if listType == 'ordered' or listType == 'horizontal_ordered' then  
    )
    data.listStyleType = args.list_style_type or args['list-style-type']
    data.type = args['type']
     
    -- Detect invalid type attributes and attempt to convert them to
    -- list-style-type CSS properties.
    if data.type
    and not data.listStyleType
    and not tostring(data.type):find('^%s*[1AaIi]%s*$')
    then
    data.listStyleType = data.type
    data.type = nil
    end
    end
    -- List tag type
    if listType == 'ordered' or listType == 'horizontal_ordered' then
    data.listTag = 'ol'
    else
    else
    return mw.ustring.format(
    data.listTag = 'ul'
    '<li>%s</li>',
    data
    )
    end
    end
    end


    local function getArgNums(args)
    -- Start number for ordered lists
    -- Returns an array containing the keys of all positional arguments
    data.start = args.start
    -- that contain data (i.e. non-whitespace values).
    if listType == 'horizontal_ordered' then
    local nums = {}
    -- Apply fix to get start numbers working with horizontal ordered lists.
    for k, v in pairs(args) do
    local startNum = tonumber(data.start)
    if type(k) == 'number' and
    if startNum then
    k >= 1 and
    data.counterReset = 'listitem ' .. tostring(startNum - 1)
    math.floor(k) == k and
    mw.ustring.match(v, '%S') then
    table.insert(nums, k)
    end
    end
    end
    end
    table.sort(nums)
     
    return nums
    -- List style
    -- ul_style and ol_style are included for backwards compatibility. No
    -- distinction is made for ordered or unordered lists.
    data.listStyle = args.list_style
     
    -- List items
    -- li_style is included for backwards compatibility. item_style was included
    -- to be easier to understand for non-coders.
    data.itemStyle = args.item_style or args.li_style
    data.items = {}
    for i, num in ipairs(mTableTools.numKeys(args)) do
    local item = {}
    item.content = args[num]
    item.style = args['item' .. tostring(num) .. '_style']
    or args['item_style' .. tostring(num)]
    item.value = args['item' .. tostring(num) .. '_value']
    or args['item_value' .. tostring(num)]
    table.insert(data.items, item)
    end
    return data
    end
    end


    local function getClass(listType, class)
    function p.renderList(data)
    local classes = {}
    -- Renders the list HTML.
    if listType == 'horizontal' then
    table.insert(classes, 'hlist')
    -- Return the blank string if there are no list items.
    elseif listType == 'unbulleted' then
    if type(data.items) ~= 'table' or #data.items < 1 then
    table.insert(classes, 'plainlist')
    return ''
    end
    -- Render the main div tag.
    local root = mw.html.create('div')
    for i, class in ipairs(data.classes or {}) do
    root:addClass(class)
    end
    root:css{['margin-left'] = data.marginLeft}
    if data.style then
    root:cssText(data.style)
    end
    end
    table.insert(classes, class)
     
    local ret
    -- Render the list tag.
    if #classes == 0 then
    local list = root:tag(data.listTag or 'ul')
    return nil
    list
    :attr{start = data.start, type = data.type}
    :css{
    ['counter-reset'] = data.counterReset,
    ['list-style-type'] = data.listStyleType
    }
    if data.listStyle then
    list:cssText(data.listStyle)
    end
    end
    return mw.ustring.format(' class="%s"', table.concat(classes, ' '))
     
    -- Render the list items
    for i, t in ipairs(data.items or {}) do
    local item = list:tag('li')
    if data.itemStyle then
    item:cssText(data.itemStyle)
    end
    if t.style then
    item:cssText(t.style)
    end
    item
    :attr{value = t.value}
    :wikitext(t.content)
    end
     
    return tostring(root)
    end
    end


    local function getStyle(listType, indent, style)
    function p.renderTrackingCategories(args)
    local styles = {}
    local isDeprecated = false -- Tracks deprecated parameters.
    if listType == 'horizontal' then
    for k, v in pairs(args) do
    indent = indent and tonumber(indent)
    k = tostring(k)
    indent = tostring((indent and indent * 1.6) or 0)
    if k:find('^item_style%d+$') or k:find('^item_value%d+$') then
    table.insert(styles, 'margin-left: ' .. indent .. 'em;')
    isDeprecated = true
    break
    end
    end
    end
    table.insert(styles, style)
    local ret = ''
    if #styles == 0 then
    if isDeprecated then
    return nil
    ret = ret .. '[[Category:List templates with deprecated parameters]]'
    end
    end
    return mw.ustring.format(' style="%s"', table.concat(styles, ' '))
    return ret
    end
    end


    function p.makeList(listType, args)
    function p.makeList(listType, args)
    -- This is the main function to be called from other Lua modules.
    if not listType or not listTypes[listType] then
    -- First, get the list items.
    error(string.format(
    local listItems = {}
    "bad argument #1 to 'makeList' ('%s' is not a valid list type)",
    local argNums = getArgNums(args)
    tostring(listType)
    for i, num in ipairs(argNums) do
    ), 2)
    local item = getListItem(
    args[num],
    args.item_style or args.li_style, -- li_style is included for backwards compatibility. item_style was included to be easier to understand for non-coders.
    args['item_style' .. tostring(num)] or args['li_style' .. tostring(num)]
    )
    table.insert(listItems, item)
    end
    if #listItems == 0 then
    return ''
    end
    end
    -- Check if we need a ul tag or an ol tag, and get the start and type attributes for ordered lists.
    checkType('makeList', 2, args, 'table')
    local listTag = 'ul'
    local data = p.makeListData(listType, args)
    local startAttr, typeAttr
    local list = p.renderList(data)
    if listType == 'numbered' then
    local trackingCategories = p.renderTrackingCategories(args)
    listTag = 'ol'
    return list .. trackingCategories
    startAttr = args.start
    if startAttr then
    startAttr = ' start="' .. startAttr .. '"'
    end
    typeAttr = args.type
    if typeAttr then
    typeAttr = ' type="' .. typeAttr .. '"'
    end
    end
    startAttr = startAttr or ''
    typeAttr = typeAttr or ''
    -- Get the classes and styles and output the list.
    local class = getClass(listType, args.class) or ''
    local style = getStyle(listType, args.indent, args.style) or ''
    local list_style = args.list_style or args.ul_style -- ul_style is included for backwards compatibility, and will work even with ordered lists.
    list_style = list_style and (' style="' .. list_style .. '"') or ''
    return mw.ustring.format(
    '<div%s%s><%s%s%s%s>%s</%s></div>',
    class, style, listTag, startAttr, typeAttr, list_style, table.concat(listItems), listTag
    )
    end
    end


    local function makeWrapper(listType)
    for listType in pairs(listTypes) do
    return function(frame)
    p[listType] = function (frame)
    local origArgs
    local mArguments = require('Module:Arguments')
    if frame == mw.getCurrentFrame() then
    local origArgs = mArguments.getArgs(frame)
    origArgs = frame:getParent().args
    -- Copy all the arguments to a new table, for faster indexing.
    for k, v in pairs(frame.args) do
    origArgs = frame.args
    break
    end
    else
    origArgs = frame
    end
    local args = {}
    local args = {}
    for k, v in pairs(origArgs) do
    for k, v in pairs(origArgs) do
    if type(k) == 'number' or v ~= '' then
    args[k] = v
    args[k] = v
    end
    end
    end
    return p.makeList(listType, args)
    return p.makeList(listType, args)
    end
    end
    end
    local funcNames = {'bulleted', 'unbulleted', 'horizontal', 'numbered'}
    for _, funcName in ipairs(funcNames) do
    p[funcName] = makeWrapper(funcName)
    end
    end


    return p
    return p

    Latest revision as of 11:42, 21 May 2021

    Documentation for this module may be created at Module:List/doc

    -- This module outputs different kinds of lists. At the moment, bulleted,
    -- unbulleted, horizontal, ordered, and horizontal ordered lists are supported.
    
    local libUtil = require('libraryUtil')
    local checkType = libUtil.checkType
    local mTableTools = require('Module:TableTools')
    
    local p = {}
    
    local listTypes = {
    	['bulleted'] = true,
    	['unbulleted'] = true,
    	['horizontal'] = true,
    	['ordered'] = true,
    	['horizontal_ordered'] = true
    }
    
    function p.makeListData(listType, args)
    	-- Constructs a data table to be passed to p.renderList.
    	local data = {}
    
    	-- Classes
    	data.classes = {}
    	if listType == 'horizontal' or listType == 'horizontal_ordered' then
    		table.insert(data.classes, 'hlist hlist-separated')
    	elseif listType == 'unbulleted' then
    		table.insert(data.classes, 'plainlist')
    	end
    	table.insert(data.classes, args.class)
    
    	-- Main div style
    	data.style = args.style
    
    	-- Indent for horizontal lists
    	if listType == 'horizontal' or listType == 'horizontal_ordered' then
    		local indent = tonumber(args.indent)
    		indent = indent and indent * 1.6 or 0
    		if indent > 0 then
    			data.marginLeft = indent .. 'em'
    		end
    	end
    	
    	-- List style types for ordered lists
    	-- This could be "1, 2, 3", "a, b, c", or a number of others. The list style
    	-- type is either set by the "type" attribute or the "list-style-type" CSS
    	-- property.
    	if listType == 'ordered' or listType == 'horizontal_ordered' then 
    		data.listStyleType = args.list_style_type or args['list-style-type']
    		data.type = args['type']
    
    		-- Detect invalid type attributes and attempt to convert them to
    		-- list-style-type CSS properties.
    		if data.type 
    			and not data.listStyleType
    			and not tostring(data.type):find('^%s*[1AaIi]%s*$')
    		then
    			data.listStyleType = data.type
    			data.type = nil
    		end
    	end
    	
    	-- List tag type
    	if listType == 'ordered' or listType == 'horizontal_ordered' then
    		data.listTag = 'ol'
    	else
    		data.listTag = 'ul'
    	end
    
    	-- Start number for ordered lists
    	data.start = args.start
    	if listType == 'horizontal_ordered' then
    		-- Apply fix to get start numbers working with horizontal ordered lists.
    		local startNum = tonumber(data.start)
    		if startNum then
    			data.counterReset = 'listitem ' .. tostring(startNum - 1)
    		end
    	end
    
    	-- List style
    	 -- ul_style and ol_style are included for backwards compatibility. No
    	 -- distinction is made for ordered or unordered lists.
    	data.listStyle = args.list_style
    
    	-- List items
    	-- li_style is included for backwards compatibility. item_style was included
    	-- to be easier to understand for non-coders.
    	data.itemStyle = args.item_style or args.li_style
    	data.items = {}
    	for i, num in ipairs(mTableTools.numKeys(args)) do
    		local item = {}
    		item.content = args[num]
    		item.style = args['item' .. tostring(num) .. '_style']
    			or args['item_style' .. tostring(num)]
    		item.value = args['item' .. tostring(num) .. '_value']
    			or args['item_value' .. tostring(num)]
    		table.insert(data.items, item)
    	end
    	
    	return data
    end
    
    function p.renderList(data)
    	-- Renders the list HTML.
    	
    	-- Return the blank string if there are no list items.
    	if type(data.items) ~= 'table' or #data.items < 1 then
    		return ''
    	end
    	
    	-- Render the main div tag.
    	local root = mw.html.create('div')
    	for i, class in ipairs(data.classes or {}) do
    		root:addClass(class)
    	end
    	root:css{['margin-left'] = data.marginLeft}
    	if data.style then
    		root:cssText(data.style)
    	end
    
    	-- Render the list tag.
    	local list = root:tag(data.listTag or 'ul')
    	list
    		:attr{start = data.start, type = data.type}
    		:css{
    			['counter-reset'] = data.counterReset,
    			['list-style-type'] = data.listStyleType
    		}
    	if data.listStyle then
    		list:cssText(data.listStyle)
    	end
    
    	-- Render the list items
    	for i, t in ipairs(data.items or {}) do
    		local item = list:tag('li')
    		if data.itemStyle then
    			item:cssText(data.itemStyle)
    		end
    		if t.style then
    			item:cssText(t.style)
    		end
    		item
    			:attr{value = t.value}
    			:wikitext(t.content)
    	end
    
    	return tostring(root)
    end
    
    function p.renderTrackingCategories(args)
    	local isDeprecated = false -- Tracks deprecated parameters.
    	for k, v in pairs(args) do
    		k = tostring(k)
    		if k:find('^item_style%d+$') or k:find('^item_value%d+$') then
    			isDeprecated = true
    			break
    		end
    	end
    	local ret = ''
    	if isDeprecated then
    		ret = ret .. '[[Category:List templates with deprecated parameters]]'
    	end
    	return ret
    end
    
    function p.makeList(listType, args)
    	if not listType or not listTypes[listType] then
    		error(string.format(
    			"bad argument #1 to 'makeList' ('%s' is not a valid list type)",
    			tostring(listType)
    		), 2)
    	end
    	checkType('makeList', 2, args, 'table')
    	local data = p.makeListData(listType, args)
    	local list = p.renderList(data)
    	local trackingCategories = p.renderTrackingCategories(args)
    	return list .. trackingCategories
    end
    
    for listType in pairs(listTypes) do
    	p[listType] = function (frame)
    		local mArguments = require('Module:Arguments')
    		local origArgs = mArguments.getArgs(frame)
    		-- Copy all the arguments to a new table, for faster indexing.
    		local args = {}
    		for k, v in pairs(origArgs) do
    			args[k] = v
    		end
    		return p.makeList(listType, args)
    	end
    end
    
    return p