Module:Labelled list hatnote
--------------------------------------------------------------------------------
 --                               Labelled list                                --
 --                                                                            --
 -- This module does the core work of creating a hatnote composed of a list    --
 -- prefixed by a colon-terminated label, i.e. &quot;LABEL: [andList of pages]&quot;,    --
 -- for {{see also}} and similar templates.                                    --
 --------------------------------------------------------------------------------
 
 local mHatnote = require('Module:Hatnote')
 local mHatlist = require('Module:Hatnote list')
 local mArguments --initialize lazily
 local yesno --initialize lazily
 local p = {}
 
 -- Defaults global to this module
 local defaults = {
 	label = 'See also', --Final fallback for label argument
 	labelForm = '%s: %s',
 	prefixes = {'label', 'label ', 'l'},
 	template = 'Module:Labelled list hatnote'
 }
 
 -- Localizable message strings
 local msg = {
 	errorSuffix = '#Errors',
 	noInputWarning = 'no page names specified',
 	noOutputWarning =
 		&quot;'''[[%s]] — no output: none of the target pages exist.'''&quot;
 }
 
 -- Helper function that pre-combines display parameters into page arguments.
 -- Also compresses sparse arrays, as a desirable side-effect.
 function p.preprocessDisplays (args, prefixes)
 	-- Prefixes specify which parameters, in order, to check for display options
 	-- They each have numbers auto-appended, e.g. 'label1', 'label 1', &amp; 'l1'
 	prefixes = prefixes or defaults.prefixes
 	local indices = {}
 	local sparsePages = {}
 	for k, v in pairs(args) do
 		if type(k) == 'number' then
 			indices[#indices + 1] = k
 			local display
 			for i = 1, #prefixes do
 				display = args[prefixes[i] .. k]
 				if display then break end
 			end
 			sparsePages[k] = display and
 				string.format('%s|%s', string.gsub(v, '|.*$', ''), display) or v
 		end
 	end
 	table.sort(indices)
 	local pages = {}
 	for k, v in ipairs(indices) do pages[#pages + 1] = sparsePages[v] end
 	return pages
 end
 
 --Helper function to get a page target from a processed page string
 --e.g. &quot;Page|Label&quot; → &quot;Page&quot; or &quot;Target&quot; → &quot;Target&quot;
 local function getTarget(pagename)
  	local pipe = string.find(pagename, '|')
 	return string.sub(pagename, 0, pipe and pipe - 1 or nil)
 end
 
 -- Produces a labelled pages-list hatnote.
 -- The main frame (template definition) takes 1 or 2 arguments, for a singular
 -- and (optionally) plural label respectively:
 -- * {{#invoke:Labelled list hatnote|labelledList|Singular label|Plural label}}
 -- The resulting template takes pagename &amp; label parameters normally.
 function p.labelledList (frame)
 	mArguments = require('Module:Arguments')
 	yesno = require('Module:Yesno')
 	local labels = {frame.args[1] or defaults.label}
 	labels[2] = frame.args[2] or labels[1]
 	labels[3] = frame.args[3] --no defaulting
 	labels[4] = frame.args[4] --no defaulting
 	local template = frame:getParent():getTitle()
 	local args = mArguments.getArgs(frame, {parentOnly = true})
 	local pages = p.preprocessDisplays(args)
 	local options = {
 		category = yesno(args.category),
 		extraclasses = frame.args.extraclasses,
 		ifexists = yesno(frame.args.ifexists),
 		namespace = frame.args.namespace or args.namespace,
 		selfref = yesno(frame.args.selfref or args.selfref),
 		template = template
 	}
 	return p._labelledList(pages, labels, options)
 end
 
 function p._labelledList (pages, labels, options)
 	if options.ifexists then
 		for k = #pages, 1, -1 do --iterate backwards to allow smooth removals
 			local v = pages[k]
 			local title = mw.title.new(getTarget(v), namespace)
 			if (v == '') or title == nil or not title.exists then
 				table.remove(pages, k)
 			end
 		end
 	end
 	labels = labels or {}
 	label = (#pages == 1 and labels[1] or labels[2]) or defaults.label
 	for k, v in pairs(pages) do 
 		if mHatnote.findNamespaceId(v) ~= 0 then
 			label =
 				(
 					#pages == 1 and
 					(labels[3] or labels[1] or defaults.label) or
 					(labels[4] or labels[2] or defaults.label)
 				) or defaults.label
 		end
 	end
 	if #pages == 0 then
 		if options.ifexists then
 			mw.addWarning(
 				string.format(
 					msg.noOutputWarning, options.template or defaults.template
 				)
 			)
 			return ''
 		else
 			return mHatnote.makeWikitextError(
 				msg.noInputWarning,
 				(options.template or defaults.template) .. msg.errorSuffix,
 				options.category
 			)
 		end
 	end
 	local text = string.format(
 		options.labelForm or defaults.labelForm,
 		label,
 		mHatlist.andList(pages, true)
 	)
 	local hnOptions = {
 		extraclasses = options.extraclasses,
 		selfref = options.selfref
 	}
 	return mHatnote._hatnote(text, hnOptions)
 end