Module:term list

From Wiktionary, the free dictionary
Jump to navigation Jump to search

This module creates a list of terms, automatically selecting the most appropriate way to display it. It should not be used directly in entries, but in templates such as {{derived terms}} and {{related terms}}.

The current implementation displays a regular bulleted list if there are not more than 8 items and there are no nested items, and displays a collapsible box with up to four columns otherwise. This behavior may change over time. It would be desirable to take user preference and screen size into consideration.

Tracking

local export = {}

local m_debug = require("Module:debug")
local m_lang = require("Module:languages")
local m_links = require("Module:links")

function export.display(frame)
	local args = require("Module:parameters").process(frame:getParent().args, {
		[1] = {required = true},
		[2] = {list = true},
		["title"] = {}
	})

	local args = frame:getParent().args
	if args.title then
		m_debug.track("term list/title")
	end
	
	local lang = mw.text.trim(args[1])
	lang = m_lang.getByCode(lang) or error("The language code \"" .. lang .. "\" is not valid.")

	local item_count
	local items = {}
	local last_item
	local nested
	local markup
	for _, item in ipairs(args) do
		if string.find(item, '[{%[]') then
			m_debug.track("term list/markup")
		end
		if _ == 1 then
			-- nothing
		elseif string.byte(item) == 42 then
			if not last_item then
				error("List of terms begins with a nested item.")
			end
			table.insert(last_item.nested_terms, mw.text.trim(string.sub(item, 2)))
			nested = 1
		else
			last_item = {nested_terms = {}, term = mw.text.trim(item)}
			table.insert(items, last_item)
		end
		item_count = _
	end
	item_count = item_count - 1
	if item_count < 1 then
		error("No list items are specified.")
	end
	if nested then
		m_debug.track("term list/nested")
	end

	local get_sort_value = require("Module:fun").memoize(function (element)
		return (lang:makeSortKey((lang:makeEntryName(element))))
	end)
	local function compare_items(element1, element2)
		return get_sort_value(element1.term) < get_sort_value(element2.term)
	end
	local function compare_terms(element1, element2)
		return get_sort_value(element1) < get_sort_value(element2)
	end

	local column_size
	local column_width
	local limit
	local result
	local tail
	if nested and item_count > 8 then
		local column_count = math.min(4, item_count)
		column_size = math.ceil(item_count / column_count)
		limit = column_size
		column_width = math.floor(80 / column_count)
		result = {'<div class="NavFrame">\n<div class="NavHead">',
			args.title or 'Derived terms',
			'</div>\n<div class="NavContent">\n{| style="width:100%;" role="presentation" class="',
			"derivedterms",
			'"\n|-\n| style="vertical-align: top; text-align: left; width: ', column_width, '%" |\n'}
		tail = "\n|}</div></div>"
	elseif item_count > 8 then

		-- If no nested lists, just return an invocation of {{col3}}
		-- Note this is TEMPORARY and will be upgraded to account for
		-- column widths in deciding how many columns to use

		result = {'{{col3|' .. lang:getCode()}
		table.sort(items, compare_items)
		local last_term
		for _, item in ipairs(items) do
			local term = item.term
			if term ~= last_term then
				last_term = term
				table.insert(result, '|' .. item.term)
			end
		end
		table.insert(result, '}}')
		return mw.getCurrentFrame():preprocess(table.concat(result))

	else
		limit = 8
		result = {'<div>\n'} -- needed to prevent MediaWiki from adding unnecessary empty paragraph before the template
		tail = '</div>'
	end
	table.sort(items, compare_items)
	local last_term
	for _, item in ipairs(items) do
		local term = item.term
		if term ~= last_term then
			last_term = term
			if limit <= 0 then
				table.insert(result, '| style=width:1% |\n| style="text-align:left;vertical-align:top;width:' .. column_width .. '%" |\n')
				limit = column_size
			end
			table.insert(result, "*" .. m_links.full_link({term = term, lang = lang}) .. "\n")
			limit = limit - 1
			local nested_terms = item.nested_terms
			if nested_terms[1] then
				table.sort(nested_terms, compare_terms)
				local last_term
				for _, term in ipairs(nested_terms) do
					if term == last_term then
						error("Duplicate item \"" .. term .. "\".")
					end
					last_term = term
					table.insert(result, "**" .. m_links.full_link({term = term, lang = lang}) .. "\n")
					limit = limit - 1
				end
			end
		end
	end

	if tail ~= nil then
		table.insert(result, tail)
	end

	return table.concat(result)
end

return export