<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.sanctions.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Zoran</id>
	<title>sanctions - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.sanctions.net/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Zoran"/>
	<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php/Special:Contributions/Zoran"/>
	<updated>2026-04-24T18:08:05Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.5</generator>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Template:Unbulleted_indent_list&amp;diff=289</id>
		<title>Template:Unbulleted indent list</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Template:Unbulleted_indent_list&amp;diff=289"/>
		<updated>2022-04-07T15:37:01Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;{{&amp;lt;includeonly&amp;gt;safesubst:&amp;lt;/includeonly&amp;gt;#invoke:list|unbulleted|list_style=margin-left:{{{in|{{{indent|1em}}}}}};text-indent:-{{{in|{{{indent|1em}}}}}}}}&amp;lt;noinclude&amp;gt; {{documenta...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{&amp;lt;includeonly&amp;gt;safesubst:&amp;lt;/includeonly&amp;gt;#invoke:list|unbulleted|list_style=margin-left:{{{in|{{{indent|1em}}}}}};text-indent:-{{{in|{{{indent|1em}}}}}}}}&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{documentation}}&lt;br /&gt;
&amp;lt;!-- Categories go on the /doc subpage, and interwikis go on Wikidata. --&amp;gt;&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:List&amp;diff=288</id>
		<title>Module:List</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:List&amp;diff=288"/>
		<updated>2022-04-07T15:36:38Z</updated>

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

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- This module provides a generic exponential search algorithm.  local checkType = require('libraryUtil').checkType local floor = math.floor  local function midPoint(lower, up...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module provides a generic exponential search algorithm.&lt;br /&gt;
&lt;br /&gt;
local checkType = require('libraryUtil').checkType&lt;br /&gt;
local floor = math.floor&lt;br /&gt;
&lt;br /&gt;
local function midPoint(lower, upper)&lt;br /&gt;
	return floor(lower + (upper - lower) / 2)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function search(testFunc, i, lower, upper)&lt;br /&gt;
	if testFunc(i) then&lt;br /&gt;
		if i + 1 == upper then&lt;br /&gt;
			return i&lt;br /&gt;
		end&lt;br /&gt;
		lower = i&lt;br /&gt;
		if upper then&lt;br /&gt;
			i = midPoint(lower, upper)&lt;br /&gt;
		else&lt;br /&gt;
			i = i * 2&lt;br /&gt;
		end&lt;br /&gt;
		return search(testFunc, i, lower, upper)&lt;br /&gt;
	else&lt;br /&gt;
		upper = i&lt;br /&gt;
		i = midPoint(lower, upper)&lt;br /&gt;
		return search(testFunc, i, lower, upper)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return function (testFunc, init)&lt;br /&gt;
	checkType('Exponential search', 1, testFunc, 'function')&lt;br /&gt;
	checkType('Exponential search', 2, init, 'number', true)&lt;br /&gt;
	if init and (init &amp;lt; 1 or init ~= floor(init) or init == math.huge) then&lt;br /&gt;
		error(string.format(&lt;br /&gt;
			&amp;quot;invalid init value '%s' detected in argument #2 to &amp;quot; ..&lt;br /&gt;
			&amp;quot;'Exponential search' (init value must be a positive integer)&amp;quot;,&lt;br /&gt;
			tostring(init)&lt;br /&gt;
		), 2)&lt;br /&gt;
	end&lt;br /&gt;
	init = init or 2&lt;br /&gt;
	if not testFunc(1) then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	return search(testFunc, init, 1, nil)&lt;br /&gt;
end&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:TableTools&amp;diff=286</id>
		<title>Module:TableTools</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:TableTools&amp;diff=286"/>
		<updated>2022-04-07T15:35:29Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;------------------------------------------------------------------------------------ --                                   TableTools                                   -- --...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;------------------------------------------------------------------------------------&lt;br /&gt;
--                                   TableTools                                   --&lt;br /&gt;
--                                                                                --&lt;br /&gt;
-- This module includes a number of functions for dealing with Lua tables.        --&lt;br /&gt;
-- It is a meta-module, meant to be called from other Lua modules, and should not --&lt;br /&gt;
-- be called directly from #invoke.                                               --&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local libraryUtil = require('libraryUtil')&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Define often-used variables and functions.&lt;br /&gt;
local floor = math.floor&lt;br /&gt;
local infinity = math.huge&lt;br /&gt;
local checkType = libraryUtil.checkType&lt;br /&gt;
local checkTypeMulti = libraryUtil.checkTypeMulti&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- isPositiveInteger&lt;br /&gt;
--&lt;br /&gt;
-- This function returns true if the given value is a positive integer, and false&lt;br /&gt;
-- if not. Although it doesn't operate on tables, it is included here as it is&lt;br /&gt;
-- useful for determining whether a given table key is in the array part or the&lt;br /&gt;
-- hash part of a table.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.isPositiveInteger(v)&lt;br /&gt;
	return type(v) == 'number' and v &amp;gt;= 1 and floor(v) == v and v &amp;lt; infinity&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- isNan&lt;br /&gt;
--&lt;br /&gt;
-- This function returns true if the given number is a NaN value, and false if&lt;br /&gt;
-- not. Although it doesn't operate on tables, it is included here as it is useful&lt;br /&gt;
-- for determining whether a value can be a valid table key. Lua will generate an&lt;br /&gt;
-- error if a NaN is used as a table key.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.isNan(v)&lt;br /&gt;
	return type(v) == 'number' and v ~= v&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- shallowClone&lt;br /&gt;
--&lt;br /&gt;
-- This returns a clone of a table. The value returned is a new table, but all&lt;br /&gt;
-- subtables and functions are shared. Metamethods are respected, but the returned&lt;br /&gt;
-- table will have no metatable of its own.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.shallowClone(t)&lt;br /&gt;
	checkType('shallowClone', 1, t, 'table')&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for k, v in pairs(t) do&lt;br /&gt;
		ret[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- removeDuplicates&lt;br /&gt;
--&lt;br /&gt;
-- This removes duplicate values from an array. Non-positive-integer keys are&lt;br /&gt;
-- ignored. The earliest value is kept, and all subsequent duplicate values are&lt;br /&gt;
-- removed, but otherwise the array order is unchanged.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.removeDuplicates(arr)&lt;br /&gt;
	checkType('removeDuplicates', 1, arr, 'table')&lt;br /&gt;
	local isNan = p.isNan&lt;br /&gt;
	local ret, exists = {}, {}&lt;br /&gt;
	for _, v in ipairs(arr) do&lt;br /&gt;
		if isNan(v) then&lt;br /&gt;
			-- NaNs can't be table keys, and they are also unique, so we don't need to check existence.&lt;br /&gt;
			ret[#ret + 1] = v&lt;br /&gt;
		else&lt;br /&gt;
			if not exists[v] then&lt;br /&gt;
				ret[#ret + 1] = v&lt;br /&gt;
				exists[v] = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- numKeys&lt;br /&gt;
--&lt;br /&gt;
-- This takes a table and returns an array containing the numbers of any numerical&lt;br /&gt;
-- keys that have non-nil values, sorted in numerical order.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.numKeys(t)&lt;br /&gt;
	checkType('numKeys', 1, t, 'table')&lt;br /&gt;
	local isPositiveInteger = p.isPositiveInteger&lt;br /&gt;
	local nums = {}&lt;br /&gt;
	for k in pairs(t) do&lt;br /&gt;
		if isPositiveInteger(k) then&lt;br /&gt;
			nums[#nums + 1] = k&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(nums)&lt;br /&gt;
	return nums&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- affixNums&lt;br /&gt;
--&lt;br /&gt;
-- This takes a table and returns an array containing the numbers of keys with the&lt;br /&gt;
-- specified prefix and suffix. For example, for the table&lt;br /&gt;
-- {a1 = 'foo', a3 = 'bar', a6 = 'baz'} and the prefix &amp;quot;a&amp;quot;, affixNums will return&lt;br /&gt;
-- {1, 3, 6}.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.affixNums(t, prefix, suffix)&lt;br /&gt;
	checkType('affixNums', 1, t, 'table')&lt;br /&gt;
	checkType('affixNums', 2, prefix, 'string', true)&lt;br /&gt;
	checkType('affixNums', 3, suffix, 'string', true)&lt;br /&gt;
&lt;br /&gt;
	local function cleanPattern(s)&lt;br /&gt;
		-- Cleans a pattern so that the magic characters ()%.[]*+-?^$ are interpreted literally.&lt;br /&gt;
		return s:gsub('([%(%)%%%.%[%]%*%+%-%?%^%$])', '%%%1')&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	prefix = prefix or ''&lt;br /&gt;
	suffix = suffix or ''&lt;br /&gt;
	prefix = cleanPattern(prefix)&lt;br /&gt;
	suffix = cleanPattern(suffix)&lt;br /&gt;
	local pattern = '^' .. prefix .. '([1-9]%d*)' .. suffix .. '$'&lt;br /&gt;
&lt;br /&gt;
	local nums = {}&lt;br /&gt;
	for k in pairs(t) do&lt;br /&gt;
		if type(k) == 'string' then&lt;br /&gt;
			local num = mw.ustring.match(k, pattern)&lt;br /&gt;
			if num then&lt;br /&gt;
				nums[#nums + 1] = tonumber(num)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(nums)&lt;br /&gt;
	return nums&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- numData&lt;br /&gt;
--&lt;br /&gt;
-- Given a table with keys like {&amp;quot;foo1&amp;quot;, &amp;quot;bar1&amp;quot;, &amp;quot;foo2&amp;quot;, &amp;quot;baz2&amp;quot;}, returns a table&lt;br /&gt;
-- of subtables in the format&lt;br /&gt;
-- {[1] = {foo = 'text', bar = 'text'}, [2] = {foo = 'text', baz = 'text'}}.&lt;br /&gt;
-- Keys that don't end with an integer are stored in a subtable named &amp;quot;other&amp;quot;. The&lt;br /&gt;
-- compress option compresses the table so that it can be iterated over with&lt;br /&gt;
-- ipairs.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.numData(t, compress)&lt;br /&gt;
	checkType('numData', 1, t, 'table')&lt;br /&gt;
	checkType('numData', 2, compress, 'boolean', true)&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for k, v in pairs(t) do&lt;br /&gt;
		local prefix, num = mw.ustring.match(tostring(k), '^([^0-9]*)([1-9][0-9]*)$')&lt;br /&gt;
		if num then&lt;br /&gt;
			num = tonumber(num)&lt;br /&gt;
			local subtable = ret[num] or {}&lt;br /&gt;
			if prefix == '' then&lt;br /&gt;
				-- Positional parameters match the blank string; put them at the start of the subtable instead.&lt;br /&gt;
				prefix = 1&lt;br /&gt;
			end&lt;br /&gt;
			subtable[prefix] = v&lt;br /&gt;
			ret[num] = subtable&lt;br /&gt;
		else&lt;br /&gt;
			local subtable = ret.other or {}&lt;br /&gt;
			subtable[k] = v&lt;br /&gt;
			ret.other = subtable&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if compress then&lt;br /&gt;
		local other = ret.other&lt;br /&gt;
		ret = p.compressSparseArray(ret)&lt;br /&gt;
		ret.other = other&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- compressSparseArray&lt;br /&gt;
--&lt;br /&gt;
-- This takes an array with one or more nil values, and removes the nil values&lt;br /&gt;
-- while preserving the order, so that the array can be safely traversed with&lt;br /&gt;
-- ipairs.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.compressSparseArray(t)&lt;br /&gt;
	checkType('compressSparseArray', 1, t, 'table')&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	local nums = p.numKeys(t)&lt;br /&gt;
	for _, num in ipairs(nums) do&lt;br /&gt;
		ret[#ret + 1] = t[num]&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- sparseIpairs&lt;br /&gt;
--&lt;br /&gt;
-- This is an iterator for sparse arrays. It can be used like ipairs, but can&lt;br /&gt;
-- handle nil values.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.sparseIpairs(t)&lt;br /&gt;
	checkType('sparseIpairs', 1, t, 'table')&lt;br /&gt;
	local nums = p.numKeys(t)&lt;br /&gt;
	local i = 0&lt;br /&gt;
	local lim = #nums&lt;br /&gt;
	return function ()&lt;br /&gt;
		i = i + 1&lt;br /&gt;
		if i &amp;lt;= lim then&lt;br /&gt;
			local key = nums[i]&lt;br /&gt;
			return key, t[key]&lt;br /&gt;
		else&lt;br /&gt;
			return nil, nil&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- size&lt;br /&gt;
--&lt;br /&gt;
-- This returns the size of a key/value pair table. It will also work on arrays,&lt;br /&gt;
-- but for arrays it is more efficient to use the # operator.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.size(t)&lt;br /&gt;
	checkType('size', 1, t, 'table')&lt;br /&gt;
	local i = 0&lt;br /&gt;
	for _ in pairs(t) do&lt;br /&gt;
		i = i + 1&lt;br /&gt;
	end&lt;br /&gt;
	return i&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function defaultKeySort(item1, item2)&lt;br /&gt;
	-- &amp;quot;number&amp;quot; &amp;lt; &amp;quot;string&amp;quot;, so numbers will be sorted before strings.&lt;br /&gt;
	local type1, type2 = type(item1), type(item2)&lt;br /&gt;
	if type1 ~= type2 then&lt;br /&gt;
		return type1 &amp;lt; type2&lt;br /&gt;
	elseif type1 == 'table' or type1 == 'boolean' or type1 == 'function' then&lt;br /&gt;
		return tostring(item1) &amp;lt; tostring(item2)&lt;br /&gt;
	else&lt;br /&gt;
		return item1 &amp;lt; item2&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- keysToList&lt;br /&gt;
--&lt;br /&gt;
-- Returns an array of the keys in a table, sorted using either a default&lt;br /&gt;
-- comparison function or a custom keySort function.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.keysToList(t, keySort, checked)&lt;br /&gt;
	if not checked then&lt;br /&gt;
		checkType('keysToList', 1, t, 'table')&lt;br /&gt;
		checkTypeMulti('keysToList', 2, keySort, {'function', 'boolean', 'nil'})&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local arr = {}&lt;br /&gt;
	local index = 1&lt;br /&gt;
	for k in pairs(t) do&lt;br /&gt;
		arr[index] = k&lt;br /&gt;
		index = index + 1&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if keySort ~= false then&lt;br /&gt;
		keySort = type(keySort) == 'function' and keySort or defaultKeySort&lt;br /&gt;
		table.sort(arr, keySort)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return arr&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- sortedPairs&lt;br /&gt;
--&lt;br /&gt;
-- Iterates through a table, with the keys sorted using the keysToList function.&lt;br /&gt;
-- If there are only numerical keys, sparseIpairs is probably more efficient.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.sortedPairs(t, keySort)&lt;br /&gt;
	checkType('sortedPairs', 1, t, 'table')&lt;br /&gt;
	checkType('sortedPairs', 2, keySort, 'function', true)&lt;br /&gt;
&lt;br /&gt;
	local arr = p.keysToList(t, keySort, true)&lt;br /&gt;
&lt;br /&gt;
	local i = 0&lt;br /&gt;
	return function ()&lt;br /&gt;
		i = i + 1&lt;br /&gt;
		local key = arr[i]&lt;br /&gt;
		if key ~= nil then&lt;br /&gt;
			return key, t[key]&lt;br /&gt;
		else&lt;br /&gt;
			return nil, nil&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- isArray&lt;br /&gt;
--&lt;br /&gt;
-- Returns true if the given value is a table and all keys are consecutive&lt;br /&gt;
-- integers starting at 1.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.isArray(v)&lt;br /&gt;
	if type(v) ~= 'table' then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	local i = 0&lt;br /&gt;
	for _ in pairs(v) do&lt;br /&gt;
		i = i + 1&lt;br /&gt;
		if v[i] == nil then&lt;br /&gt;
			return false&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- isArrayLike&lt;br /&gt;
--&lt;br /&gt;
-- Returns true if the given value is iterable and all keys are consecutive&lt;br /&gt;
-- integers starting at 1.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.isArrayLike(v)&lt;br /&gt;
	if not pcall(pairs, v) then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	local i = 0&lt;br /&gt;
	for _ in pairs(v) do&lt;br /&gt;
		i = i + 1&lt;br /&gt;
		if v[i] == nil then&lt;br /&gt;
			return false&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return true&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- invert&lt;br /&gt;
--&lt;br /&gt;
-- Transposes the keys and values in an array. For example, {&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;} -&amp;gt;&lt;br /&gt;
-- {a = 1, b = 2, c = 3}. Duplicates are not supported (result values refer to&lt;br /&gt;
-- the index of the last duplicate) and NaN values are ignored.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.invert(arr)&lt;br /&gt;
	checkType(&amp;quot;invert&amp;quot;, 1, arr, &amp;quot;table&amp;quot;)&lt;br /&gt;
	local isNan = p.isNan&lt;br /&gt;
	local map = {}&lt;br /&gt;
	for i, v in ipairs(arr) do&lt;br /&gt;
		if not isNan(v) then&lt;br /&gt;
			map[v] = i&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return map&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- listToSet&lt;br /&gt;
--&lt;br /&gt;
-- Creates a set from the array part of the table. Indexing the set by any of the&lt;br /&gt;
-- values of the array returns true. For example, {&amp;quot;a&amp;quot;, &amp;quot;b&amp;quot;, &amp;quot;c&amp;quot;} -&amp;gt;&lt;br /&gt;
-- {a = true, b = true, c = true}. NaN values are ignored as Lua considers them&lt;br /&gt;
-- never equal to any value (including other NaNs or even themselves).&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.listToSet(arr)&lt;br /&gt;
	checkType(&amp;quot;listToSet&amp;quot;, 1, arr, &amp;quot;table&amp;quot;)&lt;br /&gt;
	local isNan = p.isNan&lt;br /&gt;
	local set = {}&lt;br /&gt;
	for _, v in ipairs(arr) do&lt;br /&gt;
		if not isNan(v) then&lt;br /&gt;
			set[v] = true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return set&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- deepCopy&lt;br /&gt;
--&lt;br /&gt;
-- Recursive deep copy function. Preserves identities of subtables.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
local function _deepCopy(orig, includeMetatable, already_seen)&lt;br /&gt;
	-- Stores copies of tables indexed by the original table.&lt;br /&gt;
	already_seen = already_seen or {}&lt;br /&gt;
&lt;br /&gt;
	local copy = already_seen[orig]&lt;br /&gt;
	if copy ~= nil then&lt;br /&gt;
		return copy&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if type(orig) == 'table' then&lt;br /&gt;
		copy = {}&lt;br /&gt;
		for orig_key, orig_value in pairs(orig) do&lt;br /&gt;
			copy[_deepCopy(orig_key, includeMetatable, already_seen)] = _deepCopy(orig_value, includeMetatable, already_seen)&lt;br /&gt;
		end&lt;br /&gt;
		already_seen[orig] = copy&lt;br /&gt;
&lt;br /&gt;
		if includeMetatable then&lt;br /&gt;
			local mt = getmetatable(orig)&lt;br /&gt;
			if mt ~= nil then&lt;br /&gt;
				local mt_copy = _deepCopy(mt, includeMetatable, already_seen)&lt;br /&gt;
				setmetatable(copy, mt_copy)&lt;br /&gt;
				already_seen[mt] = mt_copy&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else -- number, string, boolean, etc&lt;br /&gt;
		copy = orig&lt;br /&gt;
	end&lt;br /&gt;
	return copy&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.deepCopy(orig, noMetatable, already_seen)&lt;br /&gt;
	checkType(&amp;quot;deepCopy&amp;quot;, 3, already_seen, &amp;quot;table&amp;quot;, true)&lt;br /&gt;
	return _deepCopy(orig, not noMetatable, already_seen)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- sparseConcat&lt;br /&gt;
--&lt;br /&gt;
-- Concatenates all values in the table that are indexed by a number, in order.&lt;br /&gt;
-- sparseConcat{a, nil, c, d}  =&amp;gt;  &amp;quot;acd&amp;quot;&lt;br /&gt;
-- sparseConcat{nil, b, c, d}  =&amp;gt;  &amp;quot;bcd&amp;quot;&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.sparseConcat(t, sep, i, j)&lt;br /&gt;
	local arr = {}&lt;br /&gt;
&lt;br /&gt;
	local arr_i = 0&lt;br /&gt;
	for _, v in p.sparseIpairs(t) do&lt;br /&gt;
		arr_i = arr_i + 1&lt;br /&gt;
		arr[arr_i] = v&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat(arr, sep, i, j)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- length&lt;br /&gt;
--&lt;br /&gt;
-- Finds the length of an array, or of a quasi-array with keys such as &amp;quot;data1&amp;quot;,&lt;br /&gt;
-- &amp;quot;data2&amp;quot;, etc., using an exponential search algorithm. It is similar to the&lt;br /&gt;
-- operator #, but may return a different value when there are gaps in the array&lt;br /&gt;
-- portion of the table. Intended to be used on data loaded with mw.loadData. For&lt;br /&gt;
-- other tables, use #.&lt;br /&gt;
-- Note: #frame.args in frame object always be set to 0, regardless of  the number&lt;br /&gt;
-- of unnamed template parameters, so use this function for frame.args.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.length(t, prefix)&lt;br /&gt;
	-- requiring module inline so that [[Module:Exponential search]] which is&lt;br /&gt;
	-- only needed by this one function doesn't get millions of transclusions&lt;br /&gt;
	local expSearch = require(&amp;quot;Module:Exponential search&amp;quot;)&lt;br /&gt;
	checkType('length', 1, t, 'table')&lt;br /&gt;
	checkType('length', 2, prefix, 'string', true)&lt;br /&gt;
	return expSearch(function (i)&lt;br /&gt;
		local key&lt;br /&gt;
		if prefix then&lt;br /&gt;
			key = prefix .. tostring(i)&lt;br /&gt;
		else&lt;br /&gt;
			key = i&lt;br /&gt;
		end&lt;br /&gt;
		return t[key] ~= nil&lt;br /&gt;
	end) or 0&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
-- inArray&lt;br /&gt;
--&lt;br /&gt;
-- Returns true if valueToFind is a member of the array, and false otherwise.&lt;br /&gt;
------------------------------------------------------------------------------------&lt;br /&gt;
function p.inArray(arr, valueToFind)&lt;br /&gt;
	checkType(&amp;quot;inArray&amp;quot;, 1, arr, &amp;quot;table&amp;quot;)&lt;br /&gt;
	-- if valueToFind is nil, error?&lt;br /&gt;
&lt;br /&gt;
	for _, v in ipairs(arr) do&lt;br /&gt;
		if v == valueToFind then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Template:Indented_plainlist&amp;diff=285</id>
		<title>Template:Indented plainlist</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Template:Indented_plainlist&amp;diff=285"/>
		<updated>2022-04-07T15:34:26Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;&amp;lt;div class=&amp;quot;plainlist {{{class|}}}&amp;quot; style=&amp;quot;margin-left:{{{in|{{{indent|1em}}}}}};text-indent:-{{{in|{{{indent|1em}}}}}};{{{style|}}}&amp;quot;&amp;gt;{{#if:{{{1|}}}| {{{1}}} &amp;lt;/div&amp;gt;}}&amp;lt;noinclud...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;plainlist {{{class|}}}&amp;quot; style=&amp;quot;margin-left:{{{in|{{{indent|1em}}}}}};text-indent:-{{{in|{{{indent|1em}}}}}};{{{style|}}}&amp;quot;&amp;gt;{{#if:{{{1|}}}|&lt;br /&gt;
{{{1}}}&lt;br /&gt;
&amp;lt;/div&amp;gt;}}&amp;lt;noinclude&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
{{documentation}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=MediaWiki:Common.css&amp;diff=284</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=MediaWiki:Common.css&amp;diff=284"/>
		<updated>2022-04-07T15:24:20Z</updated>

		<summary type="html">&lt;p&gt;Zoran: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS placed here will be applied to all skins */&lt;br /&gt;
@import url('https://fonts.googleapis.com/css2?family=Saira:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&amp;amp;display=swap');&lt;br /&gt;
&lt;br /&gt;
* {&lt;br /&gt;
  font-family: 'Saira', sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* CSS placed here will be applied to all skins */&lt;br /&gt;
&lt;br /&gt;
div.container {&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.menu {&lt;br /&gt;
  flex-basis: 300px !important;&lt;br /&gt;
  flex-grow:0 !important;&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.main {&lt;br /&gt;
  margin-left:100px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.footer {&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
  margin: 0px !important;&lt;br /&gt;
  padding: 3px !important;&lt;br /&gt;
  padding-left: 145px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.disable-margin, div.disable-margin {&lt;br /&gt;
  margin: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.disable-padding, div.disable-padding {&lt;br /&gt;
  padding: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.header {&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.edit-box {&lt;br /&gt;
  flex-basis: 300px !important;&lt;br /&gt;
  flex-grow:0 !important;&lt;br /&gt;
  background-color: #DDD !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.search-box {&lt;br /&gt;
  padding-top:20px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.noedit-box {&lt;br /&gt;
  flex-basis: 300px !important;&lt;br /&gt;
  flex-grow:0 !important;&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
h1, h2 {&lt;br /&gt;
  border-bottom: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.content h1, h2, h3, h4, h5, h6 {&lt;br /&gt;
  margin-left:-30px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.content {&lt;br /&gt;
  margin-left:30px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.fa-home::before, .n-Homepage::before, .n-mainpage-description::before, .n-mainpage::before, .pt-userpage::before, .fa-backward::before, .n-recentchanges::before, .t-recentchangeslinked::before, .fa-random::before, .n-randompage::before, .fa-question::before, .ca-nstab-help::before, .n-help-mediawiki::before, .n-help::before, .pt-anoncontribs::before, .pt-mycontris::before, .t-cite::before, .fa-sitemap::before, .t-whatlinkshere::before,  .fa-cogs::before, .ca-nstab-mediawiki::before, .ca-nstab-special::before, .t-specialpages::before, .fa-info::before, .t-info::before, .fa-print::before, .t-print::before, .fa-link::before, .t-permalink::before, .fa-upload::before, .n-upload::before, .t-upload::before, .fa-comments::before, .ca-talk::before, .pt-anontalk::before, .pt-mytalk::before, .fa-history::before, .ca-history::before, .fa-trash-alt::before, .ca-delete::before, .fa-location-arrow::before, .ca-move::before, .fa-lock::before, .ca-protect::before, .ca-unprotect::before, .fa-eye-slash::before, .ca-unwatch::before, #ca-unwatch &amp;gt; a::before, .fa-sliders-h::before, .pt-preferences::before, .fa-eye::before, .ca-watch::before, .pt-watchlist::before, #ca-watch &amp;gt; a::before, .fa-sign-out-alt::before, .pt-logout::before, .fa-sign-in-alt::before, .pt-login::before {&lt;br /&gt;
  content: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.nonflex .navbar-nav {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.nonflex a {&lt;br /&gt;
  padding-left:0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div#mw-editpage-minoredit {&lt;br /&gt;
  display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
pre {&lt;br /&gt;
  font-family: Courier,SFMono-Regular,Menlo,Monaco,Consolas,&amp;quot;Liberation Mono&amp;quot;,&amp;quot;Courier New&amp;quot;,monospace;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.mainpage {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.mainrow {&lt;br /&gt;
    flex-grow:1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.p-navbar {&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* Unbulleted lists */&lt;br /&gt;
.plainlist ol,&lt;br /&gt;
.plainlist ul {&lt;br /&gt;
	line-height: inherit;&lt;br /&gt;
	list-style: none none;&lt;br /&gt;
	margin: 0;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.plainlist ol li,&lt;br /&gt;
.plainlist ul li {&lt;br /&gt;
	margin-bottom: 0;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=MediaWiki:Common.css&amp;diff=283</id>
		<title>MediaWiki:Common.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=MediaWiki:Common.css&amp;diff=283"/>
		<updated>2022-04-07T15:19:41Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Fix for plainlist&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* CSS placed here will be applied to all skins */&lt;br /&gt;
@import url('https://fonts.googleapis.com/css2?family=Saira:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&amp;amp;display=swap');&lt;br /&gt;
&lt;br /&gt;
* {&lt;br /&gt;
  font-family: 'Saira', sans-serif;&lt;br /&gt;
}&lt;br /&gt;
/* CSS placed here will be applied to all skins */&lt;br /&gt;
&lt;br /&gt;
div.container {&lt;br /&gt;
  max-width: 100% !important;&lt;br /&gt;
  width: 100% !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.menu {&lt;br /&gt;
  flex-basis: 300px !important;&lt;br /&gt;
  flex-grow:0 !important;&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.main {&lt;br /&gt;
  margin-left:100px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.footer {&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
  margin: 0px !important;&lt;br /&gt;
  padding: 3px !important;&lt;br /&gt;
  padding-left: 145px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.disable-margin, div.disable-margin {&lt;br /&gt;
  margin: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.disable-padding, div.disable-padding {&lt;br /&gt;
  padding: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.header {&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.edit-box {&lt;br /&gt;
  flex-basis: 300px !important;&lt;br /&gt;
  flex-grow:0 !important;&lt;br /&gt;
  background-color: #DDD !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.search-box {&lt;br /&gt;
  padding-top:20px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.noedit-box {&lt;br /&gt;
  flex-basis: 300px !important;&lt;br /&gt;
  flex-grow:0 !important;&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
h1, h2 {&lt;br /&gt;
  border-bottom: 0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.content h1, h2, h3, h4, h5, h6 {&lt;br /&gt;
  margin-left:-30px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.content {&lt;br /&gt;
  margin-left:30px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.fa-home::before, .n-Homepage::before, .n-mainpage-description::before, .n-mainpage::before, .pt-userpage::before, .fa-backward::before, .n-recentchanges::before, .t-recentchangeslinked::before, .fa-random::before, .n-randompage::before, .fa-question::before, .ca-nstab-help::before, .n-help-mediawiki::before, .n-help::before, .pt-anoncontribs::before, .pt-mycontris::before, .t-cite::before, .fa-sitemap::before, .t-whatlinkshere::before,  .fa-cogs::before, .ca-nstab-mediawiki::before, .ca-nstab-special::before, .t-specialpages::before, .fa-info::before, .t-info::before, .fa-print::before, .t-print::before, .fa-link::before, .t-permalink::before, .fa-upload::before, .n-upload::before, .t-upload::before, .fa-comments::before, .ca-talk::before, .pt-anontalk::before, .pt-mytalk::before, .fa-history::before, .ca-history::before, .fa-trash-alt::before, .ca-delete::before, .fa-location-arrow::before, .ca-move::before, .fa-lock::before, .ca-protect::before, .ca-unprotect::before, .fa-eye-slash::before, .ca-unwatch::before, #ca-unwatch &amp;gt; a::before, .fa-sliders-h::before, .pt-preferences::before, .fa-eye::before, .ca-watch::before, .pt-watchlist::before, #ca-watch &amp;gt; a::before, .fa-sign-out-alt::before, .pt-logout::before, .fa-sign-in-alt::before, .pt-login::before {&lt;br /&gt;
  content: none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.nonflex .navbar-nav {&lt;br /&gt;
  display: block !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
nav.nonflex a {&lt;br /&gt;
  padding-left:0px !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div#mw-editpage-minoredit {&lt;br /&gt;
  display:none !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
pre {&lt;br /&gt;
  font-family: Courier,SFMono-Regular,Menlo,Monaco,Consolas,&amp;quot;Liberation Mono&amp;quot;,&amp;quot;Courier New&amp;quot;,monospace;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.mainpage {&lt;br /&gt;
    display: flex;&lt;br /&gt;
    align-items: stretch;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
div.mainrow {&lt;br /&gt;
    flex-grow:1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.p-navbar {&lt;br /&gt;
  background-color: #EEE !important;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.plainlist ul {&lt;br /&gt;
    line-height: inherit;&lt;br /&gt;
    list-style: none none;&lt;br /&gt;
    margin: 0;&lt;br /&gt;
}&lt;br /&gt;
.plainlist ul li {&lt;br /&gt;
    margin-bottom: 0;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Suggestions&amp;diff=267</id>
		<title>Module:Citation/CS1/Suggestions</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Suggestions&amp;diff=267"/>
		<updated>2022-04-04T14:33:47Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- Please insert new suggestions in alphabetical order -- The form is ['incorrect'] = 'correct',  suggestions = { 	['ASIN-TLD'] = 'asin-tld',													-- old parameter name...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Please insert new suggestions in alphabetical order&lt;br /&gt;
-- The form is ['incorrect'] = 'correct',&lt;br /&gt;
&lt;br /&gt;
suggestions = {&lt;br /&gt;
	['ASIN-TLD'] = 'asin-tld',													-- old parameter name&lt;br /&gt;
	['abruf'] = 'access-date',													-- German&lt;br /&gt;
	['abruf-verborgen'] = 'access-date',										-- German&lt;br /&gt;
	['accessmonth'] = 'access-date',&lt;br /&gt;
	['accesso'] = 'access-date',												-- Italian&lt;br /&gt;
	['accessyear'] = 'access-date',&lt;br /&gt;
	['acessodata'] = 'access-date',												-- Brazilian Portuguese&lt;br /&gt;
	['ad'] = 'first',															-- Turkish&lt;br /&gt;
	['address'] = 'location',&lt;br /&gt;
	['anno'] = 'date',															-- Italian&lt;br /&gt;
	['annoaccesso'] = 'access-date',											-- Italian&lt;br /&gt;
	['annodiaccesso'] = 'access-date',											-- Italian&lt;br /&gt;
	['annooriginale'] = 'orig-date',											-- Italian&lt;br /&gt;
	['année'] = 'date',															-- French&lt;br /&gt;
	['apellido'] = 'last',														-- Spanish&lt;br /&gt;
	['apellidos'] = 'last',														-- Spanish&lt;br /&gt;
	['archiv-datum'] = 'archive-date',											-- German&lt;br /&gt;
	['archiv-url'] = 'archive-url',												-- German&lt;br /&gt;
	['archive date'] = 'archive-date',&lt;br /&gt;
	['archive url'] = 'archive-url',											-- Spanish&lt;br /&gt;
	['archive-link'] = 'archive-url',	&lt;br /&gt;
	['artikelnr'] = 'id',														-- German (as 'id=Article No. ...')&lt;br /&gt;
	['artist'] = 'others',&lt;br /&gt;
	['arşiv-tarihi'] = 'archive-date',											-- Turkish&lt;br /&gt;
	['arşiv-url'] = 'archive-url',												-- Turkish&lt;br /&gt;
	['arşivtarihi'] = 'archive-date',											-- Turkish&lt;br /&gt;
	['arşivurl'] = 'archive-url',												-- Turkish&lt;br /&gt;
	['auflage'] = 'edition',													-- German&lt;br /&gt;
	['auteur'] = 'author',														-- French&lt;br /&gt;
	['auther'] = 'author',&lt;br /&gt;
	['author link'] = 'author-link',											-- Polish&lt;br /&gt;
	['authorfirst'] = 'author-first',											-- old parameter name&lt;br /&gt;
	['authorgiven'] = 'author-given',											-- old parameter name&lt;br /&gt;
	['authorlast'] = 'author-last',												-- old parameter name&lt;br /&gt;
	['authormask'] = 'author-mask',												-- old parameter name&lt;br /&gt;
	['authorsurname'] = 'author-surname',										-- old parameter name&lt;br /&gt;
	['autor'] = 'author',														-- Spanish, German (singular and plural)&lt;br /&gt;
	['autore'] = 'author',														-- Italian&lt;br /&gt;
	['autthor'] = 'author',&lt;br /&gt;
	['ay'] = 'month',															-- Turkish&lt;br /&gt;
	['año'] = 'date',															-- Spanish&lt;br /&gt;
	['año-original'] = 'orig-date',												-- Spanish&lt;br /&gt;
	['añoacceso'] = 'access-date',												-- Spanish&lt;br /&gt;
	['añooriginal'] = 'orig-date',												-- Spanish&lt;br /&gt;
	['band'] = 'volume',														-- German&lt;br /&gt;
	['bandreihe'] = 'volume',													-- German&lt;br /&gt;
	['baskı'] = 'edition',														-- Turkish&lt;br /&gt;
	['başlık'] = 'title',														-- Turkish&lt;br /&gt;
	['began'] = 'date',															-- old parameter name (cite serial only); or orig-date=&lt;br /&gt;
	['booktitle'] = 'book-title',												-- old parameter name&lt;br /&gt;
	['ch'] = 'language',														-- German (as 'language=de-CH')&lt;br /&gt;
	['chapter_title'] = 'chapter',&lt;br /&gt;
	['chapterurl'] = 'chapter-url',												-- old parameter name&lt;br /&gt;
	['cid'] = 'ref',															-- Italian&lt;br /&gt;
	['cilt'] = 'volume',														-- Turkish&lt;br /&gt;
	['cita'] = 'quote',															-- Spanish&lt;br /&gt;
	['citazione'] = 'quote',													-- Italian&lt;br /&gt;
	['città'] = 'location',														-- Italian&lt;br /&gt;
	['city'] = 'location',														-- old parameter name (cite serial only)&lt;br /&gt;
	['coauthor'] = 'author',&lt;br /&gt;
	['coauthors'] = 'author',&lt;br /&gt;
	['coautores'] = 'author',													-- Spanish&lt;br /&gt;
	['coautori'] = 'author',													-- Italian&lt;br /&gt;
	['cognome'] = 'last',														-- Italian&lt;br /&gt;
	['conferenceurl'] = 'conference-url',										-- old parameter name&lt;br /&gt;
	['consulté le'] = 'access-date',											-- French&lt;br /&gt;
	['contributionurl'] = 'contribution-url',									-- old parameter name&lt;br /&gt;
	['curatore'] = 'publisher',													-- Italian&lt;br /&gt;
	['czasopismo'] = 'journal',													-- Polish&lt;br /&gt;
	['data dostępu'] = 'access-date',											-- Polish&lt;br /&gt;
	['data'] = 'date',															-- Polish, Italian&lt;br /&gt;
	['dataaccesso'] = 'access-date',											-- Italian&lt;br /&gt;
	['dataarchivio'] = 'archive-date',											-- Italian&lt;br /&gt;
	['datum'] = 'date',															-- German&lt;br /&gt;
	['dead-url'] = 'url-status',												-- old parameter name&lt;br /&gt;
	['deadlink'] = 'url-status',&lt;br /&gt;
	['deadurl'] = 'url-status',													-- old parameter name&lt;br /&gt;
	['dergi'] = 'work',															-- Turkish&lt;br /&gt;
	['dil'] = 'language',														-- Turkish&lt;br /&gt;
	['displayauthors'] = 'display-authors',										-- old parameter name&lt;br /&gt;
	['displayeditors'] = 'display-editors',										-- old parameter name&lt;br /&gt;
	['distributor'] = 'publisher',&lt;br /&gt;
	['dnb'] = 'id',																-- German (as 'id={{DNB-IDN|...}}')&lt;br /&gt;
	['doi-broken'] = 'doi-broken-date',											-- old parameter alias&lt;br /&gt;
	['doi-inactive'] = 'doi-broken-date',										-- invalid name found in mainspace&lt;br /&gt;
	['doi-inactive-date'] = 'doi-broken-date',									-- old parameter alias&lt;br /&gt;
	['doi_brokendate'] = 'doi-broken-date',										-- old parameter alias&lt;br /&gt;
	['doi_inactivedate'] = 'doi-inactive-date',									-- old parameter alias&lt;br /&gt;
	['doibroken'] = 'doi-broken-date',											-- invalid name found in mainspace&lt;br /&gt;
	['doiinactive'] = 'doi-broken-date',										-- invalid name found in mainspace&lt;br /&gt;
	['e-print'] = 'eprint',														-- misspelling&lt;br /&gt;
	-- ['ed'] = 'edition',														-- avoid suggestion as in English this could be short for editor or edition&lt;br /&gt;
	-- ['editora'] = 'publisher',												-- can be either editor or publisher&lt;br /&gt;
	-- ['editore'] = 'agency',													-- can be either publisher or agency&lt;br /&gt;
	-- ['editori'] = 'editor',													-- can be either editor or publisher&lt;br /&gt;
	-- ['editorial'] = 'publisher',												-- can be either publisher or work&lt;br /&gt;
	['editoin'] = 'edition',													-- misspelling&lt;br /&gt;
	['editon'] = 'edition',														-- misspelling&lt;br /&gt;
	['editorfirst'] = 'editor-first',											-- old parameter name&lt;br /&gt;
	['editorgiven'] = 'editor-given',											-- old parameter name&lt;br /&gt;
	['editorlast'] = 'editor-last',												-- old parameter name&lt;br /&gt;
	['editorlink'] = 'editor-link',												-- old parameter name&lt;br /&gt;
	['editormask'] = 'editor-mask',												-- old parameter name&lt;br /&gt;
	['editors'] = 'editor',														-- old parameter name (can be emulated using multiple singular |editor= params)&lt;br /&gt;
	['editorsurname'] = 'editor-surname',										-- old parameter name&lt;br /&gt;
	['edizione'] = 'edition',													-- Italian&lt;br /&gt;
	['embargo'] = 'pmc-embargo-date',											-- old parameter name&lt;br /&gt;
	['ended'] = 'date',															-- old parameter name (cite serial only)&lt;br /&gt;
	['en ligne le'] = 'archive-date',											-- French&lt;br /&gt;
	['encyclopædia'] = 'encyclopedia',&lt;br /&gt;
	['enlaceautor'] = 'author-link',											-- Spanish&lt;br /&gt;
	['enlaceroto'] = 'url-status',												-- Spanish&lt;br /&gt;
	['episodelink'] = 'episode-link',											-- old parameter name&lt;br /&gt;
	['erişimtarihi'] = 'access-date',											-- Turkish&lt;br /&gt;
	['eser'] = 'work',															-- Turkish&lt;br /&gt;
	['family'] = 'surname',														-- misguess&lt;br /&gt;
	['fecha'] = 'date',															-- Spanish&lt;br /&gt;
	['fechaacceso'] = 'access-date',											-- Spanish&lt;br /&gt;
	['fechaarchivo'] = 'archive-date',											-- Spanish&lt;br /&gt;
	['filetype'] = 'format',&lt;br /&gt;
	['foramt'] = 'format',														-- misspelling&lt;br /&gt;
	['fore-name'] = 'given',													-- misguess/misspelling&lt;br /&gt;
	['forename'] = 'given',														-- misguess&lt;br /&gt;
	['formato'] = 'format',														-- Spanish, Italian, Polish&lt;br /&gt;
	['frist'] = 'first',														-- misspelling&lt;br /&gt;
	['fundstelle'] = 'at',														-- German&lt;br /&gt;
	['gazete'] = 'work',														-- Turkish&lt;br /&gt;
	['giornale'] = 'journal',													-- Italian&lt;br /&gt;
	['herausgeber'] = 'editor',													-- German (singular and plural)&lt;br /&gt;
	['hrsg'] = 'publisher',														-- German&lt;br /&gt;
	['hrsgreihe'] = 'editor',													-- German&lt;br /&gt;
	['idioma'] = 'language',													-- Spanish&lt;br /&gt;
	['ignore-isbn-error'] = 'isbn',												-- old parameter (can be fully emulated using ((syntax)))&lt;br /&gt;
	['ignoreisbnerror'] = 'isbn',												-- old parameter alias (suggest |isbn as |ignore-isbn-error is deprecated), not a direct replacement, but can be fully emulated using ((syntax))&lt;br /&gt;
	['imię'] = 'first',															-- Polish&lt;br /&gt;
	['in-set'] = 'inset',														-- misspelling&lt;br /&gt;
	['interviewerlink'] = 'interviewer-link',									-- old parameter alias&lt;br /&gt;
	['interviewermask'] = 'interviewer-mask',									-- old parameter alias&lt;br /&gt;
	['isbndefekt'] = 'isbn',													-- German (suggest |isbn as |ignore-isbn-error is deprecated), not a direct replacement, but can be fully emulated using ((syntax))&lt;br /&gt;
	['isbnformalfalsch'] = 'isbn',												-- German (suggest |isbn as |ignore-isbn-error is deprecated), not a direct replacement, but can be fully emulated using ((syntax))&lt;br /&gt;
	['isbnistformalfalsch'] = 'isbn',											-- German (suggest |isbn as |ignore-isbn-error is deprecated), not a direct replacement, but can be fully emulated using ((syntax))&lt;br /&gt;
	['isnb'] = 'isbn',															-- misspelling&lt;br /&gt;
	['issnformalfalsch'] = 'issn',												-- German (can be fully emulated using ((syntax)))&lt;br /&gt;
	['jahr'] = 'date',															-- German&lt;br /&gt;
	['jahrea'] = 'orig-date',													-- German (not a direct replacement, but can be emulated)&lt;br /&gt;
	['kapitel'] = 'chapter',													-- German&lt;br /&gt;
	['langauge'] = 'language',													-- misspelling&lt;br /&gt;
	['langue'] = 'language',													-- French&lt;br /&gt;
	['last-author-amp'] = 'name-list-style',									-- old parameter name (as |name-list-style=amp)&lt;br /&gt;
	['lastauthoramp'] = 'name-list-style',										-- old parameter name (as |name-list-style=amp)&lt;br /&gt;
	['laydate'] = 'lay-date',													-- old parameter name&lt;br /&gt;
	['laysource'] = 'lay-source',												-- old parameter name&lt;br /&gt;
	['lay-summary'] = 'lay-url',												-- old parameter&lt;br /&gt;
	['laysummary'] = 'lay-url',													-- old parameter&lt;br /&gt;
	['layurl'] = 'lay-url',														-- old parameter name&lt;br /&gt;
	['lieu'] = 'location',														-- French&lt;br /&gt;
	['lingua'] = 'language',													-- Italian&lt;br /&gt;
	['lire en ligne'] = 'url',													-- French&lt;br /&gt;
	['lizenznummer'] = 'id',													-- German (as 'id=License No. ...')&lt;br /&gt;
&lt;br /&gt;
	['loaction'] = 'location',													-- misspelling&lt;br /&gt;
	['local'] = 'location',														-- Brazilian Portuguese&lt;br /&gt;
	['locatoin'] = 'location',													-- misspelling&lt;br /&gt;
	['lugar'] = 'location',														-- Spanish&lt;br /&gt;
	['mailinglist'] = 'mailing-list',											-- old parameter name&lt;br /&gt;
	['mapurl'] = 'map-url',														-- old parameter name&lt;br /&gt;
	['mes'] = 'date',															-- Spanish (not a direct replacement)&lt;br /&gt;
	['mese'] = 'date',															-- Italian (not a direct replacement)&lt;br /&gt;
	['miejsce'] = 'location',													-- Polish&lt;br /&gt;
	['miesiąc'] = 'date',														-- Polish&lt;br /&gt;
	['mois'] = 'date',															-- French&lt;br /&gt;
	['monat'] = 'date',															-- German&lt;br /&gt;
--	['name'] = 'author',														-- 'name' is often erroneously used for 'title' and 'work' as well, so no suggestion is better than a wrong suggestion&lt;br /&gt;
	['name-list-format'] = 'name-list-style',									-- old parameter name (as |name-list-style=amp)&lt;br /&gt;
	['nazwisko'] = 'last',														-- Polish&lt;br /&gt;
	['nespaper'] = 'newspaper',													-- misspelling&lt;br /&gt;
	['net-work'] = 'network',													-- misspelling&lt;br /&gt;
	['newpaper'] = 'newspaper',													-- misspelling&lt;br /&gt;
	['news'] = 'newspaper',&lt;br /&gt;
	['news-group'] = 'newsgroup',												-- misspelling&lt;br /&gt;
	['news-paper'] = 'newspaper',												-- misspelling&lt;br /&gt;
	['no-cat'] = 'no-tracking',													-- old parameter&lt;br /&gt;
	['nocat'] = 'no-tracking',													-- old parameter&lt;br /&gt;
	['nom'] = 'last',															-- French&lt;br /&gt;
	['nombre'] = 'first',														-- Spanish&lt;br /&gt;
	['nome'] = 'first',															-- Italian&lt;br /&gt;
	['nopp'] = 'no-pp',															-- old parameter name&lt;br /&gt;
	['notracking'] = 'no-tracking',												-- old parameter&lt;br /&gt;
	['numero'] = 'number',														-- Italian, Spanish&lt;br /&gt;
	['nummer'] = 'number',														-- German&lt;br /&gt;
	['nummerreihe'] = 'number',													-- German&lt;br /&gt;
	['obra'] = 'work',															-- Spanish&lt;br /&gt;
	['odpowiedzialność'] = 'agency',											-- Polish&lt;br /&gt;
	['offline'] = 'url-status',													-- German (as 'url-status=dead')&lt;br /&gt;
	['online'] = 'url',															-- German (not a direct replacement, but can be emulated)&lt;br /&gt;
	['opera'] = 'work',															-- Italian&lt;br /&gt;
	['opublikowany'] = 'agency',												-- Polish&lt;br /&gt;
	['origdate'] = 'orig-date',													-- misspelling&lt;br /&gt;
	['originaljahr'] = 'orig-date',												-- German&lt;br /&gt;
	['originalort'] = 'publication-place',										-- German&lt;br /&gt;
	['originalsprache'] = 'language',											-- German&lt;br /&gt;
	['originaltitel'] = 'title',												-- German (if 'originaltitel' is specified, any possible contents of 'title' should be put in 'trans-title')&lt;br /&gt;
	['ort'] = 'publication-place',												-- German&lt;br /&gt;
	['ortea'] = 'publication-place',											-- German (not a direct replacement, but can be emulated)&lt;br /&gt;
	['other'] = 'others',&lt;br /&gt;
	['oznaczenie'] = 'agency',													-- Polish&lt;br /&gt;
	['pagees'] = 'pages',														-- misspelling&lt;br /&gt;
	['pagina'] = 'page',														-- Italian&lt;br /&gt;
	['pagina'] = 'pages',														-- Italian&lt;br /&gt;
	['pagine'] = 'pages',														-- Italian&lt;br /&gt;
	['pagine'] = 'pages',														-- Italian&lt;br /&gt;
	['passage'] = 'pages',														-- French&lt;br /&gt;
	['periodico'] = 'magazine',													-- Spanish&lt;br /&gt;
	['plublisher'] = 'publisher',												-- misspelling&lt;br /&gt;
	['pmcid'] = 'pmc',&lt;br /&gt;
	['post-script'] = 'postscript',												-- misspelling&lt;br /&gt;
	['praca'] = 'work',															-- Polish&lt;br /&gt;
	['primero'] = 'first',														-- Spanish&lt;br /&gt;
	['prénom'] = 'first',														-- French&lt;br /&gt;
	['prénom1'] = 'first1',														-- French&lt;br /&gt;
	['ps'] = 'postscript',&lt;br /&gt;
	['pub'] = 'publisher',&lt;br /&gt;
	-- ['pubblicazione'] = 'magazine',											-- could be any kind of work&lt;br /&gt;
	-- ['publicación'] = 'journal',												-- could be any kind of work&lt;br /&gt;
	['publicationdate'] = 'publication-date',									-- old parameter name&lt;br /&gt;
	-- ['published'] = 'publisher',												-- could be date, location, or name of publisher&lt;br /&gt;
	['publicationplace'] = 'publication-place',									-- old parameter name&lt;br /&gt;
	['pulbication-place'] = 'publication-place',								-- misspelling&lt;br /&gt;
	['página'] = 'page',														-- Spanish&lt;br /&gt;
	['páginas'] = 'pages',														-- Spanish&lt;br /&gt;
	['périodique'] = 'publisher',												-- French&lt;br /&gt;
	['registration'] = 'url-access',											-- old parameter name&lt;br /&gt;
	['reihe'] = 'series',														-- German&lt;br /&gt;
	['retrieved'] = 'access-date',&lt;br /&gt;
	['richiestasottoscrizione'] = 'url-access',									-- Italian (as |url-access=subscription)&lt;br /&gt;
	['rok'] = 'date',															-- Polish&lt;br /&gt;
	['sammelwerk'] = 'work',													-- German&lt;br /&gt;
	['sayfa'] = 'page',															-- Turkish&lt;br /&gt;
	['sayfalar'] = 'pages',														-- Turkish&lt;br /&gt;
	['sayı'] = 'issue',															-- Turkish&lt;br /&gt;
	['script-post'] = 'postscript',												-- misspelling&lt;br /&gt;
	['script-trans'] = 'transcript',											-- misspelling&lt;br /&gt;
	['season'] = 'date',														-- old parameter name (cite serial only)&lt;br /&gt;
	['sectionurl'] = 'section-url',												-- old parameter name&lt;br /&gt;
	['seiten'] = 'pages',														-- German&lt;br /&gt;
	['seria'] = 'series',														-- Spanish, Polish&lt;br /&gt;
	['serie'] = 'series',														-- Italian&lt;br /&gt;
	['série'] = 'series',														-- French&lt;br /&gt;
	['serieslink'] = 'series-link',												-- old parameter name&lt;br /&gt;
	['seriesno'] = 'series-number',												-- old parameter name&lt;br /&gt;
	['service'] = 'agency',&lt;br /&gt;
	['sitioweb'] = 'website',													-- Spanish&lt;br /&gt;
	['sito'] = 'website',														-- Italian&lt;br /&gt;
	['soyadı'] = 'last',														-- Turkish&lt;br /&gt;
	['spalten'] = 'at',															-- German (not a direct replacement, but can be emulated)&lt;br /&gt;
	['sprache'] = 'language',													-- German&lt;br /&gt;
	['stron'] = 'page',															-- Polish&lt;br /&gt;
	['strony'] = 'pages',														-- Polish&lt;br /&gt;
	['subjectlink'] = 'subject-link',											-- old parameter name&lt;br /&gt;
	['subscription'] = 'url-access',											-- old parameter name (emulated as |url-access=subscription)&lt;br /&gt;
	['sur-name'] = 'surname',													-- misspelling&lt;br /&gt;
	['suscripción'] = 'url-access',												-- Spanish, Polish (as |url-access=subscription)&lt;br /&gt;
	['tag'] = 'date',															-- German&lt;br /&gt;
	['tarih'] = 'date',															-- Turkish&lt;br /&gt;
	['template doc demo'] = 'no-tracking',										-- old parameter alias&lt;br /&gt;
	['tile'] = 'title',															-- misspelling&lt;br /&gt;
	['timecaption'] = 'time-caption',											-- old parameter name&lt;br /&gt;
	['titlelink'] = 'title-link',												-- old parameter name&lt;br /&gt;
	['tipo'] = 'type',															-- Italian&lt;br /&gt;
	['tite'] = 'title',															-- misspelling&lt;br /&gt;
	['titel'] = 'title',														-- German&lt;br /&gt;
	['titel-p'] = 'title',														-- German ('postscript=none' should be added as well)&lt;br /&gt;
	['titelerg'] = 'contribution',												-- German (not a direct replacement, but can be emulated)&lt;br /&gt;
	['titled'] = 'title',														-- Brazilian Portuguese&lt;br /&gt;
	['titolo'] = 'title',														-- Italian&lt;br /&gt;
	['titre'] = 'title',														-- French&lt;br /&gt;
	['trans-script'] = 'transcript',											-- misspelling&lt;br /&gt;
	['trans_chapter'] = 'trans-chapter',										-- old parameter alias&lt;br /&gt;
	['trans_title'] = 'trans-title',											-- old parameter alias&lt;br /&gt;
	['transchapter'] = 'trans-chapter',&lt;br /&gt;
	['transcripturl'] = 'transcript-url',										-- old parameter name&lt;br /&gt;
	['transscript'] = 'transcript',												-- misspelling&lt;br /&gt;
	['transscript-format'] = 'transcript-format',								-- misspelling&lt;br /&gt;
	['transscript-url'] = 'transcript-url',										-- misspelling&lt;br /&gt;
	['transscripturl'] = 'transcript-url',										-- misspelling&lt;br /&gt;
	['transtitle'] = 'trans-title',&lt;br /&gt;
	['typ'] = 'author-mask',													-- German (not a direct replacement, but the only valid argument 'typ=wl' can be emulated using 'author-mask')&lt;br /&gt;
	['tytuł'] = 'title',														-- Polish&lt;br /&gt;
	['títle'] = 'title',&lt;br /&gt;
	['título'] = 'title',														-- Spanish&lt;br /&gt;
	['ubicación'] = 'location',													-- Spanish&lt;br /&gt;
	['urlarchivio'] = 'archive-url',											-- Italian&lt;br /&gt;
	['urlarchivo'] = 'archive-url',												-- Spanish&lt;br /&gt;
	['urlmorto'] = 'url-status',												-- Italian&lt;br /&gt;
	['urn'] = 'id',																-- German (as 'id={{URN|...}}')&lt;br /&gt;
	['v-authors'] = 'vauthors',													-- misspelling&lt;br /&gt;
	['v-editors'] = 'veditors',													-- misspelling&lt;br /&gt;
	['verlag'] = 'publisher',													-- German&lt;br /&gt;
	['verlagea'] = 'publisher',													-- German (not a direct replacement, but can be emulated)&lt;br /&gt;
	['vol'] = 'volume',&lt;br /&gt;
	['volumen'] = 'volume',														-- Spanish&lt;br /&gt;
	['werk'] = 'work',															-- German&lt;br /&gt;
	['werkerg'] = 'contribution',												-- German (not a direct replacement, but can be emulated)&lt;br /&gt;
	['wkautore'] = 'author-link',												-- Italian&lt;br /&gt;
	['wolumin'] = 'volume',														-- Polish&lt;br /&gt;
	['wydanie'] = 'number',														-- Polish&lt;br /&gt;
	['wydawca'] = 'publisher',													-- French&lt;br /&gt;
	['yardımcıyazarlar'] = 'author',											-- Turkish&lt;br /&gt;
	['yayımcı'] = 'publisher',													-- Turkish&lt;br /&gt;
	['yayıncı'] = 'publisher',													-- Turkish&lt;br /&gt;
	['yazar'] = 'author',														-- Turkish&lt;br /&gt;
	['yazarbağı'] = 'author-link',												-- Turkish&lt;br /&gt;
	['yer'] = 'location',														-- Turkish&lt;br /&gt;
	['yıl'] = 'date',															-- Turkish&lt;br /&gt;
	['zaprezentowany'] = 'publisher',											-- French&lt;br /&gt;
	['zdb'] = 'id',																-- German (as 'id={{ZDB|...}}')&lt;br /&gt;
	['zitat'] = 'quote',														-- German&lt;br /&gt;
	['zugriff'] = 'access-date',												-- German&lt;br /&gt;
	['éditeur'] = 'editor',														-- French&lt;br /&gt;
	['ölüurl'] = 'url-status',													-- Turkish&lt;br /&gt;
	['übersetzer'] = 'translator',												-- German (singular and plural)&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P A T T E R N S &amp;gt;--------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Here we use Lua patterns to make suggestions.  The form is&lt;br /&gt;
&lt;br /&gt;
	['pattern'] = 'correct',&lt;br /&gt;
&lt;br /&gt;
Lua patterns are not REGEX though they are similar.  The escape character is '%', not '\'.&lt;br /&gt;
For more information about Lua patterns, see: Extension:Scribunto/Lua_reference_manual#Patterns&lt;br /&gt;
&lt;br /&gt;
Patterns should probably always include the '^' and '$' anchor assertions to prevent a partial&lt;br /&gt;
match from incorrectly suggesting the wrong parameter name.  For instance, the pattern 'a[utho]+r'&lt;br /&gt;
matches 'author' in the no-longer-supported parameter |author-separator= so the code suggests&lt;br /&gt;
'|author='; the same pattern also matches the no-longer-supported parameter |separator= (returning 'ator')&lt;br /&gt;
so again, the code suggests '|author='.&lt;br /&gt;
&lt;br /&gt;
One capture is supported, typically the enumerator from an enumerated parameter (the '6' in |author6=, etc.)&lt;br /&gt;
The value from the capture replaces $1 in the 'correct' value.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local patterns = {&lt;br /&gt;
	['^ac+es+ ?d?a?t?e?$'] = 'access-date',										-- misspelling&lt;br /&gt;
	['^apellido[s]?(%d+)$'] = 'last$1',											-- Spanish, enumerated&lt;br /&gt;
	['^a[utho]+r$'] = 'author',													-- misspelling&lt;br /&gt;
	['^a[utho]+r(%d+)$'] = 'author$1',											-- misspelling, enumerated&lt;br /&gt;
	['^author link(%d+)$'] = 'author-link$1',									-- Polish, enumerated&lt;br /&gt;
	['^autor[e]?(%d+)$'] = 'author$1',											-- Italian/Spanish/German, enumerated&lt;br /&gt;
	['^authorfirst(%d+)$'] = 'author-first$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^author(%d+)first$'] = 'author-first$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^authorgiven(%d+)$'] = 'author-given$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^author(%d+)given$'] = 'author-given$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^authorlast(%d+)$'] = 'author-last$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^author(%d+)last$'] = 'author-last$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^authormask(%d+)$'] = 'author-mask$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^author(%d+)mask$'] = 'author-mask$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^authorsurname(%d+)$'] = 'author-surname$1',								-- old parameter name, enumerated&lt;br /&gt;
	['^author(%d+)surname$'] = 'author-surname$1',								-- old parameter name, enumerated&lt;br /&gt;
	['^cognome(%d+)$'] = 'last$1',												-- Italian, enumerated&lt;br /&gt;
	['^editorfirst(%d+)$'] = 'editor-first$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editor(%d+)first$'] = 'editor-first$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editorgiven(%d+)$'] = 'editor-given$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editor(%d+)given$'] = 'editor-given$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editorlast(%d+)$'] = 'editor-last$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editor(%d+)last$'] = 'editor-last$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editorlink(%d+)$'] = 'editor-link$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editor(%d+)link$'] = 'editor-link$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editormask(%d+)$'] = 'editor-mask$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editor(%d+)mask$'] = 'editor-mask$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^editorsurname(%d+)$'] = 'editor-surname$1',								-- old parameter name, enumerated&lt;br /&gt;
	['^editor(%d+)surname$'] = 'editor-surname$1',								-- old parameter name, enumerated&lt;br /&gt;
	['^enlaceautore(%d+)$'] = 'author-link$1',									-- Spanish, enumerated&lt;br /&gt;
	['^first (%d+)$'] = 'first$1',												-- enumerated&lt;br /&gt;
	['^last (%d+)$'] = 'last$1',												-- enumerated&lt;br /&gt;
	['^nom[e]?(%d+)$'] = 'last$1',												-- Italian/French, enumerated&lt;br /&gt;
	['^nombre(%d+)$'] = 'first$1',												-- Spanish, enumerated&lt;br /&gt;
	['^primero(%d+)$'] = 'first$1',												-- Spanish, enumerated&lt;br /&gt;
	['^pu[blish]+ers?$'] = 'publisher',											-- misspelling&lt;br /&gt;
	['^subjectlink(%d+)$'] = 'subject-link$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^subject(%d+)link$'] = 'subject-link$1',									-- old parameter name, enumerated&lt;br /&gt;
	['^wkautore(%d+)$'] = 'author-link$1',										-- Italian, enumerated&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
return {suggestions = suggestions, patterns=patterns};&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/COinS&amp;diff=266</id>
		<title>Module:Citation/CS1/COinS</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/COinS&amp;diff=266"/>
		<updated>2022-04-04T14:06:06Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;----------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------   local has_accept_as_written, is_set, in_array, remove_wiki_...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[--------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local has_accept_as_written, is_set, in_array, remove_wiki_link, strip_apostrophe_markup;	-- functions in Module:Citation/CS1/Utilities&lt;br /&gt;
&lt;br /&gt;
local cfg;																		-- table of configuration tables that are defined in Module:Citation/CS1/Configuration&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; M A K E _ C O I N S _ T I T L E &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Makes a title for COinS from Title and / or ScriptTitle (or any other name-script pairs)&lt;br /&gt;
&lt;br /&gt;
Apostrophe markup (bold, italics) is stripped from each value so that the COinS metadata isn't corrupted with strings&lt;br /&gt;
of %27%27...&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function make_coins_title (title, script)&lt;br /&gt;
	title = has_accept_as_written (title);&lt;br /&gt;
	if is_set (title) then&lt;br /&gt;
		title = strip_apostrophe_markup (title);								-- strip any apostrophe markup&lt;br /&gt;
	else&lt;br /&gt;
		title = '';																-- if not set, make sure title is an empty string&lt;br /&gt;
	end&lt;br /&gt;
	if is_set (script) then&lt;br /&gt;
		script = script:gsub ('^%l%l%s*:%s*', '');								-- remove language prefix if present (script value may now be empty string)&lt;br /&gt;
		script = strip_apostrophe_markup (script);								-- strip any apostrophe markup&lt;br /&gt;
	else&lt;br /&gt;
		script = '';															-- if not set, make sure script is an empty string&lt;br /&gt;
	end&lt;br /&gt;
	if is_set (title) and is_set (script) then&lt;br /&gt;
		script = ' ' .. script;													-- add a space before we concatenate&lt;br /&gt;
	end&lt;br /&gt;
	return title .. script;														-- return the concatenation&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E S C A P E _ L U A _ M A G I C _ C H A R S &amp;gt;----------------------------------&lt;br /&gt;
&lt;br /&gt;
Returns a string where all of Lua's magic characters have been escaped.  This is important because functions like&lt;br /&gt;
string.gsub() treat their pattern and replace strings as patterns, not literal strings.&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function escape_lua_magic_chars (argument)&lt;br /&gt;
	argument = argument:gsub(&amp;quot;%%&amp;quot;, &amp;quot;%%%%&amp;quot;);										-- replace % with %%&lt;br /&gt;
	argument = argument:gsub(&amp;quot;([%^%$%(%)%.%[%]%*%+%-%?])&amp;quot;, &amp;quot;%%%1&amp;quot;);				-- replace all other Lua magic pattern characters&lt;br /&gt;
	return argument;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; G E T _ C O I N S _ P A G E S &amp;gt;------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Extract page numbers from external wikilinks in any of the |page=, |pages=, or |at= parameters for use in COinS.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function get_coins_pages (pages)&lt;br /&gt;
	local pattern;&lt;br /&gt;
	if not is_set (pages) then return pages; end								-- if no page numbers then we're done&lt;br /&gt;
	&lt;br /&gt;
	while true do&lt;br /&gt;
		pattern = pages:match(&amp;quot;%[(%w*:?//[^ ]+%s+)[%w%d].*%]&amp;quot;);					-- pattern is the opening bracket, the URL and following space(s): &amp;quot;[url &amp;quot;&lt;br /&gt;
		if nil == pattern then break; end										-- no more URLs&lt;br /&gt;
		pattern = escape_lua_magic_chars (pattern);								-- pattern is not a literal string; escape Lua's magic pattern characters&lt;br /&gt;
		pages = pages:gsub(pattern, &amp;quot;&amp;quot;);										-- remove as many instances of pattern as possible&lt;br /&gt;
	end&lt;br /&gt;
	pages = pages:gsub(&amp;quot;[%[%]]&amp;quot;, &amp;quot;&amp;quot;);											-- remove the brackets&lt;br /&gt;
	pages = pages:gsub(&amp;quot;–&amp;quot;, &amp;quot;-&amp;quot; );												-- replace endashes with hyphens&lt;br /&gt;
	pages = pages:gsub(&amp;quot;&amp;amp;%w+;&amp;quot;, &amp;quot;-&amp;quot; );											-- and replace HTML entities (&amp;amp;ndash; etc.) with hyphens; do we need to replace numerical entities like &amp;amp;#32; and the like?&lt;br /&gt;
	return pages;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; C O I N S _ R E P L A C E _ M A T H _ S T R I P M A R K E R &amp;gt;------------------&lt;br /&gt;
&lt;br /&gt;
There are three options for math markup rendering that depend on the editor's math preference settings.  These&lt;br /&gt;
settings are at [[Special:Preferences#mw-prefsection-rendering]] and are&lt;br /&gt;
	PNG images&lt;br /&gt;
	TeX source&lt;br /&gt;
	MathML with SVG or PNG fallback&lt;br /&gt;
&lt;br /&gt;
All three are heavy with HTML and CSS which doesn't belong in the metadata.&lt;br /&gt;
&lt;br /&gt;
Without this function, the metadata saved in the raw wikitext contained the rendering determined by the settings&lt;br /&gt;
of the last editor to save the page.&lt;br /&gt;
&lt;br /&gt;
This function gets the rendered form of an equation according to the editor's preference before the page is saved.  It&lt;br /&gt;
then searches the rendering for the text equivalent of the rendered equation and replaces the rendering with that so&lt;br /&gt;
that the page is saved without extraneous HTML/CSS markup and with a reasonably readable text form of the equation.&lt;br /&gt;
&lt;br /&gt;
When a replacement is made, this function returns true and the value with replacement; otherwise false and the initial&lt;br /&gt;
value.  To replace multipe equations it is necessary to call this function from within a loop.&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function coins_replace_math_stripmarker (value)&lt;br /&gt;
	local stripmarker = cfg.stripmarkers['math'];&lt;br /&gt;
	local rendering = value:match (stripmarker);								-- is there a math stripmarker&lt;br /&gt;
&lt;br /&gt;
	if not rendering then														-- when value doesn't have a math stripmarker, abandon this test&lt;br /&gt;
		return false, value;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	rendering = mw.text.unstripNoWiki (rendering);								-- convert stripmarker into rendered value (or nil? ''? when math render error)&lt;br /&gt;
	&lt;br /&gt;
	if rendering:match ('alt=&amp;quot;[^&amp;quot;]+&amp;quot;') then										-- if PNG math option&lt;br /&gt;
		rendering = rendering:match ('alt=&amp;quot;([^&amp;quot;]+)&amp;quot;');							-- extract just the math text&lt;br /&gt;
	elseif rendering:match ('$%s+.+%s+%$') then									-- if TeX math option; $ is legit character that is escapes as \$&lt;br /&gt;
		rendering = rendering:match ('$%s+(.+)%s+%$')							-- extract just the math text&lt;br /&gt;
	elseif rendering:match ('&amp;lt;annotation[^&amp;gt;]+&amp;gt;.+&amp;lt;/annotation&amp;gt;') then			-- if MathML math option&lt;br /&gt;
		rendering = rendering:match ('&amp;lt;annotation[^&amp;gt;]+&amp;gt;(.+)&amp;lt;/annotation&amp;gt;')		-- extract just the math text&lt;br /&gt;
	else&lt;br /&gt;
		return false, value;													-- had math stripmarker but not one of the three defined forms&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return true, value:gsub (stripmarker, rendering, 1);&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C O I N S _ C L E A N U P &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Cleanup parameter values for the metadata by removing or replacing invisible characters and certain HTML entities.&lt;br /&gt;
&lt;br /&gt;
2015-12-10: there is a bug in mw.text.unstripNoWiki ().  It replaces math stripmarkers with the appropriate content&lt;br /&gt;
when it shouldn't.  See https://phabricator.wikimedia.org/T121085 and Wikipedia_talk:Lua#stripmarkers_and_mw.text.unstripNoWiki.28.29&lt;br /&gt;
&lt;br /&gt;
TODO: move the replacement patterns and replacement values into a table in /Configuration similar to the invisible&lt;br /&gt;
characters table?&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function coins_cleanup (value)&lt;br /&gt;
	local replaced = true;														-- default state to get the do loop running&lt;br /&gt;
&lt;br /&gt;
	while replaced do															-- loop until all math stripmarkers replaced&lt;br /&gt;
		replaced, value = coins_replace_math_stripmarker (value);				-- replace math stripmarker with text representation of the equation&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	value = value:gsub (cfg.stripmarkers['math'], &amp;quot;MATH RENDER ERROR&amp;quot;);			-- one or more couldn't be replaced; insert vague error message&lt;br /&gt;
	&lt;br /&gt;
	value = mw.text.unstripNoWiki (value);										-- replace nowiki stripmarkers with their content&lt;br /&gt;
	value = value:gsub ('&amp;lt;span class=&amp;quot;nowrap&amp;quot; style=&amp;quot;padding%-left:0%.1em;&amp;quot;&amp;gt;&amp;amp;#39;(s?)&amp;lt;/span&amp;gt;', &amp;quot;'%1&amp;quot;);	-- replace {{'}} or {{'s}} with simple apostrophe or apostrophe-s&lt;br /&gt;
	value = value:gsub ('&amp;amp;nbsp;', ' ');											-- replace &amp;amp;nbsp; entity with plain space&lt;br /&gt;
	value = value:gsub ('\226\128\138', ' ');									-- replace hair space with plain space&lt;br /&gt;
	if not mw.ustring.find (value, cfg.indic_script) then						-- don't remove zero-width joiner characters from indic script&lt;br /&gt;
		value = value:gsub ('&amp;amp;zwj;', '');										-- remove &amp;amp;zwj; entities&lt;br /&gt;
		value = mw.ustring.gsub (value, '[\226\128\141\226\128\139\194\173]', '');	-- remove zero-width joiner, zero-width space, soft hyphen&lt;br /&gt;
	end&lt;br /&gt;
	value = value:gsub ('[\009\010\013 ]+', ' ');								-- replace horizontal tab, line feed, carriage return with plain space&lt;br /&gt;
	return value;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C O I N S &amp;gt;--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
COinS metadata (see &amp;lt;http://ocoins.info/&amp;gt;) allows automated tools to parse the citation information.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function COinS(data, class)&lt;br /&gt;
	if 'table' ~= type(data) or nil == next(data) then&lt;br /&gt;
		return '';&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for k, v in pairs (data) do													-- spin through all of the metadata parameter values&lt;br /&gt;
		if 'ID_list' ~= k and 'Authors' ~= k then								-- except the ID_list and Author tables (author nowiki stripmarker done when Author table processed)&lt;br /&gt;
			data[k] = coins_cleanup (v);&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ctx_ver = &amp;quot;Z39.88-2004&amp;quot;;&lt;br /&gt;
	&lt;br /&gt;
	-- treat table strictly as an array with only set values.&lt;br /&gt;
	local OCinSoutput = setmetatable( {}, {&lt;br /&gt;
		__newindex = function(self, key, value)&lt;br /&gt;
			if is_set(value) then&lt;br /&gt;
				rawset( self, #self+1, table.concat{ key, '=', mw.uri.encode( remove_wiki_link( value ) ) } );&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	});&lt;br /&gt;
	&lt;br /&gt;
	if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn', 'journal', 'news', 'magazine'}) or (in_array (class, {'conference', 'interview', 'map', 'press release', 'web'}) and is_set(data.Periodical)) or &lt;br /&gt;
		('citation' == class and is_set(data.Periodical) and not is_set (data.Encyclopedia)) then&lt;br /&gt;
			OCinSoutput.rft_val_fmt = &amp;quot;info:ofi/fmt:kev:mtx:journal&amp;quot;;			-- journal metadata identifier&lt;br /&gt;
			if in_array (class, {'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) then	-- set genre according to the type of citation template we are rendering&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.genre&amp;quot;] = &amp;quot;preprint&amp;quot;;							-- cite arxiv, cite biorxiv, cite citeseerx, cite ssrn&lt;br /&gt;
			elseif 'conference' == class then&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.genre&amp;quot;] = &amp;quot;conference&amp;quot;;						-- cite conference (when Periodical set)&lt;br /&gt;
			elseif 'web' == class then&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.genre&amp;quot;] = &amp;quot;unknown&amp;quot;;							-- cite web (when Periodical set)&lt;br /&gt;
			else&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.genre&amp;quot;] = &amp;quot;article&amp;quot;;							-- journal and other 'periodical' articles&lt;br /&gt;
			end&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.jtitle&amp;quot;] = data.Periodical;						-- journal only&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.atitle&amp;quot;] = data.Title;								-- 'periodical' article titles&lt;br /&gt;
&lt;br /&gt;
																				-- these used only for periodicals&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.ssn&amp;quot;] = data.Season;								-- keywords: winter, spring, summer, fall&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.quarter&amp;quot;] = data.Quarter;							-- single digits 1-&amp;gt;first quarter, etc.&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.chron&amp;quot;] = data.Chron;								-- free-form date components&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.volume&amp;quot;] = data.Volume;							-- does not apply to books&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.issue&amp;quot;] = data.Issue;&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.pages&amp;quot;] = data.Pages;								-- also used in book metadata&lt;br /&gt;
&lt;br /&gt;
	elseif 'thesis' ~= class then												-- all others except cite thesis are treated as 'book' metadata; genre distinguishes&lt;br /&gt;
		OCinSoutput.rft_val_fmt = &amp;quot;info:ofi/fmt:kev:mtx:book&amp;quot;;					-- book metadata identifier&lt;br /&gt;
		if 'report' == class or 'techreport' == class then						-- cite report and cite techreport&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.genre&amp;quot;] = &amp;quot;report&amp;quot;;&lt;br /&gt;
		elseif 'conference' == class then										-- cite conference when Periodical not set&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.genre&amp;quot;] = &amp;quot;conference&amp;quot;;&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.atitle&amp;quot;] = data.Chapter;							-- conference paper as chapter in proceedings (book)&lt;br /&gt;
		elseif in_array (class, {'book', 'citation', 'encyclopaedia', 'interview', 'map'}) then&lt;br /&gt;
			if is_set (data.Chapter) then&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.genre&amp;quot;] = &amp;quot;bookitem&amp;quot;;&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.atitle&amp;quot;] = data.Chapter;						-- book chapter, encyclopedia article, interview in a book, or map title&lt;br /&gt;
			else&lt;br /&gt;
				if 'map' == class or 'interview' == class then&lt;br /&gt;
					OCinSoutput[&amp;quot;rft.genre&amp;quot;] = 'unknown';						-- standalone map or interview&lt;br /&gt;
				else&lt;br /&gt;
					OCinSoutput[&amp;quot;rft.genre&amp;quot;] = 'book';							-- book and encyclopedia&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		else	-- {'audio-visual', 'AV-media-notes', 'DVD-notes', 'episode', 'interview', 'mailinglist', 'map', 'newsgroup', 'podcast', 'press release', 'serial', 'sign', 'speech', 'web'}&lt;br /&gt;
			OCinSoutput[&amp;quot;rft.genre&amp;quot;] = &amp;quot;unknown&amp;quot;;&lt;br /&gt;
		end&lt;br /&gt;
		OCinSoutput[&amp;quot;rft.btitle&amp;quot;] = data.Title;									-- book only&lt;br /&gt;
		OCinSoutput[&amp;quot;rft.place&amp;quot;] = data.PublicationPlace;						-- book only&lt;br /&gt;
		OCinSoutput[&amp;quot;rft.series&amp;quot;] = data.Series;								-- book only&lt;br /&gt;
		OCinSoutput[&amp;quot;rft.pages&amp;quot;] = data.Pages;									-- book, journal&lt;br /&gt;
		OCinSoutput[&amp;quot;rft.edition&amp;quot;] = data.Edition;								-- book only&lt;br /&gt;
		OCinSoutput[&amp;quot;rft.pub&amp;quot;] = data.PublisherName;							-- book and dissertation&lt;br /&gt;
		&lt;br /&gt;
	else																		-- cite thesis&lt;br /&gt;
		OCinSoutput.rft_val_fmt = &amp;quot;info:ofi/fmt:kev:mtx:dissertation&amp;quot;;			-- dissertation metadata identifier&lt;br /&gt;
		OCinSoutput[&amp;quot;rft.title&amp;quot;] = data.Title;									-- dissertation (also patent but that is not yet supported)&lt;br /&gt;
		OCinSoutput[&amp;quot;rft.degree&amp;quot;] = data.Degree;								-- dissertation only&lt;br /&gt;
		OCinSoutput['rft.inst'] = data.PublisherName;							-- book and dissertation&lt;br /&gt;
	end&lt;br /&gt;
	-- NB. Not currently supported are &amp;quot;info:ofi/fmt:kev:mtx:patent&amp;quot;, &amp;quot;info:ofi/fmt:kev:mtx:dc&amp;quot;, &amp;quot;info:ofi/fmt:kev:mtx:sch_svc&amp;quot;, &amp;quot;info:ofi/fmt:kev:mtx:ctx&amp;quot;&lt;br /&gt;
																				-- and now common parameters (as much as possible)&lt;br /&gt;
	OCinSoutput[&amp;quot;rft.date&amp;quot;] = data.Date;										-- book, journal, dissertation&lt;br /&gt;
&lt;br /&gt;
	for k, v in pairs( data.ID_list ) do										-- what to do about these? For now assume that they are common to all?&lt;br /&gt;
		if k == 'ISBN' then v = v:gsub( &amp;quot;[^-0-9X]&amp;quot;, &amp;quot;&amp;quot; ); end&lt;br /&gt;
		local id = cfg.id_handlers[k].COinS;&lt;br /&gt;
		if string.sub( id or &amp;quot;&amp;quot;, 1, 4 ) == 'info' then							-- for ids that are in the info:registry&lt;br /&gt;
			OCinSoutput[&amp;quot;rft_id&amp;quot;] = table.concat{ id, &amp;quot;/&amp;quot;, v };&lt;br /&gt;
		elseif string.sub (id or &amp;quot;&amp;quot;, 1, 3 ) == 'rft' then						-- for isbn, issn, eissn, etc. that have defined COinS keywords&lt;br /&gt;
			OCinSoutput[ id ] = v;&lt;br /&gt;
		elseif 'url' == id then													-- for urls that are assembled in ~/Identifiers; |asin= and |ol=&lt;br /&gt;
			OCinSoutput[&amp;quot;rft_id&amp;quot;] = table.concat ({data.ID_list[k], &amp;quot;#id-name=&amp;quot;, cfg.id_handlers[k].label});&lt;br /&gt;
		elseif id then															-- when cfg.id_handlers[k].COinS is not nil so urls created here&lt;br /&gt;
			OCinSoutput[&amp;quot;rft_id&amp;quot;] = table.concat{ cfg.id_handlers[k].prefix, v, cfg.id_handlers[k].suffix or '', &amp;quot;#id-name=&amp;quot;, cfg.id_handlers[k].label };	-- others; provide a URL and indicate identifier name as #fragment (human-readable, but transparent to browsers)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local last, first;&lt;br /&gt;
	for k, v in ipairs( data.Authors ) do&lt;br /&gt;
		last, first = coins_cleanup (v.last), coins_cleanup (v.first or '');	-- replace any nowiki stripmarkers, non-printing or invisible characters&lt;br /&gt;
		if k == 1 then															-- for the first author name only&lt;br /&gt;
			if is_set(last) and is_set(first) then								-- set these COinS values if |first= and |last= specify the first author name&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.aulast&amp;quot;] = last;								-- book, journal, dissertation&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.aufirst&amp;quot;] = first;								-- book, journal, dissertation&lt;br /&gt;
			elseif is_set(last) then &lt;br /&gt;
				OCinSoutput[&amp;quot;rft.au&amp;quot;] = last;									-- book, journal, dissertation -- otherwise use this form for the first name&lt;br /&gt;
			end&lt;br /&gt;
		else																	-- for all other authors&lt;br /&gt;
			if is_set(last) and is_set(first) then&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.au&amp;quot;] = table.concat{ last, &amp;quot;, &amp;quot;, first };		-- book, journal, dissertation&lt;br /&gt;
			elseif is_set(last) then&lt;br /&gt;
				OCinSoutput[&amp;quot;rft.au&amp;quot;] = last;									-- book, journal, dissertation&lt;br /&gt;
			end&lt;br /&gt;
			-- TODO: At present we do not report &amp;quot;et al.&amp;quot;. Add anything special if this condition applies?&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	OCinSoutput.rft_id = data.URL;&lt;br /&gt;
	OCinSoutput.rfr_id = table.concat{ &amp;quot;info:sid/&amp;quot;, mw.site.server:match( &amp;quot;[^/]*$&amp;quot; ), &amp;quot;:&amp;quot;, data.RawPage };&lt;br /&gt;
&lt;br /&gt;
	-- TODO: Add optional extra info:&lt;br /&gt;
	-- rfr_dat=#REVISION&amp;lt;version&amp;gt; (referrer private data)&lt;br /&gt;
	-- ctx_id=&amp;lt;data.RawPage&amp;gt;#&amp;lt;ref&amp;gt; (identifier for the context object)&lt;br /&gt;
	-- ctx_tim=&amp;lt;ts&amp;gt; (timestamp in format yyyy-mm-ddThh:mm:ssTZD or yyyy-mm-dd)&lt;br /&gt;
	-- ctx_enc=info:ofi/enc:UTF-8 (character encoding)&lt;br /&gt;
	&lt;br /&gt;
	OCinSoutput = setmetatable( OCinSoutput, nil );&lt;br /&gt;
&lt;br /&gt;
	-- sort with version string always first, and combine.&lt;br /&gt;
	-- table.sort( OCinSoutput );&lt;br /&gt;
	table.insert( OCinSoutput, 1, &amp;quot;ctx_ver=&amp;quot; .. ctx_ver ); -- such as &amp;quot;Z39.88-2004&amp;quot;&lt;br /&gt;
	return table.concat(OCinSoutput, &amp;quot;&amp;amp;&amp;quot;);&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E T _ S E L E C T E D _ M O D U L E S &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)&lt;br /&gt;
	cfg = cfg_table_ptr;&lt;br /&gt;
&lt;br /&gt;
	has_accept_as_written = utilities_page_ptr.has_accept_as_written;			-- import functions from selected Module:Citation/CS1/Utilities module&lt;br /&gt;
	is_set = utilities_page_ptr.is_set;&lt;br /&gt;
	in_array = utilities_page_ptr.in_array;&lt;br /&gt;
	remove_wiki_link = utilities_page_ptr.remove_wiki_link;&lt;br /&gt;
	strip_apostrophe_markup = utilities_page_ptr.strip_apostrophe_markup;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X P O R T E D   F U N C T I O N S &amp;gt;------------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
	make_coins_title = make_coins_title,&lt;br /&gt;
	get_coins_pages = get_coins_pages,&lt;br /&gt;
	COinS = COinS,&lt;br /&gt;
	set_selected_modules = set_selected_modules,&lt;br /&gt;
	}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Utilities&amp;diff=265</id>
		<title>Module:Citation/CS1/Utilities</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Utilities&amp;diff=265"/>
		<updated>2022-04-04T14:05:39Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;local z = { 	error_cats_t = {};															-- for categorizing citations that contain errors 	error_ids_t = {};															-- list of error identifiers; used to prevent...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local z = {&lt;br /&gt;
	error_cats_t = {};															-- for categorizing citations that contain errors&lt;br /&gt;
	error_ids_t = {};															-- list of error identifiers; used to prevent duplication of certain errors; local to this module&lt;br /&gt;
	error_msgs_t = {};															-- sequence table of error messages&lt;br /&gt;
	maint_cats_t = {};															-- for categorizing citations that aren't erroneous per se, but could use a little work&lt;br /&gt;
	prop_cats_t = {};															-- for categorizing citations based on certain properties, language of source for instance&lt;br /&gt;
	prop_keys_t = {};															-- for adding classes to the citation's &amp;lt;cite&amp;gt; tag&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local cfg;																		-- table of tables imported from selected Module:Citation/CS1/Configuration&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ S E T &amp;gt;------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_set (var)&lt;br /&gt;
	return not (var == nil or var == '');&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I N _ A R R A Y &amp;gt;--------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Whether needle is in haystack&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function in_array (needle, haystack)&lt;br /&gt;
	if needle == nil then&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
	for n, v in ipairs (haystack) do&lt;br /&gt;
		if v == needle then&lt;br /&gt;
			return n;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return false;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; H A S _ A C C E P T _ A S _ W R I T T E N &amp;gt;------------------------------------&lt;br /&gt;
&lt;br /&gt;
When &amp;lt;str&amp;gt; is wholly wrapped in accept-as-written markup, return &amp;lt;str&amp;gt; without markup and true; return &amp;lt;str&amp;gt; and false else&lt;br /&gt;
&lt;br /&gt;
with allow_empty = false, &amp;lt;str&amp;gt; must have at least one character inside the markup&lt;br /&gt;
with allow_empty = true, &amp;lt;str&amp;gt; the markup frame can be empty like (()) to distinguish an empty template parameter from the specific condition &amp;quot;has no applicable value&amp;quot; in citation-context.&lt;br /&gt;
&lt;br /&gt;
After further evaluation the two cases might be merged at a later stage, but should be kept separated for now.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function has_accept_as_written (str, allow_empty)&lt;br /&gt;
	if not is_set (str) then&lt;br /&gt;
		return str, false;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local count;&lt;br /&gt;
&lt;br /&gt;
	if true == allow_empty then&lt;br /&gt;
		str, count = str:gsub ('^%(%((.*)%)%)$', '%1'); 						-- allows (()) to be an empty set&lt;br /&gt;
	else&lt;br /&gt;
		str, count = str:gsub ('^%(%((.+)%)%)$', '%1');&lt;br /&gt;
	end&lt;br /&gt;
	return str, 0 ~= count;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S U B S T I T U T E &amp;gt;----------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Populates numbered arguments in a message string using an argument table. &amp;lt;args&amp;gt; may be a single string or a&lt;br /&gt;
sequence table of multiple strings.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function substitute (msg, args)&lt;br /&gt;
	return args and mw.message.newRawMessage (msg, args):plain() or msg;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E R R O R _ C O M M E N T &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Wraps error messages with CSS markup according to the state of hidden. &amp;lt;content&amp;gt; may be a single string or a&lt;br /&gt;
sequence table of multiple strings.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function error_comment (content, hidden)&lt;br /&gt;
	return substitute (hidden and cfg.presentation['hidden-error'] or cfg.presentation['visible-error'], content);&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; H Y P H E N _ T O _ D A S H &amp;gt;--------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Converts a hyphen to a dash under certain conditions.  The hyphen must separate&lt;br /&gt;
like items; unlike items are returned unmodified.  These forms are modified:&lt;br /&gt;
	letter - letter (A - B)&lt;br /&gt;
	digit - digit (4-5)&lt;br /&gt;
	digit separator digit - digit separator digit (4.1-4.5 or 4-1-4-5)&lt;br /&gt;
	letterdigit - letterdigit (A1-A5) (an optional separator between letter and&lt;br /&gt;
		digit is supported – a.1-a.5 or a-1-a-5)&lt;br /&gt;
	digitletter - digitletter (5a - 5d) (an optional separator between letter and&lt;br /&gt;
		digit is supported – 5.a-5.d or 5-a-5-d)&lt;br /&gt;
&lt;br /&gt;
any other forms are returned unmodified.&lt;br /&gt;
&lt;br /&gt;
str may be a comma- or semicolon-separated list&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function hyphen_to_dash (str)&lt;br /&gt;
	if not is_set (str) then&lt;br /&gt;
		return str;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local accept;																-- boolean&lt;br /&gt;
&lt;br /&gt;
	str = str:gsub (&amp;quot;(%(%(.-%)%))&amp;quot;, function(m) return m:gsub(&amp;quot;,&amp;quot;, &amp;quot;，&amp;quot;):gsub(&amp;quot;;&amp;quot;, &amp;quot;；&amp;quot;) end) -- replace commas and semicolons in accept-as-written markup with similar unicode characters so they'll be ignored during the split	&lt;br /&gt;
	str = str:gsub ('&amp;amp;[nm]dash;', {['&amp;amp;ndash;'] = '–', ['&amp;amp;mdash;'] = '—'});		-- replace &amp;amp;mdash; and &amp;amp;ndash; entities with their characters; semicolon mucks up the text.split&lt;br /&gt;
	str = str:gsub ('&amp;amp;#45;', '-'); -- replace HTML numeric entity with hyphen character&lt;br /&gt;
	str = str:gsub ('&amp;amp;nbsp;', ' '); -- replace &amp;amp;nbsp; entity with generic keyboard space character&lt;br /&gt;
	&lt;br /&gt;
	local out = {};&lt;br /&gt;
	local list = mw.text.split (str, '%s*[,;]%s*');								-- split str at comma or semicolon separators if there are any&lt;br /&gt;
&lt;br /&gt;
	for _, item in ipairs (list) do												-- for each item in the list&lt;br /&gt;
		item, accept = has_accept_as_written (item);							-- remove accept-this-as-written markup when it wraps all of item&lt;br /&gt;
		if not accept and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then	-- if a hyphenated range or has endash or emdash separators&lt;br /&gt;
			if item:match ('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$') or			-- letterdigit hyphen letterdigit (optional separator between letter and digit)&lt;br /&gt;
				item:match ('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$') or			-- digitletter hyphen digitletter (optional separator between digit and letter)&lt;br /&gt;
				item:match ('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$') or			-- digit separator digit hyphen digit separator digit&lt;br /&gt;
				item:match ('^%d+%s*%-%s*%d+$') or								-- digit hyphen digit&lt;br /&gt;
				item:match ('^%a+%s*%-%s*%a+$') then							-- letter hyphen letter&lt;br /&gt;
					item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2');	-- replace hyphen, remove extraneous space characters&lt;br /&gt;
			else&lt;br /&gt;
				item = mw.ustring.gsub (item, '%s*[–—]%s*', '–');				-- for endash or emdash separated ranges, replace em with en, remove extraneous whitespace&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		table.insert (out, item);												-- add the (possibly modified) item to the output table&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local temp_str = '';														-- concatenate the output table into a comma separated string&lt;br /&gt;
	temp_str, accept = has_accept_as_written (table.concat (out, ', '));		-- remove accept-this-as-written markup when it wraps all of concatenated out&lt;br /&gt;
	if accept then&lt;br /&gt;
		temp_str = has_accept_as_written (str);									-- when global markup removed, return original str; do it this way to suppress boolean second return value&lt;br /&gt;
		return temp_str:gsub(&amp;quot;，&amp;quot;, &amp;quot;,&amp;quot;):gsub(&amp;quot;；&amp;quot;, &amp;quot;;&amp;quot;);&lt;br /&gt;
	else&lt;br /&gt;
		return temp_str:gsub(&amp;quot;，&amp;quot;, &amp;quot;,&amp;quot;):gsub(&amp;quot;；&amp;quot;, &amp;quot;;&amp;quot;);						-- else, return assembled temp_str&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; M A K E _ W I K I L I N K &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Makes a wikilink; when both link and display text is provided, returns a wikilink in the form [[L|D]]; if only&lt;br /&gt;
link is provided (or link and display are the same), returns a wikilink in the form [[L]]; if neither are&lt;br /&gt;
provided or link is omitted, returns an empty string.&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function make_wikilink (link, display)&lt;br /&gt;
	if not is_set (link) then return '' end&lt;br /&gt;
&lt;br /&gt;
	if is_set (display) and link ~= display then			&lt;br /&gt;
		return table.concat ({'[[', link, '|', display, ']]'});			&lt;br /&gt;
	else&lt;br /&gt;
		return table.concat ({'[[', link, ']]'});&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E T _ M E S S A G E &amp;gt;----------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Sets an error message using the ~/Configuration error_conditions{} table along with arguments supplied in the function&lt;br /&gt;
call, inserts the resulting message in z.error_msgs_t{} sequence table, and returns the error message.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;error_id&amp;gt; – key value for appropriate error handler in ~/Configuration error_conditions{} table &lt;br /&gt;
&amp;lt;arguments&amp;gt; – may be a single string or a sequence table of multiple strings to be subsititued into error_conditions[error_id].message&lt;br /&gt;
&amp;lt;raw&amp;gt; – boolean&lt;br /&gt;
	true –	causes this function to return the error message not wrapped in visible-error, hidden-error span tag;&lt;br /&gt;
			returns error_conditions[error_id].hidden as a second return value&lt;br /&gt;
			does not add message to z.error_msgs_t sequence table&lt;br /&gt;
	false, nil – adds message wrapped in visible-error, hidden-error span tag to z.error_msgs_t&lt;br /&gt;
			returns the error message wrapped in visible-error, hidden-error span tag; there is no second return value&lt;br /&gt;
&amp;lt;prefix&amp;gt; – string to be prepended to &amp;lt;message&amp;gt;									-- TODO: remove support for these unused(?) arguments?&lt;br /&gt;
&amp;lt;suffix&amp;gt; – string to be appended to &amp;lt;message&amp;gt;&lt;br /&gt;
&lt;br /&gt;
TODO: change z.error_cats_t and z.maint_cats_t to have the form cat_name = true?  this to avoid dups without having to have an extra table&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local added_maint_cats = {}														-- list of maintenance categories that have been added to z.maint_cats_t; TODO: figure out how to delete this table&lt;br /&gt;
&lt;br /&gt;
local function set_message (error_id, arguments, raw, prefix, suffix)&lt;br /&gt;
	local error_state = cfg.error_conditions[error_id];&lt;br /&gt;
	&lt;br /&gt;
	prefix = prefix or '';&lt;br /&gt;
	suffix = suffix or '';&lt;br /&gt;
	&lt;br /&gt;
	if error_state == nil then&lt;br /&gt;
		error (cfg.messages['undefined_error'] .. ': ' .. error_id);			-- because missing error handler in Module:Citation/CS1/Configuration&lt;br /&gt;
&lt;br /&gt;
	elseif is_set (error_state.category) then&lt;br /&gt;
		if error_state.message then												-- when error_state.message defined, this is an error message&lt;br /&gt;
			table.insert (z.error_cats_t, error_state.category);&lt;br /&gt;
		else&lt;br /&gt;
			if not added_maint_cats[error_id] then&lt;br /&gt;
				added_maint_cats[error_id] = true;								-- note that we've added this category&lt;br /&gt;
				table.insert (z.maint_cats_t, substitute (error_state.category, arguments));	-- make cat name then add to table&lt;br /&gt;
			end&lt;br /&gt;
			return;																-- because no message, nothing more to do&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local message = substitute (error_state.message, arguments);&lt;br /&gt;
&lt;br /&gt;
	message = table.concat (&lt;br /&gt;
		{&lt;br /&gt;
		message,&lt;br /&gt;
		' (',&lt;br /&gt;
		make_wikilink (&lt;br /&gt;
			table.concat (&lt;br /&gt;
				{&lt;br /&gt;
				cfg.messages['help page link'],&lt;br /&gt;
				'#',&lt;br /&gt;
				error_state.anchor&lt;br /&gt;
				}),&lt;br /&gt;
			cfg.messages['help page label']),&lt;br /&gt;
		')'&lt;br /&gt;
		});&lt;br /&gt;
&lt;br /&gt;
	z.error_ids_t[error_id] = true;&lt;br /&gt;
	if z.error_ids_t['err_citation_missing_title'] and							-- if missing-title error already noted&lt;br /&gt;
		in_array (error_id, {'err_bare_url_missing_title', 'err_trans_missing_title'}) then		-- and this error is one of these&lt;br /&gt;
			return '', false;													-- don't bother because one flavor of missing title is sufficient&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	message = table.concat ({prefix, message, suffix});&lt;br /&gt;
&lt;br /&gt;
	if true == raw then&lt;br /&gt;
		return message, error_state.hidden;										-- return message not wrapped in visible-error, hidden-error span tag&lt;br /&gt;
	end		&lt;br /&gt;
&lt;br /&gt;
	message = error_comment (message, error_state.hidden);						-- wrap message in visible-error, hidden-error span tag&lt;br /&gt;
	table.insert (z.error_msgs_t, message);										-- add it to the messages sequence table&lt;br /&gt;
	return message;																-- and done; return value generally not used but is used as a flag in various functions of ~/Identifiers&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------------&amp;lt; I S _ A L I A S _ U S E D &amp;gt;-----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
This function is used by select_one() to determine if one of a list of alias parameters is in the argument list&lt;br /&gt;
provided by the template.&lt;br /&gt;
&lt;br /&gt;
Input:&lt;br /&gt;
	args – pointer to the arguments table from calling template&lt;br /&gt;
	alias – one of the list of possible aliases in the aliases lists from Module:Citation/CS1/Configuration&lt;br /&gt;
	index – for enumerated parameters, identifies which one&lt;br /&gt;
	enumerated – true/false flag used to choose how enumerated aliases are examined&lt;br /&gt;
	value – value associated with an alias that has previously been selected; nil if not yet selected&lt;br /&gt;
	selected – the alias that has previously been selected; nil if not yet selected&lt;br /&gt;
	error_list – list of aliases that are duplicates of the alias already selected&lt;br /&gt;
&lt;br /&gt;
Returns:&lt;br /&gt;
	value – value associated with alias we selected or that was previously selected or nil if an alias not yet selected&lt;br /&gt;
	selected – the alias we selected or the alias that was previously selected or nil if an alias not yet selected&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_alias_used (args, alias, index, enumerated, value, selected, error_list)&lt;br /&gt;
	if enumerated then															-- is this a test for an enumerated parameters?&lt;br /&gt;
		alias = alias:gsub ('#', index);										-- replace '#' with the value in index&lt;br /&gt;
	else&lt;br /&gt;
		alias = alias:gsub ('#', '');											-- remove '#' if it exists&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if is_set (args[alias]) then												-- alias is in the template's argument list&lt;br /&gt;
		if value ~= nil and selected ~= alias then								-- if we have already selected one of the aliases&lt;br /&gt;
			local skip;&lt;br /&gt;
			for _, v in ipairs (error_list) do									-- spin through the error list to see if we've added this alias&lt;br /&gt;
				if v == alias then&lt;br /&gt;
					skip = true;&lt;br /&gt;
					break;														-- has been added so stop looking &lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if not skip then													-- has not been added so&lt;br /&gt;
				table.insert (error_list, alias);								-- add error alias to the error list&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			value = args[alias];												-- not yet selected an alias, so select this one&lt;br /&gt;
			selected = alias;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return value, selected;														-- return newly selected alias, or previously selected alias&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; A D D _ M A I N T _ C A T &amp;gt;------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Adds a category to z.maint_cats_t using names from the configuration file with additional text if any.&lt;br /&gt;
To prevent duplication, the added_maint_cats table lists the categories by key that have been added to z.maint_cats_t.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function add_maint_cat (key, arguments)&lt;br /&gt;
	if not added_maint_cats [key] then&lt;br /&gt;
		added_maint_cats [key] = true;											-- note that we've added this category&lt;br /&gt;
		table.insert (z.maint_cats_t, substitute (cfg.maint_cats [key], arguments));	-- make name then add to table&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; A D D _ P R O P _ C A T &amp;gt;--------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Adds a category to z.prop_cats_t using names from the configuration file with additional text if any.&lt;br /&gt;
&lt;br /&gt;
foreign_lang_source and foreign_lang_source_2 keys have a language code appended to them so that multiple languages&lt;br /&gt;
may be categorized but multiples of the same language are not categorized.&lt;br /&gt;
&lt;br /&gt;
added_prop_cats is a table declared in page scope variables above&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local added_prop_cats = {};														-- list of property categories that have been added to z.prop_cats_t&lt;br /&gt;
&lt;br /&gt;
local function add_prop_cat (key, arguments, key_modifier)&lt;br /&gt;
	local key_modified = key .. ((key_modifier and key_modifier) or '');		-- modify &amp;lt;key&amp;gt; with &amp;lt;key_modifier&amp;gt; if present and not nil&lt;br /&gt;
	&lt;br /&gt;
	if not added_prop_cats [key_modified] then&lt;br /&gt;
		added_prop_cats [key_modified] = true;									-- note that we've added this category&lt;br /&gt;
		table.insert (z.prop_cats_t, substitute (cfg.prop_cats [key], arguments));	-- make name then add to table&lt;br /&gt;
		table.insert (z.prop_keys_t, 'cs1-prop-' .. key);						-- convert key to class for use in the citation's &amp;lt;cite&amp;gt; tag&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S A F E _ F O R _ I T A L I C S &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Protects a string that will be wrapped in wiki italic markup '' ... ''&lt;br /&gt;
&lt;br /&gt;
Note: We cannot use &amp;lt;i&amp;gt; for italics, as the expected behavior for italics specified by ''...'' in the title is that&lt;br /&gt;
they will be inverted (i.e. unitalicized) in the resulting references.  In addition, &amp;lt;i&amp;gt; and '' tend to interact&lt;br /&gt;
poorly under Mediawiki's HTML tidy.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function safe_for_italics (str)&lt;br /&gt;
	if not is_set (str) then return str end&lt;br /&gt;
&lt;br /&gt;
	if str:sub (1, 1) == &amp;quot;'&amp;quot; then str = &amp;quot;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;quot; .. str; end&lt;br /&gt;
	if str:sub (-1, -1) == &amp;quot;'&amp;quot; then str = str .. &amp;quot;&amp;lt;span&amp;gt;&amp;lt;/span&amp;gt;&amp;quot;; end&lt;br /&gt;
	&lt;br /&gt;
	return str:gsub ('\n', ' ');												-- Remove newlines as they break italics.&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; W R A P _ S T Y L E &amp;gt;----------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Applies styling to various parameters.  Supplied string is wrapped using a message_list configuration taking one&lt;br /&gt;
argument; protects italic styled parameters.  Additional text taken from citation_config.presentation - the reason&lt;br /&gt;
this function is similar to but separate from wrap_msg().&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function wrap_style (key, str)&lt;br /&gt;
	if not is_set (str) then&lt;br /&gt;
		return &amp;quot;&amp;quot;;&lt;br /&gt;
	elseif in_array (key, {'italic-title', 'trans-italic-title'}) then&lt;br /&gt;
		str = safe_for_italics (str);&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return substitute (cfg.presentation[key], {str});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; M A K E _ S E P _ L I S T &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
make a separated list of items using provided separators.&lt;br /&gt;
	&amp;lt;sep_list&amp;gt; - typically '&amp;lt;comma&amp;gt;&amp;lt;space&amp;gt;'&lt;br /&gt;
	&amp;lt;sep_list_pair&amp;gt; - typically '&amp;lt;space&amp;gt;and&amp;lt;space&amp;gt;'&lt;br /&gt;
	&amp;lt;sep_list_end&amp;gt; - typically '&amp;lt;comma&amp;gt;&amp;lt;space&amp;gt;and&amp;lt;space&amp;gt;' or '&amp;lt;comma&amp;gt;&amp;lt;space&amp;gt;&amp;amp;&amp;lt;space&amp;gt;'&lt;br /&gt;
&lt;br /&gt;
defaults to cfg.presentation['sep_list'], cfg.presentation['sep_list_pair'], and cfg.presentation['sep_list_end']&lt;br /&gt;
if &amp;lt;sep_list_end&amp;gt; is specified, &amp;lt;sep_list&amp;gt; and &amp;lt;sep_list_pair&amp;gt; must also be supplied&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function make_sep_list (count, list_seq, sep_list, sep_list_pair, sep_list_end)&lt;br /&gt;
	local list = '';&lt;br /&gt;
&lt;br /&gt;
	if not sep_list then														-- set the defaults&lt;br /&gt;
		sep_list = cfg.presentation['sep_list'];&lt;br /&gt;
		sep_list_pair = cfg.presentation['sep_list_pair'];&lt;br /&gt;
		sep_list_end = cfg.presentation['sep_list_end'];&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if 2 &amp;gt;= count then&lt;br /&gt;
		list = table.concat (list_seq, sep_list_pair);							-- insert separator between two items; returns list_seq[1] then only one item&lt;br /&gt;
	elseif 2 &amp;lt; count then&lt;br /&gt;
		list = table.concat (list_seq, sep_list, 1, count - 1);					-- concatenate all but last item with plain list separator&lt;br /&gt;
		list = table.concat ({list, list_seq[count]}, sep_list_end);			-- concatenate last item onto end of &amp;lt;list&amp;gt; with final separator&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return list;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E L E C T _ O N E &amp;gt;----------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Chooses one matching parameter from a list of parameters to consider.  The list of parameters to consider is just&lt;br /&gt;
names.  For parameters that may be enumerated, the position of the numerator in the parameter name is identified&lt;br /&gt;
by the '#' so |author-last1= and |author1-last= are represented as 'author-last#' and 'author#-last'.&lt;br /&gt;
&lt;br /&gt;
Because enumerated parameter |&amp;lt;param&amp;gt;1= is an alias of |&amp;lt;param&amp;gt;= we must test for both possibilities.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Generates an error if more than one match is present.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function select_one (args, aliases_list, error_condition, index)&lt;br /&gt;
	local value = nil;															-- the value assigned to the selected parameter&lt;br /&gt;
	local selected = '';														-- the name of the parameter we have chosen&lt;br /&gt;
	local error_list = {};&lt;br /&gt;
&lt;br /&gt;
	if index ~= nil then index = tostring(index); end&lt;br /&gt;
&lt;br /&gt;
	for _, alias in ipairs (aliases_list) do									-- for each alias in the aliases list&lt;br /&gt;
		if alias:match ('#') then												-- if this alias can be enumerated&lt;br /&gt;
			if '1' == index then												-- when index is 1 test for enumerated and non-enumerated aliases&lt;br /&gt;
				value, selected = is_alias_used (args, alias, index, false, value, selected, error_list);	-- first test for non-enumerated alias&lt;br /&gt;
			end&lt;br /&gt;
			value, selected = is_alias_used (args, alias, index, true, value, selected, error_list);	-- test for enumerated alias&lt;br /&gt;
		else&lt;br /&gt;
			value, selected = is_alias_used (args, alias, index, false, value, selected, error_list);	-- test for non-enumerated alias&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if #error_list &amp;gt; 0 and 'none' ~= error_condition then						-- for cases where this code is used outside of extract_names()&lt;br /&gt;
		for i, v in ipairs (error_list) do&lt;br /&gt;
			error_list[i] = wrap_style ('parameter', v);&lt;br /&gt;
		end&lt;br /&gt;
		table.insert (error_list, wrap_style ('parameter', selected));&lt;br /&gt;
		set_message (error_condition, {make_sep_list (#error_list, error_list)});&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return value, selected;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; R E M O V E _ W I K I _ L I N K &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Gets the display text from a wikilink like [[A|B]] or [[B]] gives B&lt;br /&gt;
&lt;br /&gt;
The str:gsub() returns either A|B froma [[A|B]] or B from [[B]] or B from B (no wikilink markup).&lt;br /&gt;
&lt;br /&gt;
In l(), l:gsub() removes the link and pipe (if they exist); the second :gsub() trims whitespace from the label&lt;br /&gt;
if str was wrapped in wikilink markup.  Presumably, this is because without wikimarkup in str, there is no match&lt;br /&gt;
in the initial gsub, the replacement function l() doesn't get called.&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function remove_wiki_link (str)&lt;br /&gt;
	return (str:gsub (&amp;quot;%[%[([^%[%]]*)%]%]&amp;quot;, function(l)&lt;br /&gt;
		return l:gsub (&amp;quot;^[^|]*|(.*)$&amp;quot;, &amp;quot;%1&amp;quot; ):gsub (&amp;quot;^%s*(.-)%s*$&amp;quot;, &amp;quot;%1&amp;quot;);&lt;br /&gt;
	end));&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; I S _ W I K I L I N K &amp;gt;--------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Determines if str is a wikilink, extracts, and returns the wikilink type, link text, and display text parts.&lt;br /&gt;
If str is a complex wikilink ([[L|D]]):&lt;br /&gt;
	returns wl_type 2 and D and L from [[L|D]];&lt;br /&gt;
if str is a simple wikilink ([[D]])&lt;br /&gt;
	returns wl_type 1 and D from [[D]] and L as empty string;&lt;br /&gt;
if not a wikilink:&lt;br /&gt;
	returns wl_type 0, str as D, and L as empty string.&lt;br /&gt;
&lt;br /&gt;
trims leading and trailing whitespace and pipes from L and D ([[L|]] and [[|D]] are accepted by MediaWiki and&lt;br /&gt;
treated like [[D]]; while [[|D|]] is not accepted by MediaWiki, here, we accept it and return D without the pipes).&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function is_wikilink (str)&lt;br /&gt;
	local D, L&lt;br /&gt;
	local wl_type = 2;															-- assume that str is a complex wikilink [[L|D]]&lt;br /&gt;
&lt;br /&gt;
	if not str:match ('^%[%[[^%]]+%]%]$') then									-- is str some sort of a wikilink (must have some sort of content)&lt;br /&gt;
		return 0, str, '';														-- not a wikilink; return wl_type as 0, str as D, and empty string as L&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	L, D = str:match ('^%[%[([^|]+)|([^%]]+)%]%]$');							-- get L and D from [[L|D]] &lt;br /&gt;
&lt;br /&gt;
	if not is_set (D) then														-- if no separate display&lt;br /&gt;
		D = str:match ('^%[%[([^%]]*)|*%]%]$');									-- get D from [[D]] or [[D|]]&lt;br /&gt;
		wl_type = 1; &lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	D = mw.text.trim (D, '%s|');												-- trim white space and pipe characters &lt;br /&gt;
	return wl_type, D, L or '';&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S T R I P _ A P O S T R O P H E _ M A R K U P &amp;gt;--------------------------------&lt;br /&gt;
&lt;br /&gt;
Strip wiki italic and bold markup from argument so that it doesn't contaminate COinS metadata.&lt;br /&gt;
This function strips common patterns of apostrophe markup.  We presume that editors who have taken the time to&lt;br /&gt;
markup a title have, as a result, provided valid markup. When they don't, some single apostrophes are left behind.&lt;br /&gt;
&lt;br /&gt;
Returns the argument without wiki markup and a number; the number is more-or-less meaningless except as a flag&lt;br /&gt;
to indicate that markup was replaced; do not rely on it as an indicator of how many of any kind of markup was&lt;br /&gt;
removed; returns the argument and nil when no markup removed&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function strip_apostrophe_markup (argument)&lt;br /&gt;
	if not is_set (argument) then&lt;br /&gt;
		return argument, nil;													-- no argument, nothing to do&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if nil == argument:find ( &amp;quot;''&amp;quot;, 1, true ) then								-- Is there at least one double apostrophe?  If not, exit.&lt;br /&gt;
		return argument, nil;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local flag;&lt;br /&gt;
	while true do&lt;br /&gt;
		if argument:find (&amp;quot;'''''&amp;quot;, 1, true) then								-- bold italic (5)&lt;br /&gt;
			argument, flag = argument:gsub (&amp;quot;%'%'%'%'%'&amp;quot;, &amp;quot;&amp;quot;);					-- remove all instances of it&lt;br /&gt;
		elseif argument:find (&amp;quot;''''&amp;quot;, 1, true) then								-- italic start and end without content (4)&lt;br /&gt;
			argument, flag=argument:gsub (&amp;quot;%'%'%'%'&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
		elseif argument:find (&amp;quot;'''&amp;quot;, 1, true) then								-- bold (3)&lt;br /&gt;
			argument, flag=argument:gsub (&amp;quot;%'%'%'&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
		elseif argument:find (&amp;quot;''&amp;quot;, 1, true) then								-- italic (2)&lt;br /&gt;
			argument, flag = argument:gsub (&amp;quot;%'%'&amp;quot;, &amp;quot;&amp;quot;);&lt;br /&gt;
		else&lt;br /&gt;
			break;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return argument, flag;														-- done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E T _ S E L E C T E D _ M O D U L E S &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
Sets local cfg table to same (live or sandbox) as that used by the other modules.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function set_selected_modules (cfg_table_ptr)&lt;br /&gt;
	cfg = cfg_table_ptr;&lt;br /&gt;
	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X P O R T S &amp;gt;----------------------------------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
	add_maint_cat = add_maint_cat,												-- exported functions&lt;br /&gt;
	add_prop_cat = add_prop_cat,&lt;br /&gt;
	error_comment = error_comment,&lt;br /&gt;
	has_accept_as_written = has_accept_as_written,&lt;br /&gt;
	hyphen_to_dash = hyphen_to_dash,&lt;br /&gt;
	in_array = in_array,&lt;br /&gt;
	is_set = is_set,&lt;br /&gt;
	is_wikilink = is_wikilink,&lt;br /&gt;
	make_sep_list = make_sep_list,&lt;br /&gt;
	make_wikilink = make_wikilink,&lt;br /&gt;
	remove_wiki_link = remove_wiki_link,&lt;br /&gt;
	safe_for_italics = safe_for_italics,&lt;br /&gt;
	select_one = select_one,&lt;br /&gt;
	set_message = set_message,&lt;br /&gt;
	set_selected_modules = set_selected_modules,&lt;br /&gt;
	strip_apostrophe_markup = strip_apostrophe_markup,&lt;br /&gt;
	substitute = substitute,&lt;br /&gt;
	wrap_style = wrap_style,&lt;br /&gt;
&lt;br /&gt;
	z = z,																		-- exported table&lt;br /&gt;
	}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Identifiers&amp;diff=264</id>
		<title>Module:Citation/CS1/Identifiers</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Identifiers&amp;diff=264"/>
		<updated>2022-04-04T14:05:13Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;----------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------   local has_accept_as_written, is_set, in_array, set_message,...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[--------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local has_accept_as_written, is_set, in_array, set_message, select_one,			-- functions in Module:Citation/CS1/Utilities&lt;br /&gt;
		substitute, make_wikilink;&lt;br /&gt;
&lt;br /&gt;
local z;																		-- table of tables defined in Module:Citation/CS1/Utilities&lt;br /&gt;
&lt;br /&gt;
local cfg;																		-- table of configuration tables that are defined in Module:Citation/CS1/Configuration&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P A G E   S C O P E   V A R I A B L E S &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
declare variables here that have page-wide scope that are not brought in from other modules; that are created here and used here&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local auto_link_urls = {};														-- holds identifier URLs for those identifiers that can auto-link |title=&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--============================&amp;lt;&amp;lt; H E L P E R   F U N C T I O N S &amp;gt;&amp;gt;============================================&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; W I K I D A T A _ A R T I C L E _ N A M E _ G E T &amp;gt;----------------------------&lt;br /&gt;
&lt;br /&gt;
as an aid to internationalizing identifier-label wikilinks, gets identifier article names from Wikidata.&lt;br /&gt;
&lt;br /&gt;
returns :&amp;lt;lang code&amp;gt;:&amp;lt;article title&amp;gt; when &amp;lt;q&amp;gt; has an &amp;lt;article title&amp;gt; for &amp;lt;lang code&amp;gt;; nil else&lt;br /&gt;
&lt;br /&gt;
for identifiers that do not have q, returns nil&lt;br /&gt;
&lt;br /&gt;
for wikis that do not have mw.wikibase installed, returns nil&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function wikidata_article_name_get (q)&lt;br /&gt;
	if not is_set (q) or (q and not mw.wikibase) then							-- when no q number or when a q number but mw.wikibase not installed on this wiki&lt;br /&gt;
		return nil;																-- abandon&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local wd_article;&lt;br /&gt;
	local this_wiki_code = cfg.this_wiki_code;									-- Wikipedia subdomain; 'en' for en.wikipedia.org&lt;br /&gt;
&lt;br /&gt;
	wd_article = mw.wikibase.getSitelink (q, this_wiki_code .. 'wiki');			-- fetch article title from WD; nil when no title available at this wiki&lt;br /&gt;
&lt;br /&gt;
	if wd_article then&lt;br /&gt;
		wd_article = table.concat ({':', this_wiki_code, ':', wd_article});		-- interwiki-style link without brackets if taken from WD; leading colon required&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return wd_article;															-- article title from WD; nil else&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; L I N K _ L A B E L _ M A K E &amp;gt;------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
common function to create identifier link label from handler table or from Wikidata&lt;br /&gt;
&lt;br /&gt;
returns the first available of&lt;br /&gt;
	1. redirect from local wiki's handler table (if enabled)&lt;br /&gt;
	2. Wikidata (if there is a Wikidata entry for this identifier in the local wiki's language)&lt;br /&gt;
	3. label specified in the local wiki's handler table&lt;br /&gt;
	&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function link_label_make (handler)&lt;br /&gt;
	local wd_article;&lt;br /&gt;
	&lt;br /&gt;
	if not (cfg.use_identifier_redirects and is_set (handler.redirect)) then	-- redirect has priority so if enabled and available don't fetch from Wikidata because expensive&lt;br /&gt;
		wd_article = wikidata_article_name_get (handler.q);						-- if Wikidata has an article title for this wiki, get it;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return (cfg.use_identifier_redirects and is_set (handler.redirect) and handler.redirect) or wd_article or handler.link;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X T E R N A L _ L I N K _ I D &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Formats a wiki-style external link&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function external_link_id (options)&lt;br /&gt;
	local url_string = options.id;&lt;br /&gt;
	local ext_link;&lt;br /&gt;
	local this_wiki_code = cfg.this_wiki_code;									-- Wikipedia subdomain; 'en' for en.wikipedia.org&lt;br /&gt;
	local wd_article;															-- article title from Wikidata&lt;br /&gt;
	&lt;br /&gt;
	if options.encode == true or options.encode == nil then&lt;br /&gt;
		url_string = mw.uri.encode (url_string, 'PATH');&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if options.auto_link and is_set (options.access) then&lt;br /&gt;
		auto_link_urls[options.auto_link] = table.concat ({options.prefix, url_string, options.suffix});&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	ext_link = mw.ustring.format ('[%s%s%s %s]', options.prefix, url_string, options.suffix or &amp;quot;&amp;quot;, mw.text.nowiki (options.id));&lt;br /&gt;
	if is_set (options.access) then&lt;br /&gt;
		ext_link = substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[options.access].class, cfg.presentation[options.access].title, ext_link});	-- add the free-to-read / paywall lock&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat	({&lt;br /&gt;
		make_wikilink (link_label_make (options), options.label),				-- redirect, Wikidata link, or locally specified link (in that order)&lt;br /&gt;
		options.separator or '&amp;amp;nbsp;',&lt;br /&gt;
		ext_link&lt;br /&gt;
		});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I N T E R N A L _ L I N K _ I D &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Formats a wiki-style internal link&lt;br /&gt;
&lt;br /&gt;
TODO: Does not currently need to support options.access, options.encode, auto-linking and COinS (as in external_link_id),&lt;br /&gt;
but may be needed in the future for :m:Interwiki_map custom-prefixes like :arxiv:, :bibcode:, :DOI:, :hdl:, :ISSN:,&lt;br /&gt;
:JSTOR:, :Openlibrary:, :PMID:, :RFC:.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function internal_link_id (options)&lt;br /&gt;
	local id = mw.ustring.gsub (options.id, '%d', cfg.date_names.local_digits);	-- translate 'local' digits to Western 0-9&lt;br /&gt;
&lt;br /&gt;
	return table.concat (&lt;br /&gt;
		{&lt;br /&gt;
		make_wikilink (link_label_make (options), options.label),				-- wiki-link the identifier label&lt;br /&gt;
		options.separator or '&amp;amp;nbsp;',											-- add the separator&lt;br /&gt;
		make_wikilink (&lt;br /&gt;
			table.concat (&lt;br /&gt;
				{&lt;br /&gt;
				options.prefix,&lt;br /&gt;
				id,																-- translated to Western digits&lt;br /&gt;
				options.suffix or ''&lt;br /&gt;
				}),&lt;br /&gt;
			substitute (cfg.presentation['bdi'], {'', mw.text.nowiki (options.id)})	-- bdi tags to prevent Latin script identifiers from being reversed at RTL language wikis&lt;br /&gt;
			);																	-- nowiki because MediaWiki still has magic links for ISBN and the like; TODO: is it really required?&lt;br /&gt;
		});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ E M B A R G O E D &amp;gt;------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Determines if a PMC identifier's online version is embargoed. Compares the date in |pmc-embargo-date= against&lt;br /&gt;
today's date.  If embargo date is in the future, returns the content of |pmc-embargo-date=; otherwise, returns&lt;br /&gt;
an empty string because the embargo has expired or because |pmc-embargo-date= was not set in this cite.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_embargoed (embargo)&lt;br /&gt;
	if is_set (embargo) then&lt;br /&gt;
		local lang = mw.getContentLanguage();&lt;br /&gt;
		local good1, embargo_date, todays_date;&lt;br /&gt;
		good1, embargo_date = pcall (lang.formatDate, lang, 'U', embargo);&lt;br /&gt;
		todays_date = lang:formatDate ('U');&lt;br /&gt;
	&lt;br /&gt;
		if good1 then															-- if embargo date is a good date&lt;br /&gt;
			if tonumber (embargo_date) &amp;gt;= tonumber (todays_date) then			-- is embargo date is in the future?&lt;br /&gt;
				return embargo;													-- still embargoed&lt;br /&gt;
			else&lt;br /&gt;
				set_message ('maint_pmc_embargo');								-- embargo has expired; add main cat&lt;br /&gt;
				return '';														-- unset because embargo has expired&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return '';																	-- |pmc-embargo-date= not set return empty string&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; I S _ V A L I D _ B I O R X I V _ D A T E &amp;gt;------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns true if:&lt;br /&gt;
	2019-12-11T00:00Z &amp;lt;= biorxiv_date &amp;lt; today + 2 days&lt;br /&gt;
	&lt;br /&gt;
The dated form of biorxiv identifier has a start date of 2019-12-11.  The Unix timestamp for that date is {{#time:U|2019-12-11}} = 1576022400&lt;br /&gt;
&lt;br /&gt;
biorxiv_date is the date provided in those |biorxiv= parameter values that are dated at time 00:00:00 UTC&lt;br /&gt;
today is the current date at time 00:00:00 UTC plus 48 hours&lt;br /&gt;
	if today is 2015-01-01T00:00:00 then&lt;br /&gt;
		adding 24 hours gives 2015-01-02T00:00:00 – one second more than today&lt;br /&gt;
		adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow&lt;br /&gt;
&lt;br /&gt;
This function does not work if it is fed month names for languages other than English.  Wikimedia #time: parser&lt;br /&gt;
apparently doesn't understand non-English date month names. This function will always return false when the date&lt;br /&gt;
contains a non-English month name because good1 is false after the call to lang_object.formatDate().  To get&lt;br /&gt;
around that call this function with date parts and create a YYYY-MM-DD format date.&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_biorxiv_date (y, m, d)&lt;br /&gt;
	local biorxiv_date = table.concat ({y, m, d}, '-');							-- make ymd date&lt;br /&gt;
	local good1, good2;&lt;br /&gt;
	local biorxiv_ts, tomorrow_ts;												-- to hold Unix timestamps representing the dates&lt;br /&gt;
	local lang_object = mw.getContentLanguage();&lt;br /&gt;
&lt;br /&gt;
	good1, biorxiv_ts = pcall (lang_object.formatDate, lang_object, 'U', biorxiv_date);		-- convert biorxiv_date value to Unix timestamp &lt;br /&gt;
	good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' );	-- today midnight + 2 days is one second more than all day tomorrow&lt;br /&gt;
	&lt;br /&gt;
	if good1 and good2 then														-- lang.formatDate() returns a timestamp in the local script which tonumber() may not understand&lt;br /&gt;
		biorxiv_ts = tonumber (biorxiv_ts) or lang_object:parseFormattedNumber (biorxiv_ts);	-- convert to numbers for the comparison;&lt;br /&gt;
		tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts);&lt;br /&gt;
	else&lt;br /&gt;
		return false;															-- one or both failed to convert to Unix timestamp&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return ((1576022400 &amp;lt;= biorxiv_ts) and (biorxiv_ts &amp;lt; tomorrow_ts))			-- 2012-12-11T00:00Z &amp;lt;= biorxiv_date &amp;lt; tomorrow's date&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; IS _ V A L I D _ I S X N &amp;gt;-----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
ISBN-10 and ISSN validator code calculates checksum across all ISBN/ISSN digits including the check digit.&lt;br /&gt;
ISBN-13 is checked in isbn().&lt;br /&gt;
&lt;br /&gt;
If the number is valid the result will be 0. Before calling this function, ISBN/ISSN must be checked for length&lt;br /&gt;
and stripped of dashes, spaces and other non-ISxN characters.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_isxn (isxn_str, len)&lt;br /&gt;
	local temp = 0;&lt;br /&gt;
	isxn_str = { isxn_str:byte(1, len) };										-- make a table of byte values '0' → 0x30 .. '9' → 0x39, 'X' → 0x58&lt;br /&gt;
	len = len + 1;																-- adjust to be a loop counter&lt;br /&gt;
	for i, v in ipairs (isxn_str) do											-- loop through all of the bytes and calculate the checksum&lt;br /&gt;
		if v == string.byte (&amp;quot;X&amp;quot; ) then											-- if checkdigit is X (compares the byte value of 'X' which is 0x58)&lt;br /&gt;
			temp = temp + 10 * (len - i);										-- it represents 10 decimal&lt;br /&gt;
		else&lt;br /&gt;
			temp = temp + tonumber (string.char (v) )*(len-i);&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return temp % 11 == 0;														-- returns true if calculation result is zero&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; IS _ V A L I D _ I S X N _ 1 3 &amp;gt;-----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
ISBN-13 and ISMN validator code calculates checksum across all 13 ISBN/ISMN digits including the check digit.&lt;br /&gt;
If the number is valid, the result will be 0. Before calling this function, ISBN-13/ISMN must be checked for length&lt;br /&gt;
and stripped of dashes, spaces and other non-ISxN-13 characters.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_isxn_13 (isxn_str)&lt;br /&gt;
	local temp=0;&lt;br /&gt;
	&lt;br /&gt;
	isxn_str = { isxn_str:byte(1, 13) };										-- make a table of byte values '0' → 0x30 .. '9' → 0x39&lt;br /&gt;
	for i, v in ipairs (isxn_str) do&lt;br /&gt;
		temp = temp + (3 - 2*(i % 2)) * tonumber (string.char (v) );			-- multiply odd index digits by 1, even index digits by 3 and sum; includes check digit&lt;br /&gt;
	end&lt;br /&gt;
	return temp % 10 == 0;														-- sum modulo 10 is zero when ISBN-13/ISMN is correct&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; N O R M A L I Z E _ L C C N &amp;gt;--------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
LCCN normalization (http://www.loc.gov/marc/lccn-namespace.html#normalization)&lt;br /&gt;
1. Remove all blanks.&lt;br /&gt;
2. If there is a forward slash (/) in the string, remove it, and remove all characters to the right of the forward slash.&lt;br /&gt;
3. If there is a hyphen in the string:&lt;br /&gt;
	a. Remove it.&lt;br /&gt;
	b. Inspect the substring following (to the right of) the (removed) hyphen. Then (and assuming that steps 1 and 2 have been carried out):&lt;br /&gt;
		1. All these characters should be digits, and there should be six or less. (not done in this function)&lt;br /&gt;
		2. If the length of the substring is less than 6, left-fill the substring with zeroes until the length is six.&lt;br /&gt;
&lt;br /&gt;
Returns a normalized LCCN for lccn() to validate.  There is no error checking (step 3.b.1) performed in this function.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function normalize_lccn (lccn)&lt;br /&gt;
	lccn = lccn:gsub (&amp;quot;%s&amp;quot;, &amp;quot;&amp;quot;);												-- 1. strip whitespace&lt;br /&gt;
&lt;br /&gt;
	if nil ~= string.find (lccn, '/') then&lt;br /&gt;
		lccn = lccn:match (&amp;quot;(.-)/&amp;quot;);											-- 2. remove forward slash and all character to the right of it&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local prefix&lt;br /&gt;
	local suffix&lt;br /&gt;
	prefix, suffix = lccn:match (&amp;quot;(.+)%-(.+)&amp;quot;);									-- 3.a remove hyphen by splitting the string into prefix and suffix&lt;br /&gt;
&lt;br /&gt;
	if nil ~= suffix then														-- if there was a hyphen&lt;br /&gt;
		suffix = string.rep(&amp;quot;0&amp;quot;, 6-string.len (suffix)) .. suffix;				-- 3.b.2 left fill the suffix with 0s if suffix length less than 6&lt;br /&gt;
		lccn = prefix..suffix;													-- reassemble the LCCN&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return lccn;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--============================&amp;lt;&amp;lt; I D E N T I F I E R   F U N C T I O N S &amp;gt;&amp;gt;====================================&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; A R X I V &amp;gt;--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
See: http://arxiv.org/help/arxiv_identifier&lt;br /&gt;
&lt;br /&gt;
format and error check arXiv identifier.  There are three valid forms of the identifier:&lt;br /&gt;
the first form, valid only between date codes 9107 and 0703, is:&lt;br /&gt;
	arXiv:&amp;lt;archive&amp;gt;.&amp;lt;class&amp;gt;/&amp;lt;date code&amp;gt;&amp;lt;number&amp;gt;&amp;lt;version&amp;gt;&lt;br /&gt;
where:&lt;br /&gt;
	&amp;lt;archive&amp;gt; is a string of alpha characters - may be hyphenated; no other punctuation&lt;br /&gt;
	&amp;lt;class&amp;gt; is a string of alpha characters - may be hyphenated; no other punctuation; not the same as |class= parameter which is not supported in this form&lt;br /&gt;
	&amp;lt;date code&amp;gt; is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01&lt;br /&gt;
		first digit of YY for this form can only 9 and 0&lt;br /&gt;
	&amp;lt;number&amp;gt; is a three-digit number&lt;br /&gt;
	&amp;lt;version&amp;gt; is a 1 or more digit number preceded with a lowercase v; no spaces (undocumented)&lt;br /&gt;
	&lt;br /&gt;
the second form, valid from April 2007 through December 2014 is:&lt;br /&gt;
	arXiv:&amp;lt;date code&amp;gt;.&amp;lt;number&amp;gt;&amp;lt;version&amp;gt;&lt;br /&gt;
where:&lt;br /&gt;
	&amp;lt;date code&amp;gt; is four digits in the form YYMM where YY is the last two digits of the four-digit year and MM is the month number January = 01&lt;br /&gt;
	&amp;lt;number&amp;gt; is a four-digit number&lt;br /&gt;
	&amp;lt;version&amp;gt; is a 1 or more digit number preceded with a lowercase v; no spaces&lt;br /&gt;
&lt;br /&gt;
the third form, valid from January 2015 is:&lt;br /&gt;
	arXiv:&amp;lt;date code&amp;gt;.&amp;lt;number&amp;gt;&amp;lt;version&amp;gt;&lt;br /&gt;
where:&lt;br /&gt;
	&amp;lt;date code&amp;gt; and &amp;lt;version&amp;gt; are as defined for 0704-1412&lt;br /&gt;
	&amp;lt;number&amp;gt; is a five-digit number&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function arxiv (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local class = options.Class;												-- TODO: lowercase?&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local year, month, version;&lt;br /&gt;
	local err_msg = false;														-- assume no error message&lt;br /&gt;
	local text;																	-- output text&lt;br /&gt;
	&lt;br /&gt;
	if id:match(&amp;quot;^%a[%a%.%-]+/[90]%d[01]%d%d%d%d$&amp;quot;) or id:match(&amp;quot;^%a[%a%.%-]+/[90]%d[01]%d%d%d%dv%d+$&amp;quot;) then	-- test for the 9107-0703 format with or without version&lt;br /&gt;
		year, month = id:match(&amp;quot;^%a[%a%.%-]+/([90]%d)([01]%d)%d%d%d[v%d]*$&amp;quot;);&lt;br /&gt;
		year = tonumber (year);&lt;br /&gt;
		month = tonumber (month);&lt;br /&gt;
		if ((not (90 &amp;lt; year or 8 &amp;gt; year)) or (1 &amp;gt; month or 12 &amp;lt; month)) or		-- if invalid year or invalid month&lt;br /&gt;
			((91 == year and 7 &amp;gt; month) or (7 == year and 3 &amp;lt; month)) then		-- if years ok, are starting and ending months ok?&lt;br /&gt;
				err_msg = true;													-- flag for error message&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	elseif id:match(&amp;quot;^%d%d[01]%d%.%d%d%d%d$&amp;quot;) or id:match(&amp;quot;^%d%d[01]%d%.%d%d%d%dv%d+$&amp;quot;) then	-- test for the 0704-1412 with or without version&lt;br /&gt;
		year, month = id:match(&amp;quot;^(%d%d)([01]%d)%.%d%d%d%d[v%d]*$&amp;quot;);&lt;br /&gt;
		year = tonumber (year);&lt;br /&gt;
		month = tonumber (month);&lt;br /&gt;
		if ((7 &amp;gt; year) or (14 &amp;lt; year) or (1 &amp;gt; month or 12 &amp;lt; month)) or			-- is year invalid or is month invalid? (doesn't test for future years)&lt;br /&gt;
			((7 == year) and (4 &amp;gt; month)) then									-- when year is 07, is month invalid (before April)?&lt;br /&gt;
				err_msg = true;													-- flag for error message&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	elseif id:match(&amp;quot;^%d%d[01]%d%.%d%d%d%d%d$&amp;quot;) or id:match(&amp;quot;^%d%d[01]%d%.%d%d%d%d%dv%d+$&amp;quot;) then	-- test for the 1501- format with or without version&lt;br /&gt;
		year, month = id:match(&amp;quot;^(%d%d)([01]%d)%.%d%d%d%d%d[v%d]*$&amp;quot;);&lt;br /&gt;
		year = tonumber (year);&lt;br /&gt;
		month = tonumber (month);&lt;br /&gt;
		if ((15 &amp;gt; year) or (1 &amp;gt; month or 12 &amp;lt; month)) then						-- is year invalid or is month invalid? (doesn't test for future years)&lt;br /&gt;
			err_msg = true;														-- flag for error message&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	else&lt;br /&gt;
		err_msg = true;															-- not a recognized format; flag for error message&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if err_msg then&lt;br /&gt;
		options.coins_list_t['ARXIV'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local err_msg_t = {};&lt;br /&gt;
	if err_msg then&lt;br /&gt;
		set_message ('err_bad_arxiv');&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access});&lt;br /&gt;
&lt;br /&gt;
	if is_set (class) then&lt;br /&gt;
		if id:match ('^%d+') then&lt;br /&gt;
			text = table.concat ({text, ' [[//arxiv.org/archive/', class, ' ', class, ']]'});	-- external link within square brackets, not wikilink&lt;br /&gt;
		else&lt;br /&gt;
			set_message ('err_class_ignored');&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; B I B C O D E &amp;gt;--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Validates (sort of) and formats a bibcode ID.&lt;br /&gt;
&lt;br /&gt;
Format for bibcodes is specified here: http://adsabs.harvard.edu/abs_doc/help_pages/data.html#bibcodes&lt;br /&gt;
&lt;br /&gt;
But, this: 2015arXiv151206696F is apparently valid so apparently, the only things that really matter are length, 19 characters&lt;br /&gt;
and first four digits must be a year.  This function makes these tests:&lt;br /&gt;
	length must be 19 characters&lt;br /&gt;
	characters in position&lt;br /&gt;
		1–4 must be digits and must represent a year in the range of 1000 – next year&lt;br /&gt;
		5 must be a letter&lt;br /&gt;
		6–8 must be letter, digit, ampersand, or dot (ampersand cannot directly precede a dot; &amp;amp;. )&lt;br /&gt;
		9–18 must be letter, digit, or dot&lt;br /&gt;
		19 must be a letter or dot&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function bibcode (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local access = options.access;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local err_type;&lt;br /&gt;
	local err_msg = '';&lt;br /&gt;
	local year;&lt;br /&gt;
&lt;br /&gt;
	local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode,&lt;br /&gt;
		access = access});&lt;br /&gt;
	&lt;br /&gt;
	if 19 ~= id:len() then&lt;br /&gt;
		err_type = cfg.err_msg_supl.length;&lt;br /&gt;
	else&lt;br /&gt;
		year = id:match (&amp;quot;^(%d%d%d%d)[%a][%w&amp;amp;%.][%w&amp;amp;%.][%w&amp;amp;%.][%w.]+[%a%.]$&amp;quot;);&lt;br /&gt;
		if not year then														-- if nil then no pattern match&lt;br /&gt;
			err_type = cfg.err_msg_supl.value;									-- so value error&lt;br /&gt;
		else&lt;br /&gt;
			local next_year = tonumber (os.date ('%Y')) + 1;					-- get the current year as a number and add one for next year&lt;br /&gt;
			year = tonumber (year);												-- convert year portion of bibcode to a number&lt;br /&gt;
			if (1000 &amp;gt; year) or (year &amp;gt; next_year) then&lt;br /&gt;
				err_type = cfg.err_msg_supl.year;								-- year out of bounds&lt;br /&gt;
			end&lt;br /&gt;
			if id:find('&amp;amp;%.') then&lt;br /&gt;
				err_type = cfg.err_msg_supl.journal;							-- journal abbreviation must not have '&amp;amp;.' (if it does it's missing a letter)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if is_set (err_type) then													-- if there was an error detected&lt;br /&gt;
		set_message ('err_bad_bibcode', {err_type});&lt;br /&gt;
		options.coins_list_t['BIBCODE'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; B I O R X I V &amp;gt;-----------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format bioRxiv ID and do simple error checking.  Before 2019-12-11, biorXiv IDs were 10.1101/ followed by exactly&lt;br /&gt;
6 digits.  After 2019-12-11, biorXiv IDs retained the six-digit identifier but prefixed that with a yyyy.mm.dd. &lt;br /&gt;
date and suffixed with an optional version identifier.&lt;br /&gt;
&lt;br /&gt;
The bioRxiv ID is the string of characters:&lt;br /&gt;
	https://doi.org/10.1101/078733 -&amp;gt; 10.1101/078733&lt;br /&gt;
or a date followed by a six-digit number followed by an optional version indicator 'v' and one or more digits:&lt;br /&gt;
	https://www.biorxiv.org/content/10.1101/2019.12.11.123456v2 -&amp;gt; 10.1101/2019.12.11.123456v2&lt;br /&gt;
	&lt;br /&gt;
see https://www.biorxiv.org/about-biorxiv&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function biorxiv (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local err_msg = true;														-- flag; assume that there will be an error&lt;br /&gt;
	&lt;br /&gt;
	local patterns = {&lt;br /&gt;
		'^10.1101/%d%d%d%d%d%d$',												-- simple 6-digit identifier (before 2019-12-11)&lt;br /&gt;
		'^10.1101/(20[1-9]%d)%.([01]%d)%.([0-3]%d)%.%d%d%d%d%d%dv%d+$',			-- y.m.d. date + 6-digit identifier + version (after 2019-12-11)&lt;br /&gt;
		'^10.1101/(20[1-9]%d)%.([01]%d)%.([0-3]%d)%.%d%d%d%d%d%d$',				-- y.m.d. date + 6-digit identifier (after 2019-12-11)&lt;br /&gt;
		}&lt;br /&gt;
	&lt;br /&gt;
	for _, pattern in ipairs (patterns) do										-- spin through the patterns looking for a match&lt;br /&gt;
		if id:match (pattern) then&lt;br /&gt;
			local y, m, d = id:match (pattern);									-- found a match, attempt to get year, month and date from the identifier&lt;br /&gt;
&lt;br /&gt;
			if m then															-- m is nil when id is the six-digit form&lt;br /&gt;
				if not is_valid_biorxiv_date (y, m, d) then						-- validate the encoded date; TODO: don't ignore leap-year and actual month lengths ({{#time:}} is a poor date validator)&lt;br /&gt;
					break;														-- date fail; break out early so we don't unset the error message&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			err_msg = nil;														-- we found a match so unset the error message&lt;br /&gt;
			break;																-- and done&lt;br /&gt;
		end&lt;br /&gt;
	end																			-- err_cat remains set here when no match&lt;br /&gt;
&lt;br /&gt;
	if err_msg then&lt;br /&gt;
		options.coins_list_t['BIORXIV'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
		set_message ('err_bad_biorxiv');										-- and set the error message&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator,&lt;br /&gt;
			encode = handler.encode, access = handler.access});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C I T E S E E R X &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
CiteSeerX use their own notion of &amp;quot;doi&amp;quot; (not to be confused with the identifiers resolved via doi.org).&lt;br /&gt;
&lt;br /&gt;
The description of the structure of this identifier can be found at Help_talk:Citation_Style_1/Archive_26#CiteSeerX_id_structure&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function citeseerx (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local matched;&lt;br /&gt;
&lt;br /&gt;
	local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode,&lt;br /&gt;
		access = handler.access});&lt;br /&gt;
	&lt;br /&gt;
	matched = id:match (&amp;quot;^10%.1%.1%.[1-9]%d?%d?%d?%.[1-9]%d?%d?%d?$&amp;quot;);&lt;br /&gt;
	if not matched then&lt;br /&gt;
		set_message ('err_bad_citeseerx' );&lt;br /&gt;
		options.coins_list_t['CITESEERX'] = nil;								-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; D O I &amp;gt;------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Formats a DOI and checks for DOI errors.&lt;br /&gt;
&lt;br /&gt;
DOI names contain two parts: prefix and suffix separated by a forward slash.&lt;br /&gt;
	Prefix: directory indicator '10.' followed by a registrant code&lt;br /&gt;
	Suffix: character string of any length chosen by the registrant&lt;br /&gt;
&lt;br /&gt;
This function checks a DOI name for: prefix/suffix.  If the DOI name contains spaces or endashes, or, if it ends&lt;br /&gt;
with a period or a comma, this function will emit a bad_doi error message.&lt;br /&gt;
&lt;br /&gt;
DOI names are case-insensitive and can incorporate any printable Unicode characters so the test for spaces, endash,&lt;br /&gt;
and terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely&lt;br /&gt;
if ever used in DOI names.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function doi (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local inactive = options.DoiBroken&lt;br /&gt;
	local access = options.access;&lt;br /&gt;
	local ignore_invalid = options.accept;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local err_flag;&lt;br /&gt;
&lt;br /&gt;
	local text;&lt;br /&gt;
	if is_set (inactive) then&lt;br /&gt;
		local inactive_year = inactive:match(&amp;quot;%d%d%d%d&amp;quot;) or '';					-- try to get the year portion from the inactive date&lt;br /&gt;
		local inactive_month, good;&lt;br /&gt;
&lt;br /&gt;
		if is_set (inactive_year) then&lt;br /&gt;
			if 4 &amp;lt; inactive:len() then											-- inactive date has more than just a year (could be anything)&lt;br /&gt;
				local lang_obj = mw.getContentLanguage();						-- get a language object for this wiki&lt;br /&gt;
				good, inactive_month = pcall (lang_obj.formatDate, lang_obj, 'F', inactive);	-- try to get the month name from the inactive date&lt;br /&gt;
				if not good then&lt;br /&gt;
					inactive_month = nil;										-- something went wrong so make sure this is unset&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			inactive_year = nil;												-- |doi-broken-date= has something but it isn't a date&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if is_set (inactive_year) and is_set (inactive_month) then&lt;br /&gt;
			set_message ('maint_doi_inactive_dated', {inactive_year, inactive_month, ' '});&lt;br /&gt;
		elseif is_set (inactive_year) then&lt;br /&gt;
			set_message ('maint_doi_inactive_dated', {inactive_year, '', ''});&lt;br /&gt;
		else&lt;br /&gt;
			set_message ('maint_doi_inactive');&lt;br /&gt;
		end&lt;br /&gt;
		inactive = &amp;quot; (&amp;quot; .. cfg.messages['inactive'] .. ' ' .. inactive .. ')';&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local registrant = mw.ustring.match (id, '^10%.([^/]+)/[^%s–]-[^%.,]$');	-- registrant set when DOI has the proper basic form&lt;br /&gt;
&lt;br /&gt;
	local registrant_err_patterns = {											-- these patterns are for code ranges that are not supported &lt;br /&gt;
		'^[^1-3]%d%d%d%d%.%d%d*$',												-- 5 digits with subcode (0xxxx, 40000+); accepts: 10000–39999&lt;br /&gt;
		'^[^1-5]%d%d%d%d$',														-- 5 digits without subcode (0xxxx, 60000+); accepts: 10000–59999&lt;br /&gt;
		'^[^1-9]%d%d%d%.%d%d*$',												-- 4 digits with subcode (0xxx); accepts: 1000–9999&lt;br /&gt;
		'^[^1-9]%d%d%d$',														-- 4 digits without subcode (0xxx); accepts: 1000–9999&lt;br /&gt;
		'^%d%d%d%d%d%d+',														-- 6 or more digits&lt;br /&gt;
		'^%d%d?%d?$',															-- less than 4 digits without subcode (with subcode is legitimate)&lt;br /&gt;
		'^5555$',																-- test registrant will never resolve&lt;br /&gt;
		'[^%d%.]',																-- any character that isn't a digit or a dot&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
	if not ignore_invalid then&lt;br /&gt;
		if registrant then														-- when DOI has proper form&lt;br /&gt;
			for i, pattern in ipairs (registrant_err_patterns) do				-- spin through error patterns&lt;br /&gt;
				if registrant:match (pattern) then								-- to validate registrant codes&lt;br /&gt;
					err_flag = set_message ('err_bad_doi');						-- when found, mark this DOI as bad&lt;br /&gt;
					break;														-- and done&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			err_flag = set_message ('err_bad_doi');								-- invalid directory or malformed&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		set_message ('maint_doi_ignore');&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if err_flag then&lt;br /&gt;
		options.coins_list_t['DOI'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access,&lt;br /&gt;
		auto_link = not (err_flag or is_set (inactive) or ignore_invalid) and 'doi' or nil -- do not auto-link when |doi-broken-date= has a value or when there is a DOI error or (to play it safe, after all, auto-linking is not essential) when invalid DOIs are ignored&lt;br /&gt;
		}) .. (inactive or '');&lt;br /&gt;
&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; H D L &amp;gt;------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Formats an HDL with minor error checking.&lt;br /&gt;
&lt;br /&gt;
HDL names contain two parts: prefix and suffix separated by a forward slash.&lt;br /&gt;
	Prefix: character string using any character in the UCS-2 character set except '/'&lt;br /&gt;
	Suffix: character string of any length using any character in the UCS-2 character set chosen by the registrant&lt;br /&gt;
&lt;br /&gt;
This function checks a HDL name for: prefix/suffix.  If the HDL name contains spaces, endashes, or, if it ends&lt;br /&gt;
with a period or a comma, this function will emit a bad_hdl error message.&lt;br /&gt;
&lt;br /&gt;
HDL names are case-insensitive and can incorporate any printable Unicode characters so the test for endashes and&lt;br /&gt;
terminal punctuation may not be technically correct but it appears, that in practice these characters are rarely&lt;br /&gt;
if ever used in HDLs.&lt;br /&gt;
&lt;br /&gt;
Query string parameters are named here: http://www.handle.net/proxy_servlet.html.  query strings are not displayed&lt;br /&gt;
but since '?' is an allowed character in an HDL, '?' followed by one of the query parameters is the only way we&lt;br /&gt;
have to detect the query string so that it isn't URL-encoded with the rest of the identifier.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function hdl (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local access = options.access;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local query_params = {														-- list of known query parameters from http://www.handle.net/proxy_servlet.html&lt;br /&gt;
		'noredirect',&lt;br /&gt;
		'ignore_aliases',&lt;br /&gt;
		'auth',&lt;br /&gt;
		'cert',&lt;br /&gt;
		'index',&lt;br /&gt;
		'type',&lt;br /&gt;
		'urlappend',&lt;br /&gt;
		'locatt',&lt;br /&gt;
		'action',&lt;br /&gt;
		}&lt;br /&gt;
	&lt;br /&gt;
	local hdl, suffix, param = id:match ('(.-)(%?(%a+).+)$');					-- look for query string&lt;br /&gt;
	local found;&lt;br /&gt;
&lt;br /&gt;
	if hdl then																	-- when there are query strings, this is the handle identifier portion&lt;br /&gt;
		for _, q in ipairs (query_params) do									-- spin through the list of query parameters&lt;br /&gt;
			if param:match ('^' .. q) then										-- if the query string begins with one of the parameters&lt;br /&gt;
				found = true;													-- announce a find&lt;br /&gt;
				break;															-- and stop looking&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if found then&lt;br /&gt;
		id = hdl;																-- found so replace id with the handle portion; this will be URL-encoded, suffix will not&lt;br /&gt;
	else&lt;br /&gt;
		suffix = '';															-- make sure suffix is empty string for concatenation else&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, suffix = suffix, separator = handler.separator, encode = handler.encode, access = access})&lt;br /&gt;
&lt;br /&gt;
	if nil == id:match(&amp;quot;^[^%s–]-/[^%s–]-[^%.,]$&amp;quot;) then							-- HDL must contain a forward slash, must not contain spaces, endashes, and must not end with period or comma&lt;br /&gt;
		set_message ('err_bad_hdl' );&lt;br /&gt;
		options.coins_list_t['HDL'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S B N &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Determines whether an ISBN string is valid&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function isbn (options)&lt;br /&gt;
	local isbn_str = options.id;&lt;br /&gt;
	local ignore_invalid = options.accept;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
&lt;br /&gt;
	local function return_result (check, err_type)								-- local function to handle the various returns&lt;br /&gt;
		local ISBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect,&lt;br /&gt;
						prefix = handler.prefix, id = isbn_str, separator = handler.separator});&lt;br /&gt;
		if ignore_invalid then													-- if ignoring ISBN errors&lt;br /&gt;
			set_message ('maint_isbn_ignore');									-- add a maint category even when there is no error&lt;br /&gt;
		else																	-- here when not ignoring&lt;br /&gt;
			if not check then													-- and there is an error&lt;br /&gt;
				options.coins_list_t['ISBN'] = nil;								-- when error, unset so not included in COinS&lt;br /&gt;
				set_message ('err_bad_isbn', err_type);							-- set an error message&lt;br /&gt;
				return ISBN;										 			-- return id text&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		return ISBN;															-- return id text&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if nil ~= isbn_str:match ('[^%s-0-9X]') then&lt;br /&gt;
		return return_result (false, cfg.err_msg_supl.char);					-- fail if isbn_str contains anything but digits, hyphens, or the uppercase X&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local id = isbn_str:gsub ('[%s-]', '');										-- remove hyphens and whitespace&lt;br /&gt;
&lt;br /&gt;
	local len = id:len();&lt;br /&gt;
 &lt;br /&gt;
	if len ~= 10 and len ~= 13 then&lt;br /&gt;
		return return_result (false, cfg.err_msg_supl.length);					-- fail if incorrect length&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if len == 10 then&lt;br /&gt;
		if id:match ('^%d*X?$') == nil then										-- fail if isbn_str has 'X' anywhere but last position&lt;br /&gt;
			return return_result (false, cfg.err_msg_supl.form);									&lt;br /&gt;
		end&lt;br /&gt;
		if not is_valid_isxn (id, 10) then										-- test isbn-10 for numerical validity&lt;br /&gt;
			return return_result (false, cfg.err_msg_supl.check);				-- fail if isbn-10 is not numerically valid&lt;br /&gt;
		end&lt;br /&gt;
		if id:find ('^63[01]') then												-- 630xxxxxxx and 631xxxxxxx are (apparently) not valid isbn group ids but are used by amazon as numeric identifiers (asin)&lt;br /&gt;
			return return_result (false, cfg.err_msg_supl.group);				-- fail if isbn-10 begins with 630/1&lt;br /&gt;
		end&lt;br /&gt;
		return return_result (true, cfg.err_msg_supl.check);					-- pass if isbn-10 is numerically valid&lt;br /&gt;
	else&lt;br /&gt;
		if id:match ('^%d+$') == nil then&lt;br /&gt;
			return return_result (false, cfg.err_msg_supl.char);				-- fail if ISBN-13 is not all digits&lt;br /&gt;
		end&lt;br /&gt;
		if id:match ('^97[89]%d*$') == nil then&lt;br /&gt;
			return return_result (false, cfg.err_msg_supl.prefix);				-- fail when ISBN-13 does not begin with 978 or 979&lt;br /&gt;
		end&lt;br /&gt;
		if id:match ('^9790') then&lt;br /&gt;
			return return_result (false, cfg.err_msg_supl.group);				-- group identifier '0' is reserved to ISMN&lt;br /&gt;
		end&lt;br /&gt;
		return return_result (is_valid_isxn_13 (id), cfg.err_msg_supl.check);&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; A S I N &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Formats a link to Amazon.  Do simple error checking: ASIN must be mix of 10 numeric or uppercase alpha&lt;br /&gt;
characters.  If a mix, first character must be uppercase alpha; if all numeric, ASINs must be 10-digit&lt;br /&gt;
ISBN. If 10-digit ISBN, add a maintenance category so a bot or AWB script can replace |asin= with |isbn=.&lt;br /&gt;
Error message if not 10 characters, if not ISBN-10, if mixed and first character is a digit.&lt;br /&gt;
&lt;br /&gt;
|asin=630....... and |asin=631....... are (apparently) not a legitimate ISBN though it checksums as one; these&lt;br /&gt;
do not cause this function to emit the maint_asin message&lt;br /&gt;
&lt;br /&gt;
This function is positioned here because it calls isbn()&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function asin (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local domain = options.ASINTLD;&lt;br /&gt;
	&lt;br /&gt;
	local err_flag;&lt;br /&gt;
&lt;br /&gt;
	if not id:match(&amp;quot;^[%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u][%d%u]$&amp;quot;) then&lt;br /&gt;
		err_flag = set_message ('err_bad_asin');								-- ASIN is not a mix of 10 uppercase alpha and numeric characters&lt;br /&gt;
	else&lt;br /&gt;
		if id:match(&amp;quot;^%d%d%d%d%d%d%d%d%d[%dX]$&amp;quot;) then							-- if 10-digit numeric (or 9 digits with terminal X)&lt;br /&gt;
			if is_valid_isxn (id, 10) then										-- see if ASIN value is or validates as ISBN-10&lt;br /&gt;
				if not id:find ('^63[01]') then									-- 630xxxxxxx and 631xxxxxxx are (apparently) not a valid isbn prefixes but are used by amazon as a numeric identifier&lt;br /&gt;
					err_flag = set_message ('err_bad_asin');					-- ASIN has ISBN-10 form but begins with something other than 630/1 so probably an isbn &lt;br /&gt;
				end&lt;br /&gt;
			elseif not is_set (err_flag) then&lt;br /&gt;
				err_flag = set_message ('err_bad_asin');						-- ASIN is not ISBN-10&lt;br /&gt;
			end&lt;br /&gt;
		elseif not id:match(&amp;quot;^%u[%d%u]+$&amp;quot;) then&lt;br /&gt;
			err_flag = set_message ('err_bad_asin');							-- asin doesn't begin with uppercase alpha&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if (not is_set (domain)) or in_array (domain, {'us'}) then					-- default: United States&lt;br /&gt;
		domain = &amp;quot;com&amp;quot;;&lt;br /&gt;
	elseif in_array (domain, {'jp', 'uk'}) then									-- Japan, United Kingdom&lt;br /&gt;
		domain = &amp;quot;co.&amp;quot; .. domain;&lt;br /&gt;
	elseif in_array (domain, {'z.cn'}) then 									-- China&lt;br /&gt;
		domain = &amp;quot;cn&amp;quot;;&lt;br /&gt;
	elseif in_array (domain, {'au', 'br', 'mx', 'sg', 'tr'}) then				-- Australia, Brazil, Mexico, Singapore, Turkey&lt;br /&gt;
		domain = &amp;quot;com.&amp;quot; .. domain;&lt;br /&gt;
	elseif not in_array (domain, {'ae', 'ca', 'cn', 'de', 'es', 'fr', 'in', 'it', 'nl', 'pl', 'sa', 'se', 'co.jp', 'co.uk', 'com', 'com.au', 'com.br', 'com.mx', 'com.sg', 'com.tr'}) then -- Arabic Emirates, Canada, China, Germany, Spain, France, Indonesia, Italy, Netherlands, Poland, Saudi Arabia, Sweden (as of 2021-03 Austria (.at), Liechtenstein (.li) and Switzerland (.ch) still redirect to the German site (.de) with special settings, so don't maintain local ASINs for them)&lt;br /&gt;
		err_flag = set_message ('err_bad_asin_tld');							-- unsupported asin-tld value&lt;br /&gt;
	end&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
&lt;br /&gt;
	if not is_set (err_flag) then&lt;br /&gt;
		options.coins_list_t['ASIN'] = handler.prefix .. domain .. &amp;quot;/dp/&amp;quot; .. id;	-- asin for coins&lt;br /&gt;
	else&lt;br /&gt;
		options.coins_list_t['ASIN'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix .. domain .. &amp;quot;/dp/&amp;quot;,&lt;br /&gt;
		id = id, encode = handler.encode, separator = handler.separator})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S M N &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Determines whether an ISMN string is valid.  Similar to ISBN-13, ISMN is 13 digits beginning 979-0-... and uses the&lt;br /&gt;
same check digit calculations.  See http://www.ismn-international.org/download/Web_ISMN_Users_Manual_2008-6.pdf&lt;br /&gt;
section 2, pages 9–12.&lt;br /&gt;
&lt;br /&gt;
ismn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx)&lt;br /&gt;
or an identifier registered at info-uri.info (info:)&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function ismn (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local text;&lt;br /&gt;
	local valid_ismn = true;&lt;br /&gt;
	local id_copy;&lt;br /&gt;
&lt;br /&gt;
	id_copy = id;																-- save a copy because this testing is destructive&lt;br /&gt;
	id = id:gsub ('[%s-]', '');													-- remove hyphens and white space&lt;br /&gt;
&lt;br /&gt;
	if 13 ~= id:len() or id:match (&amp;quot;^9790%d*$&amp;quot; ) == nil then					-- ISMN must be 13 digits and begin with 9790&lt;br /&gt;
		valid_ismn = false;&lt;br /&gt;
	else&lt;br /&gt;
		valid_ismn=is_valid_isxn_13 (id);										-- validate ISMN&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--	text = internal_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,		-- use this (or external version) when there is some place to link to&lt;br /&gt;
	--		prefix = handler.prefix, id = id_copy, separator = handler.separator, encode = handler.encode})&lt;br /&gt;
&lt;br /&gt;
	text = table.concat (														-- because no place to link to yet&lt;br /&gt;
		{&lt;br /&gt;
		make_wikilink (link_label_make (handler), handler.label),&lt;br /&gt;
		handler.separator,&lt;br /&gt;
		id_copy&lt;br /&gt;
		});&lt;br /&gt;
&lt;br /&gt;
	if false == valid_ismn then&lt;br /&gt;
		options.coins_list_t['ISMN'] = nil;										-- when error, unset so not included in COinS; not really necessary here because ismn not made part of COinS&lt;br /&gt;
		set_message ('err_bad_ismn');											-- create an error message if the ISMN is invalid&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S S N &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Validate and format an ISSN.  This code fixes the case where an editor has included an ISSN in the citation but&lt;br /&gt;
has separated the two groups of four digits with a space.  When that condition occurred, the resulting link looked&lt;br /&gt;
like this:&lt;br /&gt;
&lt;br /&gt;
	|issn=0819 4327 gives: [http://www.worldcat.org/issn/0819 4327 0819 4327]	-- can't have spaces in an external link&lt;br /&gt;
	&lt;br /&gt;
This code now prevents that by inserting a hyphen at the ISSN midpoint.  It also validates the ISSN for length&lt;br /&gt;
and makes sure that the checkdigit agrees with the calculated value.  Incorrect length (8 digits), characters&lt;br /&gt;
other than 0-9 and X, or checkdigit / calculated value mismatch will all cause a check ISSN error message.  The&lt;br /&gt;
ISSN is always displayed with a hyphen, even if the ISSN was given as a single group of 8 digits.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function issn (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local ignore_invalid = options.accept;&lt;br /&gt;
&lt;br /&gt;
	local issn_copy = id;														-- save a copy of unadulterated ISSN; use this version for display if ISSN does not validate&lt;br /&gt;
	local text;&lt;br /&gt;
	local valid_issn = true;&lt;br /&gt;
&lt;br /&gt;
	id = id:gsub ('[%s-]', '');													-- remove hyphens and whitespace&lt;br /&gt;
&lt;br /&gt;
	if 8 ~= id:len() or nil == id:match (&amp;quot;^%d*X?$&amp;quot; ) then						-- validate the ISSN: 8 digits long, containing only 0-9 or X in the last position&lt;br /&gt;
		valid_issn = false;														-- wrong length or improper character&lt;br /&gt;
	else&lt;br /&gt;
		valid_issn = is_valid_isxn (id, 8);										-- validate ISSN&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if true == valid_issn then&lt;br /&gt;
		id = string.sub (id, 1, 4 ) .. &amp;quot;-&amp;quot; .. string.sub (id, 5 );				-- if valid, display correctly formatted version&lt;br /&gt;
	else&lt;br /&gt;
		id = issn_copy;															-- if not valid, show the invalid ISSN with error message&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode})&lt;br /&gt;
&lt;br /&gt;
	if ignore_invalid then&lt;br /&gt;
		set_message ('maint_issn_ignore');&lt;br /&gt;
	else&lt;br /&gt;
		if false == valid_issn then&lt;br /&gt;
			options.coins_list_t['ISSN'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
			set_message ('err_bad_issn', (options.hkey == 'EISSN') and 'e' or '');	-- create an error message if the ISSN is invalid&lt;br /&gt;
		end &lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; J F M &amp;gt;-----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
A numerical identifier in the form nn.nnnn.nn&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function jfm (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local id_num;&lt;br /&gt;
&lt;br /&gt;
	id_num = id:match ('^[Jj][Ff][Mm](.*)$');									-- identifier with jfm prefix; extract identifier&lt;br /&gt;
&lt;br /&gt;
	if is_set (id_num) then&lt;br /&gt;
		set_message ('maint_jfm_format');&lt;br /&gt;
	else																		-- plain number without JFM prefix&lt;br /&gt;
		id_num = id;															-- if here id does not have prefix&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if id_num and id_num:match('^%d%d%.%d%d%d%d%.%d%d$') then&lt;br /&gt;
		id = id_num;															-- jfm matches pattern&lt;br /&gt;
	else&lt;br /&gt;
		set_message ('err_bad_jfm' );											-- set an error message&lt;br /&gt;
		options.coins_list_t['JFM'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; J S T O R &amp;gt;--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format a JSTOR with some error checking&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function jstor (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local access = options.access;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
&lt;br /&gt;
	if id:find ('[Jj][Ss][Tt][Oo][Rr]') or id:find ('^https?://') or id:find ('%s') then&lt;br /&gt;
		set_message ('err_bad_jstor');											-- set an error message&lt;br /&gt;
		options.coins_list_t['JSTOR'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; L C C N &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format LCCN link and do simple error checking.  LCCN is a character string 8-12 characters long. The length of&lt;br /&gt;
the LCCN dictates the character type of the first 1-3 characters; the rightmost eight are always digits.&lt;br /&gt;
http://info-uri.info/registry/OAIHandler?verb=GetRecord&amp;amp;metadataPrefix=reg&amp;amp;identifier=info:lccn/&lt;br /&gt;
&lt;br /&gt;
length = 8 then all digits&lt;br /&gt;
length = 9 then lccn[1] is lowercase alpha&lt;br /&gt;
length = 10 then lccn[1] and lccn[2] are both lowercase alpha or both digits&lt;br /&gt;
length = 11 then lccn[1] is lower case alpha, lccn[2] and lccn[3] are both lowercase alpha or both digits&lt;br /&gt;
length = 12 then lccn[1] and lccn[2] are both lowercase alpha&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function lccn (options)&lt;br /&gt;
	local lccn = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local err_flag;																-- presume that LCCN is valid&lt;br /&gt;
	local id = lccn;															-- local copy of the LCCN&lt;br /&gt;
&lt;br /&gt;
	id = normalize_lccn (id);													-- get canonical form (no whitespace, hyphens, forward slashes)&lt;br /&gt;
	local len = id:len();														-- get the length of the LCCN&lt;br /&gt;
&lt;br /&gt;
	if 8 == len then&lt;br /&gt;
		if id:match(&amp;quot;[^%d]&amp;quot;) then												-- if LCCN has anything but digits (nil if only digits)&lt;br /&gt;
			err_flag = set_message ('err_bad_lccn');							-- set an error message&lt;br /&gt;
		end&lt;br /&gt;
	elseif 9 == len then														-- LCCN should be adddddddd&lt;br /&gt;
		if nil == id:match(&amp;quot;%l%d%d%d%d%d%d%d%d&amp;quot;) then							-- does it match our pattern?&lt;br /&gt;
			err_flag = set_message ('err_bad_lccn');							-- set an error message&lt;br /&gt;
		end&lt;br /&gt;
	elseif 10 == len then														-- LCCN should be aadddddddd or dddddddddd&lt;br /&gt;
		if id:match(&amp;quot;[^%d]&amp;quot;) then												-- if LCCN has anything but digits (nil if only digits) ...&lt;br /&gt;
			if nil == id:match(&amp;quot;^%l%l%d%d%d%d%d%d%d%d&amp;quot;) then					-- ... see if it matches our pattern&lt;br /&gt;
				err_flag = set_message ('err_bad_lccn');						-- no match, set an error message&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	elseif 11 == len then														-- LCCN should be aaadddddddd or adddddddddd&lt;br /&gt;
		if not (id:match(&amp;quot;^%l%l%l%d%d%d%d%d%d%d%d&amp;quot;) or id:match(&amp;quot;^%l%d%d%d%d%d%d%d%d%d%d&amp;quot;)) then	-- see if it matches one of our patterns&lt;br /&gt;
			err_flag = set_message ('err_bad_lccn');							-- no match, set an error message&lt;br /&gt;
		end&lt;br /&gt;
	elseif 12 == len then														-- LCCN should be aadddddddddd&lt;br /&gt;
		if not id:match(&amp;quot;^%l%l%d%d%d%d%d%d%d%d%d%d&amp;quot;) then						-- see if it matches our pattern&lt;br /&gt;
			err_flag = set_message ('err_bad_lccn');							-- no match, set an error message&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		err_flag = set_message ('err_bad_lccn');								-- wrong length, set an error message&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not is_set (err_flag) and nil ~= lccn:find ('%s') then&lt;br /&gt;
		err_flag = set_message ('err_bad_lccn');								-- lccn contains a space, set an error message&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if is_set (err_flag) then&lt;br /&gt;
		options.coins_list_t['LCCN'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = lccn, separator = handler.separator, encode = handler.encode});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; M R &amp;gt;--------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
A seven digit number; if not seven digits, zero-fill leading digits to make seven digits.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function mr (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local id_num;&lt;br /&gt;
	local id_len;&lt;br /&gt;
&lt;br /&gt;
	id_num = id:match ('^[Mm][Rr](%d+)$');										-- identifier with mr prefix&lt;br /&gt;
&lt;br /&gt;
	if is_set (id_num) then&lt;br /&gt;
		set_message ('maint_mr_format');										-- add maint cat&lt;br /&gt;
	else																		-- plain number without mr prefix&lt;br /&gt;
		id_num = id:match ('^%d+$');											-- if here id is all digits&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	id_len = id_num and id_num:len() or 0;&lt;br /&gt;
	if (7 &amp;gt;= id_len) and (0 ~= id_len) then&lt;br /&gt;
		id = string.rep ('0', 7-id_len) .. id_num;								-- zero-fill leading digits&lt;br /&gt;
	else&lt;br /&gt;
		set_message ('err_bad_mr');												-- set an error message&lt;br /&gt;
		options.coins_list_t['MR'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; O C L C &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Validate and format an OCLC ID.  https://www.oclc.org/batchload/controlnumber.en.html {{dead link}}&lt;br /&gt;
archived at: https://web.archive.org/web/20161228233804/https://www.oclc.org/batchload/controlnumber.en.html&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function oclc (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local number;&lt;br /&gt;
&lt;br /&gt;
	if id:match('^ocm%d%d%d%d%d%d%d%d$') then									-- ocm prefix and 8 digits; 001 field (12 characters)&lt;br /&gt;
		number = id:match('ocm(%d+)');											-- get the number&lt;br /&gt;
	elseif id:match('^ocn%d%d%d%d%d%d%d%d%d$') then								-- ocn prefix and 9 digits; 001 field (12 characters)&lt;br /&gt;
		number = id:match('ocn(%d+)');											-- get the number&lt;br /&gt;
	elseif id:match('^on%d%d%d%d%d%d%d%d%d%d+$') then							-- on prefix and 10 or more digits; 001 field (12 characters)&lt;br /&gt;
		number = id:match('^on(%d%d%d%d%d%d%d%d%d%d+)$');						-- get the number&lt;br /&gt;
	elseif id:match('^%(OCoLC%)[1-9]%d*$') then									-- (OCoLC) prefix and variable number digits; no leading zeros; 035 field&lt;br /&gt;
		number = id:match('%(OCoLC%)([1-9]%d*)');								-- get the number&lt;br /&gt;
		if 9 &amp;lt; number:len() then&lt;br /&gt;
			number = nil;														-- constrain to 1 to 9 digits; change this when OCLC issues 10-digit numbers&lt;br /&gt;
		end&lt;br /&gt;
	elseif id:match('^%d+$') then												-- no prefix&lt;br /&gt;
		number = id;															-- get the number&lt;br /&gt;
		if 10 &amp;lt; number:len() then&lt;br /&gt;
			number = nil;														-- constrain to 1 to 10 digits; change this when OCLC issues 11-digit numbers&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if number then																-- proper format&lt;br /&gt;
		id = number;															-- exclude prefix, if any, from external link&lt;br /&gt;
	else&lt;br /&gt;
		set_message ('err_bad_oclc')											-- add an error message if the id is malformed&lt;br /&gt;
		options.coins_list_t['OCLC'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; O P E N L I B R A R Y &amp;gt;--------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Formats an OpenLibrary link, and checks for associated errors.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function openlibrary (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local access = options.access;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local ident, code = id:gsub('^OL', ''):match(&amp;quot;^(%d+([AMW]))$&amp;quot;);				-- strip optional OL prefix followed immediately by digits followed by 'A', 'M', or 'W';&lt;br /&gt;
	local err_flag;&lt;br /&gt;
	local prefix = {															-- these are appended to the handler.prefix according to code&lt;br /&gt;
		['A']='authors/OL',&lt;br /&gt;
		['M']='books/OL',&lt;br /&gt;
		['W']='works/OL',&lt;br /&gt;
		['X']='OL'																-- not a code; spoof when 'code' in id is invalid&lt;br /&gt;
		};&lt;br /&gt;
&lt;br /&gt;
	if not ident then&lt;br /&gt;
		code = 'X';																-- no code or id completely invalid&lt;br /&gt;
		ident = id;																-- copy id to ident so that we display the flawed identifier&lt;br /&gt;
		err_flag = set_message ('err_bad_ol');&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not is_set (err_flag) then&lt;br /&gt;
		options.coins_list_t['OL'] = handler.prefix .. prefix[code] .. ident;	-- experiment for ol coins&lt;br /&gt;
	else&lt;br /&gt;
		options.coins_list_t['OL'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix .. prefix[code],&lt;br /&gt;
		id = ident, separator = handler.separator, encode = handler.encode,&lt;br /&gt;
		access = access});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; O S T I &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format OSTI and do simple error checking. OSTIs are sequential numbers beginning at 1 and counting up.  This&lt;br /&gt;
code checks the OSTI to see that it contains only digits and is less than test_limit specified in the configuration;&lt;br /&gt;
the value in test_limit will need to be updated periodically as more OSTIs are issued.&lt;br /&gt;
&lt;br /&gt;
NB. 1018 is the lowest OSTI number found in the wild (so far) and resolving OK on the OSTI site&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function osti (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local access = options.access;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
&lt;br /&gt;
	if id:match(&amp;quot;[^%d]&amp;quot;) then													-- if OSTI has anything but digits&lt;br /&gt;
		set_message ('err_bad_osti');											-- set an error message&lt;br /&gt;
		options.coins_list_t['OSTI'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	else																		-- OSTI is only digits&lt;br /&gt;
		local id_num = tonumber (id);											-- convert id to a number for range testing&lt;br /&gt;
		if 1018 &amp;gt; id_num or handler.id_limit &amp;lt; id_num then						-- if OSTI is outside test limit boundaries&lt;br /&gt;
			set_message ('err_bad_osti');										-- set an error message&lt;br /&gt;
			options.coins_list_t['OSTI'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P M C &amp;gt;------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format a PMC, do simple error checking, and check for embargoed articles.&lt;br /&gt;
&lt;br /&gt;
The embargo parameter takes a date for a value. If the embargo date is in the future the PMC identifier will not&lt;br /&gt;
be linked to the article.  If the embargo date is today or in the past, or if it is empty or omitted, then the&lt;br /&gt;
PMC identifier is linked to the article through the link at cfg.id_handlers['PMC'].prefix.&lt;br /&gt;
&lt;br /&gt;
PMC embargo date testing is done in function is_embargoed () which is called earlier because when the citation&lt;br /&gt;
has |pmc=&amp;lt;value&amp;gt; but does not have a |url= then |title= is linked with the PMC link.  Function is_embargoed ()&lt;br /&gt;
returns the embargo date if the PMC article is still embargoed, otherwise it returns an empty string.&lt;br /&gt;
&lt;br /&gt;
PMCs are sequential numbers beginning at 1 and counting up.  This code checks the PMC to see that it contains only digits and is less&lt;br /&gt;
than test_limit; the value in local variable test_limit will need to be updated periodically as more PMCs are issued.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function pmc (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local embargo = options.Embargo;											-- TODO: lowercase?&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local err_flag;&lt;br /&gt;
	local id_num;&lt;br /&gt;
	local text;&lt;br /&gt;
&lt;br /&gt;
	id_num = id:match ('^[Pp][Mm][Cc](%d+)$');									-- identifier with PMC prefix&lt;br /&gt;
&lt;br /&gt;
	if is_set (id_num) then&lt;br /&gt;
		set_message ('maint_pmc_format');&lt;br /&gt;
	else																		-- plain number without PMC prefix&lt;br /&gt;
		id_num = id:match ('^%d+$');											-- if here id is all digits&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if is_set (id_num) then														-- id_num has a value so test it&lt;br /&gt;
		id_num = tonumber (id_num);												-- convert id_num to a number for range testing&lt;br /&gt;
		if 1 &amp;gt; id_num or handler.id_limit &amp;lt; id_num then							-- if PMC is outside test limit boundaries&lt;br /&gt;
			err_flag = set_message ('err_bad_pmc');								-- set an error message&lt;br /&gt;
		else&lt;br /&gt;
			id = tostring (id_num);												-- make sure id is a string&lt;br /&gt;
		end&lt;br /&gt;
	else																		-- when id format incorrect&lt;br /&gt;
		err_flag = set_message ('err_bad_pmc');									-- set an error message&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if is_set (embargo) and is_set (is_embargoed (embargo)) then				-- is PMC is still embargoed?&lt;br /&gt;
		text = table.concat (													-- still embargoed so no external link&lt;br /&gt;
			{&lt;br /&gt;
			make_wikilink (link_label_make (handler), handler.label),&lt;br /&gt;
			handler.separator,&lt;br /&gt;
			id,&lt;br /&gt;
			});&lt;br /&gt;
	else&lt;br /&gt;
		text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,	-- no embargo date or embargo has expired, ok to link to article&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access,&lt;br /&gt;
			auto_link = not err_flag and 'pmc' or nil							-- do not auto-link when PMC has error&lt;br /&gt;
			});&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if err_flag then&lt;br /&gt;
		options.coins_list_t['PMC'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P M I D &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format PMID and do simple error checking.  PMIDs are sequential numbers beginning at 1 and counting up.  This&lt;br /&gt;
code checks the PMID to see that it contains only digits and is less than test_limit; the value in local variable&lt;br /&gt;
test_limit will need to be updated periodically as more PMIDs are issued.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function pmid (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
&lt;br /&gt;
	if id:match(&amp;quot;[^%d]&amp;quot;) then													-- if PMID has anything but digits&lt;br /&gt;
		set_message ('err_bad_pmid');											-- set an error message&lt;br /&gt;
		options.coins_list_t['PMID'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	else																		-- PMID is only digits&lt;br /&gt;
		local id_num = tonumber (id);											-- convert id to a number for range testing&lt;br /&gt;
		if 1 &amp;gt; id_num or handler.id_limit &amp;lt; id_num then							-- if PMID is outside test limit boundaries&lt;br /&gt;
			set_message ('err_bad_pmid');										-- set an error message&lt;br /&gt;
			options.coins_list_t['PMID'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; R F C &amp;gt;------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format RFC and do simple error checking. RFCs are sequential numbers beginning at 1 and counting up.  This&lt;br /&gt;
code checks the RFC to see that it contains only digits and is less than test_limit specified in the configuration;&lt;br /&gt;
the value in test_limit will need to be updated periodically as more RFCs are issued.&lt;br /&gt;
&lt;br /&gt;
An index of all RFCs is here: https://tools.ietf.org/rfc/&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function rfc (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
&lt;br /&gt;
	if id:match(&amp;quot;[^%d]&amp;quot;) then													-- if RFC has anything but digits&lt;br /&gt;
		set_message ('err_bad_rfc');											-- set an error message&lt;br /&gt;
		options.coins_list_t['RFC'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	else																		-- RFC is only digits&lt;br /&gt;
		local id_num = tonumber (id);											-- convert id to a number for range testing&lt;br /&gt;
		if 1 &amp;gt; id_num or handler.id_limit &amp;lt; id_num then							-- if RFC is outside test limit boundaries&lt;br /&gt;
			set_message ('err_bad_rfc');										-- set an error message&lt;br /&gt;
			options.coins_list_t['RFC'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = handler.access});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S 2 C I D &amp;gt;--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format an S2CID, do simple error checking&lt;br /&gt;
&lt;br /&gt;
S2CIDs are sequential numbers beginning at 1 and counting up.  This code checks the S2CID to see that it is only&lt;br /&gt;
digits and is less than test_limit; the value in local variable test_limit will need to be updated periodically&lt;br /&gt;
as more S2CIDs are issued.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function s2cid (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local access = options.access;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local id_num;&lt;br /&gt;
	local text;&lt;br /&gt;
	&lt;br /&gt;
	id_num = id:match ('^[1-9]%d*$');											-- id must be all digits; must not begin with 0; no open access flag&lt;br /&gt;
&lt;br /&gt;
 	if is_set (id_num) then														-- id_num has a value so test it&lt;br /&gt;
		id_num = tonumber (id_num);												-- convert id_num to a number for range testing&lt;br /&gt;
		if handler.id_limit &amp;lt; id_num then										-- if S2CID is outside test limit boundaries&lt;br /&gt;
			set_message ('err_bad_s2cid');										-- set an error message&lt;br /&gt;
			options.coins_list_t['S2CID'] = nil;								-- when error, unset so not included in COinS&lt;br /&gt;
		end&lt;br /&gt;
	else																		-- when id format incorrect&lt;br /&gt;
		set_message ('err_bad_s2cid');											-- set an error message&lt;br /&gt;
		options.coins_list_t['S2CID'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = access});&lt;br /&gt;
&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S B N &amp;gt;------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
9-digit form of ISBN-10; uses same check-digit validation when SBN is prefixed with an additional '0' to make 10 digits&lt;br /&gt;
&lt;br /&gt;
sbn value not made part of COinS metadata because we don't have a url or isn't a COinS-defined identifier (rft.xxx)&lt;br /&gt;
or an identifier registered at info-uri.info (info:)&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function sbn (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local ignore_invalid = options.accept;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local function return_result (check, err_type)								-- local function to handle the various returns&lt;br /&gt;
		local SBN = internal_link_id ({link = handler.link, label = handler.label, redirect = handler.redirect,&lt;br /&gt;
						prefix = handler.prefix, id = id, separator = handler.separator});&lt;br /&gt;
		if not ignore_invalid then												-- if not ignoring SBN errors&lt;br /&gt;
			if not check then&lt;br /&gt;
				options.coins_list_t['SBN'] = nil;								-- when error, unset so not included in COinS; not really necessary here because sbn not made part of COinS&lt;br /&gt;
				set_message ('err_bad_sbn', {err_type});						-- display an error message&lt;br /&gt;
				return SBN; &lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			set_message ('maint_isbn_ignore');									-- add a maint category even when there is no error (ToDo: Possibly switch to separate message for SBNs only)&lt;br /&gt;
		end&lt;br /&gt;
		return SBN;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if id:match ('[^%s-0-9X]') then&lt;br /&gt;
		return return_result (false, cfg.err_msg_supl.char);					-- fail if SBN contains anything but digits, hyphens, or the uppercase X&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ident = id:gsub ('[%s-]', '');										-- remove hyphens and whitespace; they interfere with the rest of the tests&lt;br /&gt;
&lt;br /&gt;
	if  9 ~= ident:len() then&lt;br /&gt;
		return return_result (false, cfg.err_msg_supl.length);					-- fail if incorrect length&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if ident:match ('^%d*X?$') == nil then&lt;br /&gt;
		return return_result (false, cfg.err_msg_supl.form);					-- fail if SBN has 'X' anywhere but last position&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return return_result (is_valid_isxn ('0' .. ident, 10), cfg.err_msg_supl.check);&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S S R N &amp;gt;----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format an SSRN, do simple error checking&lt;br /&gt;
&lt;br /&gt;
SSRNs are sequential numbers beginning at 100? and counting up.  This code checks the SSRN to see that it is&lt;br /&gt;
only digits and is greater than 99 and less than test_limit; the value in local variable test_limit will need&lt;br /&gt;
to be updated periodically as more SSRNs are issued.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function ssrn (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
	local id_num;&lt;br /&gt;
	local text;&lt;br /&gt;
	&lt;br /&gt;
	id_num = id:match ('^%d+$');												-- id must be all digits&lt;br /&gt;
&lt;br /&gt;
	if is_set (id_num) then														-- id_num has a value so test it&lt;br /&gt;
		id_num = tonumber (id_num);												-- convert id_num to a number for range testing&lt;br /&gt;
		if 100 &amp;gt; id_num or handler.id_limit &amp;lt; id_num then						-- if SSRN is outside test limit boundaries&lt;br /&gt;
			set_message ('err_bad_ssrn');										-- set an error message&lt;br /&gt;
			options.coins_list_t['SSRN'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
		end&lt;br /&gt;
	else																		-- when id format incorrect&lt;br /&gt;
		set_message ('err_bad_ssrn');											-- set an error message&lt;br /&gt;
		options.coins_list_t['SSRN'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode, access = options.access});&lt;br /&gt;
&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; U S E N E T _ I D &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Validate and format a usenet message id.  Simple error checking, looks for 'id-left@id-right' not enclosed in&lt;br /&gt;
'&amp;lt;' and/or '&amp;gt;' angle brackets.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function usenet_id (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
&lt;br /&gt;
	local text = external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
		prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode})&lt;br /&gt;
 &lt;br /&gt;
	if not id:match('^.+@.+$') or not id:match('^[^&amp;lt;].*[^&amp;gt;]$') then				-- doesn't have '@' or has one or first or last character is '&amp;lt; or '&amp;gt;'&lt;br /&gt;
		set_message ('err_bad_usenet_id')										-- add an error message if the message id is invalid&lt;br /&gt;
		options.coins_list_t['USENETID'] = nil;									-- when error, unset so not included in COinS&lt;br /&gt;
	end &lt;br /&gt;
	&lt;br /&gt;
	return text;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; Z B L &amp;gt;-----------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
A numerical identifier in the form nnnn.nnnnn - leading zeros in the first quartet optional&lt;br /&gt;
&lt;br /&gt;
format described here: http://emis.mi.sanu.ac.rs/ZMATH/zmath/en/help/search/&lt;br /&gt;
&lt;br /&gt;
temporary format is apparently eight digits.  Anything else is an error&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function zbl (options)&lt;br /&gt;
	local id = options.id;&lt;br /&gt;
	local handler = options.handler;&lt;br /&gt;
&lt;br /&gt;
	if id:match('^%d%d%d%d%d%d%d%d$') then										-- is this identifier using temporary format?&lt;br /&gt;
		set_message ('maint_zbl');												-- yes, add maint cat&lt;br /&gt;
	elseif not id:match('^%d?%d?%d?%d%.%d%d%d%d%d$') then						-- not temporary, is it normal format?&lt;br /&gt;
		set_message ('err_bad_zbl');											-- no, set an error message&lt;br /&gt;
		options.coins_list_t['ZBL'] = nil;										-- when error, unset so not included in COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return external_link_id ({link = handler.link, label = handler.label, q = handler.q, redirect = handler.redirect,&lt;br /&gt;
			prefix = handler.prefix, id = id, separator = handler.separator, encode = handler.encode});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--============================&amp;lt;&amp;lt; I N T E R F A C E   F U N C T I O N S &amp;gt;&amp;gt;==========================================&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X T R A C T _ I D S &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Populates ID table from arguments using configuration settings. Loops through cfg.id_handlers and searches args for&lt;br /&gt;
any of the parameters listed in each cfg.id_handlers['...'].parameters.  If found, adds the parameter and value to&lt;br /&gt;
the identifier list.  Emits redundant error message if more than one alias exists in args&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function extract_ids (args)&lt;br /&gt;
	local id_list = {};															-- list of identifiers found in args&lt;br /&gt;
	for k, v in pairs (cfg.id_handlers) do										-- k is uppercase identifier name as index to cfg.id_handlers; e.g. cfg.id_handlers['ISBN'], v is a table&lt;br /&gt;
		v = select_one (args, v.parameters, 'err_redundant_parameters' );		-- v.parameters is a table of aliases for k; here we pick one from args if present&lt;br /&gt;
		if is_set (v) then id_list[k] = v; end									-- if found in args, add identifier to our list&lt;br /&gt;
	end&lt;br /&gt;
	return id_list;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X T R A C T _ I D _ A C C E S S _ L E V E L S &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
Fetches custom id access levels from arguments using configuration settings. Parameters which have a predefined access&lt;br /&gt;
level (e.g. arxiv) do not use this function as they are directly rendered as free without using an additional parameter.&lt;br /&gt;
&lt;br /&gt;
returns a table of k/v pairs where k is same as the identifier's key in cfg.id_handlers and v is the assigned (valid) keyword&lt;br /&gt;
&lt;br /&gt;
access-level values must match the case used in cfg.keywords_lists['id-access'] (lowercase unless there is some special reason for something else)&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function extract_id_access_levels (args, id_list)&lt;br /&gt;
	local id_accesses_list = {};&lt;br /&gt;
	for k, v in pairs (cfg.id_handlers) do&lt;br /&gt;
		local access_param = v.custom_access;									-- name of identifier's access-level parameter&lt;br /&gt;
		if is_set (access_param) then&lt;br /&gt;
			local access_level = args[access_param];							-- get the assigned value if there is one&lt;br /&gt;
			if is_set (access_level) then&lt;br /&gt;
				if not in_array (access_level, cfg.keywords_lists['id-access']) then	-- exact match required&lt;br /&gt;
					set_message ('err_invalid_param_val', {access_param, access_level});	&lt;br /&gt;
					access_level = nil;											-- invalid so unset&lt;br /&gt;
				end&lt;br /&gt;
				if not is_set (id_list[k]) then									-- identifier access-level must have a matching identifier&lt;br /&gt;
					set_message ('err_param_access_requires_param', {k:lower()});	-- parameter name is uppercase in cfg.id_handlers (k); lowercase for error message&lt;br /&gt;
				end&lt;br /&gt;
				id_accesses_list[k] = cfg.keywords_xlate[access_level];			-- get translated keyword&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return id_accesses_list;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; B U I L D _ I D _ L I S T &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
render the identifiers into a sorted sequence table&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ID_list_coins_t&amp;gt; is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value&lt;br /&gt;
&amp;lt;options_t&amp;gt; is a table of various k/v option pairs provided in the call to new_build_id_list();&lt;br /&gt;
	modified by	this function and passed to all identifier rendering functions&lt;br /&gt;
&amp;lt;access_levels_t&amp;gt; is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value (if valid)&lt;br /&gt;
&lt;br /&gt;
returns a sequence table of sorted (by hkey - 'handler' key) rendered identifier strings&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function build_id_list (ID_list_coins_t, options_t, access_levels_t)&lt;br /&gt;
	local ID_list_t = {};&lt;br /&gt;
	local accept;&lt;br /&gt;
	local func_map = {															--function map points to functions associated with hkey identifier&lt;br /&gt;
		['ARXIV'] = arxiv,&lt;br /&gt;
		['ASIN'] = asin,&lt;br /&gt;
		['BIBCODE'] = bibcode,&lt;br /&gt;
		['BIORXIV'] = biorxiv,&lt;br /&gt;
		['CITESEERX'] = citeseerx,&lt;br /&gt;
		['DOI'] = doi,&lt;br /&gt;
		['EISSN'] = issn,&lt;br /&gt;
		['HDL'] = hdl,&lt;br /&gt;
		['ISBN'] = isbn,&lt;br /&gt;
		['ISMN'] = ismn,&lt;br /&gt;
		['ISSN'] = issn,&lt;br /&gt;
		['JFM'] = jfm,&lt;br /&gt;
		['JSTOR'] = jstor,&lt;br /&gt;
		['LCCN'] = lccn,&lt;br /&gt;
		['MR'] = mr,&lt;br /&gt;
		['OCLC'] = oclc,&lt;br /&gt;
		['OL'] = openlibrary,&lt;br /&gt;
		['OSTI'] = osti,&lt;br /&gt;
		['PMC'] = pmc,&lt;br /&gt;
		['PMID'] = pmid,&lt;br /&gt;
		['RFC']  = rfc,&lt;br /&gt;
		['S2CID'] = s2cid,&lt;br /&gt;
		['SBN'] = sbn,&lt;br /&gt;
		['SSRN'] = ssrn,&lt;br /&gt;
		['USENETID'] = usenet_id,&lt;br /&gt;
		['ZBL'] = zbl,&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
	for hkey, v in pairs (ID_list_coins_t) do&lt;br /&gt;
		v, accept = has_accept_as_written (v);									-- remove accept-as-written markup if present; accept is boolean true when markup removed; false else&lt;br /&gt;
																				-- every function gets the options table with value v and accept boolean&lt;br /&gt;
		options_t.hkey = hkey;													-- ~/Configuration handler key&lt;br /&gt;
		options_t.id = v;														-- add that identifier value to the options table&lt;br /&gt;
		options_t.accept = accept;												-- add the accept boolean flag&lt;br /&gt;
		options_t.access = access_levels_t[hkey];								-- add the access level for those that have an |&amp;lt;identifier-access= parameter&lt;br /&gt;
		options_t.handler = cfg.id_handlers[hkey];&lt;br /&gt;
		options_t.coins_list_t = ID_list_coins_t;								-- pointer to ID_list_coins_t; for |asin= and |ol=; also to keep erroneous values out of the citation's metadata&lt;br /&gt;
		options_t.coins_list_t[hkey] = v;										-- id value without accept-as-written markup for metadata&lt;br /&gt;
		&lt;br /&gt;
		if options_t.handler.access and not in_array (options_t.handler.access, cfg.keywords_lists['id-access']) then&lt;br /&gt;
			error (cfg.messages['unknown_ID_access'] .. options_t.handler.access);	-- here when handler access key set to a value not listed in list of allowed id access keywords&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if func_map[hkey] then&lt;br /&gt;
			local id_text = func_map[hkey] (options_t);							-- call the function to get identifier text and any error message&lt;br /&gt;
			table.insert (ID_list_t, {hkey, id_text});							-- add identifier text to the output sequence table&lt;br /&gt;
		else&lt;br /&gt;
			error (cfg.messages['unknown_ID_key'] .. hkey);						-- here when func_map doesn't have a function for hkey&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local function comp (a, b)													-- used by following table.sort()&lt;br /&gt;
		return a[1]:lower() &amp;lt; b[1]:lower();										-- sort by hkey&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	table.sort (ID_list_t, comp);												-- sequence table of tables sort	&lt;br /&gt;
	for k, v in ipairs (ID_list_t) do											-- convert sequence table of tables to simple sequence table of strings&lt;br /&gt;
		ID_list_t[k] = v[2];													-- v[2] is the identifier rendering from the call to the various functions in func_map{}&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return ID_list_t;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; O P T I O N S _ C H E C K &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
check that certain option parameters have their associated identifier parameters with values&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ID_list_coins_t&amp;gt; is a table of k/v pairs where k is same as key in cfg.id_handlers and v is the assigned value&lt;br /&gt;
&amp;lt;ID_support_t&amp;gt; is a sequence table of tables created in citation0() where each subtable has four elements:&lt;br /&gt;
	[1] is the support parameter's assigned value; empty string if not set&lt;br /&gt;
	[2] is a text string same as key in cfg.id_handlers&lt;br /&gt;
	[3] is cfg.error_conditions key used to create error message&lt;br /&gt;
	[4] is original ID support parameter name used to create error message&lt;br /&gt;
	&lt;br /&gt;
returns nothing; on error emits an appropriate error message&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function options_check (ID_list_coins_t, ID_support_t)&lt;br /&gt;
	for _, v in ipairs (ID_support_t) do&lt;br /&gt;
		if is_set (v[1]) and not ID_list_coins_t[v[2]] then						-- when support parameter has a value but matching identifier parameter is missing or empty&lt;br /&gt;
			set_message (v[3], (v[4]));											-- emit the appropriate error message&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I D E N T I F I E R _ L I S T S _ G E T &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
Creates two identifier lists: a k/v table of identifiers and their values to be used locally and for use in the&lt;br /&gt;
COinS metadata, and a sequence table of the rendered identifier strings that will be included in the rendered&lt;br /&gt;
citation.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function identifier_lists_get (args_t, options_t, ID_support_t)&lt;br /&gt;
	local ID_list_coins_t = extract_ids (args_t);										-- get a table of identifiers and their values for use locally and for use in COinS&lt;br /&gt;
	options_check (ID_list_coins_t, ID_support_t);										-- ID support parameters must have matching identifier parameters &lt;br /&gt;
	local ID_access_levels_t = extract_id_access_levels (args_t, ID_list_coins_t);		-- get a table of identifier access levels&lt;br /&gt;
	local ID_list_t = build_id_list (ID_list_coins_t, options_t, ID_access_levels_t);	-- get a sequence table of rendered identifier strings&lt;br /&gt;
&lt;br /&gt;
	return ID_list_t, ID_list_coins_t;											-- return the tables&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E T _ S E L E C T E D _ M O D U L E S &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
Sets local cfg table and imported functions table to same (live or sandbox) as that used by the other modules.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)&lt;br /&gt;
	cfg = cfg_table_ptr;&lt;br /&gt;
&lt;br /&gt;
	has_accept_as_written = utilities_page_ptr.has_accept_as_written;			-- import functions from select Module:Citation/CS1/Utilities module&lt;br /&gt;
	is_set = utilities_page_ptr.is_set;								&lt;br /&gt;
	in_array = utilities_page_ptr.in_array;&lt;br /&gt;
	set_message = utilities_page_ptr.set_message;&lt;br /&gt;
	select_one = utilities_page_ptr.select_one;&lt;br /&gt;
	substitute = utilities_page_ptr.substitute;&lt;br /&gt;
	make_wikilink = utilities_page_ptr.make_wikilink;&lt;br /&gt;
&lt;br /&gt;
	z = utilities_page_ptr.z;													-- table of tables in Module:Citation/CS1/Utilities&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X P O R T E D   F U N C T I O N S &amp;gt;------------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
	auto_link_urls = auto_link_urls,											-- table of identifier URLs to be used when auto-linking |title=&lt;br /&gt;
	&lt;br /&gt;
	identifier_lists_get = identifier_lists_get,								-- experiment to replace individual calls to build_id_list(), extract_ids, extract_id_access_levels&lt;br /&gt;
	is_embargoed = is_embargoed;&lt;br /&gt;
	set_selected_modules = set_selected_modules;&lt;br /&gt;
	}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Date_validation&amp;diff=263</id>
		<title>Module:Citation/CS1/Date validation</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Date_validation&amp;diff=263"/>
		<updated>2022-04-04T14:04:36Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;----------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------   local add_prop_cat, is_set, in_array, set_message, substitu...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[--------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local add_prop_cat, is_set, in_array, set_message, substitute, wrap_style;		-- imported functions from selected Module:Citation/CS1/Utilities&lt;br /&gt;
local cfg;																		-- table of tables imported from selected Module:Citation/CS1/Configuration&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; F I L E - S C O P E   D E C L A R A T I O N S &amp;gt;--------------------------------&lt;br /&gt;
&lt;br /&gt;
File-scope variables are declared here&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local lang_object = mw.getContentLanguage();									-- used by is_valid_accessdate(), is_valid_year(), date_name_xlate(); TODO: move to ~/Configuration?&lt;br /&gt;
local year_limit;																-- used by is_valid_year()&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; I S _ V A L I D _ A C C E S S D A T E &amp;gt;----------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns true if:&lt;br /&gt;
	Wikipedia start date &amp;lt;= accessdate &amp;lt; today + 2 days&lt;br /&gt;
&lt;br /&gt;
Wikipedia start date is 2001-01-15T00:00:00 UTC which is 979516800 seconds after 1970-01-01T00:00:00 UTC (the start of Unix time)&lt;br /&gt;
accessdate is the date provided in |access-date= at time 00:00:00 UTC&lt;br /&gt;
today is the current date at time 00:00:00 UTC plus 48 hours&lt;br /&gt;
	if today is 2015-01-01T00:00:00 then&lt;br /&gt;
		adding 24 hours gives 2015-01-02T00:00:00 – one second more than today&lt;br /&gt;
		adding 24 hours gives 2015-01-03T00:00:00 – one second more than tomorrow&lt;br /&gt;
&lt;br /&gt;
This function does not work if it is fed month names for languages other than English.  Wikimedia #time: parser&lt;br /&gt;
apparently doesn't understand non-English date month names. This function will always return false when the date&lt;br /&gt;
contains a non-English month name because good1 is false after the call to lang.formatDate().  To get around that&lt;br /&gt;
call this function with YYYY-MM-DD format dates.&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_accessdate (accessdate)&lt;br /&gt;
	local good1, good2;&lt;br /&gt;
	local access_ts, tomorrow_ts;												-- to hold Unix time stamps representing the dates&lt;br /&gt;
&lt;br /&gt;
	good1, access_ts = pcall (lang_object.formatDate, lang_object, 'U', accessdate );			-- convert accessdate value to Unix timestamp &lt;br /&gt;
	good2, tomorrow_ts = pcall (lang_object.formatDate, lang_object, 'U', 'today + 2 days' );	-- today midnight + 2 days is one second more than all day tomorrow&lt;br /&gt;
	&lt;br /&gt;
	if good1 and good2 then														-- lang.formatDate() returns a timestamp in the local script which which tonumber() may not understand&lt;br /&gt;
		access_ts = tonumber (access_ts) or lang_object:parseFormattedNumber (access_ts);		-- convert to numbers for the comparison;&lt;br /&gt;
		tomorrow_ts = tonumber (tomorrow_ts) or lang_object:parseFormattedNumber (tomorrow_ts);&lt;br /&gt;
	else&lt;br /&gt;
		return false;															-- one or both failed to convert to Unix time stamp&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if 979516800 &amp;lt;= access_ts and access_ts &amp;lt; tomorrow_ts then					-- Wikipedia start date &amp;lt;= accessdate &amp;lt; tomorrow's date&lt;br /&gt;
		return true;&lt;br /&gt;
	else&lt;br /&gt;
		return false;															-- accessdate out of range&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; G E T _ M O N T H _ N U M B E R &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns a number according to the month in a date: 1 for January, etc.  Capitalization and spelling must be correct.&lt;br /&gt;
If not a valid month, returns 0&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function get_month_number (month)&lt;br /&gt;
	return cfg.date_names['local'].long[month] or cfg.date_names['local'].short[month] or	-- look for local names first&lt;br /&gt;
			cfg.date_names['en'].long[month] or	cfg.date_names['en'].short[month] or		-- failing that, look for English names&lt;br /&gt;
			0;																				-- not a recognized month name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; G E T _ S E A S O N _ N U M B E R &amp;gt;--------------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns a number according to the sequence of seasons in a year: 21 for Spring, etc.  Capitalization and spelling&lt;br /&gt;
must be correct. If not a valid season, returns 0.&lt;br /&gt;
	21-24 = Spring, Summer, Autumn, Winter, independent of “Hemisphere”&lt;br /&gt;
&lt;br /&gt;
returns 0 when &amp;lt;param&amp;gt; is not |date=&lt;br /&gt;
&lt;br /&gt;
Season numbering is defined by Extended Date/Time Format (EDTF) specification (https://www.loc.gov/standards/datetime/)&lt;br /&gt;
which became part of ISO 8601 in 2019.  See '§Sub-year groupings'.  The standard defines various divisions using&lt;br /&gt;
numbers 21-41.  cs1|2 only supports generic seasons.  EDTF does support the distinction between north and south&lt;br /&gt;
hemisphere seasons but cs1|2 has no way to make that distinction.&lt;br /&gt;
&lt;br /&gt;
These additional divisions not currently supported:&lt;br /&gt;
	25-28 = Spring - Northern Hemisphere, Summer- Northern Hemisphere, Autumn - Northern Hemisphere, Winter - Northern Hemisphere&lt;br /&gt;
	29-32 = Spring – Southern Hemisphere, Summer– Southern Hemisphere, Autumn – Southern Hemisphere, Winter - Southern Hemisphere&lt;br /&gt;
	33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)&lt;br /&gt;
	37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each)&lt;br /&gt;
	40-41 = Semestral 1, Semestral-2 (6 months each)&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function get_season_number (season, param)&lt;br /&gt;
	if 'date' ~= param then&lt;br /&gt;
		return 0;																-- season dates only supported by |date=&lt;br /&gt;
	end&lt;br /&gt;
	return cfg.date_names['local'].season[season] or							-- look for local names first&lt;br /&gt;
			cfg.date_names['en'].season[season] or								-- failing that, look for English names&lt;br /&gt;
			0;																	-- not a recognized season name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; G E T _ Q U A R T E R _ N U M B E R &amp;gt;------------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns a number according to the sequence of quarters in a year: 33 for first quarter, etc.  Capitalization and spelling&lt;br /&gt;
must be correct. If not a valid quarter, returns 0.&lt;br /&gt;
	33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)&lt;br /&gt;
&lt;br /&gt;
returns 0 when &amp;lt;param&amp;gt; is not |date=&lt;br /&gt;
&lt;br /&gt;
Quarter numbering is defined by Extended Date/Time Format (EDTF) specification (https://www.loc.gov/standards/datetime/)&lt;br /&gt;
which became part of ISO 8601 in 2019.  See '§Sub-year groupings'.  The standard defines various divisions using&lt;br /&gt;
numbers 21-41.  cs1|2 only supports generic seasons and quarters.&lt;br /&gt;
&lt;br /&gt;
These additional divisions not currently supported:&lt;br /&gt;
	37-39 = Quadrimester 1, Quadrimester 2, Quadrimester 3 (4 months each)&lt;br /&gt;
	40-41 = Semestral 1, Semestral-2 (6 months each)&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function get_quarter_number (quarter, param)&lt;br /&gt;
	if 'date' ~= param then&lt;br /&gt;
		return 0;																-- quarter dates only supported by |date=&lt;br /&gt;
	end&lt;br /&gt;
	quarter = mw.ustring.gsub (quarter, ' +', ' ');								-- special case replace multiple space chars with a single space char&lt;br /&gt;
	return cfg.date_names['local'].quarter[quarter] or							-- look for local names first&lt;br /&gt;
			cfg.date_names['en'].quarter[quarter] or							-- failing that, look for English names&lt;br /&gt;
			0;																	-- not a recognized quarter name&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; G E T _ P R O P E R _ N A M E _ N U M B E R &amp;gt;----------------------------------&lt;br /&gt;
&lt;br /&gt;
returns a non-zero number if date contains a recognized proper-name.  Capitalization and spelling must be correct.&lt;br /&gt;
&lt;br /&gt;
returns 0 when &amp;lt;param&amp;gt; is not |date=&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function get_proper_name_number (name, param)&lt;br /&gt;
	if 'date' ~= param then&lt;br /&gt;
		return 0;																-- proper-name dates only supported by |date=&lt;br /&gt;
	end&lt;br /&gt;
	return cfg.date_names['local'].named[name] or								-- look for local names dates first&lt;br /&gt;
			cfg.date_names['en'].named[name] or									-- failing that, look for English names&lt;br /&gt;
			0;																	-- not a recognized named date&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; G E T _ E L E M E N T _ N U M B E R &amp;lt;------------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns true if month or season or quarter or proper name is valid (properly spelled, capitalized, abbreviated)&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function get_element_number (element, param)&lt;br /&gt;
	local num;&lt;br /&gt;
	&lt;br /&gt;
	local funcs = {get_month_number, get_season_number, get_quarter_number, get_proper_name_number};	-- list of functions to execute in order&lt;br /&gt;
	&lt;br /&gt;
	for _, func in ipairs (funcs) do											-- spin through the function list&lt;br /&gt;
		num = func (element, param);											-- call the function and get the returned number&lt;br /&gt;
		if 0 ~= num then														-- non-zero when valid month season quarter &lt;br /&gt;
			return num;															-- return that number&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return nil;																	-- not valid&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ V A L I D _ Y E A R &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Function gets current year from the server and compares it to year from a citation parameter.  Years more than one&lt;br /&gt;
year in the future are not acceptable.&lt;br /&gt;
&lt;br /&gt;
Special case for |pmc-embargo-date=: years more than two years in the future are not acceptable&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_year (year, param)&lt;br /&gt;
	if not is_set (year_limit) then&lt;br /&gt;
		year_limit = tonumber(os.date(&amp;quot;%Y&amp;quot;))+1;									-- global variable so we only have to fetch it once&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	year = tonumber (year) or lang_object:parseFormattedNumber (year);			-- convert to number for the comparison;&lt;br /&gt;
	&lt;br /&gt;
	if 'pmc-embargo-date' == param then											-- special case for |pmc-embargo-date=&lt;br /&gt;
		return year and (year &amp;lt;= tonumber(os.date(&amp;quot;%Y&amp;quot;))+2) or false;			-- years more than two years in the future are not accepted&lt;br /&gt;
	end	&lt;br /&gt;
	return year and (year &amp;lt;= year_limit) or false;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ V A L I D _ D A T E &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Returns true if day is less than or equal to the number of days in month and year is no farther into the future&lt;br /&gt;
than next year; else returns false.&lt;br /&gt;
&lt;br /&gt;
Assumes Julian calendar prior to year 1582 and Gregorian calendar thereafter. Accounts for Julian calendar leap&lt;br /&gt;
years before 1582 and Gregorian leap years after 1582. Where the two calendars overlap (1582 to approximately&lt;br /&gt;
1923) dates are assumed to be Gregorian.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_date (year, month, day, param)&lt;br /&gt;
local days_in_month = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};&lt;br /&gt;
local month_length;&lt;br /&gt;
	if not is_valid_year (year, param) then										-- no farther into the future than next year except |pmc-embargo-date= no more than two years in the future&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	month = tonumber (month);													-- required for YYYY-MM-DD dates&lt;br /&gt;
	&lt;br /&gt;
	if (2 == month) then														-- if February&lt;br /&gt;
		month_length = 28;														-- then 28 days unless&lt;br /&gt;
		if 1582 &amp;gt; tonumber(year) then											-- Julian calendar&lt;br /&gt;
			if 0 == (year%4) then												-- is a leap year?&lt;br /&gt;
				month_length = 29;												-- if leap year then 29 days in February&lt;br /&gt;
			end&lt;br /&gt;
		else																	-- Gregorian calendar&lt;br /&gt;
			if (0 == (year%4) and (0 ~= (year%100) or 0 == (year%400))) then	-- is a leap year?&lt;br /&gt;
				month_length = 29;												-- if leap year then 29 days in February&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		month_length = days_in_month[month];&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if tonumber (day) &amp;gt; month_length then&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
	return true;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ V A L I D _ M O N T H _ R A N G E _ S T Y L E &amp;gt;--------------------------&lt;br /&gt;
&lt;br /&gt;
Months in a range are expected to have the same style: Jan–Mar or October–December but not February–Mar or Jul–August. &lt;br /&gt;
This function looks in cfg.date_names{} to see if both month names are listed in the long subtable or both are&lt;br /&gt;
listed in the short subtable.  When both have the same style (both are listed in the same table), returns true; false else&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_month_range_style (month1, month2)&lt;br /&gt;
	if (cfg.date_names.en.long[month1] and cfg.date_names.en.long[month2]) or					-- are both English names listed in the long subtable?&lt;br /&gt;
		(cfg.date_names.en.short[month1] and cfg.date_names.en.short[month2]) or				-- are both English names listed in the short subtable?&lt;br /&gt;
		(cfg.date_names['local'].long[month1] and cfg.date_names['local'].long[month2]) or		-- are both local names listed in the long subtable?&lt;br /&gt;
		(cfg.date_names['local'].short[month1] and cfg.date_names['local'].short[month2]) then	-- are both local names listed in the short subtable?&lt;br /&gt;
			return true;&lt;br /&gt;
	end&lt;br /&gt;
	return false;																-- names are mixed&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ V A L I D _ M O N T H _ S E A S O N _ R A N G E &amp;gt;------------------------&lt;br /&gt;
&lt;br /&gt;
Check a pair of months or seasons to see if both are valid members of a month or season pair.&lt;br /&gt;
&lt;br /&gt;
Month pairs are expected to be left to right, earliest to latest in time.&lt;br /&gt;
&lt;br /&gt;
All season ranges are accepted as valid because there are publishers out there who have published a Summer–Spring YYYY issue, hence treat as ok&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_month_season_range(range_start, range_end, param)&lt;br /&gt;
	local range_start_number = get_month_number (range_start);&lt;br /&gt;
	local range_end_number;&lt;br /&gt;
&lt;br /&gt;
	if 0 == range_start_number then												-- is this a month range?&lt;br /&gt;
		range_start_number = get_season_number (range_start, param);			-- not a month; is it a season? get start season number&lt;br /&gt;
		range_end_number = get_season_number (range_end, param);				-- get end season number&lt;br /&gt;
&lt;br /&gt;
		if (0 ~= range_start_number) and (0 ~= range_end_number) and (range_start_number ~= range_end_number) then&lt;br /&gt;
			return true;														-- any season pairing is accepted except when both are the same&lt;br /&gt;
		end&lt;br /&gt;
		return false;															-- range_start and/or range_end is not a season&lt;br /&gt;
	end&lt;br /&gt;
																				-- here when range_start is a month&lt;br /&gt;
	range_end_number = get_month_number (range_end);							-- get end month number&lt;br /&gt;
	if range_start_number &amp;lt; range_end_number and								-- range_start is a month; does range_start precede range_end?&lt;br /&gt;
		is_valid_month_range_style (range_start, range_end) then				-- do months have the same style?&lt;br /&gt;
			return true;														-- proper order and same style&lt;br /&gt;
	end&lt;br /&gt;
	return false;																-- range_start month number is greater than or equal to range end number; or range end isn't a month&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; M A K E _ C O I N S _ D A T E &amp;gt;------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
This function receives a table of date parts for one or two dates and an empty table reference declared in&lt;br /&gt;
Module:Citation/CS1.  The function is called only for |date= parameters and only if the |date=&amp;lt;value&amp;gt; is &lt;br /&gt;
determined to be a valid date format.  The question of what to do with invalid date formats is not answered here.&lt;br /&gt;
&lt;br /&gt;
The date parts in the input table are converted to an ISO 8601 conforming date string:&lt;br /&gt;
	single whole dates:		yyyy-mm-dd&lt;br /&gt;
	month and year dates:	yyyy-mm&lt;br /&gt;
	year dates:				yyyy&lt;br /&gt;
	ranges:					yyyy-mm-dd/yyyy-mm-dd&lt;br /&gt;
							yyyy-mm/yyyy-mm&lt;br /&gt;
							yyyy/yyyy&lt;br /&gt;
&lt;br /&gt;
Dates in the Julian calendar are reduced to year or year/year so that we don't have to do calendar conversion from&lt;br /&gt;
Julian to Proleptic Gregorian.&lt;br /&gt;
&lt;br /&gt;
The input table has:&lt;br /&gt;
	year, year2 – always present; if before 1582, ignore months and days if present&lt;br /&gt;
	month, month2 – 0 if not provided, 1-12 for months, 21-24 for seasons; 99 Christmas&lt;br /&gt;
	day, day2 –  0 if not provided, 1-31 for days&lt;br /&gt;
	&lt;br /&gt;
the output table receives:&lt;br /&gt;
	rftdate:	an ISO 8601 formatted date&lt;br /&gt;
	rftchron:	a free-form version of the date, usually without year which is in rftdate (season ranges and proper-name dates)&lt;br /&gt;
	rftssn:		one of four season keywords: winter, spring, summer, fall (lowercase)&lt;br /&gt;
	rftquarter:	one of four values: 1, 2, 3, 4&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function make_COinS_date (input, tCOinS_date)&lt;br /&gt;
	local date;																	-- one date or first date in a range&lt;br /&gt;
	local date2 = '';															-- end of range date&lt;br /&gt;
-- start temporary Julian / Gregorian calendar uncertainty detection&lt;br /&gt;
	local year = tonumber(input.year);											-- this temporary code to determine the extent of sources dated to the Julian/Gregorian&lt;br /&gt;
	local month = tonumber(input.month);										-- interstice 1 October 1582 – 1 January 1926&lt;br /&gt;
	local day = tonumber (input.day);&lt;br /&gt;
	if (0 ~= day) and															-- day must have a value for this to be a whole date&lt;br /&gt;
		(((1582 == year) and (10 &amp;lt;= month) and (12 &amp;gt;= month)) or				-- any whole 1582 date from 1 October to 31 December or&lt;br /&gt;
			((1926 == year) and (1 == month) and (1 == input.day)) or			-- 1 January 1926 or&lt;br /&gt;
				((1582 &amp;lt; year) and (1925 &amp;gt;= year))) then						-- any date 1 January 1583 – 31 December 1925&lt;br /&gt;
					tCOinS_date.inter_cal_cat = true;							-- set category flag true&lt;br /&gt;
	end&lt;br /&gt;
-- end temporary Julian / Gregorian calendar uncertainty detection&lt;br /&gt;
	&lt;br /&gt;
	if 1582 &amp;gt; tonumber(input.year) or 20 &amp;lt; tonumber(input.month) then			-- Julian calendar or season so &amp;amp;rft.date gets year only&lt;br /&gt;
		date = input.year;&lt;br /&gt;
		if 0 ~= input.year2 and input.year ~= input.year2 then					-- if a range, only the second year portion when not the same as range start year&lt;br /&gt;
			date = string.format ('%.4d/%.4d', tonumber(input.year), tonumber(input.year2))		-- assemble the date range&lt;br /&gt;
		end&lt;br /&gt;
		if 20 &amp;lt; tonumber(input.month) then										-- if season or proper-name date&lt;br /&gt;
			local season = {[24] = 'winter', [21] = 'spring', [22] = 'summer', [23] = 'fall', [33] = '1', [34] = '2', [35] = '3', [36] = '4', [98] = 'Easter', [99] = 'Christmas'};	-- seasons lowercase, no autumn; proper-names use title case&lt;br /&gt;
			if 0 == input.month2 then											-- single season date&lt;br /&gt;
				if 40 &amp;lt; tonumber(input.month) then&lt;br /&gt;
					tCOinS_date.rftchron = season[input.month];					-- proper-name dates&lt;br /&gt;
				elseif 30 &amp;lt; tonumber(input.month) then&lt;br /&gt;
					tCOinS_date.rftquarter = season[input.month];				-- quarters&lt;br /&gt;
				else&lt;br /&gt;
					tCOinS_date.rftssn = season[input.month];					-- seasons&lt;br /&gt;
				end&lt;br /&gt;
			else																-- season range with a second season specified&lt;br /&gt;
				if input.year ~= input.year2 then								-- season year – season year range or season year–year&lt;br /&gt;
					tCOinS_date.rftssn = season[input.month];					-- start of range season; keep this?&lt;br /&gt;
					if 0~= input.month2 then&lt;br /&gt;
						tCOinS_date.rftchron = string.format ('%s %s – %s %s', season[input.month], input.year, season[input.month2], input.year2);&lt;br /&gt;
					end&lt;br /&gt;
				else															-- season–season year range&lt;br /&gt;
					tCOinS_date.rftssn = season[input.month];					-- start of range season; keep this?&lt;br /&gt;
					tCOinS_date.rftchron = season[input.month] .. '–' .. season[input.month2];	-- season–season year range&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		tCOinS_date.rftdate = date;&lt;br /&gt;
		return;																	-- done&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if 0 ~= input.day then&lt;br /&gt;
		date = string.format ('%s-%.2d-%.2d', input.year, tonumber(input.month), tonumber(input.day));	-- whole date&lt;br /&gt;
	elseif 0 ~= input.month then&lt;br /&gt;
		date = string.format ('%s-%.2d', input.year, tonumber(input.month));	-- year and month&lt;br /&gt;
	else&lt;br /&gt;
		date = string.format ('%s', input.year);								-- just year&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if 0 ~= input.year2 then&lt;br /&gt;
		if 0 ~= input.day2 then&lt;br /&gt;
			date2 = string.format ('/%s-%.2d-%.2d', input.year2, tonumber(input.month2), tonumber(input.day2));		-- whole date&lt;br /&gt;
		elseif 0 ~= input.month2 then&lt;br /&gt;
			date2 = string.format ('/%s-%.2d', input.year2, tonumber(input.month2));	-- year and month&lt;br /&gt;
		else&lt;br /&gt;
			date2 = string.format ('/%s', input.year2);							-- just year&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	tCOinS_date.rftdate = date .. date2;										-- date2 has the '/' separator&lt;br /&gt;
	return;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P A T T E R N S &amp;gt;--------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
this is the list of patterns for date formats that this module recognizes.  Approximately the first half of these&lt;br /&gt;
patterns represent formats that might be reformatted into another format.  Those that might be reformatted have&lt;br /&gt;
'indicator' letters that identify the content of the matching capture: 'd' (day), 'm' (month), 'a' (anchor year),&lt;br /&gt;
'y' (year); second day, month, year have a '2' suffix.&lt;br /&gt;
&lt;br /&gt;
These patterns are used for both date validation and for reformatting.  This table should not be moved to ~/Configuration&lt;br /&gt;
because changes to this table require changes to check_date() and to reformatter() and reformat_date()&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local patterns = {&lt;br /&gt;
	 																			-- year-initial numerical year-month-day&lt;br /&gt;
	['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'},					&lt;br /&gt;
																				-- month-initial: month day, year&lt;br /&gt;
	['Mdy'] = {'^(%D-) +([1-9]%d?), +((%d%d%d%d?)%a?)$', 'm', 'd', 'a', 'y'},&lt;br /&gt;
																				-- month-initial day range: month day–day, year; days are separated by endash&lt;br /&gt;
	['Md-dy'] = {'^(%D-) +([1-9]%d?)[%-–]([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'd2', 'a', 'y'},&lt;br /&gt;
																				-- day-initial: day month year&lt;br /&gt;
	['dMy'] = {'^([1-9]%d?) +(%D-) +((%d%d%d%d?)%a?)$', 'd', 'm', 'a', 'y'},&lt;br /&gt;
																				-- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed; not supported at en.wiki&lt;br /&gt;
	--	['yMd'] = {'^((%d%d%d%d?)%a?) +(%D-) +(%d%d?)$', 'a', 'y', 'm', 'd'},&lt;br /&gt;
																				-- day-range-initial: day–day month year; days are separated by endash&lt;br /&gt;
	['d-dMy'] = {'^([1-9]%d?)[%-–]([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'd2', 'm', 'a', 'y'},&lt;br /&gt;
																				-- day initial month-day-range: day month - day month year; uses spaced endash&lt;br /&gt;
	['dM-dMy'] = {'^([1-9]%d?) +(%D-) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'd2', 'm2', 'a', 'y'},&lt;br /&gt;
																				-- month initial month-day-range: month day – month day, year;  uses spaced endash&lt;br /&gt;
	['Md-Mdy'] = {'^(%D-) +([1-9]%d?) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$','m', 'd', 'm2', 'd2', 'a', 'y'},&lt;br /&gt;
																				-- day initial month-day-year-range: day month year - day month year; uses spaced endash&lt;br /&gt;
	['dMy-dMy'] = {'^([1-9]%d?) +(%D-) +(%d%d%d%d) +[%-–] +([1-9]%d?) +(%D-) +((%d%d%d%d)%a?)$', 'd', 'm', 'y', 'd2', 'm2', 'a', 'y2'},&lt;br /&gt;
																				-- month initial month-day-year-range: month day, year – month day, year;  uses spaced endash&lt;br /&gt;
	['Mdy-Mdy'] = {'^(%D-) +([1-9]%d?), +(%d%d%d%d) +[%-–] +(%D-) +([1-9]%d?), +((%d%d%d%d)%a?)$', 'm', 'd', 'y', 'm2', 'd2', 'a', 'y2'},&lt;br /&gt;
&lt;br /&gt;
																				-- these date formats cannot be converted, per se, but month name can be rendered short or long&lt;br /&gt;
																				-- month/season year - month/season year; separated by spaced endash&lt;br /&gt;
	['My-My'] = {'^(%D-) +(%d%d%d%d) +[%-–] +(%D-) +((%d%d%d%d)%a?)$', 'm', 'y', 'm2', 'a', 'y2'},&lt;br /&gt;
																				-- month/season range year; months separated by endash&lt;br /&gt;
	['M-My'] = {'^(%D-)[%-–](%D-) +((%d%d%d%d)%a?)$', 'm', 'm2', 'a', 'y'},&lt;br /&gt;
																				-- month/season year or proper-name year; quarter year when First Quarter YYYY etc.&lt;br /&gt;
	['My'] = {'^([^%d–]-) +((%d%d%d%d)%a?)$', 'm', 'a', 'y'},					-- this way because endash is a member of %D; %D- will match January–March 2019 when it shouldn't&lt;br /&gt;
&lt;br /&gt;
																				-- these date formats cannot be converted&lt;br /&gt;
	['Sy4-y2'] = {'^(%D-) +((%d%d)%d%d)[%-–]((%d%d)%a?)$'},						-- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash&lt;br /&gt;
	['Sy-y'] = {'^(%D-) +(%d%d%d%d)[%-–]((%d%d%d%d)%a?)$'},						-- special case Winter/Summer year-year; year separated with unspaced endash&lt;br /&gt;
	['y-y'] = {'^(%d%d%d%d?)[%-–]((%d%d%d%d?)%a?)$'},							-- year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999&lt;br /&gt;
	['y4-y2'] = {'^((%d%d)%d%d)[%-–]((%d%d)%a?)$'},								-- year range: YYYY–YY; separated by unspaced endash&lt;br /&gt;
	['y'] = {'^((%d%d%d%d?)%a?)$'},												-- year; here accept either YYY or YYYY&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ V A L I D _ E M B A R G O _ D A T E &amp;gt;------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns true and date value if that value has proper dmy, mdy, ymd format.&lt;br /&gt;
&lt;br /&gt;
returns false and 9999 (embargoed forever) when date value is not proper format; assumes that when |pmc-embargo-date= is&lt;br /&gt;
set, the editor intended to embargo a PMC but |pmc-embargo-date= does not hold a single date.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_embargo_date (v)&lt;br /&gt;
	if v:match (patterns['ymd'][1]) or											-- ymd&lt;br /&gt;
		v:match (patterns['Mdy'][1]) or											-- dmy&lt;br /&gt;
		v:match (patterns['dMy'][1]) then										-- mdy&lt;br /&gt;
			return true, v;&lt;br /&gt;
	end&lt;br /&gt;
	return false, '9999';														-- if here not good date so return false and set embargo date to long time in future&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C H E C K _ D A T E &amp;gt;----------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Check date format to see that it is one of the formats approved by WP:DATESNO or WP:DATERANGE. Exception: only&lt;br /&gt;
allowed range separator is endash.  Additionally, check the date to see that it is a real date: no 31 in 30-day&lt;br /&gt;
months; no 29 February when not a leap year.  Months, both long-form and three character abbreviations, and seasons&lt;br /&gt;
must be spelled correctly.  Future years beyond next year are not allowed.&lt;br /&gt;
&lt;br /&gt;
If the date fails the format tests, this function returns false and does not return values for anchor_year and&lt;br /&gt;
COinS_date.  When this happens, the date parameter is (DEBUG: not?) used in the COinS metadata and the CITEREF identifier gets&lt;br /&gt;
its year from the year parameter if present otherwise CITEREF does not get a date value.&lt;br /&gt;
&lt;br /&gt;
Inputs:&lt;br /&gt;
	date_string - date string from date-holding parameters (date, year, publication-date, access-date, pmc-embargo-date, archive-date, lay-date)&lt;br /&gt;
&lt;br /&gt;
Returns:&lt;br /&gt;
	false if date string is not a real date; else&lt;br /&gt;
	true, anchor_year, COinS_date&lt;br /&gt;
		anchor_year can be used in CITEREF anchors&lt;br /&gt;
		COinS_date is ISO 8601 format date; see make_COInS_date()&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function check_date (date_string, param, tCOinS_date)&lt;br /&gt;
	local year;																	-- assume that year2, months, and days are not used;&lt;br /&gt;
	local year2 = 0;															-- second year in a year range&lt;br /&gt;
	local month = 0;&lt;br /&gt;
	local month2 = 0;															-- second month in a month range&lt;br /&gt;
	local day = 0;&lt;br /&gt;
	local day2 = 0;																-- second day in a day range&lt;br /&gt;
	local anchor_year;&lt;br /&gt;
	local coins_date;&lt;br /&gt;
&lt;br /&gt;
	if date_string:match (patterns['ymd'][1]) then								-- year-initial numerical year month day format&lt;br /&gt;
		year, month, day = date_string:match (patterns['ymd'][1]);&lt;br /&gt;
		if 12 &amp;lt; tonumber(month) or 1 &amp;gt; tonumber(month) or 1582 &amp;gt; tonumber(year) or 0 == tonumber(day) then return false; end	-- month or day number not valid or not Gregorian calendar&lt;br /&gt;
		anchor_year = year;&lt;br /&gt;
	&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['Mdy'][1]) then				-- month-initial: month day, year&lt;br /&gt;
		month, day, anchor_year, year = mw.ustring.match(date_string, patterns['Mdy'][1]);&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end									-- return false if month text isn't one of the twelve months&lt;br /&gt;
				&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['Md-dy'][1]) then				-- month-initial day range: month day–day, year; days are separated by endash&lt;br /&gt;
		month, day, day2, anchor_year, year = mw.ustring.match(date_string, patterns['Md-dy'][1]);&lt;br /&gt;
		if tonumber(day) &amp;gt;= tonumber(day2) then return false; end				-- date range order is left to right: earlier to later; dates may not be the same;&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end									-- return false if month text isn't one of the twelve months&lt;br /&gt;
		month2=month;															-- for metadata&lt;br /&gt;
		year2 = year;&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['dMy'][1]) then				-- day-initial: day month year&lt;br /&gt;
		day, month, anchor_year, year = mw.ustring.match(date_string, patterns['dMy'][1]);&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end									-- return false if month text isn't one of the twelve months&lt;br /&gt;
&lt;br /&gt;
--[[ NOT supported at en.wiki&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['yMd'][1]) then				-- year-initial: year month day; day: 1 or 2 two digits, leading zero allowed&lt;br /&gt;
		anchor_year, year, month, day = mw.ustring.match(date_string, patterns['yMd'][1]);&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end									-- return false if month text isn't one of the twelve months&lt;br /&gt;
-- end NOT supported at en.wiki ]]&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['d-dMy'][1]) then				-- day-range-initial: day–day month year; days are separated by endash&lt;br /&gt;
		day, day2, month, anchor_year, year = mw.ustring.match(date_string, patterns['d-dMy'][1]);&lt;br /&gt;
		if tonumber(day) &amp;gt;= tonumber(day2) then return false; end				-- date range order is left to right: earlier to later; dates may not be the same;&lt;br /&gt;
		month = get_month_number (month);&lt;br /&gt;
		if 0 == month then return false; end									-- return false if month text isn't one of the twelve months&lt;br /&gt;
		month2 = month;															-- for metadata&lt;br /&gt;
		year2 = year;&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['dM-dMy'][1]) then			-- day initial month-day-range: day month - day month year; uses spaced endash&lt;br /&gt;
		day, month, day2, month2, anchor_year, year = mw.ustring.match(date_string, patterns['dM-dMy'][1]);&lt;br /&gt;
		if (not is_valid_month_season_range(month, month2)) or not is_valid_year(year) then return false; end	-- date range order is left to right: earlier to later;&lt;br /&gt;
		month = get_month_number (month);										-- for metadata&lt;br /&gt;
		month2 = get_month_number (month2);&lt;br /&gt;
		year2 = year;&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['Md-Mdy'][1]) then			-- month initial month-day-range: month day – month day, year; uses spaced endash&lt;br /&gt;
		month, day, month2, day2, anchor_year, year = mw.ustring.match(date_string, patterns['Md-Mdy'][1]);&lt;br /&gt;
		if (not is_valid_month_season_range(month, month2, param)) or not is_valid_year(year) then return false; end&lt;br /&gt;
		month = get_month_number (month);										-- for metadata&lt;br /&gt;
		month2 = get_month_number (month2);&lt;br /&gt;
		year2 = year;&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['dMy-dMy'][1]) then			-- day initial month-day-year-range: day month year - day month year; uses spaced endash&lt;br /&gt;
		day, month, year, day2, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns['dMy-dMy'][1]);&lt;br /&gt;
		if tonumber(year2) &amp;lt;= tonumber(year) then return false; end				-- must be sequential years, left to right, earlier to later&lt;br /&gt;
		if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end		-- year2 no more than one year in the future; months same style&lt;br /&gt;
		month = get_month_number (month);										-- for metadata&lt;br /&gt;
		month2 = get_month_number (month2);&lt;br /&gt;
		if 0 == month or 0 == month2 then return false; end						-- both must be valid&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['Mdy-Mdy'][1]) then			-- month initial month-day-year-range: month day, year – month day, year; uses spaced endash&lt;br /&gt;
		month, day, year, month2, day2, anchor_year, year2 = mw.ustring.match(date_string, patterns['Mdy-Mdy'][1]);&lt;br /&gt;
		if tonumber(year2) &amp;lt;= tonumber(year) then return false; end				-- must be sequential years, left to right, earlier to later&lt;br /&gt;
		if not is_valid_year(year2) or not is_valid_month_range_style(month, month2) then return false; end		-- year2 no more than one year in the future; months same style&lt;br /&gt;
		month = get_month_number (month);										-- for metadata&lt;br /&gt;
		month2 = get_month_number(month2);&lt;br /&gt;
		if 0 == month or 0 == month2 then return false; end						-- both must be valid&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['Sy4-y2'][1]) then			-- special case Winter/Summer year-year (YYYY-YY); year separated with unspaced endash&lt;br /&gt;
		local century;&lt;br /&gt;
		month, year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy4-y2'][1]);&lt;br /&gt;
		if 'Winter' ~= month and 'Summer' ~= month then return false end;		-- 'month' can only be Winter or Summer&lt;br /&gt;
		anchor_year = year .. '–' .. anchor_year;								-- assemble anchor_year from both years&lt;br /&gt;
		year2 = century..year2;													-- add the century to year2 for comparisons&lt;br /&gt;
		if 1 ~= tonumber(year2) - tonumber(year) then return false; end			-- must be sequential years, left to right, earlier to later&lt;br /&gt;
		if not is_valid_year(year2) then return false; end						-- no year farther in the future than next year&lt;br /&gt;
		month = get_season_number(month, param);&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['Sy-y'][1]) then				-- special case Winter/Summer year-year; year separated with unspaced endash&lt;br /&gt;
		month, year, anchor_year, year2 = mw.ustring.match(date_string, patterns['Sy-y'][1]);&lt;br /&gt;
		if 'Winter' ~= month and 'Summer' ~= month then return false end;		-- 'month' can only be Winter or Summer&lt;br /&gt;
		anchor_year = year .. '–' .. anchor_year;										-- assemble anchor_year from both years&lt;br /&gt;
		if 1 ~= tonumber(year2) - tonumber(year) then return false; end			-- must be sequential years, left to right, earlier to later&lt;br /&gt;
		if not is_valid_year(year2) then return false; end						-- no year farther in the future than next year&lt;br /&gt;
		month = get_season_number (month, param);								-- for metadata&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['My-My'][1]) then				-- month/season year - month/season year; separated by spaced endash&lt;br /&gt;
		month, year, month2, anchor_year, year2 = mw.ustring.match(date_string, patterns['My-My'][1]);&lt;br /&gt;
		anchor_year = year .. '–' .. anchor_year;								-- assemble anchor_year from both years&lt;br /&gt;
		if tonumber(year) &amp;gt;= tonumber(year2) then return false; end				-- left to right, earlier to later, not the same&lt;br /&gt;
		if not is_valid_year(year2) then return false; end						-- no year farther in the future than next year&lt;br /&gt;
		if 0 ~= get_month_number(month) and 0 ~= get_month_number(month2) and is_valid_month_range_style(month, month2) then 	-- both must be month year, same month style&lt;br /&gt;
			month = get_month_number(month);&lt;br /&gt;
			month2 = get_month_number(month2);&lt;br /&gt;
		elseif 0 ~= get_season_number(month, param) and 0 ~= get_season_number(month2, param) then	-- both must be season year, not mixed&lt;br /&gt;
			month = get_season_number(month, param);&lt;br /&gt;
			month2 = get_season_number(month2, param);&lt;br /&gt;
		else&lt;br /&gt;
			 return false;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['M-My'][1]) then				-- month/season range year; months separated by endash &lt;br /&gt;
		month, month2, anchor_year, year = mw.ustring.match(date_string, patterns['M-My'][1]);&lt;br /&gt;
		if (not is_valid_month_season_range(month, month2, param)) or (not is_valid_year(year)) then return false; end&lt;br /&gt;
		if 0 ~= get_month_number(month) then									-- determined to be a valid range so just check this one to know if month or season&lt;br /&gt;
			month = get_month_number(month);&lt;br /&gt;
			month2 = get_month_number(month2);&lt;br /&gt;
			if 0 == month or 0 == month2 then return false; end&lt;br /&gt;
		else&lt;br /&gt;
			month = get_season_number(month, param);&lt;br /&gt;
			month2 = get_season_number(month2, param);&lt;br /&gt;
		end&lt;br /&gt;
		year2 = year;&lt;br /&gt;
		&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['My'][1]) then				-- month/season/quarter/proper-name year&lt;br /&gt;
		month, anchor_year, year = mw.ustring.match(date_string, patterns['My'][1]);&lt;br /&gt;
		if not is_valid_year(year) then return false; end&lt;br /&gt;
		month = get_element_number(month, param);								-- get month season quarter proper-name number or nil&lt;br /&gt;
		if not month then return false; end										-- not valid whatever it is&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['y-y'][1]) then				-- Year range: YYY-YYY or YYY-YYYY or YYYY–YYYY; separated by unspaced endash; 100-9999&lt;br /&gt;
		year, anchor_year, year2 = mw.ustring.match(date_string, patterns['y-y'][1]);&lt;br /&gt;
		anchor_year = year .. '–' .. anchor_year;								-- assemble anchor year from both years&lt;br /&gt;
		if tonumber(year) &amp;gt;= tonumber(year2) then return false; end				-- left to right, earlier to later, not the same&lt;br /&gt;
		if not is_valid_year(year2) then return false; end						-- no year farther in the future than next year&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['y4-y2'][1]) then				-- Year range: YYYY–YY; separated by unspaced endash&lt;br /&gt;
		local century;&lt;br /&gt;
		year, century, anchor_year, year2 = mw.ustring.match(date_string, patterns['y4-y2'][1]);&lt;br /&gt;
		anchor_year = year .. '–' .. anchor_year;								-- assemble anchor year from both years&lt;br /&gt;
&lt;br /&gt;
		if in_array (param, {'date', 'publication-date', 'year'}) then&lt;br /&gt;
			add_prop_cat ('year-range-abbreviated');&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if 13 &amp;gt; tonumber(year2) then return false; end							-- don't allow 2003-05 which might be May 2003&lt;br /&gt;
		year2 = century .. year2;													-- add the century to year2 for comparisons&lt;br /&gt;
		if tonumber(year) &amp;gt;= tonumber(year2) then return false; end				-- left to right, earlier to later, not the same&lt;br /&gt;
		if not is_valid_year(year2) then return false; end						-- no year farther in the future than next year&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, patterns['y'][1]) then					-- year; here accept either YYY or YYYY&lt;br /&gt;
		anchor_year, year = mw.ustring.match(date_string, patterns['y'][1]);&lt;br /&gt;
		if false == is_valid_year(year) then&lt;br /&gt;
			return false;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	else&lt;br /&gt;
		return false;															-- date format not one of the MOS:DATE approved formats&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if 'access-date' == param then												-- test accessdate here because we have numerical date parts&lt;br /&gt;
		if 0 ~= year and 0 ~= month and 0 ~= day and 							-- all parts of a single date required&lt;br /&gt;
			0 == year2 and 0 == month2 and 0 == day2 then						-- none of these; accessdate must not be a range&lt;br /&gt;
				if not is_valid_accessdate(year .. '-' .. month .. '-' .. day) then	&lt;br /&gt;
					return false;												-- return false when accessdate out of bounds&lt;br /&gt;
				end&lt;br /&gt;
		else&lt;br /&gt;
			return false;														-- return false when accessdate is a range of two dates&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local result=true;															-- check whole dates for validity; assume true because not all dates will go through this test&lt;br /&gt;
	if 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 == day2 then		-- YMD (simple whole date)&lt;br /&gt;
		result = is_valid_date (year, month, day, param);						-- &amp;lt;param&amp;gt; for |pmc-embargo-date=&lt;br /&gt;
&lt;br /&gt;
	elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 == month2 and 0 ~= day2 then	-- YMD-d (day range)&lt;br /&gt;
		result = is_valid_date (year, month, day);&lt;br /&gt;
		result = result and is_valid_date (year, month, day2);&lt;br /&gt;
&lt;br /&gt;
	elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 == year2 and 0 ~= month2 and 0 ~= day2 then	-- YMD-md (day month range)&lt;br /&gt;
		result = is_valid_date (year, month, day);&lt;br /&gt;
		result = result and is_valid_date (year, month2, day2);&lt;br /&gt;
&lt;br /&gt;
	elseif 0 ~= year and 0 ~= month and 0 ~= day and 0 ~= year2 and 0 ~= month2 and 0 ~= day2 then	-- YMD-ymd (day month year range)&lt;br /&gt;
		result = is_valid_date(year, month, day);&lt;br /&gt;
		result = result and is_valid_date(year2, month2, day2);&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if false == result then return false; end&lt;br /&gt;
&lt;br /&gt;
	if nil ~= tCOinS_date then													-- this table only passed into this function when testing |date= parameter values&lt;br /&gt;
		make_COinS_date ({year = year, month = month, day = day, year2 = year2, month2 = month2, day2 = day2}, tCOinS_date);	-- make an ISO 8601 date string for COinS&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return true, anchor_year;													-- format is good and date string represents a real date&lt;br /&gt;
end	&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; D A T E S &amp;gt;--------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Cycle the date-holding parameters in passed table date_parameters_list through check_date() to check compliance with MOS:DATE. For all valid dates, check_date() returns&lt;br /&gt;
true. The |date= parameter test is unique, it is the only date holding parameter from which values for anchor_year (used in CITEREF identifiers) and COinS_date (used in&lt;br /&gt;
the COinS metadata) are derived.  The |date= parameter is the only date-holding parameter that is allowed to contain the no-date keywords &amp;quot;n.d.&amp;quot; or &amp;quot;nd&amp;quot; (without quotes).&lt;br /&gt;
&lt;br /&gt;
Unlike most error messages created in this module, only one error message is created by this function. Because all of the date holding parameters are processed serially,&lt;br /&gt;
parameters with errors are added to the &amp;lt;error_list&amp;gt; sequence table as the dates are tested.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function dates(date_parameters_list, tCOinS_date, error_list)&lt;br /&gt;
	local anchor_year;															-- will return as nil if the date being tested is not |date=&lt;br /&gt;
	local COinS_date;															-- will return as nil if the date being tested is not |date=&lt;br /&gt;
	local embargo_date;															-- if embargo date is a good dmy, mdy, ymd date then holds original value else reset to 9999&lt;br /&gt;
	local good_date = false;&lt;br /&gt;
&lt;br /&gt;
	for k, v in pairs(date_parameters_list) do									-- for each date-holding parameter in the list&lt;br /&gt;
		if is_set(v.val) then													-- if the parameter has a value&lt;br /&gt;
			v.val = mw.ustring.gsub(v.val, '%d', cfg.date_names.local_digits);	-- translate 'local' digits to Western 0-9&lt;br /&gt;
			if v.val:match(&amp;quot;^c%. [1-9]%d%d%d?%a?$&amp;quot;) then						-- special case for c. year or with or without CITEREF disambiguator - only |date= and |year=&lt;br /&gt;
				local year = v.val:match(&amp;quot;c%. ([1-9]%d%d%d?)%a?&amp;quot;);				-- get the year portion so it can be tested&lt;br /&gt;
				if 'date' == k then&lt;br /&gt;
					anchor_year, COinS_date = v.val:match(&amp;quot;((c%. [1-9]%d%d%d?)%a?)&amp;quot;);	-- anchor year and COinS_date only from |date= parameter&lt;br /&gt;
					good_date = is_valid_year(year);&lt;br /&gt;
				elseif 'year' == k then&lt;br /&gt;
					good_date = is_valid_year(year);&lt;br /&gt;
				end&lt;br /&gt;
			elseif 'date' == k then												-- if the parameter is |date=&lt;br /&gt;
				if v.val:match(&amp;quot;^n%.d%.%a?$&amp;quot;) then -- ToDo: I18N								-- if |date=n.d. with or without a CITEREF disambiguator&lt;br /&gt;
					good_date, anchor_year, COinS_date = true, v.val:match(&amp;quot;((n%.d%.)%a?)&amp;quot;); -- ToDo: I18N	-- &amp;quot;n.d.&amp;quot;; no error when date parameter is set to no date&lt;br /&gt;
				elseif v.val:match(&amp;quot;^nd%a?$&amp;quot;) then -- ToDo: I18N								-- if |date=nd with or without a CITEREF disambiguator&lt;br /&gt;
					good_date, anchor_year, COinS_date = true, v.val:match(&amp;quot;((nd)%a?)&amp;quot;); -- ToDo: I18N	-- &amp;quot;nd&amp;quot;;	no error when date parameter is set to no date&lt;br /&gt;
				else&lt;br /&gt;
					good_date, anchor_year, COinS_date = check_date (v.val, k, tCOinS_date);	-- go test the date&lt;br /&gt;
				end&lt;br /&gt;
			elseif 'year' == k then												-- if the parameter is |year= it should hold only a year value&lt;br /&gt;
				if v.val:match(&amp;quot;^[1-9]%d%d%d?%a?$&amp;quot;) then						-- if |year = 3 or 4 digits only with or without a CITEREF disambiguator&lt;br /&gt;
					good_date, anchor_year, COinS_date = true, v.val:match(&amp;quot;((%d+)%a?)&amp;quot;);&lt;br /&gt;
				end&lt;br /&gt;
			elseif 'pmc-embargo-date' == k then									-- if the parameter is |pmc-embargo-date=&lt;br /&gt;
				good_date = check_date (v.val, k);								-- go test the date&lt;br /&gt;
				if true == good_date then										-- if the date is a valid date&lt;br /&gt;
					good_date, embargo_date = is_valid_embargo_date (v.val);	-- is |pmc-embargo-date= date a single dmy, mdy, or ymd formatted date? yes: returns embargo date; no: returns 9999&lt;br /&gt;
				end&lt;br /&gt;
			else																-- any other date-holding parameter&lt;br /&gt;
				good_date = check_date (v.val, k);								-- go test the date&lt;br /&gt;
			end&lt;br /&gt;
			if false == good_date then											-- assemble one error message so we don't add the tracking category multiple times&lt;br /&gt;
				table.insert (error_list, wrap_style ('parameter', v.name));	-- make parameter name suitable for error message list&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return anchor_year, embargo_date;											-- and done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; Y E A R _ D A T E _ C H E C K &amp;gt;------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Compare the value provided in |year= with the year value(s) provided in |date=.  This function sets a local numeric value:&lt;br /&gt;
	0 - year value does not match the year value in date&lt;br /&gt;
	1 - (default) year value matches the year value in date or one of the year values when date contains two years&lt;br /&gt;
	2 - year value matches the year value in date when date is in the form YYYY-MM-DD and year is disambiguated (|year=YYYYx)&lt;br /&gt;
&lt;br /&gt;
the numeric value in &amp;lt;result&amp;gt; determines the 'output' if any from this function:&lt;br /&gt;
	0 – adds error message to error_list sequence table&lt;br /&gt;
	1 – adds maint cat&lt;br /&gt;
	2 – does nothing&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function year_date_check (year_string, year_origin, date_string, date_origin, error_list)&lt;br /&gt;
	local year;&lt;br /&gt;
	local date1;&lt;br /&gt;
	local date2;&lt;br /&gt;
	local result = 1;															-- result of the test; assume that the test passes&lt;br /&gt;
&lt;br /&gt;
	year = year_string:match ('(%d%d%d%d?)');&lt;br /&gt;
&lt;br /&gt;
	if date_string:match ('%d%d%d%d%-%d%d%-%d%d') and year_string:match ('%d%d%d%d%a') then	--special case where both date and year are required YYYY-MM-DD and YYYYx&lt;br /&gt;
		date1 = date_string:match ('(%d%d%d%d)');&lt;br /&gt;
		year = year_string:match ('(%d%d%d%d)');&lt;br /&gt;
		if year ~= date1 then&lt;br /&gt;
			result = 0;															-- years don't match&lt;br /&gt;
		else&lt;br /&gt;
			result = 2;															-- years match; but because disambiguated, don't add to maint cat&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
	elseif date_string:match (&amp;quot;%d%d%d%d?.-%d%d%d%d?&amp;quot;) then						-- any of the standard range formats of date with two three- or four-digit years&lt;br /&gt;
		date1, date2 = date_string:match (&amp;quot;(%d%d%d%d?).-(%d%d%d%d?)&amp;quot;);&lt;br /&gt;
		if year ~= date1 and year ~= date2 then&lt;br /&gt;
			result = 0;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	elseif mw.ustring.match(date_string, &amp;quot;%d%d%d%d[%-–]%d%d&amp;quot;) then				-- YYYY-YY date ranges&lt;br /&gt;
		local century;&lt;br /&gt;
		date1, century, date2 = mw.ustring.match(date_string, &amp;quot;((%d%d)%d%d)[%-–]+(%d%d)&amp;quot;);&lt;br /&gt;
		date2 = century..date2;													-- convert YY to YYYY&lt;br /&gt;
		if year ~= date1 and year ~= date2 then&lt;br /&gt;
			result = 0;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	elseif date_string:match (&amp;quot;%d%d%d%d?&amp;quot;) then									-- any of the standard formats of date with one year&lt;br /&gt;
		date1 = date_string:match (&amp;quot;(%d%d%d%d?)&amp;quot;);&lt;br /&gt;
		if year ~= date1 then&lt;br /&gt;
			result = 0;&lt;br /&gt;
		end&lt;br /&gt;
	else																		-- should never get here; this function called only when no other date errors&lt;br /&gt;
		result = 0;																-- no recognizable year in date&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if 0 == result then															-- year / date mismatch&lt;br /&gt;
		table.insert (error_list, substitute (cfg.messages['mismatch'], {year_origin, date_origin}));	-- add error message to error_list sequence table&lt;br /&gt;
	elseif 1 == result then														-- redundant year / date&lt;br /&gt;
		set_message ('maint_date_year');										-- add a maint cat&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; R E F O R M A T T E R &amp;gt;--------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
reformat 'date' into new format specified by format_param if pattern_idx (the current format of 'date') can be&lt;br /&gt;
reformatted.  Does the grunt work for reformat_dates().&lt;br /&gt;
&lt;br /&gt;
The table re_formats maps pattern_idx (current format) and format_param (desired format) to a table that holds:&lt;br /&gt;
	format string used by string.format()&lt;br /&gt;
	identifier letters ('d', 'm', 'y', 'd2', 'm2', 'y2') that serve as indexes into a table t{} that holds captures&lt;br /&gt;
		from mw.ustring.match() for the various date parts specified by  patterns[pattern_idx][1]&lt;br /&gt;
&lt;br /&gt;
Items in patterns{} have the general form:&lt;br /&gt;
	['ymd'] = {'^(%d%d%d%d)%-(%d%d)%-(%d%d)$', 'y', 'm', 'd'}, where:&lt;br /&gt;
		['ymd'] is pattern_idx&lt;br /&gt;
		patterns['ymd'][1] is the match pattern with captures for mw.ustring.match()&lt;br /&gt;
		patterns['ymd'][2] is an indicator letter identifying the content of the first capture&lt;br /&gt;
		patterns['ymd'][3] ... the second capture etc.&lt;br /&gt;
&lt;br /&gt;
when a pattern matches a date, the captures are loaded into table t{} in capture order using the idemtifier&lt;br /&gt;
characters as indexes into t{}  For the above, a ymd date is in t{} as:&lt;br /&gt;
	t.y = first capture (year), t.m = second capture (month), t.d = third capture (day)&lt;br /&gt;
&lt;br /&gt;
To reformat, this function is called with the pattern_idx that matches the current format of the date and with&lt;br /&gt;
format_param set to the desired format.  This function loads table t{} as described and then calls string.format()&lt;br /&gt;
with the format string specified by re_format[pattern_idx][format_param][1] using values taken from t{} according&lt;br /&gt;
to the capture identifier letters specified by patterns[pattern_idx][format_param][n] where n is 2..&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local re_formats = {&lt;br /&gt;
	['ymd'] = {																	-- date format is ymd; reformat to:&lt;br /&gt;
		['mdy'] = {'%s %s, %s', 'm', 'd', 'y'},									-- |df=mdy&lt;br /&gt;
		['dmy'] = {'%s %s %s', 'd', 'm', 'y'},									-- |df=dmy&lt;br /&gt;
	--		['yMd'] = {'%s %s %s', 'y', 'm', 'd'},								-- |df=yMd; not supported at en.wiki&lt;br /&gt;
		},&lt;br /&gt;
	['Mdy'] = {																	-- date format is Mdy; reformat to:&lt;br /&gt;
		['mdy'] = {'%s %s, %s', 'm', 'd', 'y'},									-- for long/short reformatting&lt;br /&gt;
		['dmy'] = {'%s %s %s', 'd', 'm', 'y'},									-- |df=dmy&lt;br /&gt;
		['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'},									-- |df=ymd&lt;br /&gt;
	--		['yMd'] = {'%s %s %s', 'y', 'm', 'd'},								-- |df=yMd; not supported at en.wiki&lt;br /&gt;
		},&lt;br /&gt;
	['dMy'] = {																	-- date format is dMy; reformat to:&lt;br /&gt;
		['dmy'] = {'%s %s %s', 'd', 'm', 'y'},									-- for long/short reformatting&lt;br /&gt;
		['mdy'] = {'%s %s, %s', 'm', 'd', 'y'},									-- |df=mdy&lt;br /&gt;
		['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'},									-- |df=ymd&lt;br /&gt;
	--		['yMd'] = {'%s %s %s', 'y', 'm', 'd'},								-- |df=yMd; not supported at en.wiki&lt;br /&gt;
		},&lt;br /&gt;
	['Md-dy'] = {																-- date format is Md-dy; reformat to:&lt;br /&gt;
		['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'},						-- for long/short reformatting&lt;br /&gt;
		['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'},							-- |df=dmy -&amp;gt; d-dMy &lt;br /&gt;
		},&lt;br /&gt;
	['d-dMy'] = {																-- date format is d-d&amp;gt;y; reformat to:&lt;br /&gt;
		['dmy'] = {'%s–%s %s %s', 'd', 'd2', 'm', 'y'},							-- for long/short reformatting&lt;br /&gt;
		['mdy'] = {'%s %s–%s, %s', 'm', 'd', 'd2', 'y'},						-- |df=mdy -&amp;gt; Md-dy &lt;br /&gt;
		},&lt;br /&gt;
	['dM-dMy'] = {																-- date format is dM-dMy; reformat to:&lt;br /&gt;
		['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'},				-- for long/short reformatting&lt;br /&gt;
		['mdy'] = {'%s %s – %s %s, %s', 'm', 'd', 'm2', 'd2', 'y'},				-- |df=mdy -&amp;gt; Md-Mdy &lt;br /&gt;
		},&lt;br /&gt;
	['Md-Mdy'] = {																-- date format is Md-Mdy; reformat to:&lt;br /&gt;
		['mdy'] = {'%s %s – %s %s, %s', 'm', 'd',  'm2', 'd2', 'y'},			-- for long/short reformatting&lt;br /&gt;
		['dmy'] = {'%s %s – %s %s %s', 'd', 'm', 'd2', 'm2', 'y'},				-- |df=dmy -&amp;gt; dM-dMy &lt;br /&gt;
		},&lt;br /&gt;
	['dMy-dMy'] = {																-- date format is dMy-dMy; reformat to:&lt;br /&gt;
		['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'},		-- for long/short reformatting&lt;br /&gt;
		['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'},	-- |df=mdy -&amp;gt; Mdy-Mdy &lt;br /&gt;
		},&lt;br /&gt;
	['Mdy-Mdy'] = {																-- date format is Mdy-Mdy; reformat to:&lt;br /&gt;
		['mdy'] = {'%s %s, %s – %s %s, %s', 'm', 'd', 'y', 'm2', 'd2', 'y2'},	-- for long/short reformatting&lt;br /&gt;
		['dmy'] = {'%s %s %s – %s %s %s', 'd', 'm', 'y', 'd2', 'm2', 'y2'},		-- |df=dmy -&amp;gt; dMy-dMy &lt;br /&gt;
		},&lt;br /&gt;
	['My-My'] = {																-- these for long/short reformatting&lt;br /&gt;
		['any'] = {'%s %s – %s %s', 'm', 'y', 'm2', 'y2'},						-- dmy/mdy agnostic&lt;br /&gt;
		},&lt;br /&gt;
	['M-My'] = {																-- these for long/short reformatting&lt;br /&gt;
		['any'] = {'%s–%s %s', 'm', 'm2', 'y'},									-- dmy/mdy agnostic&lt;br /&gt;
		},&lt;br /&gt;
	['My'] = {																	-- these for long/short reformatting&lt;br /&gt;
		['any'] = {'%s %s', 'm', 'y'},											-- dmy/mdy agnostic&lt;br /&gt;
		},&lt;br /&gt;
	--	['yMd'] = {																-- not supported at en.wiki&lt;br /&gt;
	--		['mdy'] = {'%s %s, %s', 'm', 'd', 'y'},								-- |df=mdy&lt;br /&gt;
	--		['dmy'] = {'%s %s %s', 'd', 'm', 'y'},								-- |df=dmy&lt;br /&gt;
	--		['ymd'] = {'%s-%s-%s', 'y', 'm', 'd'},								-- |df=ymd&lt;br /&gt;
	--		},&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function reformatter (date, pattern_idx, format_param, mon_len)&lt;br /&gt;
	if not in_array (pattern_idx, {'ymd', 'Mdy', 'Md-dy', 'dMy', 'yMd', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then&lt;br /&gt;
		return;																	-- not in this set of date format patterns then not a reformattable date&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if 'ymd' == format_param and in_array (pattern_idx, {'ymd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy', 'My-My', 'M-My', 'My'}) then&lt;br /&gt;
		return;																	-- ymd date ranges not supported at en.wiki; no point in reformatting ymd to ymd&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if in_array (pattern_idx, {'My', 'M-My', 'My-My'}) then						-- these are not dmy/mdy so can't be 'reformatted' into either&lt;br /&gt;
		format_param = 'any';													-- so format-agnostic &lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
																				-- yMd is not supported at en.wiki; when yMd is supported at your wiki, uncomment the next line&lt;br /&gt;
	--	if 'yMd' == format_param and in_array (pattern_idx, {'yMd', 'Md-dy', 'd-dMy', 'dM-dMy', 'Md-Mdy', 'dMy-dMy', 'Mdy-Mdy'}) then	-- these formats not convertable; yMd not supported at en.wiki&lt;br /&gt;
	if 'yMd' == format_param then												-- yMd not supported at en.wiki; when yMd is supported at your wiki, remove or comment-out this line&lt;br /&gt;
		return;																	-- not a reformattable date&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local c1, c2, c3, c4, c5, c6, c7;											-- these hold the captures specified in patterns[pattern_idx][1]&lt;br /&gt;
	c1, c2, c3, c4, c5, c6, c7 = mw.ustring.match (date, patterns[pattern_idx][1]);	-- get the captures&lt;br /&gt;
&lt;br /&gt;
	local t = {																	-- table that holds k/v pairs of date parts from the captures and patterns[pattern_idx][2..]&lt;br /&gt;
		[patterns[pattern_idx][2]] = c1;										-- at minimum there is always one capture with a matching indicator letter&lt;br /&gt;
		[patterns[pattern_idx][3] or 'x'] = c2;									-- patterns can have a variable number of captures; each capture requires an indicator letter;&lt;br /&gt;
		[patterns[pattern_idx][4] or 'x'] = c3;									-- where there is no capture, there is no indicator letter so n in patterns[pattern_idx][n] will be nil;&lt;br /&gt;
		[patterns[pattern_idx][5] or 'x'] = c4;									-- the 'x' here spoofs an indicator letter to prevent 'table index is nil' error&lt;br /&gt;
		[patterns[pattern_idx][6] or 'x'] = c5;&lt;br /&gt;
		[patterns[pattern_idx][7] or 'x'] = c6;&lt;br /&gt;
		[patterns[pattern_idx][8] or 'x'] = c7;&lt;br /&gt;
		};&lt;br /&gt;
&lt;br /&gt;
	if t.a then																	-- if this date has an anchor year capture&lt;br /&gt;
		t.y = t.a;																-- use the anchor year capture when reassembling the date&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if tonumber(t.m) then														-- if raw month is a number (converting from ymd)&lt;br /&gt;
		if 's' == mon_len then													-- if we are to use abbreviated month names&lt;br /&gt;
			t.m = cfg.date_names['inv_local_s'][tonumber(t.m)];					-- convert it to a month name&lt;br /&gt;
		else&lt;br /&gt;
			t.m = cfg.date_names['inv_local_l'][tonumber(t.m)];					-- convert it to a month name&lt;br /&gt;
		end&lt;br /&gt;
		t.d = t.d:gsub ('0(%d)', '%1');											-- strip leading '0' from day if present&lt;br /&gt;
	elseif 'ymd' == format_param then											-- when converting to ymd&lt;br /&gt;
		t.y = t.y:gsub ('%a', '');												-- strip CITREF disambiguator if present; anchor year already known so process can proceed; TODO: maint message?&lt;br /&gt;
		if 1582 &amp;gt; tonumber (t.y) then											-- ymd format dates not allowed before 1582&lt;br /&gt;
			return;&lt;br /&gt;
		end&lt;br /&gt;
		t.m = string.format ('%02d', get_month_number (t.m));					-- make sure that month and day are two digits&lt;br /&gt;
		t.d = string.format ('%02d', t.d);&lt;br /&gt;
	elseif mon_len then															-- if mon_len is set to either 'short' or 'long'&lt;br /&gt;
		for _, mon in ipairs ({'m', 'm2'}) do									-- because there can be two month names, check both &lt;br /&gt;
			if t[mon] then&lt;br /&gt;
				t[mon] = get_month_number (t[mon]);								-- get the month number for this month (is length agnostic)&lt;br /&gt;
				if 0 == t[mon] then return; end									-- seasons and named dates can't be converted&lt;br /&gt;
				t[mon] = (('s' == mon_len) and cfg.date_names['inv_local_s'][t[mon]]) or cfg.date_names['inv_local_l'][t[mon]];	-- fetch month name according to length&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local new_date = string.format (re_formats[pattern_idx][format_param][1],	-- format string&lt;br /&gt;
		t[re_formats[pattern_idx][format_param][2]],							-- named captures from t{}&lt;br /&gt;
		t[re_formats[pattern_idx][format_param][3]],&lt;br /&gt;
		t[re_formats[pattern_idx][format_param][4]],&lt;br /&gt;
		t[re_formats[pattern_idx][format_param][5]],&lt;br /&gt;
		t[re_formats[pattern_idx][format_param][6]],&lt;br /&gt;
		t[re_formats[pattern_idx][format_param][7]],&lt;br /&gt;
		t[re_formats[pattern_idx][format_param][8]]&lt;br /&gt;
		);&lt;br /&gt;
&lt;br /&gt;
	return new_date;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------------&amp;lt; R E F O R M A T _ D A T E S &amp;gt;--------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Reformats existing dates into the format specified by format.&lt;br /&gt;
&lt;br /&gt;
format is one of several manual keywords: dmy, dmy-all, mdy, mdy-all, ymd, ymd-all.  The -all version includes&lt;br /&gt;
access- and archive-dates; otherwise these dates are not reformatted.&lt;br /&gt;
&lt;br /&gt;
This function allows automatic date formatting.  In ~/Configuration, the article source is searched for one of&lt;br /&gt;
the {{use xxx dates}} templates.  If found, xxx becomes the global date format as xxx-all.  If |cs1-dates= in&lt;br /&gt;
{{use xxx dates}} has legitimate value then that value determines how cs1|2 dates will be rendered.  Legitimate&lt;br /&gt;
values for |cs1-dates= are:&lt;br /&gt;
	l - all dates are rendered with long month names&lt;br /&gt;
	ls - publication dates use long month names; access-/archive-dates use abbreviated month names&lt;br /&gt;
	ly - publication dates use long month names; access-/archive-dates rendered in ymd format&lt;br /&gt;
	s - all dates are rendered with abbreviated (short) month names&lt;br /&gt;
	sy - publication dates use abbreviated month names; access-/archive-dates rendered in ymd format&lt;br /&gt;
	y - all dates are rendered in ymd format&lt;br /&gt;
&lt;br /&gt;
the format argument for automatic date formatting will be the format specified by {{use xxx dates}} with the&lt;br /&gt;
value supplied by |cs1-dates so one of: xxx-l, xxx-ls, xxx-ly, xxx-s, xxx-sy, xxx-y, or simply xxx (|cs1-dates=&lt;br /&gt;
empty, omitted, or invalid) where xxx shall be either of dmy or mdy.&lt;br /&gt;
&lt;br /&gt;
dates are extracted from date_parameters_list, reformatted (if appropriate), and then written back into the&lt;br /&gt;
list in the new format.  Dates in date_parameters_list are presumed here to be valid (no errors).  This function&lt;br /&gt;
returns true when a date has been reformatted, false else.  Actual reformatting is done by reformatter().&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function reformat_dates (date_parameters_list, format)&lt;br /&gt;
	local all = false;															-- set to false to skip access- and archive-dates&lt;br /&gt;
	local len_p = 'l';															-- default publication date length shall be long&lt;br /&gt;
	local len_a = 'l';															-- default access-/archive-date length shall be long&lt;br /&gt;
	local result = false;&lt;br /&gt;
	local new_date;																&lt;br /&gt;
	&lt;br /&gt;
	if format:match('%a+%-all') then											-- manual df keyword; auto df keyword when length not specified in {{use xxx dates}}; &lt;br /&gt;
		format = format:match('(%a+)%-all');									-- extract the format&lt;br /&gt;
		all = true;																-- all dates are long format dates because this keyword doesn't specify length&lt;br /&gt;
	elseif format:match('%a+%-[lsy][sy]?') then									-- auto df keywords; internal only&lt;br /&gt;
		all = true;																-- auto df applies to all dates; use length specified by capture len_p for all dates&lt;br /&gt;
		format, len_p, len_a = format:match('(%a+)%-([lsy])([sy]?)');			-- extract the format and length keywords&lt;br /&gt;
		if 'y' == len_p then													-- because allowed by MOS:DATEUNIFY (sort of) range dates and My dates not reformatted&lt;br /&gt;
			format = 'ymd';														-- override {{use xxx dates}}&lt;br /&gt;
		elseif (not is_set(len_a)) or (len_p == len_a) then						-- no access-/archive-date length specified or same length as publication dates then&lt;br /&gt;
			len_a = len_p;														-- in case len_a not set&lt;br /&gt;
		end&lt;br /&gt;
	end																			-- else only publication dates and they are long&lt;br /&gt;
&lt;br /&gt;
	for param_name, param_val in pairs (date_parameters_list) do				-- for each date-holding parameter in the list&lt;br /&gt;
		if is_set (param_val.val) then											-- if the parameter has a value&lt;br /&gt;
			if not (not all and in_array (param_name, {'access-date', 'archive-date'})) then	-- skip access- or archive-date unless format is xxx-all; yeah, ugly; TODO: find a better way&lt;br /&gt;
				for pattern_idx, pattern in pairs (patterns) do&lt;br /&gt;
					if mw.ustring.match (param_val.val, pattern[1]) then&lt;br /&gt;
						if all and in_array (param_name, {'access-date', 'archive-date'}) then	-- if this date is an access- or archive-date&lt;br /&gt;
							new_date = reformatter (param_val.val, pattern_idx, (('y' == len_a) and 'ymd') or format, len_a);	-- choose ymd or dmy/mdy according to len_a setting&lt;br /&gt;
						else													-- all other dates&lt;br /&gt;
							new_date = reformatter (param_val.val, pattern_idx, format, len_p);&lt;br /&gt;
						end&lt;br /&gt;
						&lt;br /&gt;
						if new_date then										-- set when date was reformatted&lt;br /&gt;
							date_parameters_list[param_name].val = new_date;	-- update date in date list&lt;br /&gt;
							result = true;										-- and announce that changes have been made&lt;br /&gt;
						end&lt;br /&gt;
					end	-- if&lt;br /&gt;
				end		-- for&lt;br /&gt;
			end			-- if&lt;br /&gt;
		end				-- if&lt;br /&gt;
	end					-- for&lt;br /&gt;
	return result;																-- declare boolean result and done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; D A T E _ H Y P H E N _ T O _ D A S H &amp;gt;----------------------------------------&lt;br /&gt;
&lt;br /&gt;
Loops through the list of date-holding parameters and converts any hyphen to an ndash.  Not called if the cs1|2&lt;br /&gt;
template has any date errors.&lt;br /&gt;
&lt;br /&gt;
Modifies the date_parameters_list and returns true if hyphens are replaced, else returns false.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function date_hyphen_to_dash (date_parameters_list)&lt;br /&gt;
	local result = false;&lt;br /&gt;
	local n;&lt;br /&gt;
	for param_name, param_val in pairs(date_parameters_list) do					-- for each date-holding parameter in the list&lt;br /&gt;
		if is_set (param_val.val) and&lt;br /&gt;
			not mw.ustring.match (param_val.val, patterns.ymd[1]) then			-- for those that are not ymd dates (ustring because here digits may not be Western)&lt;br /&gt;
				param_val.val, n = param_val.val:gsub ('%-', '–');				-- replace any hyphen with ndash&lt;br /&gt;
				if 0 ~= n then&lt;br /&gt;
					date_parameters_list[param_name].val = param_val.val;		-- update the list&lt;br /&gt;
					result = true;&lt;br /&gt;
				end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return result;																-- so we know if any hyphens were replaced&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------------&amp;lt; D A T E _ N A M E _ X L A T E &amp;gt;------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Attempts to translate English month names to local-language month names using names supplied by MediaWiki's&lt;br /&gt;
date parser function.  This is simple name-for-name replacement and may not work for all languages.&lt;br /&gt;
&lt;br /&gt;
if xlat_dig is true, this function will also translate Western (English) digits to the local language's digits.&lt;br /&gt;
This will also translate ymd dates.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function date_name_xlate (date_parameters_list, xlt_dig)&lt;br /&gt;
	local xlate;&lt;br /&gt;
	local mode;																	-- long or short month names&lt;br /&gt;
	local modified = false;&lt;br /&gt;
	local date;&lt;br /&gt;
	&lt;br /&gt;
	for param_name, param_val in pairs(date_parameters_list) do					-- for each date-holding parameter in the list&lt;br /&gt;
		if is_set(param_val.val) then											-- if the parameter has a value&lt;br /&gt;
			date = param_val.val;&lt;br /&gt;
			for month in mw.ustring.gmatch (date, '%a+') do						-- iterate through all dates in the date (single date or date range)&lt;br /&gt;
				if cfg.date_names.en.long[month] then&lt;br /&gt;
					mode = 'F';													-- English name is long so use long local name&lt;br /&gt;
				elseif cfg.date_names.en.short[month] then&lt;br /&gt;
					mode = 'M';													-- English name is short so use short local name&lt;br /&gt;
				else&lt;br /&gt;
					mode = nil;													-- not an English month name; could be local language month name or an English season name&lt;br /&gt;
				end&lt;br /&gt;
		&lt;br /&gt;
				if mode then													-- might be a season&lt;br /&gt;
					xlate = lang_object:formatDate(mode, '1' .. month);			-- translate the month name to this local language&lt;br /&gt;
					date = mw.ustring.gsub (date, month, xlate);				-- replace the English with the translation&lt;br /&gt;
					date_parameters_list[param_name].val = date;				-- save the translated date&lt;br /&gt;
					modified = true;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if xlt_dig then														-- shall we also translate digits?&lt;br /&gt;
				date = date:gsub ('%d', cfg.date_names.xlate_digits);			-- translate digits from Western to 'local digits'&lt;br /&gt;
				date_parameters_list[param_name].val = date;					-- save the translated date&lt;br /&gt;
				modified = true;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return modified;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E T _ S E L E C T E D _ M O D U L E S &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
Sets local imported functions table to same (live or sandbox) as that used by the other modules.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function set_selected_modules (cfg_table_ptr, utilities_page_ptr)&lt;br /&gt;
	add_prop_cat = utilities_page_ptr.add_prop_cat ;							-- import functions from selected Module:Citation/CS1/Utilities module&lt;br /&gt;
	is_set = utilities_page_ptr.is_set;&lt;br /&gt;
	in_array = utilities_page_ptr.in_array;&lt;br /&gt;
	set_message = utilities_page_ptr.set_message;&lt;br /&gt;
	substitute = utilities_page_ptr.substitute;&lt;br /&gt;
	wrap_style = utilities_page_ptr.wrap_style;&lt;br /&gt;
&lt;br /&gt;
	cfg = cfg_table_ptr;														-- import tables from selected Module:Citation/CS1/Configuration&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X P O R T E D   F U N C T I O N S &amp;gt;------------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
return {																		-- return exported functions&lt;br /&gt;
	dates = dates,&lt;br /&gt;
	year_date_check = year_date_check,&lt;br /&gt;
	reformat_dates = reformat_dates,&lt;br /&gt;
	date_hyphen_to_dash = date_hyphen_to_dash,&lt;br /&gt;
	date_name_xlate = date_name_xlate,&lt;br /&gt;
	set_selected_modules = set_selected_modules&lt;br /&gt;
	}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Whitelist&amp;diff=262</id>
		<title>Module:Citation/CS1/Whitelist</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Whitelist&amp;diff=262"/>
		<updated>2022-04-04T14:03:47Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;--[[--------------------------&amp;lt; S U P P O R T E D   P A R A M E T E R S &amp;gt;--------------------------------------  Because a steady-state signal conveys no useful information, w...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[--------------------------&amp;lt; S U P P O R T E D   P A R A M E T E R S &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
Because a steady-state signal conveys no useful information, whitelist.basic_arguments[] list items can have three values:&lt;br /&gt;
	true - these parameters are valid and supported parameters&lt;br /&gt;
	false - these parameters are deprecated but still supported&lt;br /&gt;
	tracked - these parameters are valid and supported parameters tracked in an eponymous properties category&lt;br /&gt;
	nil - these parameters are no longer supported. remove entirely&lt;br /&gt;
	&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local basic_arguments = {&lt;br /&gt;
	['accessdate'] = true,&lt;br /&gt;
	['access-date'] = true,&lt;br /&gt;
	['agency'] = true,&lt;br /&gt;
	['archivedate'] = true,&lt;br /&gt;
	['archive-date'] = true,&lt;br /&gt;
	['archive-format'] = true,&lt;br /&gt;
	['archiveurl'] = true,&lt;br /&gt;
	['archive-url'] = true,&lt;br /&gt;
	['article'] = true,&lt;br /&gt;
	['article-format'] = true,&lt;br /&gt;
	['article-url'] = true,&lt;br /&gt;
	['article-url-access'] = true,&lt;br /&gt;
	['arxiv'] = true,															-- cite arxiv; here because allowed in cite ... as identifier&lt;br /&gt;
	['asin'] = true,&lt;br /&gt;
	['ASIN'] = true,&lt;br /&gt;
	['asin-tld'] = true,&lt;br /&gt;
	['at'] = true,&lt;br /&gt;
	['author'] = true,&lt;br /&gt;
	['author-first'] = true,&lt;br /&gt;
	['author-given'] = true,&lt;br /&gt;
	['author-last'] = true,&lt;br /&gt;
	['author-surname'] = true,&lt;br /&gt;
	['authorlink'] = true,&lt;br /&gt;
	['author-link'] = true,&lt;br /&gt;
	['author-mask'] = true,&lt;br /&gt;
	['authors'] = true,&lt;br /&gt;
	['bibcode'] = true,&lt;br /&gt;
	['bibcode-access'] = true,&lt;br /&gt;
	['biorxiv'] = true,															-- cite biorxiv; here because allowed in cite ... as identifier&lt;br /&gt;
	['chapter'] = true,&lt;br /&gt;
	['chapter-format'] = true,&lt;br /&gt;
	['chapter-url'] = true,&lt;br /&gt;
	['chapter-url-access'] = true,&lt;br /&gt;
	['citeseerx'] = true,														-- cite citeseerx; here because allowed in cite ... as identifier&lt;br /&gt;
	['collaboration'] = true,&lt;br /&gt;
	['contribution'] = true,&lt;br /&gt;
	['contribution-format'] = true,&lt;br /&gt;
	['contribution-url'] = true,&lt;br /&gt;
	['contribution-url-access'] = true,&lt;br /&gt;
	['contributor'] = true,&lt;br /&gt;
	['contributor-first'] = true,&lt;br /&gt;
	['contributor-given'] = true,&lt;br /&gt;
	['contributor-last'] = true,&lt;br /&gt;
	['contributor-surname'] = true,&lt;br /&gt;
	['contributor-link'] = true,&lt;br /&gt;
	['contributor-mask'] = true,&lt;br /&gt;
	['date'] = true,&lt;br /&gt;
	['department'] = true,&lt;br /&gt;
	['df'] = true,&lt;br /&gt;
	['dictionary'] = true,&lt;br /&gt;
	['display-authors'] = true,&lt;br /&gt;
	['display-contributors'] = true,&lt;br /&gt;
	['display-editors'] = true,&lt;br /&gt;
	['display-interviewers'] = true,&lt;br /&gt;
	['display-subjects'] = true,&lt;br /&gt;
	['display-translators'] = true,&lt;br /&gt;
	['doi'] = true,&lt;br /&gt;
	['DOI'] = true,&lt;br /&gt;
	['doi-access'] = true,&lt;br /&gt;
	['doi-broken-date'] = true,&lt;br /&gt;
	['edition'] = true,&lt;br /&gt;
	['editor'] = true,&lt;br /&gt;
	['editor-first'] = true,&lt;br /&gt;
	['editor-given'] = true,&lt;br /&gt;
	['editor-last'] = true,&lt;br /&gt;
	['editor-surname'] = true,&lt;br /&gt;
	['editor-link'] = true,&lt;br /&gt;
	['editor-mask'] = true,&lt;br /&gt;
	['eissn'] = true,&lt;br /&gt;
	['EISSN'] = true,&lt;br /&gt;
	['encyclopaedia'] = true,&lt;br /&gt;
	['encyclopedia'] = true,&lt;br /&gt;
	['entry'] = true,&lt;br /&gt;
	['entry-format'] = true,&lt;br /&gt;
	['entry-url'] = true,&lt;br /&gt;
	['entry-url-access'] = true,&lt;br /&gt;
	['eprint'] = true,															-- cite arxiv; here because allowed in cite ... as identifier&lt;br /&gt;
	['first'] = true,&lt;br /&gt;
	['format'] = true,&lt;br /&gt;
	['given'] = true,&lt;br /&gt;
	['hdl'] = true,&lt;br /&gt;
	['HDL'] = true,&lt;br /&gt;
	['hdl-access'] = true,&lt;br /&gt;
	['host'] = true,															-- unique to certain templates?&lt;br /&gt;
	['id'] = true,&lt;br /&gt;
	['ID'] = true,&lt;br /&gt;
	['institution'] = true,														-- constrain to cite thesis?&lt;br /&gt;
	['interviewer'] = true,&lt;br /&gt;
	['interviewer-first'] = true,&lt;br /&gt;
	['interviewer-given'] = true,&lt;br /&gt;
	['interviewer-last'] = true,&lt;br /&gt;
	['interviewer-surname'] = true,&lt;br /&gt;
	['interviewer-link'] = true,&lt;br /&gt;
	['interviewer-mask'] = true,&lt;br /&gt;
	['isbn'] = true,&lt;br /&gt;
	['ISBN'] = true,&lt;br /&gt;
	['ismn'] = true,&lt;br /&gt;
	['ISMN'] = true,&lt;br /&gt;
	['issn'] = true,&lt;br /&gt;
	['ISSN'] = true,&lt;br /&gt;
	['issue'] = true,&lt;br /&gt;
	['jfm'] = true,&lt;br /&gt;
	['JFM'] = true,&lt;br /&gt;
	['journal'] = true,&lt;br /&gt;
	['jstor'] = true,&lt;br /&gt;
	['JSTOR'] = true,&lt;br /&gt;
	['jstor-access'] = true,&lt;br /&gt;
	['lang'] = true,&lt;br /&gt;
	['language'] = true,&lt;br /&gt;
	['last'] = true,&lt;br /&gt;
	['lay-date'] = false,&lt;br /&gt;
	['lay-format'] = false,&lt;br /&gt;
	['lay-source'] = false,&lt;br /&gt;
	['lay-url'] = false,&lt;br /&gt;
	['lccn'] = true,&lt;br /&gt;
	['LCCN'] = true,&lt;br /&gt;
	['location'] = true,&lt;br /&gt;
	['magazine'] = true,&lt;br /&gt;
	['medium'] = true,&lt;br /&gt;
	['minutes'] = true,															-- constrain to cite AV media and podcast?&lt;br /&gt;
	['mode'] = true,&lt;br /&gt;
	['mr'] = true,&lt;br /&gt;
	['MR'] = true,&lt;br /&gt;
	['name-list-style'] = true,&lt;br /&gt;
	['newspaper'] = true,&lt;br /&gt;
	['no-pp'] = true,&lt;br /&gt;
	['no-tracking'] = true,&lt;br /&gt;
	['number'] = true,&lt;br /&gt;
	['oclc'] = true,&lt;br /&gt;
	['OCLC'] = true,&lt;br /&gt;
	['ol'] = true,&lt;br /&gt;
	['OL'] = true,&lt;br /&gt;
	['ol-access'] = true,&lt;br /&gt;
	['orig-date'] = true,&lt;br /&gt;
	['origyear'] = true,&lt;br /&gt;
	['orig-year'] = true,&lt;br /&gt;
	['osti'] = true,&lt;br /&gt;
	['OSTI'] = true,&lt;br /&gt;
	['osti-access'] = true,&lt;br /&gt;
	['others'] = true,&lt;br /&gt;
	['p'] = true,&lt;br /&gt;
	['page'] = true,&lt;br /&gt;
	['pages'] = true,&lt;br /&gt;
	['people'] = true,&lt;br /&gt;
	['periodical'] = true,&lt;br /&gt;
	['place'] = true,&lt;br /&gt;
	['pmc'] = true,&lt;br /&gt;
	['PMC'] = true,&lt;br /&gt;
	['pmc-embargo-date'] = true,&lt;br /&gt;
	['pmid'] = true,&lt;br /&gt;
	['PMID'] = true,&lt;br /&gt;
	['postscript'] = true,&lt;br /&gt;
	['pp'] = true,&lt;br /&gt;
	['publication-date'] = true,&lt;br /&gt;
	['publication-place'] = true,&lt;br /&gt;
	['publisher'] = true,&lt;br /&gt;
	['quotation'] = true,&lt;br /&gt;
	['quote'] = true,&lt;br /&gt;
	['quote-page'] = true,&lt;br /&gt;
	['quote-pages'] = true,&lt;br /&gt;
	['ref'] = true,&lt;br /&gt;
	['rfc'] = true,&lt;br /&gt;
	['RFC'] = true,&lt;br /&gt;
	['sbn'] = true,&lt;br /&gt;
	['SBN'] = true,&lt;br /&gt;
	['scale'] = true,&lt;br /&gt;
	['script-article'] = true,&lt;br /&gt;
	['script-chapter'] = true,&lt;br /&gt;
	['script-contribution'] = true,&lt;br /&gt;
	['script-entry'] = true,&lt;br /&gt;
	['script-journal'] = true,&lt;br /&gt;
	['script-magazine'] = true,&lt;br /&gt;
	['script-newspaper'] = true,&lt;br /&gt;
	['script-periodical'] = true,&lt;br /&gt;
	['script-quote'] = true,&lt;br /&gt;
	['script-section'] = true,&lt;br /&gt;
	['script-title'] = true,&lt;br /&gt;
	['script-website'] = true,&lt;br /&gt;
	['script-work'] = true,&lt;br /&gt;
	['section'] = true,&lt;br /&gt;
	['section-format'] = true,&lt;br /&gt;
	['section-url'] = true,&lt;br /&gt;
	['section-url-access'] = true,&lt;br /&gt;
	['series'] = true,&lt;br /&gt;
	['ssrn'] = true,															-- cite ssrn; these three here because allowed in cite ... as identifier&lt;br /&gt;
	['SSRN'] = true,&lt;br /&gt;
	['ssrn-access'] = true,&lt;br /&gt;
	['subject'] = true,&lt;br /&gt;
	['subject-link'] = true,&lt;br /&gt;
	['subject-mask'] = true,&lt;br /&gt;
	['surname'] = true,&lt;br /&gt;
	['s2cid'] = true,&lt;br /&gt;
	['S2CID'] = true,&lt;br /&gt;
	['s2cid-access'] = true,&lt;br /&gt;
	['template-doc-demo'] = true,&lt;br /&gt;
	['time'] = true,															-- constrain to cite av media and podcast?&lt;br /&gt;
	['time-caption'] = true,													-- constrain to cite av media and podcast?&lt;br /&gt;
	['title'] = true,&lt;br /&gt;
	['title-link'] = true,&lt;br /&gt;
	['translator'] = true,&lt;br /&gt;
	['translator-first'] = true,&lt;br /&gt;
	['translator-given'] = true,&lt;br /&gt;
	['translator-last'] = true,	&lt;br /&gt;
	['translator-surname'] = true,&lt;br /&gt;
	['translator-link'] = true,&lt;br /&gt;
	['translator-mask'] = true,&lt;br /&gt;
	['trans-article'] = true,&lt;br /&gt;
	['trans-chapter'] = true,&lt;br /&gt;
	['trans-contribution'] = true,&lt;br /&gt;
	['trans-entry'] = true,&lt;br /&gt;
	['trans-journal'] = true,&lt;br /&gt;
	['trans-magazine'] = true,&lt;br /&gt;
	['trans-newspaper'] = true,&lt;br /&gt;
	['trans-periodical'] = true,&lt;br /&gt;
	['trans-quote'] = true,&lt;br /&gt;
	['trans-section'] = true,&lt;br /&gt;
	['trans-title'] = true,&lt;br /&gt;
	['trans-website'] = true,&lt;br /&gt;
	['trans-work'] = true,&lt;br /&gt;
	['type'] = true,&lt;br /&gt;
	['url'] = true,&lt;br /&gt;
	['URL'] = true,&lt;br /&gt;
	['url-access'] = true,&lt;br /&gt;
	['url-status'] = true,&lt;br /&gt;
	['vauthors'] = true,&lt;br /&gt;
	['veditors'] = true,&lt;br /&gt;
	['version'] = true,&lt;br /&gt;
	['via'] = true,&lt;br /&gt;
	['volume'] = true,&lt;br /&gt;
	['website'] = true,&lt;br /&gt;
	['work'] = true,&lt;br /&gt;
	['year'] = true,&lt;br /&gt;
	['zbl'] = true,&lt;br /&gt;
	['ZBL'] = true,&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
local numbered_arguments = {&lt;br /&gt;
	['author#'] = true,&lt;br /&gt;
	['author-first#'] = true,&lt;br /&gt;
	['author#-first'] = true,&lt;br /&gt;
	['author-given#'] = true,&lt;br /&gt;
	['author#-given'] = true,&lt;br /&gt;
	['author-last#'] = true,&lt;br /&gt;
	['author#-last'] = true,&lt;br /&gt;
	['author-surname#'] = true,&lt;br /&gt;
	['author#-surname'] = true,&lt;br /&gt;
	['author-link#'] = true,&lt;br /&gt;
	['author#-link'] = true,&lt;br /&gt;
	['authorlink#'] = true,&lt;br /&gt;
	['author#link'] = true,&lt;br /&gt;
	['author-mask#'] = true,&lt;br /&gt;
	['author#-mask'] = true,&lt;br /&gt;
	['contributor#'] = true,&lt;br /&gt;
	['contributor-first#'] = true,&lt;br /&gt;
	['contributor#-first'] = true,&lt;br /&gt;
	['contributor-given#'] = true,&lt;br /&gt;
	['contributor#-given'] = true,&lt;br /&gt;
	['contributor-last#'] = true,&lt;br /&gt;
	['contributor#-last'] = true,&lt;br /&gt;
	['contributor-surname#'] = true,&lt;br /&gt;
	['contributor#-surname'] = true,&lt;br /&gt;
	['contributor-link#'] = true,&lt;br /&gt;
	['contributor#-link'] = true,&lt;br /&gt;
	['contributor-mask#'] = true,&lt;br /&gt;
	['contributor#-mask'] = true,&lt;br /&gt;
	['editor#'] = true,&lt;br /&gt;
	['editor-first#'] = true,&lt;br /&gt;
	['editor#-first'] = true,&lt;br /&gt;
	['editor-given#'] = true,&lt;br /&gt;
	['editor#-given'] = true,&lt;br /&gt;
	['editor-last#'] = true,&lt;br /&gt;
	['editor#-last'] = true,&lt;br /&gt;
	['editor-surname#'] = true,&lt;br /&gt;
	['editor#-surname'] = true,&lt;br /&gt;
	['editor-link#'] = true,&lt;br /&gt;
	['editor#-link'] = true,&lt;br /&gt;
	['editor-mask#'] = true,&lt;br /&gt;
	['editor#-mask'] = true,&lt;br /&gt;
	['first#'] = true,&lt;br /&gt;
	['given#'] = true,&lt;br /&gt;
	['host#'] = true,&lt;br /&gt;
	['interviewer#'] = true,&lt;br /&gt;
	['interviewer-first#'] = true,&lt;br /&gt;
	['interviewer#-first'] = true,&lt;br /&gt;
	['interviewer-given#'] = true,&lt;br /&gt;
	['interviewer#-given'] = true,&lt;br /&gt;
	['interviewer-last#'] = true,&lt;br /&gt;
	['interviewer#-last'] = true,&lt;br /&gt;
	['interviewer-surname#'] = true,&lt;br /&gt;
	['interviewer#-surname'] = true,&lt;br /&gt;
	['interviewer-link#'] = true,&lt;br /&gt;
	['interviewer#-link'] = true,&lt;br /&gt;
	['interviewer-mask#'] = true,&lt;br /&gt;
	['interviewer#-mask'] = true,&lt;br /&gt;
	['last#'] = true,&lt;br /&gt;
	['subject#'] = true,&lt;br /&gt;
	['subject-link#'] = true,&lt;br /&gt;
	['subject#-link'] = true,&lt;br /&gt;
	['subject-mask#'] = true,&lt;br /&gt;
	['subject#-mask'] = true,&lt;br /&gt;
	['surname#'] = true,&lt;br /&gt;
	['translator#'] = true,&lt;br /&gt;
	['translator-first#'] = true,&lt;br /&gt;
	['translator#-first'] = true,&lt;br /&gt;
	['translator-given#'] = true,&lt;br /&gt;
	['translator#-given'] = true,&lt;br /&gt;
	['translator-last#'] = true,&lt;br /&gt;
	['translator#-last'] = true,&lt;br /&gt;
	['translator-surname#'] = true,&lt;br /&gt;
	['translator#-surname'] = true,&lt;br /&gt;
	['translator-link#'] = true,&lt;br /&gt;
	['translator#-link'] = true,&lt;br /&gt;
	['translator-mask#'] = true,&lt;br /&gt;
	['translator#-mask'] = true,&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P R E P R I N T   S U P P O R T E D   P A R A M E T E R S &amp;gt;--------------------&lt;br /&gt;
&lt;br /&gt;
Cite arXiv, cite biorxiv, cite citeseerx, and cite ssrn are preprint templates that use the limited set of parameters&lt;br /&gt;
defined in the limited_basic_arguments and limited_numbered_arguments tables.  Those lists are supplemented with a&lt;br /&gt;
template-specific list of parameters that are required by the particular template and may be exclusive to one of the&lt;br /&gt;
preprint templates.  Some of these parameters may also be available to the general cs1|2 templates.&lt;br /&gt;
&lt;br /&gt;
Same conventions for true/false/tracked/nil as above.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local preprint_arguments = {&lt;br /&gt;
	arxiv = {&lt;br /&gt;
		['arxiv'] = true,														-- cite arxiv and arxiv identifiers&lt;br /&gt;
		['class'] = true,&lt;br /&gt;
		['eprint'] = true,														-- cite arxiv and arxiv identifiers&lt;br /&gt;
		},&lt;br /&gt;
	biorxiv = {&lt;br /&gt;
		['biorxiv'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	citeseerx = {&lt;br /&gt;
		['citeseerx'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	ssrn = {&lt;br /&gt;
		['ssrn'] = true,&lt;br /&gt;
		['SSRN'] = true,&lt;br /&gt;
		['ssrn-access'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; L I M I T E D   S U P P O R T E D   P A R A M E T E R S &amp;gt;----------------------&lt;br /&gt;
&lt;br /&gt;
cite arxiv, cite biorxiv, cite citeseerx, and cite ssrn templates are preprint templates so are allowed only a&lt;br /&gt;
limited subset of parameters allowed to all other cs1|2 templates.  The limited subset is defined here.&lt;br /&gt;
&lt;br /&gt;
Same conventions for true/false/tracked/nil as above.&lt;br /&gt;
	&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local limited_basic_arguments = {&lt;br /&gt;
	['at'] = true,&lt;br /&gt;
	['author'] = true,&lt;br /&gt;
	['author-first'] = true,&lt;br /&gt;
	['author-given'] = true,&lt;br /&gt;
	['author-last'] = true,&lt;br /&gt;
	['author-surname'] = true,&lt;br /&gt;
	['author-link'] = true,&lt;br /&gt;
	['authorlink'] = true,&lt;br /&gt;
	['author-mask'] = true,&lt;br /&gt;
	['authors'] = true,&lt;br /&gt;
	['collaboration'] = true,&lt;br /&gt;
	['date'] = true,&lt;br /&gt;
	['df'] = true,&lt;br /&gt;
	['display-authors'] = true,&lt;br /&gt;
	['first'] = true,&lt;br /&gt;
	['given'] = true,&lt;br /&gt;
	['language'] = true,&lt;br /&gt;
	['last'] = true,&lt;br /&gt;
	['mode'] = true,&lt;br /&gt;
	['name-list-style'] = true,&lt;br /&gt;
	['no-tracking'] = true,&lt;br /&gt;
	['p'] = true,&lt;br /&gt;
	['page'] = true,&lt;br /&gt;
	['pages'] = true,&lt;br /&gt;
	['postscript'] = true,&lt;br /&gt;
	['pp'] = true,&lt;br /&gt;
	['quotation'] = true,&lt;br /&gt;
	['quote'] = true,&lt;br /&gt;
	['ref'] = true,&lt;br /&gt;
	['surname'] = true,&lt;br /&gt;
	['template-doc-demo'] = true,&lt;br /&gt;
	['title'] = true,&lt;br /&gt;
	['trans-title'] = true,&lt;br /&gt;
	['vauthors'] = true,&lt;br /&gt;
	['year'] = true,&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
local limited_numbered_arguments = {&lt;br /&gt;
	['author#'] = true,&lt;br /&gt;
	['author-first#'] = true,&lt;br /&gt;
	['author#-first'] = true,&lt;br /&gt;
	['author-given#'] = true,&lt;br /&gt;
	['author#-given'] = true,&lt;br /&gt;
	['author-last#'] = true,&lt;br /&gt;
	['author#-last'] = true,&lt;br /&gt;
	['author-surname#'] = true,&lt;br /&gt;
	['author#-surname'] = true,&lt;br /&gt;
	['author-link#'] = true,&lt;br /&gt;
	['author#-link'] = true,&lt;br /&gt;
	['authorlink#'] = true,&lt;br /&gt;
	['author#link'] = true,&lt;br /&gt;
	['author-mask#'] = true,&lt;br /&gt;
	['author#-mask'] = true,&lt;br /&gt;
	['first#'] = true,&lt;br /&gt;
	['given#'] = true,&lt;br /&gt;
	['last#'] = true,&lt;br /&gt;
	['surname#'] = true,&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; U N I Q U E _ A R G U M E N T S &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Some templates have unique parameters.  Those templates and their unique parameters are listed here. Keys in this&lt;br /&gt;
table are the template's CitationClass parameter value&lt;br /&gt;
&lt;br /&gt;
Same conventions for true/false/tracked/nil as above.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local unique_arguments = {&lt;br /&gt;
	['audio-visual'] = {&lt;br /&gt;
		['transcript'] = true,&lt;br /&gt;
		['transcript-format'] = true,&lt;br /&gt;
		['transcript-url'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	conference = {&lt;br /&gt;
		['book-title'] = true,&lt;br /&gt;
		['conference'] = true,&lt;br /&gt;
		['conference-format'] = true,&lt;br /&gt;
		['conference-url'] = true,&lt;br /&gt;
		['event'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	episode = {&lt;br /&gt;
		['airdate'] = true,&lt;br /&gt;
		['air-date'] = true,&lt;br /&gt;
		['credits'] = true,&lt;br /&gt;
		['episode-link'] = true,												-- alias of |title-link=&lt;br /&gt;
		['network'] = true,&lt;br /&gt;
		['season'] = true,&lt;br /&gt;
		['series-link'] = true,&lt;br /&gt;
		['series-no'] = true,&lt;br /&gt;
		['series-number'] = true,&lt;br /&gt;
		['station'] = true,&lt;br /&gt;
		['transcript'] = true,&lt;br /&gt;
		['transcript-format'] = true,&lt;br /&gt;
		['transcripturl'] = false,&lt;br /&gt;
		['transcript-url'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	mailinglist = {&lt;br /&gt;
		['mailing-list'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	map = {&lt;br /&gt;
		['cartography'] = true,&lt;br /&gt;
		['inset'] = true,&lt;br /&gt;
		['map'] = true,&lt;br /&gt;
		['map-format'] = true,&lt;br /&gt;
		['map-url'] = true,&lt;br /&gt;
		['map-url-access'] = true,&lt;br /&gt;
		['script-map'] = true,&lt;br /&gt;
		['sections'] = true,&lt;br /&gt;
		['sheet'] = true,&lt;br /&gt;
		['sheets'] = true,&lt;br /&gt;
		['trans-map'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	newsgroup = {&lt;br /&gt;
		['message-id'] = true,&lt;br /&gt;
		['newsgroup'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	report = {&lt;br /&gt;
		['docket'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	serial = {&lt;br /&gt;
		['airdate'] = true,&lt;br /&gt;
		['air-date'] = true,&lt;br /&gt;
		['credits'] = true,&lt;br /&gt;
		['episode'] = true,														-- cite serial only TODO: make available to cite episode?&lt;br /&gt;
		['episode-link'] = true,												-- alias of |title-link=&lt;br /&gt;
		['network'] = true,&lt;br /&gt;
		['series-link'] = true,&lt;br /&gt;
		['station'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	speech = {&lt;br /&gt;
		['conference'] = true,&lt;br /&gt;
		['conference-format'] = true,&lt;br /&gt;
		['conference-url'] = true,&lt;br /&gt;
		['event'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	thesis = {&lt;br /&gt;
		['degree'] = true,&lt;br /&gt;
		['docket'] = true,&lt;br /&gt;
		},&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; T E M P L A T E _ L I S T _ G E T &amp;gt;--------------------------------------------&lt;br /&gt;
&lt;br /&gt;
gets a list of the templates from table t&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function template_list_get (t)&lt;br /&gt;
	local out = {};																-- a table for output&lt;br /&gt;
	for k, _ in pairs (t) do													-- spin through the table and collect the keys&lt;br /&gt;
		table.insert (out, k)													-- add each key to the output table&lt;br /&gt;
	end&lt;br /&gt;
	return out;																	-- and done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X P O R T E D   T A B L E S &amp;gt;------------------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
	basic_arguments = basic_arguments,&lt;br /&gt;
	numbered_arguments = numbered_arguments,&lt;br /&gt;
	limited_basic_arguments = limited_basic_arguments,&lt;br /&gt;
	limited_numbered_arguments = limited_numbered_arguments,&lt;br /&gt;
&lt;br /&gt;
	preprint_arguments = preprint_arguments,&lt;br /&gt;
	preprint_template_list = template_list_get (preprint_arguments),			-- make a template list from preprint_arguments{} table&lt;br /&gt;
	unique_arguments = unique_arguments,&lt;br /&gt;
	unique_param_template_list = template_list_get (unique_arguments),			-- make a template list from unique_arguments{} table&lt;br /&gt;
	};&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Configuration&amp;diff=261</id>
		<title>Module:Citation/CS1/Configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Citation/CS1/Configuration&amp;diff=261"/>
		<updated>2022-04-04T14:02:48Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;--[[--------------------------&amp;lt; U N C A T E G O R I Z E D _ N A M E S P A C E S &amp;gt;------------------------------  List of namespaces that should not be included in citation err...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[--------------------------&amp;lt; U N C A T E G O R I Z E D _ N A M E S P A C E S &amp;gt;------------------------------&lt;br /&gt;
&lt;br /&gt;
List of namespaces that should not be included in citation error categories.&lt;br /&gt;
Same as setting notracking = true by default.&lt;br /&gt;
&lt;br /&gt;
Note: Namespace names should use underscores instead of spaces.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local uncategorized_namespaces = { 'User', 'Talk', 'User_talk', 'Wikipedia_talk',&lt;br /&gt;
	'File_talk', 'Template_talk', 'Help_talk', 'Category_talk', 'Portal_talk',&lt;br /&gt;
	'Book_talk', 'Draft_talk', 'Education_Program_talk', 'Module_talk', 'MediaWiki_talk' };&lt;br /&gt;
local uncategorized_subpages = {'/[Ss]andbox', '/[Tt]estcases', '/[^/]*[Ll]og', '/[Aa]rchive'};		-- list of Lua patterns found in page names of pages we should not categorize&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; M E S S A G E S &amp;gt;--------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Translation table&lt;br /&gt;
&lt;br /&gt;
The following contains fixed text that may be output as part of a citation.&lt;br /&gt;
This is separated from the main body to aid in future translations of this&lt;br /&gt;
module.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local messages = {&lt;br /&gt;
	['agency'] = '$1 $2',														-- $1 is sepc, $2 is agency&lt;br /&gt;
	['archived-dead'] = 'Archived from $1 on $2',&lt;br /&gt;
	['archived-live'] = '$1 from the original on $2',&lt;br /&gt;
	['archived-missing'] = 'Archived from the original$1 on $2',&lt;br /&gt;
	['archived-unfit'] = 'Archived from the original on ',&lt;br /&gt;
	['archived'] = 'Archived',&lt;br /&gt;
	['by'] = 'By',																-- contributions to authored works: introduction, foreword, afterword&lt;br /&gt;
	['cartography'] = 'Cartography by $1',&lt;br /&gt;
	['editor'] = 'ed.',&lt;br /&gt;
	['editors'] = 'eds.',&lt;br /&gt;
	['edition'] = '($1&amp;amp;nbsp;ed.)',&lt;br /&gt;
	['episode'] = 'Episode $1',&lt;br /&gt;
	['et al'] = 'et&amp;amp;nbsp;al.',&lt;br /&gt;
	['in'] = 'In',																-- edited works&lt;br /&gt;
	['inactive'] = 'inactive',&lt;br /&gt;
	['inset'] = '$1 inset',&lt;br /&gt;
	['interview'] = 'Interviewed by $1',										&lt;br /&gt;
	['lay summary'] = 'Lay summary',&lt;br /&gt;
	['mismatch'] = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; / &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$2=&amp;lt;/code&amp;gt; mismatch',	-- $1 is year param name; $2 is date param name&lt;br /&gt;
	['newsgroup'] = '[[Usenet newsgroup|Newsgroup]]:&amp;amp;nbsp;$1',&lt;br /&gt;
	['notitle'] = 'No title',													-- for |title=(()) and (in the future) |title=none&lt;br /&gt;
	['original'] = 'the original',&lt;br /&gt;
	['origdate'] = ' [$1]',&lt;br /&gt;
	['published'] = ' (published $1)',&lt;br /&gt;
	['retrieved'] = 'Retrieved $1',&lt;br /&gt;
	['season'] = 'Season $1',&lt;br /&gt;
	['section'] = '§&amp;amp;nbsp;$1',&lt;br /&gt;
	['sections'] = '§§&amp;amp;nbsp;$1',&lt;br /&gt;
	['series'] = '$1 $2',														-- $1 is sepc, $2 is series&lt;br /&gt;
	['seriesnum'] = 'Series $1',&lt;br /&gt;
	['translated'] = 'Translated by $1',&lt;br /&gt;
	['type'] = ' ($1)',															-- for titletype&lt;br /&gt;
	['written'] = 'Written at $1',&lt;br /&gt;
&lt;br /&gt;
	['vol'] = '$1 Vol.&amp;amp;nbsp;$2',												-- $1 is sepc; bold journal style volume is in presentation{}&lt;br /&gt;
	['vol-no'] = '$1 Vol.&amp;amp;nbsp;$2, no.&amp;amp;nbsp;$3',									-- sepc, volume, issue (alternatively insert $1 after $2, but then we'd also have to change capitalization)&lt;br /&gt;
	['issue'] = '$1 No.&amp;amp;nbsp;$2',												-- $1 is sepc&lt;br /&gt;
&lt;br /&gt;
	['j-vol'] = '$1 $2',														-- sepc, volume; bold journal volume is in presentation{}&lt;br /&gt;
	['j-issue'] = ' ($1)',&lt;br /&gt;
&lt;br /&gt;
	['nopp'] = '$1 $2';															-- page(s) without prefix; $1 is sepc&lt;br /&gt;
&lt;br /&gt;
	['p-prefix'] = &amp;quot;$1 p.&amp;amp;nbsp;$2&amp;quot;,												-- $1 is sepc&lt;br /&gt;
	['pp-prefix'] = &amp;quot;$1 pp.&amp;amp;nbsp;$2&amp;quot;,											-- $1 is sepc&lt;br /&gt;
	['j-page(s)'] = ': $1',														-- same for page and pages&lt;br /&gt;
&lt;br /&gt;
	['sheet'] = '$1 Sheet&amp;amp;nbsp;$2',												-- $1 is sepc&lt;br /&gt;
	['sheets'] = '$1 Sheets&amp;amp;nbsp;$2',											-- $1 is sepc&lt;br /&gt;
	['j-sheet'] = ': Sheet&amp;amp;nbsp;$1',&lt;br /&gt;
	['j-sheets'] = ': Sheets&amp;amp;nbsp;$1',&lt;br /&gt;
	&lt;br /&gt;
	['language'] = '(in $1)',&lt;br /&gt;
	['via'] = &amp;quot; &amp;amp;ndash; via $1&amp;quot;,&lt;br /&gt;
	['event'] = 'Event occurs at',&lt;br /&gt;
	['minutes'] = 'minutes in',&lt;br /&gt;
	&lt;br /&gt;
	-- Determines the location of the help page&lt;br /&gt;
	['help page link'] = 'Help:CS1 errors',&lt;br /&gt;
	['help page label'] = 'help',&lt;br /&gt;
	&lt;br /&gt;
	-- categories&lt;br /&gt;
	['cat wikilink'] = '[[Category:$1]]',										-- $1 is the category name&lt;br /&gt;
	[':cat wikilink'] = '[[:Category:$1|link]]',								-- category name as maintenance message wikilink; $1 is the category name&lt;br /&gt;
&lt;br /&gt;
	-- Internal errors (should only occur if configuration is bad)&lt;br /&gt;
	['undefined_error'] = 'Called with an undefined error condition',&lt;br /&gt;
	['unknown_ID_key'] = 'Unrecognized ID key: ',								-- an ID key in id_handlers not found in ~/Identifiers func_map{}&lt;br /&gt;
	['unknown_ID_access'] = 'Unrecognized ID access keyword: ',					-- an ID access keyword in id_handlers not found in keywords_lists['id-access']{}&lt;br /&gt;
	['unknown_argument_map'] = 'Argument map not defined for this variable',&lt;br /&gt;
	['bare_url_no_origin'] = 'Bare URL found but origin indicator is nil or empty',&lt;br /&gt;
	&lt;br /&gt;
	['warning_msg_e'] = '&amp;lt;span style=&amp;quot;color:#d33&amp;quot;&amp;gt;One or more &amp;lt;code style=&amp;quot;color: inherit; background: inherit; border: none; padding: inherit;&amp;quot;&amp;gt;&amp;amp;#123;{$1}}&amp;lt;/code&amp;gt; templates have errors&amp;lt;/span&amp;gt;; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).';	-- $1 is template link&lt;br /&gt;
	['warning_msg_m'] = '&amp;lt;span style=&amp;quot;color:#3a3&amp;quot;&amp;gt;One or more &amp;lt;code style=&amp;quot;color: inherit; background: inherit; border: none; padding: inherit;&amp;quot;&amp;gt;&amp;amp;#123;{$1}}&amp;lt;/code&amp;gt; templates have maintenance messages&amp;lt;/span&amp;gt;; messages may be hidden ([[Help:CS1_errors#Controlling_error_message_display|help]]).';	-- $1 is template link&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C I T A T I O N _ C L A S S _ M A P &amp;gt;------------------------------------------&lt;br /&gt;
&lt;br /&gt;
this table maps the value assigned to |CitationClass= in the cs1|2 templates to the canonical template name when&lt;br /&gt;
the value assigned to |CitationClass= is different from the canonical template name.  |CitationClass= values are&lt;br /&gt;
used as class attributes in the &amp;lt;cite&amp;gt; tag that encloses the citation so these names may not contain spaces while&lt;br /&gt;
the canonical template name may.  These names are used in warning_msg_e and warning_msg_m to create links to the&lt;br /&gt;
template's documentation when an article is displayed in preivew mode.&lt;br /&gt;
&lt;br /&gt;
Most cs1|2 template |CitationClass= values at en.wiki match their canonical template names so are not listed here.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
	local citation_class_map_t = {												-- TODO: if kept, these and all other config.CitationClass 'names' require some sort of i18n&lt;br /&gt;
		['audio-visual'] = 'AV media',											-- TODO: move to ~/Configuration&lt;br /&gt;
		['AV-media-notes'] = 'AV media notes',&lt;br /&gt;
		['encyclopaedia'] = 'encyclopedia',&lt;br /&gt;
		['mailinglist'] = 'mailing list',&lt;br /&gt;
		['pressrelease'] = 'press release'&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; E T _ A L _ P A T T E R N S &amp;gt;--------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
This table provides Lua patterns for the phrase &amp;quot;et al&amp;quot; and variants in name text&lt;br /&gt;
(author, editor, etc.). The main module uses these to identify and emit the 'etal' message.&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local et_al_patterns = {&lt;br /&gt;
	&amp;quot;[;,]? *[\&amp;quot;']*%f[%a][Ee][Tt]%.? *[Aa][Ll][%.\&amp;quot;']*$&amp;quot;,						-- variations on the 'et al' theme&lt;br /&gt;
	&amp;quot;[;,]? *[\&amp;quot;']*%f[%a][Ee][Tt]%.? *[Aa][Ll][Ii][AaIi][Ee]?[%.\&amp;quot;']*$&amp;quot;,				-- variations on the 'et alia', 'et alii' and 'et aliae' themes (false positive 'et aliie' unlikely to match)&lt;br /&gt;
	&amp;quot;[;,]? *%f[%a]and [Oo]thers&amp;quot;,												-- an alternative to et al.&lt;br /&gt;
	&amp;quot;%[%[ *[Ee][Tt]%.? *[Aa][Ll]%.? *%]%]&amp;quot;,										-- a wikilinked form&lt;br /&gt;
	&amp;quot;%(%( *[Ee][Tt]%.? *[Aa][Ll]%.? *%)%)&amp;quot;,									-- a double-bracketed form (to counter partial removal of ((...)) syntax)&lt;br /&gt;
	&amp;quot;[%(%[] *[Ee][Tt]%.? *[Aa][Ll]%.? *[%)%]]&amp;quot;,									-- a bracketed form&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P R E S E N T A T I O N &amp;gt;------------------------&lt;br /&gt;
&lt;br /&gt;
Fixed presentation markup.  Originally part of citation_config.messages it has&lt;br /&gt;
been moved into its own, more semantically correct place.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local presentation = &lt;br /&gt;
	{&lt;br /&gt;
	-- .citation-comment class is specified at Help:CS1_errors#Controlling_error_message_display&lt;br /&gt;
	['hidden-error'] = '&amp;lt;span class=&amp;quot;cs1-hidden-error citation-comment&amp;quot;&amp;gt;$1&amp;lt;/span&amp;gt;',&lt;br /&gt;
	['visible-error'] = '&amp;lt;span class=&amp;quot;cs1-visible-error citation-comment&amp;quot;&amp;gt;$1&amp;lt;/span&amp;gt;',&lt;br /&gt;
	['hidden-maint'] = '&amp;lt;span class=&amp;quot;cs1-maint citation-comment&amp;quot;&amp;gt;$1&amp;lt;/span&amp;gt;',&lt;br /&gt;
	&lt;br /&gt;
	['accessdate'] = '&amp;lt;span class=&amp;quot;reference-accessdate&amp;quot;&amp;gt;$1$2&amp;lt;/span&amp;gt;',			-- to allow editors to hide accessdate using personal CSS&lt;br /&gt;
&lt;br /&gt;
	['bdi'] = '&amp;lt;bdi$1&amp;gt;$2&amp;lt;/bdi&amp;gt;',												-- bidirectional isolation used with |script-title= and the like&lt;br /&gt;
&lt;br /&gt;
	['cite'] = '&amp;lt;cite class=&amp;quot;$1&amp;quot;&amp;gt;$2&amp;lt;/cite&amp;gt;';									-- for use when citation does not have a namelist and |ref= not set so no id=&amp;quot;...&amp;quot; attribute&lt;br /&gt;
	['cite-id'] = '&amp;lt;cite id=&amp;quot;$1&amp;quot; class=&amp;quot;$2&amp;quot;&amp;gt;$3&amp;lt;/cite&amp;gt;';							-- for use when when |ref= is set or when citation has a namelist&lt;br /&gt;
&lt;br /&gt;
	['format'] = ' &amp;lt;span class=&amp;quot;cs1-format&amp;quot;&amp;gt;($1)&amp;lt;/span&amp;gt;',						-- for |format=, |chapter-format=, etc.&lt;br /&gt;
&lt;br /&gt;
	-- various access levels, for |access=, |doi-access=, |arxiv=, ...&lt;br /&gt;
	-- narrow no-break space &amp;amp;#8239; may work better than nowrap CSS. Or not? Browser support?&lt;br /&gt;
&lt;br /&gt;
	['ext-link-access-signal'] = '&amp;lt;span class=&amp;quot;$1&amp;quot; title=&amp;quot;$2&amp;quot;&amp;gt;$3&amp;lt;/span&amp;gt;',		-- external link with appropriate lock icon&lt;br /&gt;
		['free'] = {class='cs1-lock-free', title='Freely accessible'},			-- classes defined in Module:Citation/CS1/styles.css&lt;br /&gt;
		['registration'] = {class='cs1-lock-registration', title='Free registration required'},&lt;br /&gt;
		['limited'] = {class='cs1-lock-limited', title='Free access subject to limited trial, subscription normally required'},&lt;br /&gt;
		['subscription'] = {class='cs1-lock-subscription', title='Paid subscription required'},&lt;br /&gt;
&lt;br /&gt;
	['interwiki-icon'] = '&amp;lt;span class=&amp;quot;$1&amp;quot; title=&amp;quot;$2&amp;quot;&amp;gt;$3&amp;lt;/span&amp;gt;',&lt;br /&gt;
		['class-wikisource'] = 'cs1-ws-icon',&lt;br /&gt;
&lt;br /&gt;
	['italic-title'] = &amp;quot;''$1''&amp;quot;,&lt;br /&gt;
&lt;br /&gt;
	['kern-left'] = '&amp;lt;span class=&amp;quot;cs1-kern-left&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;$1',					-- spacing to use when title contains leading single or double quote mark&lt;br /&gt;
	['kern-right'] = '$1&amp;lt;span class=&amp;quot;cs1-kern-right&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;',					-- spacing to use when title contains trailing single or double quote mark&lt;br /&gt;
&lt;br /&gt;
	['nowrap1'] = '&amp;lt;span class=&amp;quot;nowrap&amp;quot;&amp;gt;$1&amp;lt;/span&amp;gt;',								-- for nowrapping an item: &amp;lt;span ...&amp;gt;yyyy-mm-dd&amp;lt;/span&amp;gt;&lt;br /&gt;
	['nowrap2'] = '&amp;lt;span class=&amp;quot;nowrap&amp;quot;&amp;gt;$1&amp;lt;/span&amp;gt; $2',							-- for nowrapping portions of an item: &amp;lt;span ...&amp;gt;dd mmmm&amp;lt;/span&amp;gt; yyyy (note white space)&lt;br /&gt;
&lt;br /&gt;
	['ocins'] = '&amp;lt;span title=&amp;quot;$1&amp;quot; class=&amp;quot;Z3988&amp;quot;&amp;gt;&amp;lt;/span&amp;gt;',&lt;br /&gt;
	&lt;br /&gt;
	['parameter'] = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;',&lt;br /&gt;
	&lt;br /&gt;
	['ps_cs1'] = '.';															-- CS1 style postscript (terminal) character&lt;br /&gt;
	['ps_cs2'] = '';															-- CS2 style postscript (terminal) character (empty string)&lt;br /&gt;
&lt;br /&gt;
	['quoted-text'] = '&amp;lt;q&amp;gt;$1&amp;lt;/q&amp;gt;',												-- for wrapping |quote= content&lt;br /&gt;
	['quoted-title'] = '&amp;quot;$1&amp;quot;',&lt;br /&gt;
&lt;br /&gt;
	['sep_cs1'] = '.',															-- CS1 element separator&lt;br /&gt;
	['sep_cs2'] = ',',															-- CS2 separator&lt;br /&gt;
	['sep_nl'] = ';',															-- CS1|2 style name-list separator between names is a semicolon&lt;br /&gt;
	['sep_nl_and'] = ' and ',													-- used as last nl sep when |name-list-style=and and list has 2 items&lt;br /&gt;
	['sep_nl_end'] = '; and ',													-- used as last nl sep when |name-list-style=and and list has 3+ names&lt;br /&gt;
	['sep_name'] = ', ',														-- CS1|2 style last/first separator is &amp;lt;comma&amp;gt;&amp;lt;space&amp;gt;&lt;br /&gt;
	['sep_nl_vanc'] = ',',														-- Vancouver style name-list separator between authors is a comma&lt;br /&gt;
	['sep_name_vanc'] = ' ',													-- Vancouver style last/first separator is a space&lt;br /&gt;
&lt;br /&gt;
	['sep_list'] = ', ',														-- used for |language= when list has 3+ items except for last sep which uses sep_list_end&lt;br /&gt;
	['sep_list_pair'] = ' and ',												-- used for |language= when list has 2 items&lt;br /&gt;
	['sep_list_end'] = ', and ',												-- used as last list sep for |language= when list has 3+ items&lt;br /&gt;
	&lt;br /&gt;
	['trans-italic-title'] = &amp;quot;&amp;amp;#91;''$1''&amp;amp;#93;&amp;quot;,&lt;br /&gt;
	['trans-quoted-title'] = &amp;quot;&amp;amp;#91;$1&amp;amp;#93;&amp;quot;,									-- for |trans-title= and |trans-quote=&lt;br /&gt;
	['vol-bold'] = '$1 &amp;lt;b&amp;gt;$2&amp;lt;/b&amp;gt;',												-- sepc, volume; for bold journal cites; for other cites ['vol'] in messages{}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
--[[--------------------------&amp;lt; A L I A S E S &amp;gt;---------------------------------&lt;br /&gt;
&lt;br /&gt;
Aliases table for commonly passed parameters.&lt;br /&gt;
&lt;br /&gt;
Parameter names on the right side in the assignments in this table must have been&lt;br /&gt;
defined in the Whitelist before they will be recognized as valid parameter names&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local aliases = {&lt;br /&gt;
	['AccessDate'] = {'access-date', 'accessdate'},								-- Used by InternetArchiveBot&lt;br /&gt;
	['Agency'] = 'agency',&lt;br /&gt;
	['ArchiveDate'] = {'archive-date', 'archivedate'},							-- Used by InternetArchiveBot&lt;br /&gt;
	['ArchiveFormat'] = 'archive-format',&lt;br /&gt;
	['ArchiveURL'] = {'archive-url', 'archiveurl'},								-- Used by InternetArchiveBot&lt;br /&gt;
	['ASINTLD'] = 'asin-tld',&lt;br /&gt;
	['At'] = 'at',																-- Used by InternetArchiveBot&lt;br /&gt;
	['Authors'] = {'authors', 'people', 'credits'},&lt;br /&gt;
	['BookTitle'] = {'book-title', 'booktitle'},&lt;br /&gt;
	['Cartography'] = 'cartography',&lt;br /&gt;
	['Chapter'] = {'chapter', 'contribution', 'entry', 'article', 'section'},&lt;br /&gt;
	['ChapterFormat'] = {'chapter-format', 'contribution-format', 'entry-format',&lt;br /&gt;
		'article-format', 'section-format'};&lt;br /&gt;
	['ChapterURL'] = {'chapter-url', 'contribution-url', 'entry-url', 'article-url', 'section-url', 'chapterurl'},	-- Used by InternetArchiveBot&lt;br /&gt;
	['ChapterUrlAccess'] = {'chapter-url-access', 'contribution-url-access',&lt;br /&gt;
		'entry-url-access', 'article-url-access', 'section-url-access'},		-- Used by InternetArchiveBot&lt;br /&gt;
	['Class'] = 'class',														-- cite arxiv and arxiv identifier&lt;br /&gt;
	['Collaboration'] = 'collaboration',&lt;br /&gt;
	['Conference'] = {'conference', 'event'},&lt;br /&gt;
	['ConferenceFormat'] = 'conference-format',&lt;br /&gt;
	['ConferenceURL'] = 'conference-url',										-- Used by InternetArchiveBot&lt;br /&gt;
	['Date'] = {'date', 'air-date', 'airdate'},									-- air-date and airdate for cite episode and cite serial only&lt;br /&gt;
	['Degree'] = 'degree',&lt;br /&gt;
	['DF'] = 'df',&lt;br /&gt;
	['DisplayAuthors'] = {'display-authors', 'display-subjects'},&lt;br /&gt;
	['DisplayContributors'] = 'display-contributors',&lt;br /&gt;
	['DisplayEditors'] = 'display-editors',&lt;br /&gt;
	['DisplayInterviewers'] = 'display-interviewers',&lt;br /&gt;
	['DisplayTranslators'] = 'display-translators',&lt;br /&gt;
	['Docket'] = 'docket',&lt;br /&gt;
	['DoiBroken'] = 'doi-broken-date',&lt;br /&gt;
	['Edition'] = 'edition',&lt;br /&gt;
	['Embargo'] = 'pmc-embargo-date',&lt;br /&gt;
	['Encyclopedia'] = {'encyclopedia', 'encyclopaedia', 'dictionary'},			-- cite encyclopedia only&lt;br /&gt;
	['Episode'] = 'episode',													-- cite serial only TODO: make available to cite episode?&lt;br /&gt;
	['Format'] = 'format',&lt;br /&gt;
	['ID'] = {'id', 'ID'},&lt;br /&gt;
	['Inset'] = 'inset',&lt;br /&gt;
	['Issue'] = {'issue', 'number'},&lt;br /&gt;
	['Language'] = {'language', 'lang'},&lt;br /&gt;
	['LayDate'] = 'lay-date',&lt;br /&gt;
	['LayFormat'] = 'lay-format',&lt;br /&gt;
	['LaySource'] = 'lay-source',&lt;br /&gt;
	['LayURL'] = 'lay-url',&lt;br /&gt;
	['MailingList'] = {'mailing-list', 'mailinglist'},							-- cite mailing list only&lt;br /&gt;
	['Map'] = 'map',															-- cite map only&lt;br /&gt;
	['MapFormat'] = 'map-format',												-- cite map only&lt;br /&gt;
	['MapURL'] = {'map-url', 'mapurl'},											-- cite map only -- Used by InternetArchiveBot&lt;br /&gt;
	['MapUrlAccess'] = 'map-url-access',										-- cite map only -- Used by InternetArchiveBot&lt;br /&gt;
	['Minutes'] = 'minutes',&lt;br /&gt;
	['Mode'] = 'mode',&lt;br /&gt;
	['NameListStyle'] = 'name-list-style',&lt;br /&gt;
	['Network'] = 'network',&lt;br /&gt;
	['Newsgroup'] = 'newsgroup',												-- cite newsgroup only&lt;br /&gt;
	['NoPP'] = {'no-pp', 'nopp'},&lt;br /&gt;
	['NoTracking'] = {'no-tracking', 'template-doc-demo'},&lt;br /&gt;
	['Number'] = 'number',														-- this case only for cite techreport&lt;br /&gt;
	['OrigDate'] = {'orig-date', 'orig-year', 'origyear'},&lt;br /&gt;
	['Others'] = 'others',&lt;br /&gt;
	['Page'] = {'page', 'p'},													-- Used by InternetArchiveBot&lt;br /&gt;
	['Pages'] = {'pages', 'pp'},												-- Used by InternetArchiveBot&lt;br /&gt;
	['Periodical'] = {'journal', 'magazine', 'newspaper', 'periodical', 'website', 'work'},&lt;br /&gt;
	['Place'] = {'place', 'location'},&lt;br /&gt;
	['PostScript'] = 'postscript',&lt;br /&gt;
	['PublicationDate'] = {'publication-date', 'publicationdate'},&lt;br /&gt;
	['PublicationPlace'] = {'publication-place', 'publicationplace'},&lt;br /&gt;
	['PublisherName'] = {'publisher', 'institution'},&lt;br /&gt;
	['Quote'] = {'quote', 'quotation'},&lt;br /&gt;
	['QuotePage'] = 'quote-page',&lt;br /&gt;
	['QuotePages'] = 'quote-pages',&lt;br /&gt;
	['Ref'] = 'ref',&lt;br /&gt;
	['Scale'] = 'scale',&lt;br /&gt;
	['ScriptChapter'] = {'script-chapter', 'script-contribution', 'script-entry',&lt;br /&gt;
		'script-article', 'script-section'},&lt;br /&gt;
	['ScriptMap'] = 'script-map',&lt;br /&gt;
	['ScriptPeriodical'] = {'script-journal', 'script-magazine', 'script-newspaper',&lt;br /&gt;
		'script-periodical', 'script-website', 'script-work'},&lt;br /&gt;
	['ScriptQuote'] = 'script-quote',&lt;br /&gt;
	['ScriptTitle'] = 'script-title',											-- Used by InternetArchiveBot&lt;br /&gt;
	['Season'] = 'season',&lt;br /&gt;
	['Sections'] = 'sections',													-- cite map only&lt;br /&gt;
	['Series'] = {'series', 'version'},&lt;br /&gt;
	['SeriesLink'] = {'series-link', 'serieslink'},&lt;br /&gt;
	['SeriesNumber'] = {'series-number', 'series-no'},&lt;br /&gt;
	['Sheet'] = 'sheet',														-- cite map only&lt;br /&gt;
	['Sheets'] = 'sheets',														-- cite map only&lt;br /&gt;
	['Station'] = 'station',&lt;br /&gt;
	['Time'] = 'time',&lt;br /&gt;
	['TimeCaption'] = 'time-caption',&lt;br /&gt;
	['Title'] = 'title',														-- Used by InternetArchiveBot&lt;br /&gt;
	['TitleLink'] = {'title-link', 'episode-link', 'episodelink'},				-- Used by InternetArchiveBot&lt;br /&gt;
	['TitleNote'] = 'department',&lt;br /&gt;
	['TitleType'] = {'type', 'medium'},&lt;br /&gt;
	['TransChapter'] = {'trans-article', 'trans-chapter', 'trans-contribution',&lt;br /&gt;
		'trans-entry', 'trans-section'},&lt;br /&gt;
	['Transcript'] = 'transcript',&lt;br /&gt;
	['TranscriptFormat'] = 'transcript-format',	&lt;br /&gt;
	['TranscriptURL'] = {'transcript-url', 'transcripturl'},					-- Used by InternetArchiveBot&lt;br /&gt;
	['TransMap'] = 'trans-map',													-- cite map only&lt;br /&gt;
	['TransPeriodical'] = {'trans-journal', 'trans-magazine', 'trans-newspaper',&lt;br /&gt;
		'trans-periodical', 'trans-website', 'trans-work'},&lt;br /&gt;
	['TransQuote'] = 'trans-quote',&lt;br /&gt;
	['TransTitle'] = 'trans-title',												-- Used by InternetArchiveBot&lt;br /&gt;
	['URL'] = {'url', 'URL'},													-- Used by InternetArchiveBot&lt;br /&gt;
	['UrlAccess'] = 'url-access',												-- Used by InternetArchiveBot&lt;br /&gt;
	['UrlStatus'] = 'url-status',												-- Used by InternetArchiveBot&lt;br /&gt;
	['Vauthors'] = 'vauthors',&lt;br /&gt;
	['Veditors'] = 'veditors',&lt;br /&gt;
	['Via'] = 'via',&lt;br /&gt;
	['Volume'] = 'volume',&lt;br /&gt;
	['Year'] = 'year',&lt;br /&gt;
&lt;br /&gt;
	['AuthorList-First'] = {&amp;quot;first#&amp;quot;, &amp;quot;author-first#&amp;quot;, &amp;quot;author#-first&amp;quot;, &amp;quot;given#&amp;quot;,&lt;br /&gt;
		&amp;quot;author-given#&amp;quot;, &amp;quot;author#-given&amp;quot;},&lt;br /&gt;
	['AuthorList-Last'] = {&amp;quot;last#&amp;quot;, &amp;quot;author-last#&amp;quot;, &amp;quot;author#-last&amp;quot;, &amp;quot;surname#&amp;quot;,&lt;br /&gt;
		&amp;quot;author-surname#&amp;quot;, &amp;quot;author#-surname&amp;quot;, &amp;quot;author#&amp;quot;, &amp;quot;subject#&amp;quot;, 'host#'},&lt;br /&gt;
	['AuthorList-Link'] = {&amp;quot;author-link#&amp;quot;, &amp;quot;author#-link&amp;quot;, &amp;quot;subject-link#&amp;quot;,&lt;br /&gt;
		&amp;quot;subject#-link&amp;quot;, &amp;quot;authorlink#&amp;quot;, &amp;quot;author#link&amp;quot;},&lt;br /&gt;
	['AuthorList-Mask'] = {&amp;quot;author-mask#&amp;quot;, &amp;quot;author#-mask&amp;quot;, &amp;quot;subject-mask#&amp;quot;, &amp;quot;subject#-mask&amp;quot;},&lt;br /&gt;
&lt;br /&gt;
	['ContributorList-First'] = {'contributor-first#', 'contributor#-first',&lt;br /&gt;
		'contributor-given#', 'contributor#-given'},&lt;br /&gt;
	['ContributorList-Last'] = {'contributor-last#', 'contributor#-last',&lt;br /&gt;
		'contributor-surname#', 'contributor#-surname', 'contributor#'},&lt;br /&gt;
	['ContributorList-Link'] = {'contributor-link#', 'contributor#-link'},&lt;br /&gt;
	['ContributorList-Mask'] = {'contributor-mask#', 'contributor#-mask'},&lt;br /&gt;
&lt;br /&gt;
	['EditorList-First'] = {&amp;quot;editor-first#&amp;quot;, &amp;quot;editor#-first&amp;quot;, &amp;quot;editor-given#&amp;quot;, &amp;quot;editor#-given&amp;quot;},&lt;br /&gt;
	['EditorList-Last'] = {&amp;quot;editor-last#&amp;quot;, &amp;quot;editor#-last&amp;quot;, &amp;quot;editor-surname#&amp;quot;,&lt;br /&gt;
		&amp;quot;editor#-surname&amp;quot;, &amp;quot;editor#&amp;quot;},&lt;br /&gt;
	['EditorList-Link'] = {&amp;quot;editor-link#&amp;quot;, &amp;quot;editor#-link&amp;quot;},&lt;br /&gt;
	['EditorList-Mask'] = {&amp;quot;editor-mask#&amp;quot;, &amp;quot;editor#-mask&amp;quot;},&lt;br /&gt;
	&lt;br /&gt;
	['InterviewerList-First'] = {'interviewer-first#', 'interviewer#-first',&lt;br /&gt;
		'interviewer-given#', 'interviewer#-given'},&lt;br /&gt;
	['InterviewerList-Last'] = {'interviewer-last#', 'interviewer#-last',&lt;br /&gt;
		'interviewer-surname#', 'interviewer#-surname', 'interviewer#'},&lt;br /&gt;
	['InterviewerList-Link'] = {'interviewer-link#', 'interviewer#-link'},&lt;br /&gt;
	['InterviewerList-Mask'] = {'interviewer-mask#', 'interviewer#-mask'},&lt;br /&gt;
&lt;br /&gt;
	['TranslatorList-First'] = {'translator-first#', 'translator#-first',&lt;br /&gt;
		'translator-given#', 'translator#-given'},&lt;br /&gt;
	['TranslatorList-Last'] = {'translator-last#', 'translator#-last',&lt;br /&gt;
		'translator-surname#', 'translator#-surname', 'translator#'},&lt;br /&gt;
	['TranslatorList-Link'] = {'translator-link#', 'translator#-link'},&lt;br /&gt;
	['TranslatorList-Mask'] = {'translator-mask#', 'translator#-mask'},&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P U N C T _ S K I P &amp;gt;---------------------------&lt;br /&gt;
&lt;br /&gt;
builds a table of parameter names that the extraneous terminal punctuation check should not check.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local punct_meta_params = {														-- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value&lt;br /&gt;
	'BookTitle', 'Chapter', 'ScriptChapter', 'ScriptTitle', 'Title', 'TransChapter', 'Transcript', 'TransMap',	'TransTitle',	-- title-holding parameters&lt;br /&gt;
	'AuthorList-Mask', 'ContributorList-Mask', 'EditorList-Mask', 'InterviewerList-Mask', 'TranslatorList-Mask',	-- name-list mask may have name separators&lt;br /&gt;
	'PostScript', 'Quote', 'ScriptQuote', 'TransQuote', 'Ref',											-- miscellaneous&lt;br /&gt;
	'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'LayURL', 'MapURL', 'TranscriptURL', 'URL',			-- URL-holding parameters&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
local url_meta_params = {														-- table of aliases[] keys (meta parameters); each key has a table of parameter names for a value&lt;br /&gt;
	'ArchiveURL', 'ChapterURL', 'ConferenceURL', 'ID', 'LayURL', 'MapURL', 'TranscriptURL', 'URL',		-- parameters allowed to hold urls&lt;br /&gt;
	'Page', 'Pages', 'At', 'QuotePage', 'QuotePages',							-- insource locators allowed to hold urls&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
local function build_skip_table (skip_t, meta_params)&lt;br /&gt;
	for _, meta_param in ipairs (meta_params) do								-- for each meta parameter key&lt;br /&gt;
		local params = aliases[meta_param];										-- get the parameter or the table of parameters associated with the meta parameter name&lt;br /&gt;
		if 'string' == type (params) then&lt;br /&gt;
			skip_t[params] = 1;													-- just a single parameter&lt;br /&gt;
		else&lt;br /&gt;
			for _, param in ipairs (params) do									-- get the parameter name&lt;br /&gt;
				skip_t[param] = 1;												-- add the parameter name to the skip table&lt;br /&gt;
				local count;&lt;br /&gt;
				param, count = param:gsub ('#', '');							-- remove enumerator marker from enumerated parameters&lt;br /&gt;
				if 0 ~= count then												-- if removed&lt;br /&gt;
					skip_t[param] = 1;											-- add param name without enumerator marker&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return skip_t;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local punct_skip = {};&lt;br /&gt;
local url_skip = {};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-----------&amp;lt; S P E C I A L   C A S E   T R A N S L A T I O N S &amp;gt;------------&lt;br /&gt;
&lt;br /&gt;
This table is primarily here to support internationalization.  Translations in&lt;br /&gt;
this table are used, for example, when an error message, category name, etc.,&lt;br /&gt;
is extracted from the English alias key.  There may be other cases where&lt;br /&gt;
this translation table may be useful.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
local is_Latn = 'A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143';&lt;br /&gt;
local special_case_translation = {&lt;br /&gt;
	['AuthorList'] = 'authors list',											-- used to assemble maintenance category names&lt;br /&gt;
	['ContributorList'] = 'contributors list',									-- translation of these names plus translation of the base mainenance category names in maint_cats{} table below&lt;br /&gt;
	['EditorList'] = 'editors list',											-- must match the names of the actual categories&lt;br /&gt;
	['InterviewerList'] = 'interviewers list',									-- this group or translations used by name_has_ed_markup() and name_has_mult_names()&lt;br /&gt;
	['TranslatorList'] = 'translators list',&lt;br /&gt;
	&lt;br /&gt;
																				-- Lua patterns to match pseudo-titles used by InternetArchiveBot and others as placeholder for unknown |title= value&lt;br /&gt;
	['archived_copy'] = {														-- used with CS1 maint: Archive[d] copy as title&lt;br /&gt;
		['en'] = '^archived?%s+copy$',											-- for English; translators: keep this because templates imported from en.wiki&lt;br /&gt;
		['local'] = nil,														-- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language&lt;br /&gt;
		},&lt;br /&gt;
&lt;br /&gt;
																				-- Lua patterns to match generic titles; usually created by bots or reference filling tools&lt;br /&gt;
																				-- translators: replace ['local'] = nil with lowercase translation only when bots or tools create generic titles in your language&lt;br /&gt;
	['generic_titles'] = {&lt;br /&gt;
		-- generic titles and patterns in this table should be lowercase only&lt;br /&gt;
		-- leave ['local'] nil except when there is a matching generic title in your language&lt;br /&gt;
		-- boolean 'true' for plain-text searches; 'false' for pattern searches&lt;br /&gt;
		{['en'] = {'^wayback%s+machine$', false},				['local'] = nil},&lt;br /&gt;
		{['en'] = {'are you a robot', true},					['local'] = nil},&lt;br /&gt;
		{['en'] = {'hugedomains.com', true},					['local'] = nil},&lt;br /&gt;
		{['en'] = {'^[%(%[{&amp;lt;]?no +title[&amp;gt;}%]%)]?$', false},		['local'] = nil},&lt;br /&gt;
		{['en'] = {'page not found', true},						['local'] = nil},&lt;br /&gt;
		{['en'] = {'subscribe to read', true},					['local'] = nil},&lt;br /&gt;
		{['en'] = {'^[%(%[{&amp;lt;]?unknown[&amp;gt;}%]%)]?$', false},		['local'] = nil},&lt;br /&gt;
		{['en'] = {'website is for sale', true},				['local'] = nil},&lt;br /&gt;
		{['en'] = {'^404', false},								['local'] = nil},&lt;br /&gt;
		{['en'] = {'internet archive wayback machine', true},	['local'] = nil},&lt;br /&gt;
		{['en'] = {'log into facebook', true},					['local'] = nil},&lt;br /&gt;
		{['en'] = {'login • instagram', true},					['local'] = nil},&lt;br /&gt;
		{['en'] = {'redirecting...', true},						['local'] = nil},&lt;br /&gt;
		{['en'] = {'usurped title', true},						['local'] = nil},	-- added by a GreenC bot&lt;br /&gt;
		{['en'] = {'webcite query result', true},				['local'] = nil},&lt;br /&gt;
		{['en'] = {'wikiwix\'s cache', true},					['local'] = nil},&lt;br /&gt;
		},&lt;br /&gt;
&lt;br /&gt;
	['generic_names'] = {&lt;br /&gt;
		-- generic names and patterns in this table should be lowercase only&lt;br /&gt;
		-- leave ['local'] nil except when there is a matching generic name in your language&lt;br /&gt;
		-- boolean 'true' for plain-text searches; 'false' for pattern searches&lt;br /&gt;
		{['en'] = {'about us', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'%f[%a][Aa]dvisor%f[%A]', false},						['local'] = nil},&lt;br /&gt;
		{['en'] = {'%f[%a][Aa]uthor%f[%A]', false},							['local'] = nil},&lt;br /&gt;
		{['en'] = {'collaborator', true},									['local'] = nil},&lt;br /&gt;
		{['en'] = {'contributor', true},									['local'] = nil},&lt;br /&gt;
		{['en'] = {'contact us', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'directory', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'%f[%(%[][%(%[]%s*eds?%.?%s*[%)%]]?$', false},			['local'] = nil},&lt;br /&gt;
		{['en'] = {'[,%.%s]%f[e]eds?%.?$', false},							['local'] = nil},&lt;br /&gt;
		{['en'] = {'^eds?[%.,;]', false},									['local'] = nil},&lt;br /&gt;
		{['en'] = {'^[%(%[]%s*[Ee][Dd][Ss]?%.?%s*[%)%]]', false},			['local'] = nil},&lt;br /&gt;
		{['en'] = {'%f[%a][Ee]dited%f[%A]', false},							['local'] = nil},&lt;br /&gt;
		{['en'] = {'%f[%a][Ee]ditors?%f[%A]', false},						['local'] = nil},&lt;br /&gt;
		{['en'] = {'%f[%a]]Ee]mail%f[%A]', false},							['local'] = nil},&lt;br /&gt;
		{['en'] = {'facebook', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'google', true},											['local'] = nil},&lt;br /&gt;
		{['en'] = {'home page', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'instagram', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'interviewer', true},									['local'] = nil},&lt;br /&gt;
		{['en'] = {'linkedIn', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'pinterest', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'policy', true},											['local'] = nil},&lt;br /&gt;
		{['en'] = {'privacy', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'translator', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'tumblr', true},											['local'] = nil},&lt;br /&gt;
		{['en'] = {'twitter', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'site name', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'statement', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'submitted', true},										['local'] = nil},&lt;br /&gt;
		{['en'] = {'super.?user', false},									['local'] = nil},&lt;br /&gt;
		{['en'] = {'%f['..is_Latn..'][Uu]ser%f[^'..is_Latn..']', false},	['local'] = nil},&lt;br /&gt;
		{['en'] = {'verfasser', true},										['local'] = nil},&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; D A T E _ N A M E S &amp;gt;----------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
This table of tables lists local language date names and fallback English date names.&lt;br /&gt;
The code in Date_validation will look first in the local table for valid date names.&lt;br /&gt;
If date names are not found in the local table, the code will look in the English table.&lt;br /&gt;
&lt;br /&gt;
Because citations can be copied to the local wiki from en.wiki, the English is&lt;br /&gt;
required when the date-name translation function date_name_xlate() is used.&lt;br /&gt;
&lt;br /&gt;
In these tables, season numbering is defined by&lt;br /&gt;
Extended Date/Time Format (EDTF) Specification (https://www.loc.gov/standards/datetime/)&lt;br /&gt;
which became part of ISO 8601 in 2019.  See '§Sub-year groupings'. The standard&lt;br /&gt;
defines various divisions using numbers 21-41. CS1|2 only supports generic seasons.&lt;br /&gt;
EDTF does support the distinction between north and south hemisphere seasons&lt;br /&gt;
but CS1|2 has no way to make that distinction.&lt;br /&gt;
&lt;br /&gt;
33-36 = Quarter 1, Quarter 2, Quarter 3, Quarter 4 (3 months each)&lt;br /&gt;
&lt;br /&gt;
The standard does not address 'named' dates so, for the purposes of CS1|2,&lt;br /&gt;
Easter and Christmas are defined here as 98 and 99, which should be out of the&lt;br /&gt;
ISO 8601 (EDTF) range of uses for a while.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local date_names = {&lt;br /&gt;
	['en'] = {																	-- English&lt;br /&gt;
		['long']	= {['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12},&lt;br /&gt;
		['short']	= {['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12},&lt;br /&gt;
		['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36},&lt;br /&gt;
		['season']	= {['Winter'] = 24, ['Spring'] = 21, ['Summer'] = 22, ['Fall'] = 23, ['Autumn'] = 23},&lt;br /&gt;
		['named']	= {['Easter'] = 98, ['Christmas'] = 99},&lt;br /&gt;
		},&lt;br /&gt;
	['local'] = {																-- replace these English date names with the local language equivalents&lt;br /&gt;
		['long']	= {['January'] = 1, ['February'] = 2, ['March'] = 3, ['April'] = 4, ['May'] = 5, ['June'] = 6, ['July'] = 7, ['August'] = 8, ['September'] = 9, ['October'] = 10, ['November'] = 11, ['December'] = 12},&lt;br /&gt;
		['short']	= {['Jan'] = 1, ['Feb'] = 2, ['Mar'] = 3, ['Apr'] = 4, ['May'] = 5, ['Jun'] = 6, ['Jul'] = 7, ['Aug'] = 8, ['Sep'] = 9, ['Oct'] = 10, ['Nov'] = 11, ['Dec'] = 12},&lt;br /&gt;
		['quarter'] = {['First Quarter'] = 33, ['Second Quarter'] = 34, ['Third Quarter'] = 35, ['Fourth Quarter'] = 36},&lt;br /&gt;
		['season']	= {['Winter'] = 24, ['Spring'] = 21, ['Summer'] = 22, ['Fall'] = 23, ['Autumn'] = 23},&lt;br /&gt;
		['named']	= {['Easter'] = 98, ['Christmas'] = 99},&lt;br /&gt;
		},&lt;br /&gt;
	['inv_local_l'] = {},														-- used in date reformatting; copy of date_names['local'].long where k/v are inverted: [1]='&amp;lt;local name&amp;gt;' etc.&lt;br /&gt;
	['inv_local_s'] = {},														-- used in date reformatting; copy of date_names['local'].short where k/v are inverted: [1]='&amp;lt;local name&amp;gt;' etc.&lt;br /&gt;
	['local_digits'] = {['0'] = '0', ['1'] = '1', ['2'] = '2', ['3'] = '3', ['4'] = '4', ['5'] = '5', ['6'] = '6', ['7'] = '7', ['8'] = '8', ['9'] = '9'},	-- used to convert local language digits to Western 0-9&lt;br /&gt;
	['xlate_digits'] = {},&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
for name, i in pairs (date_names['local'].long) do								-- this table is ['name'] = i&lt;br /&gt;
	date_names['inv_local_l'][i] = name;										-- invert to get [i] = 'name' for conversions from ymd&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
for name, i in pairs (date_names['local'].short) do								-- this table is ['name'] = i&lt;br /&gt;
	date_names['inv_local_s'][i] = name;										-- invert to get [i] = 'name' for conversions from ymd&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
for ld, ed in pairs (date_names.local_digits) do								-- make a digit translation table for simple date translation from en to local language using local_digits table&lt;br /&gt;
	date_names.xlate_digits [ed] = ld;											-- en digit becomes index with local digit as the value&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local df_template_patterns = {													-- table of redirects to {{Use dmy dates}} and {{Use mdy dates}}&lt;br /&gt;
	'{{ *[Uu]se +(dmy) +dates *[|}]',	-- 1159k								-- sorted by approximate transclusion count&lt;br /&gt;
	'{{ *[Uu]se +(mdy) +dates *[|}]',	-- 212k&lt;br /&gt;
	'{{ *[Uu]se +(MDY) +dates *[|}]',	-- 788&lt;br /&gt;
	'{{ *[Uu]se +(DMY) +dates *[|}]',	-- 343&lt;br /&gt;
	'{{ *([Mm]dy) *[|}]',				-- 176&lt;br /&gt;
	'{{ *[Uu]se *(dmy) *[|}]',			-- 156 + 18&lt;br /&gt;
	'{{ *[Uu]se *(mdy) *[|}]',			-- 149 + 11&lt;br /&gt;
	'{{ *([Dd]my) *[|}]',				-- 56&lt;br /&gt;
	'{{ *[Uu]se +(MDY) *[|}]',			-- 5&lt;br /&gt;
	'{{ *([Dd]MY) *[|}]',				-- 3&lt;br /&gt;
	'{{ *[Uu]se(mdy)dates *[|}]',		-- 1&lt;br /&gt;
	'{{ *[Uu]se +(DMY) *[|}]',			-- 0&lt;br /&gt;
	'{{ *([Mm]DY) *[|}]',				-- 0&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
local function get_date_format ()&lt;br /&gt;
	local content = mw.title.getCurrentTitle():getContent() or '';				-- get the content of the article or ''; new pages edited w/ve do not have 'content' until saved; ve does not preview; phab:T221625&lt;br /&gt;
	for _, pattern in ipairs (df_template_patterns) do							-- loop through the patterns looking for {{Use dmy dates}} or {{Use mdy dates}} or any of their redirects&lt;br /&gt;
		local start, _, match = content:find(pattern);							-- match is the three letters indicating desired date format&lt;br /&gt;
		if match then&lt;br /&gt;
			content = content:match ('%b{}', start);							-- get the whole template&lt;br /&gt;
			if content:match ('| *cs1%-dates *= *[lsy][sy]?') then				-- look for |cs1-dates=publication date length access-/archive-date length&lt;br /&gt;
				return match:lower() .. '-' .. content:match ('| *cs1%-dates *= *([lsy][sy]?)');&lt;br /&gt;
			else&lt;br /&gt;
				return match:lower() .. '-all';									-- no |cs1-dates= k/v pair; return value appropriate for use in |df=&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local global_df;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-----------------&amp;lt; V O L U M E ,  I S S U E ,  P A G E S &amp;gt;------------------&lt;br /&gt;
&lt;br /&gt;
These tables hold cite class values (from the template invocation) and identify those templates that support&lt;br /&gt;
|volume=, |issue=, and |page(s)= parameters.  Cite conference and cite map require further qualification which&lt;br /&gt;
is handled in the main module.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local templates_using_volume = {'citation', 'audio-visual', 'book', 'conference', 'encyclopaedia', 'interview', 'journal', 'magazine', 'map', 'news', 'report', 'techreport', 'thesis'}&lt;br /&gt;
local templates_using_issue = {'citation', 'conference', 'episode', 'interview', 'journal', 'magazine', 'map', 'news', 'podcast'}&lt;br /&gt;
local templates_not_using_page = {'audio-visual', 'episode', 'mailinglist', 'newsgroup', 'podcast', 'serial', 'sign', 'speech'}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
&lt;br /&gt;
Patterns for finding extra text in |volume=, |issue=, |page=, |pages=&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local vol_iss_pg_patterns = {&lt;br /&gt;
	good_ppattern = '^P[^%.PpGg]',												-- OK to begin with uppercase P: P7 (page 7 of section P), but not p123 (page 123); TODO: this allows 'Pages' which it should not&lt;br /&gt;
	bad_ppatterns = {															-- patterns for |page= and |pages=&lt;br /&gt;
		'^[Pp][PpGg]?%.?[ %d]',&lt;br /&gt;
		'^[Pp][Pp]?%.&amp;amp;nbsp;',													-- from {{p.}} and {{pp.}} templates&lt;br /&gt;
		'^[Pp]ages?',&lt;br /&gt;
		'^[Pp]gs.?',&lt;br /&gt;
		},&lt;br /&gt;
	vpatterns = {																-- patterns for |volume=&lt;br /&gt;
		'^volumes?',&lt;br /&gt;
		'^vols?[%.:=]?'&lt;br /&gt;
		},&lt;br /&gt;
	ipatterns = {																-- patterns for |issue=&lt;br /&gt;
		'^issues?',&lt;br /&gt;
		'^iss[%.:=]?',&lt;br /&gt;
		'^numbers?',&lt;br /&gt;
		'^nos?%A',																-- don't match 'november' or 'nostradamus'&lt;br /&gt;
		'^nr[%.:=]?',&lt;br /&gt;
		'^n[%.:= ]'																-- might be a valid issue without separator (space char is sep char here)&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; K E Y W O R D S &amp;gt;-------------------------------&lt;br /&gt;
&lt;br /&gt;
These tables hold keywords for those parameters that have defined sets of acceptable keywords.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
--[[-------------------&amp;lt; K E Y W O R D S   T A B L E &amp;gt;--------------------------&lt;br /&gt;
&lt;br /&gt;
this is a list of keywords; each key in the list is associated with a table of&lt;br /&gt;
synonymous keywords possibly from different languages.&lt;br /&gt;
&lt;br /&gt;
for I18N: add local-language keywords to value table; do not change the key.&lt;br /&gt;
For example, adding the German keyword 'ja':&lt;br /&gt;
	['affirmative'] = {'yes', 'true', 'y', 'ja'},&lt;br /&gt;
&lt;br /&gt;
Because CS1|2 templates from en.wiki articles are often copied to other local wikis,&lt;br /&gt;
it is recommended that the English keywords remain in these tables.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local keywords = {&lt;br /&gt;
	['amp'] = {'&amp;amp;', 'amp', 'ampersand'}, 										-- |name-list-style=&lt;br /&gt;
	['and'] = {'and', 'serial'},												-- |name-list-style=&lt;br /&gt;
	['affirmative'] = {'yes', 'true', 'y'},										-- |no-tracking=, |no-pp= -- Used by InternetArchiveBot&lt;br /&gt;
	['afterword'] = {'afterword'},												-- |contribution=&lt;br /&gt;
	['bot: unknown'] = {'bot: unknown'},										-- |url-status= -- Used by InternetArchiveBot&lt;br /&gt;
	['cs1'] = {'cs1'},															-- |mode=&lt;br /&gt;
	['cs2'] = {'cs2'},															-- |mode=&lt;br /&gt;
	['dead'] = {'dead', 'deviated'},											-- |url-status= -- Used by InternetArchiveBot&lt;br /&gt;
	['dmy'] = {'dmy'},															-- |df=&lt;br /&gt;
	['dmy-all'] = {'dmy-all'},													-- |df=&lt;br /&gt;
	['foreword'] = {'foreword'},												-- |contribution=&lt;br /&gt;
	['free'] = {'free'},														-- |&amp;lt;id&amp;gt;-access= -- Used by InternetArchiveBot&lt;br /&gt;
	['harv'] = {'harv'},														-- |ref=; this no longer supported; is_valid_parameter_value() called with &amp;lt;invert&amp;gt; = true&lt;br /&gt;
	['introduction'] = {'introduction'},										-- |contribution=&lt;br /&gt;
	['limited'] = {'limited'},													-- |url-access= -- Used by InternetArchiveBot&lt;br /&gt;
	['live'] = {'live'},														-- |url-status= -- Used by InternetArchiveBot&lt;br /&gt;
	['mdy'] = {'mdy'},															-- |df=&lt;br /&gt;
	['mdy-all'] = {'mdy-all'},													-- |df=&lt;br /&gt;
	['none'] = {'none'},														-- |postscript=, |ref=, |title=, |type= -- Used by InternetArchiveBot&lt;br /&gt;
	['off'] = {'off'},															-- |title= (potentially also: |title-link=, |postscript=, |ref=, |type=)&lt;br /&gt;
	['preface'] = {'preface'},													-- |contribution=&lt;br /&gt;
	['registration'] = {'registration'},										-- |url-access= -- Used by InternetArchiveBot&lt;br /&gt;
	['subscription'] = {'subscription'},										-- |url-access= -- Used by InternetArchiveBot&lt;br /&gt;
	['unfit'] = {'unfit'},														-- |url-status= -- Used by InternetArchiveBot&lt;br /&gt;
	['usurped'] = {'usurped'},													-- |url-status= -- Used by InternetArchiveBot&lt;br /&gt;
	['vanc'] = {'vanc'},														-- |name-list-style=&lt;br /&gt;
	['ymd'] = {'ymd'},															-- |df=&lt;br /&gt;
	['ymd-all'] = {'ymd-all'},													-- |df=&lt;br /&gt;
	--	['yMd'] = {'yMd'},														-- |df=; not supported at en.wiki&lt;br /&gt;
	--	['yMd-all'] = {'yMd-all'},												-- |df=; not supported at en.wiki&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[------------------------&amp;lt; X L A T E _ K E Y W O R D S &amp;gt;---------------------&lt;br /&gt;
&lt;br /&gt;
this function builds a list, keywords_xlate{}, of the keywords found in keywords{} where the values from keywords{}&lt;br /&gt;
become the keys in keywords_xlate{} and the keys from keywords{} become the values in keywords_xlate{}:&lt;br /&gt;
	['affirmative'] = {'yes', 'true', 'y'},		-- in keywords{}&lt;br /&gt;
becomes&lt;br /&gt;
	['yes'] = 'affirmative',					-- in keywords_xlate{}&lt;br /&gt;
	['true'] = 'affirmative',&lt;br /&gt;
	['y'] = 'affirmative',&lt;br /&gt;
&lt;br /&gt;
the purpose of this function is to act as a translator between a non-English keyword and its English equivalent&lt;br /&gt;
that may be used in other modules of this suite&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function xlate_keywords ()&lt;br /&gt;
	local out_table = {};														-- output goes here&lt;br /&gt;
	for k, keywords_t in pairs (keywords) do									-- spin through the keywords table&lt;br /&gt;
		for _, keyword in ipairs (keywords_t) do								-- for each keyword&lt;br /&gt;
			out_table[keyword] = k;												-- create an entry in the output table where keyword is the key&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return out_table;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local keywords_xlate = xlate_keywords ();										-- the list of translated keywords&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[----------------&amp;lt; M A K E _ K E Y W O R D S _ L I S T &amp;gt;---------------------&lt;br /&gt;
&lt;br /&gt;
this function assembles, for parameter-value validation, the list of keywords appropriate to that parameter.&lt;br /&gt;
&lt;br /&gt;
keywords_lists{}, is a table of tables from keywords{}&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function make_keywords_list (keywords_lists)&lt;br /&gt;
	local out_table = {};														-- output goes here&lt;br /&gt;
	&lt;br /&gt;
	for _, keyword_list in ipairs (keywords_lists) do							-- spin through keywords_lists{} and get a table of keywords&lt;br /&gt;
		for _, keyword in ipairs (keyword_list) do								-- spin through keyword_list{} and add each keyword, ...&lt;br /&gt;
			table.insert (out_table, keyword);									-- ... as plain text, to the output list&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return out_table;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[----------------&amp;lt; K E Y W O R D S _ L I S T S &amp;gt;-----------------------------&lt;br /&gt;
&lt;br /&gt;
this is a list of lists of valid keywords for the various parameters in [key].&lt;br /&gt;
Generally the keys in this table are the canonical en.wiki parameter names though&lt;br /&gt;
some are contrived because of use in multiple differently named parameters:&lt;br /&gt;
['yes_true_y'], ['id-access'].&lt;br /&gt;
&lt;br /&gt;
The function make_keywords_list() extracts the individual keywords from the&lt;br /&gt;
appropriate list in keywords{}.&lt;br /&gt;
&lt;br /&gt;
The lists in this table are used to validate the keyword assignment for the&lt;br /&gt;
parameters named in this table's keys.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local keywords_lists = {&lt;br /&gt;
	['yes_true_y'] = make_keywords_list ({keywords.affirmative}),&lt;br /&gt;
	['contribution'] = make_keywords_list ({keywords.afterword, keywords.foreword, keywords.introduction, keywords.preface}),&lt;br /&gt;
	['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all']}),&lt;br /&gt;
	--	['df'] = make_keywords_list ({keywords.dmy, keywords['dmy-all'], keywords.mdy, keywords['mdy-all'], keywords.ymd, keywords['ymd-all'], keywords.yMd, keywords['yMd-all']}),	-- not supported at en.wiki&lt;br /&gt;
	['mode'] = make_keywords_list ({keywords.cs1, keywords.cs2}),&lt;br /&gt;
	['name-list-style'] = make_keywords_list ({keywords.amp, keywords['and'], keywords.vanc}),&lt;br /&gt;
	['ref'] = make_keywords_list ({keywords.harv}),								-- inverted check; |ref=harv no longer supported&lt;br /&gt;
	['url-access'] = make_keywords_list ({keywords.subscription, keywords.limited, keywords.registration}),&lt;br /&gt;
	['url-status'] = make_keywords_list ({keywords.dead, keywords.live, keywords.unfit, keywords.usurped, keywords['bot: unknown']}),&lt;br /&gt;
	['id-access'] = make_keywords_list ({keywords.free}),&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[---------------------&amp;lt; S T R I P M A R K E R S &amp;gt;----------------------------&lt;br /&gt;
&lt;br /&gt;
Common pattern definition location for stripmarkers so that we don't have to go&lt;br /&gt;
hunting for them if (when) MediaWiki changes their form.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local stripmarkers = {&lt;br /&gt;
	['any'] = '\127[^\127]*UNIQ%-%-(%a+)%-[%a%d]+%-QINU[^\127]*\127',			-- capture returns name of stripmarker&lt;br /&gt;
	['math'] = '\127[^\127]*UNIQ%-%-math%-[%a%d]+%-QINU[^\127]*\127'			-- math stripmarkers used in coins_cleanup() and coins_replace_math_stripmarker()&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[------------&amp;lt; I N V I S I B L E _ C H A R A C T E R S &amp;gt;---------------------&lt;br /&gt;
&lt;br /&gt;
This table holds non-printing or invisible characters indexed either by name or&lt;br /&gt;
by Unicode group. Values are decimal representations of UTF-8 codes.  The table&lt;br /&gt;
is organized as a table of tables because the Lua pairs keyword returns table&lt;br /&gt;
data in an arbitrary order.  Here, we want to process the table from top to bottom&lt;br /&gt;
because the entries at the top of the table are also found in the ranges specified&lt;br /&gt;
by the entries at the bottom of the table.&lt;br /&gt;
&lt;br /&gt;
Also here is a pattern that recognizes stripmarkers that begin and end with the&lt;br /&gt;
delete characters.  The nowiki stripmarker is not an error but some others are&lt;br /&gt;
because the parameter values that include them become part of the template's&lt;br /&gt;
metadata before stripmarker replacement.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local invisible_defs = {&lt;br /&gt;
	del = '\127',																-- used to distinguish between stripmarker and del char&lt;br /&gt;
	zwj = '\226\128\141',														-- used with capture because zwj may be allowed&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
local invisible_chars = {&lt;br /&gt;
	{'replacement', '\239\191\189'},											-- U+FFFD, EF BF BD&lt;br /&gt;
	{'zero width joiner', '('.. invisible_defs.zwj .. ')'},						-- U+200D, E2 80 8D; capture because zwj may be allowed&lt;br /&gt;
	{'zero width space', '\226\128\139'},										-- U+200B, E2 80 8B&lt;br /&gt;
	{'hair space', '\226\128\138'},												-- U+200A, E2 80 8A&lt;br /&gt;
	{'soft hyphen', '\194\173'},												-- U+00AD, C2 AD&lt;br /&gt;
	{'horizontal tab', '\009'},													-- U+0009 (HT), 09&lt;br /&gt;
	{'line feed', '\010'},														-- U+000A (LF), 0A&lt;br /&gt;
	{'no-break space', '\194\160'},												-- U+00A0 (NBSP), C2 A0&lt;br /&gt;
	{'carriage return', '\013'},												-- U+000D (CR), 0D&lt;br /&gt;
	{'stripmarker', stripmarkers.any},											-- stripmarker; may or may not be an error; capture returns the stripmaker type&lt;br /&gt;
	{'delete', '('.. invisible_defs.del .. ')'},								-- U+007F (DEL), 7F; must be done after stripmarker test; capture to distinguish isolated del chars not part of stripmarker&lt;br /&gt;
	{'C0 control', '[\000-\008\011\012\014-\031]'},								-- U+0000–U+001F (NULL–US), 00–1F (except HT, LF, CR (09, 0A, 0D))&lt;br /&gt;
	{'C1 control', '[\194\128-\194\159]'},										-- U+0080–U+009F (XXX–APC), C2 80 – C2 9F&lt;br /&gt;
	--	{'Specials', '[\239\191\185-\239\191\191]'},								-- U+FFF9-U+FFFF, EF BF B9 – EF BF BF&lt;br /&gt;
	--	{'Private use area', '[\238\128\128-\239\163\191]'},						-- U+E000–U+F8FF, EE 80 80 – EF A3 BF&lt;br /&gt;
	--	{'Supplementary Private Use Area-A', '[\243\176\128\128-\243\191\191\189]'},	-- U+F0000–U+FFFFD, F3 B0 80 80 – F3 BF BF BD&lt;br /&gt;
	--	{'Supplementary Private Use Area-B', '[\244\128\128\128-\244\143\191\189]'},	-- U+100000–U+10FFFD, F4 80 80 80 – F4 8F BF BD&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
&lt;br /&gt;
Indic script makes use of zero width joiner as a character modifier so zwj&lt;br /&gt;
characters must be left in.  This pattern covers all of the unicode characters&lt;br /&gt;
for these languages:&lt;br /&gt;
	Devanagari					0900–097F – https://unicode.org/charts/PDF/U0900.pdf&lt;br /&gt;
		Devanagari extended		A8E0–A8FF – https://unicode.org/charts/PDF/UA8E0.pdf&lt;br /&gt;
	Bengali						0980–09FF – https://unicode.org/charts/PDF/U0980.pdf&lt;br /&gt;
	Gurmukhi					0A00–0A7F – https://unicode.org/charts/PDF/U0A00.pdf&lt;br /&gt;
	Gujarati					0A80–0AFF – https://unicode.org/charts/PDF/U0A80.pdf&lt;br /&gt;
	Oriya						0B00–0B7F – https://unicode.org/charts/PDF/U0B00.pdf&lt;br /&gt;
	Tamil						0B80–0BFF – https://unicode.org/charts/PDF/U0B80.pdf&lt;br /&gt;
	Telugu						0C00–0C7F – https://unicode.org/charts/PDF/U0C00.pdf&lt;br /&gt;
	Kannada						0C80–0CFF – https://unicode.org/charts/PDF/U0C80.pdf&lt;br /&gt;
	Malayalam					0D00–0D7F – https://unicode.org/charts/PDF/U0D00.pdf&lt;br /&gt;
plus the not-necessarily Indic scripts for Sinhala and Burmese:&lt;br /&gt;
	Sinhala						0D80-0DFF - https://unicode.org/charts/PDF/U0D80.pdf&lt;br /&gt;
	Myanmar						1000-109F - https://unicode.org/charts/PDF/U1000.pdf&lt;br /&gt;
		Myanmar extended A		AA60-AA7F - https://unicode.org/charts/PDF/UAA60.pdf&lt;br /&gt;
		Myanmar extended B		A9E0-A9FF - https://unicode.org/charts/PDF/UA9E0.pdf&lt;br /&gt;
the pattern is used by has_invisible_chars() and coins_cleanup()&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local indic_script = '[\224\164\128-\224\181\191\224\163\160-\224\183\191\225\128\128-\225\130\159\234\167\160-\234\167\191\234\169\160-\234\169\191]';&lt;br /&gt;
&lt;br /&gt;
-- list of emoji that use zwj character (U+200D) to combine with another emoji&lt;br /&gt;
local emoji = {																	-- indexes are decimal forms of the hex values in U+xxxx&lt;br /&gt;
	[127752] = true,															-- U+1F308 🌈 rainbow&lt;br /&gt;
	[127806] = true,															-- U+1F33E 🌾 ear of rice&lt;br /&gt;
	[127859] = true,															-- U+1F373 🍳 cooking&lt;br /&gt;
	[127891] = true,															-- U+1F393 🎓 graduation cap&lt;br /&gt;
	[127908] = true,															-- U+1F3A4 🎤 microphone&lt;br /&gt;
	[127912] = true,															-- U+1F3A8 🎨 artist palette&lt;br /&gt;
	[127979] = true,															-- U+1F3EB 🏫 school&lt;br /&gt;
	[127981] = true,															-- U+1F3ED 🏭 factory&lt;br /&gt;
	[128102] = true,															-- U+1F466 👦 boy&lt;br /&gt;
	[128103] = true,															-- U+1F467 👧 girl&lt;br /&gt;
	[128104] = true,															-- U+1F468 👨 man&lt;br /&gt;
	[128105] = true,															-- U+1F469 👩 woman&lt;br /&gt;
	[128139] = true,															-- U+1F48B 💋 kiss mark&lt;br /&gt;
	[128187] = true,															-- U+1F4BB 💻 personal computer&lt;br /&gt;
	[128188] = true,															-- U+1F4BC 💼 brief case&lt;br /&gt;
	[128295] = true,															-- U+1F527 🔧 wrench&lt;br /&gt;
	[128300] = true,															-- U+1F52C 🔬 microscope&lt;br /&gt;
	[128488] = true,															-- U+1F5E8 🗨 left speech bubble&lt;br /&gt;
	[128640] = true,															-- U+1F680 🚀 rocket&lt;br /&gt;
	[128658] = true,															-- U+1F692 🚒 fire engine&lt;br /&gt;
	[129309] = true,															-- U+1F91D 🤝 handshake&lt;br /&gt;
	[129455] = true,															-- U+1F9AF 🦯 probing cane&lt;br /&gt;
	[129456] = true,															-- U+1F9B0 🦰 emoji component red hair&lt;br /&gt;
	[129457] = true,															-- U+1F9B1 🦱 emoji component curly hair&lt;br /&gt;
	[129458] = true,															-- U+1F9B2 🦲 emoji component bald&lt;br /&gt;
	[129459] = true,															-- U+1F9B3 🦳 emoji component white hair&lt;br /&gt;
	[129466] = true,															-- U+1F9BA 🦺 safety vest&lt;br /&gt;
	[129468] = true,															-- U+1F9BC 🦼 motorized wheelchair&lt;br /&gt;
	[129469] = true,															-- U+1F9BD 🦽 manual wheelchair&lt;br /&gt;
	[129489] = true,															-- U+1F9D1 🧑 adult&lt;br /&gt;
	[9760] = true,																-- U+2620 ☠ skull and crossbones&lt;br /&gt;
	[9792] = true,																-- U+2640 ♀ female sign&lt;br /&gt;
	[9794] = true,																-- U+2642 ♂ male sign&lt;br /&gt;
	[9877] = true,																-- U+2695 ⚕ staff of aesculapius&lt;br /&gt;
	[9878] = true,																-- U+2696 ⚖ scales&lt;br /&gt;
	[9992] = true,																-- U+2708 ✈ airplane&lt;br /&gt;
	[10084] = true,																-- U+2764 ❤ heavy black heart&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[----------------------&amp;lt; L A N G U A G E   S U P P O R T &amp;gt;-------------------&lt;br /&gt;
&lt;br /&gt;
These tables and constants support various language-specific functionality.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local this_wiki_code = mw.getContentLanguage():getCode();						-- get this wiki's language code&lt;br /&gt;
	if string.match (mw.site.server, 'wikidata') then&lt;br /&gt;
		this_wiki_code = mw.getCurrentFrame():preprocess('{{int:lang}}');		-- on Wikidata so use interface language setting instead&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
local mw_languages_by_tag_t = mw.language.fetchLanguageNames (this_wiki_code, 'all');	-- get a table of language tag/name pairs known to Wikimedia; used for interwiki tests&lt;br /&gt;
local mw_languages_by_name_t = {};&lt;br /&gt;
	for k, v in pairs (mw_languages_by_tag_t) do								-- build a 'reversed' table name/tag language pairs know to MediaWiki; used for |language=&lt;br /&gt;
		v = mw.ustring.lower (v);												-- lowercase for tag fetch; get name's proper case from mw_languages_by_tag_t[&amp;lt;tag&amp;gt;]&lt;br /&gt;
		if mw_languages_by_name_t[v] then										-- when name already in the table&lt;br /&gt;
			if 2 == #k or 3 == #k then											-- if tag does not have subtags&lt;br /&gt;
				mw_languages_by_name_t[v] = k;									-- prefer the shortest tag for this name&lt;br /&gt;
			end&lt;br /&gt;
		else																	-- here when name not in the table&lt;br /&gt;
			mw_languages_by_name_t[v] = k;										-- so add name and matching tag&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
local inter_wiki_map = {};														-- map of interwiki prefixes that are language-code prefixes&lt;br /&gt;
	for k, v in pairs (mw.site.interwikiMap ('local')) do						-- spin through the base interwiki map (limited to local)&lt;br /&gt;
		if mw_languages_by_tag_t[v[&amp;quot;prefix&amp;quot;]] then								-- if the prefix matches a known language tag&lt;br /&gt;
			inter_wiki_map[v[&amp;quot;prefix&amp;quot;]] = true;									-- add it to our local map&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------&amp;lt; S C R I P T _ L A N G _ C O D E S &amp;gt;-------------------&lt;br /&gt;
&lt;br /&gt;
This table is used to hold ISO 639-1 two-character and ISO 639-3 three-character&lt;br /&gt;
language codes that apply only to |script-title= and |script-chapter=&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local script_lang_codes = {&lt;br /&gt;
	'ab', 'am', 'ar', 'be', 'bg', 'bn', 'bo', 'bs', 'dv', 'dz', 'el', 'fa', 'gu', &lt;br /&gt;
	'he', 'hi', 'hy', 'ja', 'ka', 'kk', 'km', 'kn', 'ko', 'ku', 'ky', 'lo', 'mk',&lt;br /&gt;
	'ml', 'mn', 'mr', 'my', 'ne', 'or', 'ota', 'ps', 'ru', 'sd', 'si', 'sr', 'syc',&lt;br /&gt;
	'ta', 'te', 'tg', 'th', 'ti', 'ug', 'uk', 'ur', 'uz', 'yi', 'yue', 'zh'&lt;br /&gt;
	};&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[---------------&amp;lt; L A N G U A G E   R E M A P P I N G &amp;gt;----------------------&lt;br /&gt;
&lt;br /&gt;
These tables hold language information that is different (correct) from MediaWiki's definitions&lt;br /&gt;
&lt;br /&gt;
For each ['code'] = 'language name' in lang_code_remap{} there must be a matching ['language name'] = {'language name', 'code'} in lang_name_remap{}&lt;br /&gt;
&lt;br /&gt;
lang_code_remap{}:&lt;br /&gt;
	key is always lowercase ISO 639-1, -2, -3 language code or a valid lowercase IETF language tag&lt;br /&gt;
	value is properly spelled and capitalized language name associated with key&lt;br /&gt;
	only one language name per key;&lt;br /&gt;
	key/value pair must have matching entry in lang_name_remap{}&lt;br /&gt;
&lt;br /&gt;
lang_name_remap{}:&lt;br /&gt;
	key is always lowercase language name&lt;br /&gt;
	value is a table the holds correctly spelled and capitalized language name [1] and associated code [2] (code must match a code key in lang_code_remap{})&lt;br /&gt;
	may have multiple keys referring to a common preferred name and code; For example:&lt;br /&gt;
		['kolsch'] and ['kölsch'] both refer to 'Kölsch' and 'ksh'&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local lang_code_remap = {														-- used for |language= and |script-title= / |script-chapter=&lt;br /&gt;
	['als'] = 'Tosk Albanian',													-- MediaWiki returns Alemannisch &lt;br /&gt;
	['bh'] = 'Bihari',															-- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org&lt;br /&gt;
	['bla'] = 'Blackfoot',														-- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name&lt;br /&gt;
	['bn'] = 'Bengali',															-- MediaWiki returns Bangla&lt;br /&gt;
	['ca-valencia'] = 'Valencian',												-- IETF variant of Catalan&lt;br /&gt;
	['ilo'] = 'Ilocano',														-- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name&lt;br /&gt;
	['ksh'] = 'Kölsch',															-- MediaWiki: Colognian; use IANA/ISO 639 preferred name&lt;br /&gt;
	['ksh-x-colog'] = 'Colognian',												-- override MediaWiki ksh; no IANA/ISO 639 code for Colognian; IETF private code created at Module:Lang/data&lt;br /&gt;
	['mis-x-ripuar'] = 'Ripuarian',												-- override MediaWiki ksh; no IANA/ISO 639 code for Ripuarian; IETF private code created at Module:Lang/data&lt;br /&gt;
	['nan-tw'] = 'Taiwanese Hokkien',											-- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese and support en.wiki preferred name&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
local lang_name_remap = {														-- used for |language=&lt;br /&gt;
	['alemannisch'] = {'Swiss German', 'gsw'},									-- not an ISO or IANA language name; MediaWiki uses 'als' as a subdomain name for Alemannic Wikipedia: als.wikipedia.org&lt;br /&gt;
	['bangla'] = {'Bengali', 'bn'},												-- MediaWiki returns Bangla (the endonym) but we want Bengali (the exonym); here we remap&lt;br /&gt;
	['bengali'] = {'Bengali', 'bn'},											-- MediaWiki doesn't use exonym so here we provide correct language name and 639-1 code&lt;br /&gt;
	['bhojpuri'] = {'Bhojpuri', 'bho'},											-- MediaWiki uses 'bh' as a subdomain name for Bhojpuri Wikipedia: bh.wikipedia.org&lt;br /&gt;
	['bihari'] = {'Bihari', 'bh'},												-- MediaWiki replaces 'Bihari' with 'Bhojpuri' so 'Bihari' cannot be found&lt;br /&gt;
	['blackfoot'] = {'Blackfoot', 'bla'},										-- MediaWiki/IANA/ISO 639: Siksika; use en.wiki preferred name&lt;br /&gt;
	['colognian'] = {'Colognian', 'ksh-x-colog'},								-- MediaWiki preferred name for ksh&lt;br /&gt;
	['ilocano'] = {'Ilocano', 'ilo'},											-- MediaWiki/IANA/ISO 639: Iloko; use en.wiki preferred name&lt;br /&gt;
	['kolsch'] = {'Kölsch', 'ksh'},												-- use IANA/ISO 639 preferred name (use non-diacritical o instead of umlaut ö)&lt;br /&gt;
	['kölsch'] = {'Kölsch', 'ksh'},												-- use IANA/ISO 639 preferred name&lt;br /&gt;
	['ripuarian'] = {'Ripuarian', 'mis-x-ripuar'},								-- group of dialects; no code in MediaWiki or in IANA/ISO 639&lt;br /&gt;
	['taiwanese hokkien'] = {'Taiwanese Hokkien', 'nan-TW'},					-- make room for MediaWiki/IANA/ISO 639 nan: Min Nan Chinese &lt;br /&gt;
	['tosk albanian'] = {'Tosk Albanian', 'als'},								-- MediaWiki replaces 'Tosk Albanian' with 'Alemannisch' so 'Tosk Albanian' cannot be found&lt;br /&gt;
	['valencian'] = {'Valencian', 'ca'},										-- variant of Catalan; categorizes as Catalan&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[---------------&amp;lt; P R O P E R T I E S _ C A T E G O R I E S &amp;gt;----------------&lt;br /&gt;
&lt;br /&gt;
Properties categories. These are used for investigating qualities of citations.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local prop_cats = {&lt;br /&gt;
	['foreign-lang-source'] = 'CS1 $1-language sources ($2)',					-- |language= categories; $1 is foreign-language name, $2 is ISO639-1 code&lt;br /&gt;
	['foreign-lang-source-2'] = 'CS1 foreign language sources (ISO 639-2)|$1',	-- |language= category; a cat for ISO639-2 languages; $1 is the ISO 639-2 code used as a sort key&lt;br /&gt;
	['jul-greg-uncertainty'] = 'CS1: Julian–Gregorian uncertainty',				-- probably temporary cat to identify scope of template with dates 1 October 1582 – 1 January 1926&lt;br /&gt;
	['local-lang-source'] = 'CS1 $1-language sources ($2)',						-- |language= categories; $1 is local-language name, $2 is ISO639-1 code; not emitted when local_lang_cat_enable is false&lt;br /&gt;
	['location-test'] = 'CS1 location test',&lt;br /&gt;
	['long-vol'] = 'CS1: long volume value',									-- probably temporary cat to identify scope of |volume= values longer than 4 charachters&lt;br /&gt;
	['script'] = 'CS1 uses $1-language script ($2)',							-- |script-title=xx: has matching category; $1 is language name, $2 is ISO639-1 code&lt;br /&gt;
	['tracked-param'] = 'CS1 tracked parameter: $1',							-- $1 is base (enumerators removed) parameter name&lt;br /&gt;
	['year-range-abbreviated'] = 'CS1: abbreviated year range',					-- probably temporary cat to identify scope of |date=, |year= values using YYYY–YY form&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------&amp;lt; T I T L E _ T Y P E S &amp;gt;--------------------------------&lt;br /&gt;
&lt;br /&gt;
Here we map a template's CitationClass to TitleType (default values for |type= parameter)&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local title_types = {&lt;br /&gt;
	['AV-media-notes'] = 'Media notes',&lt;br /&gt;
	['interview'] = 'Interview',&lt;br /&gt;
	['mailinglist'] = 'Mailing list',&lt;br /&gt;
	['map'] = 'Map',&lt;br /&gt;
	['podcast'] = 'Podcast',&lt;br /&gt;
	['pressrelease'] = 'Press release',&lt;br /&gt;
	['report'] = 'Report',&lt;br /&gt;
	['speech'] = 'Speech',&lt;br /&gt;
	['techreport'] = 'Technical report',&lt;br /&gt;
	['thesis'] = 'Thesis',&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[===================&amp;lt;&amp;lt; E R R O R   M E S S A G I N G &amp;gt;&amp;gt;======================&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
--[[----------&amp;lt; E R R O R   M E S S A G E   S U P P L I M E N T S &amp;gt;-------------&lt;br /&gt;
&lt;br /&gt;
I18N for those messages that are supplemented with additional specific text that&lt;br /&gt;
describes the reason for the error&lt;br /&gt;
&lt;br /&gt;
TODO: merge this with special_case_translations{}?&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local err_msg_supl = {&lt;br /&gt;
	['char'] = 'invalid character',												-- |isbn=, |sbn=&lt;br /&gt;
	['check'] = 'checksum',														-- |isbn=, |sbn=&lt;br /&gt;
	['flag'] = 'flag',															-- |archive-url=&lt;br /&gt;
	['form'] = 'invalid form',													-- |isbn=, |sbn=&lt;br /&gt;
	['group'] = 'invalid group id',												-- |isbn=&lt;br /&gt;
	['initials'] = 'initials',													-- Vancouver&lt;br /&gt;
	['invalid language code'] = 'invalid language code',						-- |script-&amp;lt;param&amp;gt;=&lt;br /&gt;
	['journal'] = 'journal',													-- |bibcode=&lt;br /&gt;
	['length'] = 'length',														-- |isbn=, |bibcode=, |sbn=&lt;br /&gt;
	['liveweb'] = 'liveweb',													-- |archive-url=&lt;br /&gt;
	['missing comma'] = 'missing comma',										-- Vancouver&lt;br /&gt;
	['missing prefix'] = 'missing prefix',										-- |script-&amp;lt;param&amp;gt;=&lt;br /&gt;
	['missing title part'] = 'missing title part',								-- |script-&amp;lt;param&amp;gt;=&lt;br /&gt;
	['name'] = 'name',															-- Vancouver&lt;br /&gt;
	['non-Latin char'] = 'non-Latin character',									-- Vancouver&lt;br /&gt;
	['path'] = 'path',															-- |archive-url=&lt;br /&gt;
	['prefix'] = 'invalid prefix',												-- |isbn=&lt;br /&gt;
	['punctuation'] = 'punctuation',											-- Vancouver&lt;br /&gt;
	['save'] = 'save command',													-- |archive-url=&lt;br /&gt;
	['suffix'] = 'suffix',														-- Vancouver&lt;br /&gt;
	['timestamp'] = 'timestamp',												-- |archive-url=&lt;br /&gt;
	['unknown language code'] = 'unknown language code',						-- |script-&amp;lt;param&amp;gt;=&lt;br /&gt;
	['value'] = 'value',														-- |bibcode=&lt;br /&gt;
	['year'] = 'year',															-- |bibcode=&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------&amp;lt; E R R O R _ C O N D I T I O N S &amp;gt;---------------------------&lt;br /&gt;
&lt;br /&gt;
Error condition table.  This table has two sections: errors at the top, maintenance&lt;br /&gt;
at the bottom.  Maint 'messaging' does not have a 'message' (message=nil)&lt;br /&gt;
&lt;br /&gt;
The following contains a list of IDs for various error conditions defined in the&lt;br /&gt;
code.  For each ID, we specify a text message to display, an error category to&lt;br /&gt;
include, and whether the error message should be wrapped as a hidden comment.&lt;br /&gt;
&lt;br /&gt;
Anchor changes require identical changes to matching anchor in Help:CS1 errors&lt;br /&gt;
&lt;br /&gt;
TODO: rename error_conditions{} to something more generic; create separate error&lt;br /&gt;
and maint tables inside that?&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local error_conditions = {&lt;br /&gt;
	err_accessdate_missing_url = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;access-date=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;url=&amp;lt;/code&amp;gt;',&lt;br /&gt;
		anchor = 'accessdate_missing_url',&lt;br /&gt;
		category = 'CS1 errors: access-date without URL',&lt;br /&gt;
		hidden = false&lt;br /&gt;
 		},&lt;br /&gt;
	err_apostrophe_markup = {&lt;br /&gt;
		message = 'Italic or bold markup not allowed in: &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'apostrophe_markup',&lt;br /&gt;
		category = 'CS1 errors: markup',&lt;br /&gt;
		hidden = false&lt;br /&gt;
 		},&lt;br /&gt;
	err_archive_missing_date = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;archive-url=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;archive-date=&amp;lt;/code&amp;gt;',&lt;br /&gt;
		anchor = 'archive_missing_date',&lt;br /&gt;
		category = 'CS1 errors: archive-url',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_archive_missing_url = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;archive-url=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;url=&amp;lt;/code&amp;gt;',&lt;br /&gt;
		anchor = 'archive_missing_url',&lt;br /&gt;
		category = 'CS1 errors: archive-url',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_archive_url = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;archive-url=&amp;lt;/code&amp;gt; is malformed: $1',	-- $1 is error message detail&lt;br /&gt;
		anchor = 'archive_url',&lt;br /&gt;
		category = 'CS1 errors: archive-url',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_arxiv_missing = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;arxiv=&amp;lt;/code&amp;gt; required',&lt;br /&gt;
		anchor = 'arxiv_missing',&lt;br /&gt;
		category = 'CS1 errors: arXiv',											-- same as bad arxiv&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_asintld_missing_asin = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;asin=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'asintld_missing_asin',&lt;br /&gt;
		category = 'CS1 errors: ASIN TLD',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_arxiv = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;arxiv=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_arxiv',&lt;br /&gt;
		category = 'CS1 errors: arXiv',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_asin = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;asin=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_asin',&lt;br /&gt;
		category ='CS1 errors: ASIN',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_asin_tld = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;asin-tld=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_asin_tld',&lt;br /&gt;
		category ='CS1 errors: ASIN TLD',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_bibcode = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;bibcode=&amp;lt;/code&amp;gt; $1',		-- $1 is error message detail&lt;br /&gt;
		anchor = 'bad_bibcode',&lt;br /&gt;
		category = 'CS1 errors: bibcode',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_biorxiv = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;biorxiv=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_biorxiv',&lt;br /&gt;
		category = 'CS1 errors: bioRxiv',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_citeseerx = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;citeseerx=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_citeseerx',&lt;br /&gt;
		category = 'CS1 errors: citeseerx',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_date = {&lt;br /&gt;
		message = 'Check date values in: $1',									-- $1 is a parameter name list&lt;br /&gt;
		anchor = 'bad_date',&lt;br /&gt;
		category = 'CS1 errors: dates',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_doi = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;doi=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_doi',&lt;br /&gt;
		category = 'CS1 errors: DOI',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_hdl = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;hdl=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_hdl',&lt;br /&gt;
		category = 'CS1 errors: HDL',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_isbn = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;isbn=&amp;lt;/code&amp;gt; value: $1',	-- $1 is error message detail&lt;br /&gt;
		anchor = 'bad_isbn',&lt;br /&gt;
		category = 'CS1 errors: ISBN',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_ismn = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;ismn=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_ismn',&lt;br /&gt;
		category = 'CS1 errors: ISMN',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_issn = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1issn=&amp;lt;/code&amp;gt; value',	-- $1 is 'e' or '' for eissn or issn&lt;br /&gt;
		anchor = 'bad_issn',&lt;br /&gt;
		category = 'CS1 errors: ISSN',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_jfm = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;jfm=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_jfm',&lt;br /&gt;
		category = 'CS1 errors: JFM',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_jstor = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;jstor=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_jstor',&lt;br /&gt;
		category = 'CS1 errors: JSTOR',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_lccn = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;lccn=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_lccn',&lt;br /&gt;
		category = 'CS1 errors: LCCN',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_mr = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;mr=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_mr',&lt;br /&gt;
		category = 'CS1 errors: MR',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_oclc = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;oclc=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_oclc',&lt;br /&gt;
		category = 'CS1 errors: OCLC',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_ol = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;ol=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_ol',&lt;br /&gt;
		category = 'CS1 errors: OL',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_osti = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;osti=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_osti',&lt;br /&gt;
		category = 'CS1 errors: OSTI',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_paramlink = {														-- for |title-link=, |author/editor/translator-link=, |series-link=, |episode-link=&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; value',		-- $1 is parameter name&lt;br /&gt;
		anchor = 'bad_paramlink',&lt;br /&gt;
		category = 'CS1 errors: parameter link',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_pmc = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;pmc=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_pmc',&lt;br /&gt;
		category = 'CS1 errors: PMC',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_pmid = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;pmid=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_pmid',&lt;br /&gt;
		category = 'CS1 errors: PMID',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_rfc = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;rfc=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_rfc',&lt;br /&gt;
		category = 'CS1 errors: RFC',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_s2cid = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;s2cid=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_s2cid',&lt;br /&gt;
		category = 'CS1 errors: S2CID',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_sbn = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;sbn=&amp;lt;/code&amp;gt; value: $1',	-- $1 is error message detail&lt;br /&gt;
		anchor = 'bad_sbn',&lt;br /&gt;
		category = 'CS1 errors: SBN',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_ssrn = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;ssrn=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_ssrn',&lt;br /&gt;
		category = 'CS1 errors: SSRN',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_url = {&lt;br /&gt;
		message = 'Check $1 value',												-- $1 is parameter name&lt;br /&gt;
		anchor = 'bad_url',&lt;br /&gt;
		category = 'CS1 errors: URL',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_usenet_id = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;message-id=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_message_id',&lt;br /&gt;
		category = 'CS1 errors: message-id',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bad_zbl = {&lt;br /&gt;
		message = 'Check &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;zbl=&amp;lt;/code&amp;gt; value',&lt;br /&gt;
		anchor = 'bad_zbl',&lt;br /&gt;
		category = 'CS1 errors: Zbl',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_bare_url_missing_title = {&lt;br /&gt;
		message = '$1 missing title',											-- $1 is parameter name&lt;br /&gt;
		anchor = 'bare_url_missing_title',&lt;br /&gt;
		category = 'CS1 errors: bare URL',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_biorxiv_missing = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;biorxiv=&amp;lt;/code&amp;gt; required',&lt;br /&gt;
		anchor = 'biorxiv_missing',&lt;br /&gt;
		category = 'CS1 errors: bioRxiv',										-- same as bad bioRxiv&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_chapter_ignored = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; ignored',			-- $1 is parameter name&lt;br /&gt;
		anchor = 'chapter_ignored',&lt;br /&gt;
		category = 'CS1 errors: chapter ignored',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_citation_missing_title = {&lt;br /&gt;
		message = 'Missing or empty &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'citation_missing_title',&lt;br /&gt;
		category = 'CS1 errors: missing title',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_citeseerx_missing = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;citeseerx=&amp;lt;/code&amp;gt; required',&lt;br /&gt;
		anchor = 'citeseerx_missing',&lt;br /&gt;
		category = 'CS1 errors: citeseerx',										-- same as bad citeseerx&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_cite_web_url = {														-- this error applies to cite web and to cite podcast&lt;br /&gt;
		message = 'Missing or empty &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;url=&amp;lt;/code&amp;gt;',&lt;br /&gt;
		anchor = 'cite_web_url',&lt;br /&gt;
		category = 'CS1 errors: requires URL',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_class_ignored = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;class=&amp;lt;/code&amp;gt; ignored',&lt;br /&gt;
		anchor = 'class_ignored',&lt;br /&gt;
		category = 'CS1 errors: class',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_contributor_ignored = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;contributor=&amp;lt;/code&amp;gt; ignored',&lt;br /&gt;
		anchor = 'contributor_ignored',&lt;br /&gt;
		category = 'CS1 errors: contributor',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_contributor_missing_required_param = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;contributor=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'contributor_missing_required_param',&lt;br /&gt;
		category = 'CS1 errors: contributor',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_deprecated_params = {&lt;br /&gt;
		message = 'Cite uses deprecated parameter &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'deprecated_params',&lt;br /&gt;
		category = 'CS1 errors: deprecated parameters',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_disp_name = {&lt;br /&gt;
		message = 'Invalid &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=$2&amp;lt;/code&amp;gt;',			-- $1 is parameter name; $2 is the assigned value&lt;br /&gt;
		anchor = 'disp_name',&lt;br /&gt;
		category = 'CS1 errors: display-names',&lt;br /&gt;
		hidden = false,&lt;br /&gt;
		},&lt;br /&gt;
	err_doibroken_missing_doi = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;doi=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'doibroken_missing_doi',&lt;br /&gt;
		category = 'CS1 errors: DOI',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_embargo_missing_pmc = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;pmc=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'embargo_missing_pmc',&lt;br /&gt;
		category = 'CS1 errors: PMC embargo',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_empty_citation = {&lt;br /&gt;
		message = 'Empty citation',&lt;br /&gt;
		anchor = 'empty_citation',&lt;br /&gt;
		category = 'CS1 errors: empty citation',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_etal = {&lt;br /&gt;
		message = 'Explicit use of et al. in: &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'explicit_et_al',&lt;br /&gt;
		category = 'CS1 errors: explicit use of et al.',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_extra_text_edition = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;edition=&amp;lt;/code&amp;gt; has extra text',&lt;br /&gt;
		anchor = 'extra_text_edition',&lt;br /&gt;
		category = 'CS1 errors: extra text: edition',&lt;br /&gt;
		hidden = false,&lt;br /&gt;
		},&lt;br /&gt;
	err_extra_text_issue = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; has extra text',		-- $1 is parameter name&lt;br /&gt;
		anchor = 'extra_text_issue',&lt;br /&gt;
		category = 'CS1 errors: extra text: issue',&lt;br /&gt;
		hidden = false,&lt;br /&gt;
		},&lt;br /&gt;
	err_extra_text_pages = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; has extra text',		-- $1 is parameter name&lt;br /&gt;
		anchor = 'extra_text_pages',&lt;br /&gt;
		category = 'CS1 errors: extra text: pages',&lt;br /&gt;
		hidden = false,&lt;br /&gt;
		},&lt;br /&gt;
	err_extra_text_volume = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; has extra text',		-- $1 is parameter name&lt;br /&gt;
		anchor = 'extra_text_volume',&lt;br /&gt;
		category = 'CS1 errors: extra text: volume',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	err_first_missing_last = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; missing &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$2=&amp;lt;/code&amp;gt;',	-- $1 is first alias, $2 is matching last alias&lt;br /&gt;
		anchor = 'first_missing_last',&lt;br /&gt;
		category = 'CS1 errors: missing name', -- author, contributor, editor, interviewer, translator&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_format_missing_url = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$2=&amp;lt;/code&amp;gt;',	-- $1 is format parameter $2 is url parameter&lt;br /&gt;
		anchor = 'format_missing_url',&lt;br /&gt;
		category = 'CS1 errors: format without URL',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_generic_name = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; has generic name',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'generic_name',&lt;br /&gt;
		category = 'CS1 errors: generic name',&lt;br /&gt;
		hidden = false,&lt;br /&gt;
		},&lt;br /&gt;
	err_generic_title = {&lt;br /&gt;
		message = 'Cite uses generic title',&lt;br /&gt;
		anchor = 'generic_title',&lt;br /&gt;
		category = 'CS1 errors: generic title',&lt;br /&gt;
		hidden = false,&lt;br /&gt;
		},&lt;br /&gt;
	err_invalid_param_val = {&lt;br /&gt;
		message = 'Invalid &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=$2&amp;lt;/code&amp;gt;',			-- $1 is parameter name $2 is parameter value&lt;br /&gt;
		anchor = 'invalid_param_val',&lt;br /&gt;
		category = 'CS1 errors: invalid parameter value',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_invisible_char = {&lt;br /&gt;
		message = '$1 in $2 at position $3',									-- $1 is invisible char $2 is parameter name $3 is position number&lt;br /&gt;
		anchor = 'invisible_char',&lt;br /&gt;
		category = 'CS1 errors: invisible characters',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_missing_name = {&lt;br /&gt;
		message = 'Missing &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1$2=&amp;lt;/code&amp;gt;',			-- $1 is modified NameList; $2 is enumerator&lt;br /&gt;
		anchor = 'missing_name',&lt;br /&gt;
		category = 'CS1 errors: missing name',									-- author, contributor, editor, interviewer, translator&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_missing_periodical = {&lt;br /&gt;
		message = 'Cite $1 requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$2=&amp;lt;/code&amp;gt;',	-- $1 is cs1 template name; $2 is canonical periodical parameter name for cite $1&lt;br /&gt;
		anchor = 'missing_periodical',&lt;br /&gt;
		category = 'CS1 errors: missing periodical',&lt;br /&gt;
		hidden = true&lt;br /&gt;
		},&lt;br /&gt;
	err_missing_pipe = {&lt;br /&gt;
		message = 'Missing pipe in: &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'missing_pipe',&lt;br /&gt;
		category = 'CS1 errors: missing pipe',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_param_access_requires_param = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1-access=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'param_access_requires_param',&lt;br /&gt;
		category = 'CS1 errors: param-access',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_param_has_ext_link = {&lt;br /&gt;
		message = 'External link in &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;$1&amp;lt;/code&amp;gt;',			-- $1 is parameter name&lt;br /&gt;
		anchor = 'param_has_ext_link',&lt;br /&gt;
		category = 'CS1 errors: external links',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_parameter_ignored = {&lt;br /&gt;
		message = 'Unknown parameter &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; ignored',	-- $1 is parameter name&lt;br /&gt;
		anchor = 'parameter_ignored',&lt;br /&gt;
		category = 'CS1 errors: unsupported parameter',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_parameter_ignored_suggest = {&lt;br /&gt;
		message = 'Unknown parameter &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; ignored (&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$2=&amp;lt;/code&amp;gt; suggested)',	-- $1 is unknown parameter $2 is suggested parameter name&lt;br /&gt;
		anchor = 'parameter_ignored_suggest',&lt;br /&gt;
		category = 'CS1 errors: unsupported parameter',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_redundant_parameters = {&lt;br /&gt;
		message = 'More than one of $1 specified',								-- $1 is error message detail&lt;br /&gt;
		anchor = 'redundant_parameters',&lt;br /&gt;
		category = 'CS1 errors: redundant parameter',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_script_parameter = {&lt;br /&gt;
		message = 'Invalid &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt;: $2',		-- $1 is parameter name $2 is script language code or error detail&lt;br /&gt;
		anchor = 'script_parameter',&lt;br /&gt;
		category = 'CS1 errors: script parameters',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_ssrn_missing = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;ssrn=&amp;lt;/code&amp;gt; required',&lt;br /&gt;
		anchor = 'ssrn_missing',&lt;br /&gt;
		category = 'CS1 errors: SSRN',											-- same as bad arxiv&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_text_ignored = {&lt;br /&gt;
		message = 'Text &amp;quot;$1&amp;quot; ignored',											-- $1 is ignored text&lt;br /&gt;
		anchor = 'text_ignored',&lt;br /&gt;
		category = 'CS1 errors: unrecognized parameter',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_trans_missing_title = {&lt;br /&gt;
		message = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;trans-$1=&amp;lt;/code&amp;gt; requires &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;$1=&amp;lt;/code&amp;gt; or &amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;&amp;amp;#124;script-$1=&amp;lt;/code&amp;gt;',	-- $1 is base parameter name&lt;br /&gt;
		anchor = 'trans_missing_title',&lt;br /&gt;
		category = 'CS1 errors: translated title',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_param_unknown_empty = {&lt;br /&gt;
		message = 'Cite has empty unknown parameter$1: $2',						-- $1 is 's' or empty space; $2 is emty unknown param list&lt;br /&gt;
		anchor = 'param_unknown_empty',&lt;br /&gt;
		category = 'CS1 errors: empty unknown parameters',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_vancouver = {&lt;br /&gt;
		message = 'Vancouver style error: $1 in name $2',						-- $1 is error detail, $2 is the nth name&lt;br /&gt;
		anchor = 'vancouver',&lt;br /&gt;
		category = 'CS1 errors: Vancouver style',&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
	err_wikilink_in_url = {&lt;br /&gt;
		message = 'URL–wikilink conflict',										-- uses ndash&lt;br /&gt;
		anchor = 'wikilink_in_url',&lt;br /&gt;
		category = 'CS1 errors: URL–wikilink conflict',							-- uses ndash&lt;br /&gt;
		hidden = false&lt;br /&gt;
		},&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; M A I N T &amp;gt;-------------------------------------&lt;br /&gt;
&lt;br /&gt;
maint messages do not have a message (message = nil); otherwise the structure&lt;br /&gt;
is the same as error messages&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
	maint_archived_copy = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'archived_copy',&lt;br /&gt;
		category = 'CS1 maint: archived copy as title',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_authors = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'authors',&lt;br /&gt;
		category = 'CS1 maint: uses authors parameter',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_bot_unknown = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'bot:_unknown',&lt;br /&gt;
		category = 'CS1 maint: bot: original URL status unknown',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_date_auto_xlated = {													-- date auto-translation not supported by en.wiki&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'date_auto_xlated',&lt;br /&gt;
		category = 'CS1 maint: date auto-translated',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_date_format = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'date_format',&lt;br /&gt;
		category = 'CS1 maint: date format',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_date_year = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'date_year',&lt;br /&gt;
		category = 'CS1 maint: date and year',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_doi_ignore = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'doi_ignore',&lt;br /&gt;
		category = 'CS1 maint: ignored DOI errors',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_doi_inactive = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'doi_inactive',&lt;br /&gt;
		category = 'CS1 maint: DOI inactive',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_doi_inactive_dated = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'doi_inactive_dated',&lt;br /&gt;
		category = 'CS1 maint: DOI inactive as of $2$3$1',						-- $1 is year, $2 is month-name or empty string, $3 is space or empty string&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_extra_punct = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'extra_punct',&lt;br /&gt;
		category = 'CS1 maint: extra punctuation',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_isbn_ignore = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'ignore_isbn_err',&lt;br /&gt;
		category = 'CS1 maint: ignored ISBN errors',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_issn_ignore = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'ignore_issn',&lt;br /&gt;
		category = 'CS1 maint: ignored ISSN errors',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_jfm_format = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'jfm_format',&lt;br /&gt;
		category = 'CS1 maint: JFM format',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_location = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'location',&lt;br /&gt;
		category = 'CS1 maint: location',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
	},&lt;br /&gt;
	maint_mr_format = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'mr_format',&lt;br /&gt;
		category = 'CS1 maint: MR format',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
	},&lt;br /&gt;
	maint_mult_names = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'mult_names',&lt;br /&gt;
		category = 'CS1 maint: multiple names: $1',								-- $1 is '&amp;lt;name&amp;gt;s list'; gets value from special_case_translation table&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_numeric_names = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'numeric_names',&lt;br /&gt;
		category = 'CS1 maint: numeric names: $1',								-- $1 is '&amp;lt;name&amp;gt;s list'; gets value from special_case_translation table&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_others = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'others',&lt;br /&gt;
		category = 'CS1 maint: others',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_others_avm = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'others_avm',&lt;br /&gt;
		category = 'CS1 maint: others in cite AV media (notes)',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
	},&lt;br /&gt;
	maint_pmc_embargo = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'embargo',&lt;br /&gt;
		category = 'CS1 maint: PMC embargo expired',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_pmc_format = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'pmc_format',&lt;br /&gt;
		category = 'CS1 maint: PMC format',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_postscript = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'postscript',&lt;br /&gt;
		category = 'CS1 maint: postscript',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
	},&lt;br /&gt;
	maint_ref_duplicates_default = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'ref_default',&lt;br /&gt;
		category = 'CS1 maint: ref duplicates default',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
	},&lt;br /&gt;
	maint_unfit = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'unfit',&lt;br /&gt;
		category = 'CS1 maint: unfit URL',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_unknown_lang = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'unknown_lang',&lt;br /&gt;
		category = 'CS1 maint: unrecognized language',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_untitled = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'untitled',&lt;br /&gt;
		category = 'CS1 maint: untitled periodical',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_url_status = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'url_status',&lt;br /&gt;
		category = 'CS1 maint: url-status',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	maint_zbl = {&lt;br /&gt;
		message = nil,&lt;br /&gt;
		anchor = 'zbl',&lt;br /&gt;
		category = 'CS1 maint: Zbl',&lt;br /&gt;
		hidden = true,&lt;br /&gt;
		},&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I D _ H A N D L E R S &amp;gt;--------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
The following contains a list of values for various defined identifiers.  For each&lt;br /&gt;
identifier we specify a variety of information necessary to properly render the&lt;br /&gt;
identifier in the citation.&lt;br /&gt;
&lt;br /&gt;
	parameters: a list of parameter aliases for this identifier; first in the list is the canonical form&lt;br /&gt;
	link: Wikipedia article name&lt;br /&gt;
	redirect: a local redirect to a local Wikipedia article name;  at en.wiki, 'ISBN (identifier)' is a redirect to 'International Standard Book Number'&lt;br /&gt;
	q: Wikidata q number for the identifier&lt;br /&gt;
	label: the label preceeding the identifier; label is linked to a Wikipedia article (in this order):&lt;br /&gt;
		redirect from id_handlers['&amp;lt;id&amp;gt;'].redirect when use_identifier_redirects is true&lt;br /&gt;
		Wikidata-supplied article name for the local wiki from id_handlers['&amp;lt;id&amp;gt;'].q&lt;br /&gt;
		local article name from id_handlers['&amp;lt;id&amp;gt;'].link&lt;br /&gt;
	prefix: the first part of a URL that will be concatenated with a second part which usually contains the identifier&lt;br /&gt;
	suffix: optional third part to be added after the identifier&lt;br /&gt;
	encode: true if URI should be percent-encoded; otherwise false&lt;br /&gt;
	COinS: identifier link or keyword for use in COinS:&lt;br /&gt;
		for identifiers registered at info-uri.info use: info:.... where '...' is the appropriate identifier label &lt;br /&gt;
		for identifiers that have COinS keywords, use the keyword: rft.isbn, rft.issn, rft.eissn&lt;br /&gt;
		for |asin= and |ol=, which require assembly, use the keyword: url&lt;br /&gt;
		for others make a URL using the value in prefix/suffix and #label, use the keyword: pre (not checked; any text other than 'info', 'rft', or 'url' works here)&lt;br /&gt;
		set to nil to leave the identifier out of the COinS&lt;br /&gt;
	separator: character or text between label and the identifier in the rendered citation&lt;br /&gt;
	id_limit: for those identifiers with established limits, this property holds the upper limit&lt;br /&gt;
	access: use this parameter to set the access level for all instances of this identifier.&lt;br /&gt;
		the value must be a valid access level for an identifier (see ['id-access'] in this file).&lt;br /&gt;
	custom_access: to enable custom access level for an identifier, set this parameter&lt;br /&gt;
		to the parameter that should control it (normally 'id-access')&lt;br /&gt;
		&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local id_handlers = {&lt;br /&gt;
	['ARXIV'] = {&lt;br /&gt;
		parameters = {'arxiv', 'eprint'},&lt;br /&gt;
		link = 'arXiv',&lt;br /&gt;
		redirect = 'arXiv (identifier)',&lt;br /&gt;
		q = 'Q118398',&lt;br /&gt;
		label = 'arXiv',&lt;br /&gt;
		prefix = '//arxiv.org/abs/', 											-- protocol-relative tested 2013-09-04&lt;br /&gt;
		encode = false,&lt;br /&gt;
		COinS = 'info:arxiv',&lt;br /&gt;
		separator = ':',&lt;br /&gt;
		access = 'free',														-- free to read&lt;br /&gt;
		},&lt;br /&gt;
	['ASIN'] = {&lt;br /&gt;
		parameters = { 'asin', 'ASIN' },&lt;br /&gt;
		link = 'Amazon Standard Identification Number',&lt;br /&gt;
		redirect = 'ASIN (identifier)',&lt;br /&gt;
		q = 'Q1753278',&lt;br /&gt;
		label = 'ASIN',&lt;br /&gt;
		prefix = '//www.amazon.',&lt;br /&gt;
		COinS = 'url',&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		encode = false;&lt;br /&gt;
		},&lt;br /&gt;
	['BIBCODE'] = {&lt;br /&gt;
		parameters = {'bibcode'},&lt;br /&gt;
		link = 'Bibcode',&lt;br /&gt;
		redirect = 'Bibcode (identifier)',&lt;br /&gt;
		q = 'Q25754',&lt;br /&gt;
		label = 'Bibcode',&lt;br /&gt;
		prefix = 'https://ui.adsabs.harvard.edu/abs/',&lt;br /&gt;
		encode = false,&lt;br /&gt;
		COinS = 'info:bibcode',&lt;br /&gt;
		separator = ':',&lt;br /&gt;
		custom_access = 'bibcode-access',&lt;br /&gt;
		},&lt;br /&gt;
	['BIORXIV'] = {&lt;br /&gt;
		parameters = {'biorxiv'},&lt;br /&gt;
		link = 'bioRxiv',&lt;br /&gt;
		redirect = 'bioRxiv (identifier)',&lt;br /&gt;
		q = 'Q19835482',&lt;br /&gt;
		label = 'bioRxiv',&lt;br /&gt;
		prefix = '//doi.org/',&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		access = 'free',														-- free to read&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['CITESEERX'] = {&lt;br /&gt;
		parameters = {'citeseerx'},&lt;br /&gt;
		link = 'CiteSeerX',&lt;br /&gt;
		redirect = 'CiteSeerX (identifier)',&lt;br /&gt;
		q = 'Q2715061',&lt;br /&gt;
		label = 'CiteSeerX',&lt;br /&gt;
		prefix = '//citeseerx.ist.psu.edu/viewdoc/summary?doi=',&lt;br /&gt;
		COinS =  'pre',															-- use prefix value&lt;br /&gt;
		access = 'free',														-- free to read&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['DOI'] = {																	-- Used by InternetArchiveBot&lt;br /&gt;
		parameters = { 'doi', 'DOI'},&lt;br /&gt;
		link = 'Digital object identifier',&lt;br /&gt;
		redirect = 'doi (identifier)',&lt;br /&gt;
		q = 'Q25670',&lt;br /&gt;
		label = 'doi',&lt;br /&gt;
		prefix = '//doi.org/',&lt;br /&gt;
		COinS = 'info:doi',&lt;br /&gt;
		separator = ':',&lt;br /&gt;
		encode = true,&lt;br /&gt;
		custom_access = 'doi-access',&lt;br /&gt;
		},&lt;br /&gt;
	['EISSN'] = {&lt;br /&gt;
		parameters = {'eissn', 'EISSN'},&lt;br /&gt;
		link = 'International Standard Serial Number#Electronic ISSN',&lt;br /&gt;
		redirect = 'eISSN (identifier)',&lt;br /&gt;
		q = 'Q46339674',&lt;br /&gt;
		label = 'eISSN',&lt;br /&gt;
		prefix = '//www.worldcat.org/issn/',&lt;br /&gt;
		COinS = 'rft.eissn',&lt;br /&gt;
		encode = false,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['HDL'] = {&lt;br /&gt;
		parameters = { 'hdl', 'HDL' },&lt;br /&gt;
		link = 'Handle System',&lt;br /&gt;
		redirect = 'hdl (identifier)',&lt;br /&gt;
		q = 'Q3126718',&lt;br /&gt;
		label = 'hdl',&lt;br /&gt;
		prefix = '//hdl.handle.net/',&lt;br /&gt;
		COinS = 'info:hdl',&lt;br /&gt;
		separator = ':',&lt;br /&gt;
		encode = true,&lt;br /&gt;
		custom_access = 'hdl-access',&lt;br /&gt;
		},&lt;br /&gt;
	['ISBN'] = {																-- Used by InternetArchiveBot&lt;br /&gt;
		parameters = {'isbn', 'ISBN'},&lt;br /&gt;
		link = 'International Standard Book Number',&lt;br /&gt;
		redirect = 'ISBN (identifier)',&lt;br /&gt;
		q = 'Q33057',&lt;br /&gt;
		label = 'ISBN',&lt;br /&gt;
		prefix = 'Special:BookSources/',&lt;br /&gt;
		COinS = 'rft.isbn',&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['ISMN'] = {&lt;br /&gt;
		parameters = {'ismn', 'ISMN'},&lt;br /&gt;
		link = 'International Standard Music Number',&lt;br /&gt;
		redirect = 'ISMN (identifier)',&lt;br /&gt;
		q = 'Q1666938',&lt;br /&gt;
		label = 'ISMN',&lt;br /&gt;
		prefix = '',															-- not currently used;&lt;br /&gt;
		COinS = nil,															-- nil because we can't use pre or rft or info:&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['ISSN'] = {&lt;br /&gt;
		parameters = {'issn', 'ISSN'},&lt;br /&gt;
		link = 'International Standard Serial Number',&lt;br /&gt;
		redirect = 'ISSN (identifier)',&lt;br /&gt;
		q = 'Q131276',&lt;br /&gt;
		label = 'ISSN',&lt;br /&gt;
		prefix = '//www.worldcat.org/issn/',&lt;br /&gt;
		COinS = 'rft.issn',&lt;br /&gt;
		encode = false,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['JFM'] = {&lt;br /&gt;
		parameters = {'jfm', 'JFM'},&lt;br /&gt;
		link = 'Jahrbuch über die Fortschritte der Mathematik',&lt;br /&gt;
		redirect = 'JFM (identifier)',&lt;br /&gt;
		q = '',&lt;br /&gt;
		label = 'JFM',&lt;br /&gt;
		prefix = '//zbmath.org/?format=complete&amp;amp;q=an:',&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['JSTOR'] = {&lt;br /&gt;
		parameters = {'jstor', 'JSTOR'},&lt;br /&gt;
		link = 'JSTOR',&lt;br /&gt;
		redirect = 'JSTOR (identifier)',&lt;br /&gt;
		q = 'Q1420342',&lt;br /&gt;
		label = 'JSTOR',&lt;br /&gt;
		prefix = '//www.jstor.org/stable/', 									-- protocol-relative tested 2013-09-04&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = false,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		custom_access = 'jstor-access',&lt;br /&gt;
		},&lt;br /&gt;
	['LCCN'] = {&lt;br /&gt;
		parameters = {'lccn', 'LCCN'},&lt;br /&gt;
		link = 'Library of Congress Control Number',&lt;br /&gt;
		redirect = 'LCCN (identifier)',&lt;br /&gt;
		q = 'Q620946',&lt;br /&gt;
		label = 'LCCN',&lt;br /&gt;
		prefix = '//lccn.loc.gov/', 											-- protocol-relative tested 2015-12-28&lt;br /&gt;
		COinS = 'info:lccn',&lt;br /&gt;
		encode = false,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['MR'] = {&lt;br /&gt;
		parameters = {'mr', 'MR'},&lt;br /&gt;
		link = 'Mathematical Reviews',&lt;br /&gt;
		redirect = 'MR (identifier)',&lt;br /&gt;
		q = 'Q211172',&lt;br /&gt;
		label = 'MR',&lt;br /&gt;
		prefix = '//www.ams.org/mathscinet-getitem?mr=', 						-- protocol-relative tested 2013-09-04&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['OCLC'] = {&lt;br /&gt;
		parameters = {'oclc', 'OCLC'},&lt;br /&gt;
		link = 'OCLC',&lt;br /&gt;
		redirect = 'OCLC (identifier)',&lt;br /&gt;
		q = 'Q190593',&lt;br /&gt;
		label = 'OCLC',&lt;br /&gt;
		prefix = '//www.worldcat.org/oclc/',&lt;br /&gt;
		COinS = 'info:oclcnum',&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		id_limit = 9999999999,													-- 10-digits&lt;br /&gt;
		},&lt;br /&gt;
	['OL'] = {&lt;br /&gt;
		parameters = { 'ol', 'OL' },&lt;br /&gt;
		link = 'Open Library',&lt;br /&gt;
		redirect = 'OL (identifier)',&lt;br /&gt;
		q = 'Q1201876',&lt;br /&gt;
		label = 'OL',&lt;br /&gt;
		prefix = '//openlibrary.org/',&lt;br /&gt;
		COinS = 'url',&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		encode = true,&lt;br /&gt;
		custom_access = 'ol-access',&lt;br /&gt;
		},&lt;br /&gt;
	['OSTI'] = {&lt;br /&gt;
		parameters = {'osti', 'OSTI'},&lt;br /&gt;
		link = 'Office of Scientific and Technical Information',&lt;br /&gt;
		redirect = 'OSTI (identifier)',&lt;br /&gt;
		q = 'Q2015776',&lt;br /&gt;
		label = 'OSTI',&lt;br /&gt;
		prefix = '//www.osti.gov/biblio/',										-- protocol-relative tested 2018-09-12&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		id_limit = 23000000,&lt;br /&gt;
		custom_access = 'osti-access',&lt;br /&gt;
		},&lt;br /&gt;
	['PMC'] = {&lt;br /&gt;
		parameters = {'pmc', 'PMC'},&lt;br /&gt;
		link = 'PubMed Central',&lt;br /&gt;
		redirect = 'PMC (identifier)',&lt;br /&gt;
		q = 'Q229883',&lt;br /&gt;
		label = 'PMC',&lt;br /&gt;
		prefix = '//www.ncbi.nlm.nih.gov/pmc/articles/PMC',&lt;br /&gt;
		suffix = '',&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		id_limit = 9100000,&lt;br /&gt;
		access = 'free',														-- free to read&lt;br /&gt;
		},&lt;br /&gt;
	['PMID'] = {&lt;br /&gt;
		parameters = {'pmid', 'PMID'},&lt;br /&gt;
		link = 'PubMed Identifier',&lt;br /&gt;
		redirect = 'PMID (identifier)',&lt;br /&gt;
		q = 'Q2082879',&lt;br /&gt;
		label = 'PMID',&lt;br /&gt;
		prefix = '//pubmed.ncbi.nlm.nih.gov/',&lt;br /&gt;
		COinS = 'info:pmid',&lt;br /&gt;
		encode = false,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		id_limit = 35400000,&lt;br /&gt;
		},&lt;br /&gt;
	['RFC'] = {&lt;br /&gt;
		parameters = {'rfc', 'RFC'},&lt;br /&gt;
		link = 'Request for Comments',&lt;br /&gt;
		redirect = 'RFC (identifier)',&lt;br /&gt;
		q = 'Q212971',&lt;br /&gt;
		label = 'RFC',&lt;br /&gt;
		prefix = '//tools.ietf.org/html/rfc',&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = false,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		id_limit = 9300,&lt;br /&gt;
		access = 'free',														-- free to read&lt;br /&gt;
		},&lt;br /&gt;
	['SBN'] = {&lt;br /&gt;
		parameters = {'sbn', 'SBN'},&lt;br /&gt;
		link = 'Standard Book Number',											-- redirect to International_Standard_Book_Number#History&lt;br /&gt;
		redirect = 'SBN (identifier)',&lt;br /&gt;
		label = 'SBN',&lt;br /&gt;
		prefix = 'Special:BookSources/0-',										-- prefix has leading zero necessary to make 9-digit sbn a 10-digit isbn&lt;br /&gt;
		COinS = nil,															-- nil because we can't use pre or rft or info:&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['SSRN'] = {&lt;br /&gt;
		parameters = {'ssrn', 'SSRN'},&lt;br /&gt;
		link = 'Social Science Research Network',&lt;br /&gt;
		redirect = 'SSRN (identifier)',&lt;br /&gt;
		q = 'Q7550801',&lt;br /&gt;
		label = 'SSRN',&lt;br /&gt;
		prefix = '//ssrn.com/abstract=', 										-- protocol-relative tested 2013-09-04&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		id_limit = 4100000,&lt;br /&gt;
		custom_access = 'ssrn-access',&lt;br /&gt;
		},&lt;br /&gt;
	['S2CID'] = {&lt;br /&gt;
		parameters = {'s2cid', 'S2CID'},&lt;br /&gt;
		link = 'Semantic Scholar',&lt;br /&gt;
		redirect = 'S2CID (identifier)',&lt;br /&gt;
		q = 'Q22908627',&lt;br /&gt;
		label = 'S2CID',&lt;br /&gt;
		prefix = 'https://api.semanticscholar.org/CorpusID:',&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = false,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		id_limit = 250000000,&lt;br /&gt;
		custom_access = 's2cid-access',&lt;br /&gt;
		},&lt;br /&gt;
	['USENETID'] = {&lt;br /&gt;
		parameters = {'message-id'},&lt;br /&gt;
		link = 'Usenet',&lt;br /&gt;
		redirect = 'Usenet (identifier)',&lt;br /&gt;
		q = 'Q193162',&lt;br /&gt;
		label = 'Usenet:',&lt;br /&gt;
		prefix = 'news:',&lt;br /&gt;
		encode = false,&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	['ZBL'] = {&lt;br /&gt;
		parameters = {'zbl', 'ZBL' },&lt;br /&gt;
		link = 'Zentralblatt MATH',&lt;br /&gt;
		redirect = 'Zbl (identifier)',&lt;br /&gt;
		q = 'Q190269',&lt;br /&gt;
		label = 'Zbl',&lt;br /&gt;
		prefix = '//zbmath.org/?format=complete&amp;amp;q=an:',&lt;br /&gt;
		COinS = 'pre',															-- use prefix value&lt;br /&gt;
		encode = true,&lt;br /&gt;
		separator = '&amp;amp;nbsp;',&lt;br /&gt;
		},&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X P O R T S &amp;gt;---------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
return 	{&lt;br /&gt;
	use_identifier_redirects = true,											-- when true use redirect name for identifier label links; always true at en.wiki&lt;br /&gt;
	local_lang_cat_enable = false;												-- when true categorizes pages where |language=&amp;lt;local wiki's language&amp;gt;; always false at en.wiki&lt;br /&gt;
	date_name_auto_xlate_enable = false;										-- when true translates English month-names to the local-wiki's language month names; always false at en.wiki&lt;br /&gt;
	date_digit_auto_xlate_enable = false;										-- when true translates Western date digit to the local-wiki's language digits (date_names['local_digits']); always false at en.wiki&lt;br /&gt;
	&lt;br /&gt;
	global_df = get_date_format (),												-- tables and variables created when this module is loaded&lt;br /&gt;
	punct_skip = build_skip_table (punct_skip, punct_meta_params),&lt;br /&gt;
	url_skip = build_skip_table (url_skip, url_meta_params),&lt;br /&gt;
&lt;br /&gt;
	aliases = aliases,&lt;br /&gt;
	special_case_translation = special_case_translation,&lt;br /&gt;
	date_names = date_names,&lt;br /&gt;
	err_msg_supl = err_msg_supl,&lt;br /&gt;
	error_conditions = error_conditions,&lt;br /&gt;
	editor_markup_patterns = editor_markup_patterns,&lt;br /&gt;
	et_al_patterns = et_al_patterns,&lt;br /&gt;
	id_handlers = id_handlers,&lt;br /&gt;
	keywords_lists = keywords_lists,&lt;br /&gt;
	keywords_xlate = keywords_xlate,&lt;br /&gt;
	stripmarkers=stripmarkers,&lt;br /&gt;
	invisible_chars = invisible_chars,&lt;br /&gt;
	invisible_defs = invisible_defs,&lt;br /&gt;
	indic_script = indic_script,&lt;br /&gt;
	emoji = emoji,&lt;br /&gt;
	local_lang_cat_enable = local_lang_cat_enable,&lt;br /&gt;
	maint_cats = maint_cats,&lt;br /&gt;
	messages = messages,&lt;br /&gt;
	presentation = presentation,&lt;br /&gt;
	prop_cats = prop_cats,&lt;br /&gt;
	script_lang_codes = script_lang_codes,&lt;br /&gt;
	lang_code_remap = lang_code_remap,&lt;br /&gt;
	lang_name_remap = lang_name_remap,&lt;br /&gt;
	this_wiki_code = this_wiki_code,&lt;br /&gt;
	title_types = title_types,&lt;br /&gt;
	uncategorized_namespaces = uncategorized_namespaces,&lt;br /&gt;
	uncategorized_subpages = uncategorized_subpages,&lt;br /&gt;
	templates_using_volume = templates_using_volume,&lt;br /&gt;
	templates_using_issue = templates_using_issue,&lt;br /&gt;
	templates_not_using_page = templates_not_using_page,&lt;br /&gt;
	vol_iss_pg_patterns = vol_iss_pg_patterns,&lt;br /&gt;
	&lt;br /&gt;
	inter_wiki_map = inter_wiki_map,&lt;br /&gt;
	mw_languages_by_tag_t = mw_languages_by_tag_t,&lt;br /&gt;
	mw_languages_by_name_t = mw_languages_by_name_t,&lt;br /&gt;
	citation_class_map_t = citation_class_map_t,&lt;br /&gt;
	}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Citation/CS1&amp;diff=260</id>
		<title>Module:Citation/CS1</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Citation/CS1&amp;diff=260"/>
		<updated>2022-04-04T14:01:57Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;require ('Module:No globals');  --[[--------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------  each of these counts against t...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;require ('Module:No globals');&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; F O R W A R D   D E C L A R A T I O N S &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
each of these counts against the Lua upvalue limit&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local validation;																-- functions in Module:Citation/CS1/Date_validation&lt;br /&gt;
&lt;br /&gt;
local utilities;																-- functions in Module:Citation/CS1/Utilities&lt;br /&gt;
local z ={};																	-- table of tables in Module:Citation/CS1/Utilities&lt;br /&gt;
&lt;br /&gt;
local identifiers;																-- functions and tables in Module:Citation/CS1/Identifiers&lt;br /&gt;
local metadata;																	-- functions in Module:Citation/CS1/COinS&lt;br /&gt;
local cfg = {};																	-- table of configuration tables that are defined in Module:Citation/CS1/Configuration&lt;br /&gt;
local whitelist = {};															-- table of tables listing valid template parameter names; defined in Module:Citation/CS1/Whitelist&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[------------------&amp;lt; P A G E   S C O P E   V A R I A B L E S &amp;gt;---------------&lt;br /&gt;
&lt;br /&gt;
declare variables here that have page-wide scope that are not brought in from&lt;br /&gt;
other modules; that are created here and used here&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local added_deprecated_cat;														-- Boolean flag so that the category is added only once&lt;br /&gt;
local added_vanc_errs;															-- Boolean flag so we only emit one Vancouver error / category&lt;br /&gt;
local added_generic_name_errs;													-- Boolean flag so we only emit one generic name error / category and stop testing names once an error is encountered&lt;br /&gt;
local Frame;																	-- holds the module's frame table&lt;br /&gt;
local is_preview_mode;															-- true when article is in preview mode; false when using 'Preview page with this template' (previewing the module)&lt;br /&gt;
local is_sandbox;																-- true when using sandbox modules to render citation&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; F I R S T _ S E T &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Locates and returns the first set value in a table of values where the order established in the table,&lt;br /&gt;
left-to-right (or top-to-bottom), is the order in which the values are evaluated.  Returns nil if none are set.&lt;br /&gt;
&lt;br /&gt;
This version replaces the original 'for _, val in pairs do' and a similar version that used ipairs.  With the pairs&lt;br /&gt;
version the order of evaluation could not be guaranteed.  With the ipairs version, a nil value would terminate&lt;br /&gt;
the for-loop before it reached the actual end of the list.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function first_set (list, count)&lt;br /&gt;
	local i = 1;&lt;br /&gt;
	while i &amp;lt;= count do															-- loop through all items in list&lt;br /&gt;
		if utilities.is_set( list[i] ) then&lt;br /&gt;
			return list[i];														-- return the first set list member&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1;																-- point to next&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; A D D _ V A N C _ E R R O R &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Adds a single Vancouver system error message to the template's output regardless of how many error actually exist.&lt;br /&gt;
To prevent duplication, added_vanc_errs is nil until an error message is emitted.&lt;br /&gt;
&lt;br /&gt;
added_vanc_errs is a Boolean declared in page scope variables above&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function add_vanc_error (source, position)&lt;br /&gt;
	if added_vanc_errs then return end&lt;br /&gt;
		&lt;br /&gt;
	added_vanc_errs = true;														-- note that we've added this category&lt;br /&gt;
	utilities.set_message ('err_vancouver', {source, position});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ S C H E M E &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
does this thing that purports to be a URI scheme seem to be a valid scheme?  The scheme is checked to see if it&lt;br /&gt;
is in agreement with http://tools.ietf.org/html/std66#section-3.1 which says:&lt;br /&gt;
	Scheme names consist of a sequence of characters beginning with a&lt;br /&gt;
   letter and followed by any combination of letters, digits, plus&lt;br /&gt;
   (&amp;quot;+&amp;quot;), period (&amp;quot;.&amp;quot;), or hyphen (&amp;quot;-&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
returns true if it does, else false&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_scheme (scheme)&lt;br /&gt;
	return scheme and scheme:match ('^%a[%a%d%+%.%-]*:');						-- true if scheme is set and matches the pattern&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; I S _ D O M A I N _ N A M E &amp;gt;--------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Does this thing that purports to be a domain name seem to be a valid domain name?&lt;br /&gt;
&lt;br /&gt;
Syntax defined here: http://tools.ietf.org/html/rfc1034#section-3.5&lt;br /&gt;
BNF defined here: https://tools.ietf.org/html/rfc4234&lt;br /&gt;
Single character names are generally reserved; see https://tools.ietf.org/html/draft-ietf-dnsind-iana-dns-01#page-15;&lt;br /&gt;
	see also [[Single-letter second-level domain]]&lt;br /&gt;
list of TLDs: https://www.iana.org/domains/root/db&lt;br /&gt;
&lt;br /&gt;
RFC 952 (modified by RFC 1123) requires the first and last character of a hostname to be a letter or a digit.  Between&lt;br /&gt;
the first and last characters the name may use letters, digits, and the hyphen.&lt;br /&gt;
&lt;br /&gt;
Also allowed are IPv4 addresses. IPv6 not supported&lt;br /&gt;
&lt;br /&gt;
domain is expected to be stripped of any path so that the last character in the last character of the TLD.  tld&lt;br /&gt;
is two or more alpha characters.  Any preceding '//' (from splitting a URL with a scheme) will be stripped&lt;br /&gt;
here.  Perhaps not necessary but retained in case it is necessary for IPv4 dot decimal.&lt;br /&gt;
&lt;br /&gt;
There are several tests:&lt;br /&gt;
	the first character of the whole domain name including subdomains must be a letter or a digit&lt;br /&gt;
	internationalized domain name (ASCII characters with .xn-- ASCII Compatible Encoding (ACE) prefix xn-- in the TLD) see https://tools.ietf.org/html/rfc3490&lt;br /&gt;
	single-letter/digit second-level domains in the .org, .cash, and .today TLDs&lt;br /&gt;
	q, x, and z SL domains in the .com TLD&lt;br /&gt;
	i and q SL domains in the .net TLD&lt;br /&gt;
	single-letter SL domains in the ccTLDs (where the ccTLD is two letters)&lt;br /&gt;
	two-character SL domains in gTLDs (where the gTLD is two or more letters)&lt;br /&gt;
	three-plus-character SL domains in gTLDs (where the gTLD is two or more letters)&lt;br /&gt;
	IPv4 dot-decimal address format; TLD not allowed&lt;br /&gt;
&lt;br /&gt;
returns true if domain appears to be a proper name and TLD or IPv4 address, else false&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function is_domain_name (domain)&lt;br /&gt;
	if not domain then&lt;br /&gt;
		return false;															-- if not set, abandon&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	domain = domain:gsub ('^//', '');											-- strip '//' from domain name if present; done here so we only have to do it once&lt;br /&gt;
	&lt;br /&gt;
	if not domain:match ('^[%w]') then											-- first character must be letter or digit&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if domain:match ('^%a+:') then												-- hack to detect things that look like s:Page:Title where Page: is namespace at Wikisource&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local patterns = {															-- patterns that look like URLs&lt;br /&gt;
		'%f[%w][%w][%w%-]+[%w]%.%a%a+$',										-- three or more character hostname.hostname or hostname.tld&lt;br /&gt;
		'%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$',									-- internationalized domain name with ACE prefix&lt;br /&gt;
		'%f[%a][qxz]%.com$',													-- assigned one character .com hostname (x.com times out 2015-12-10)&lt;br /&gt;
		'%f[%a][iq]%.net$',														-- assigned one character .net hostname (q.net registered but not active 2015-12-10)&lt;br /&gt;
		'%f[%w][%w]%.%a%a$',													-- one character hostname and ccTLD (2 chars)&lt;br /&gt;
		'%f[%w][%w][%w]%.%a%a+$',												-- two character hostname and TLD&lt;br /&gt;
		'^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?',								-- IPv4 address&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
	for _, pattern in ipairs (patterns) do										-- loop through the patterns list&lt;br /&gt;
		if domain:match (pattern) then&lt;br /&gt;
			return true;														-- if a match then we think that this thing that purports to be a URL is a URL&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for _, d in ipairs ({'cash', 'company', 'today', 'org'}) do					-- look for single letter second level domain names for these top level domains&lt;br /&gt;
		if domain:match ('%f[%w][%w]%.' .. d) then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return false;																-- no matches, we don't know what this thing is&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ U R L &amp;gt;------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns true if the scheme and domain parts of a URL appear to be a valid URL; else false.&lt;br /&gt;
&lt;br /&gt;
This function is the last step in the validation process.  This function is separate because there are cases that&lt;br /&gt;
are not covered by split_url(), for example is_parameter_ext_wikilink() which is looking for bracketted external&lt;br /&gt;
wikilinks.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_url (scheme, domain)&lt;br /&gt;
	if utilities.is_set (scheme) then											-- if scheme is set check it and domain&lt;br /&gt;
		return is_scheme (scheme) and is_domain_name (domain);&lt;br /&gt;
	else&lt;br /&gt;
		return is_domain_name (domain);											-- scheme not set when URL is protocol-relative&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S P L I T _ U R L &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Split a URL into a scheme, authority indicator, and domain.&lt;br /&gt;
&lt;br /&gt;
First remove Fully Qualified Domain Name terminator (a dot following TLD) (if any) and any path(/), query(?) or fragment(#).&lt;br /&gt;
&lt;br /&gt;
If protocol-relative URL, return nil scheme and domain else return nil for both scheme and domain.&lt;br /&gt;
&lt;br /&gt;
When not protocol-relative, get scheme, authority indicator, and domain.  If there is an authority indicator (one&lt;br /&gt;
or more '/' characters immediately following the scheme's colon), make sure that there are only 2.&lt;br /&gt;
&lt;br /&gt;
Any URL that does not have news: scheme must have authority indicator (//).  TODO: are there other common schemes&lt;br /&gt;
like news: that don't use authority indicator?&lt;br /&gt;
&lt;br /&gt;
Strip off any port and path;&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function split_url (url_str)&lt;br /&gt;
	local scheme, authority, domain;&lt;br /&gt;
	&lt;br /&gt;
	url_str = url_str:gsub ('([%a%d])%.?[/%?#].*$', '%1');						-- strip FQDN terminator and path(/), query(?), fragment (#) (the capture prevents false replacement of '//')&lt;br /&gt;
&lt;br /&gt;
	if url_str:match ('^//%S*') then											-- if there is what appears to be a protocol-relative URL&lt;br /&gt;
		domain = url_str:match ('^//(%S*)')&lt;br /&gt;
	elseif url_str:match ('%S-:/*%S+') then										-- if there is what appears to be a scheme, optional authority indicator, and domain name&lt;br /&gt;
		scheme, authority, domain = url_str:match ('(%S-:)(/*)(%S+)');			-- extract the scheme, authority indicator, and domain portions&lt;br /&gt;
		if utilities.is_set (authority) then&lt;br /&gt;
			authority = authority:gsub ('//', '', 1);							-- replace place 1 pair of '/' with nothing;&lt;br /&gt;
			if utilities.is_set(authority) then									-- if anything left (1 or 3+ '/' where authority should be) then&lt;br /&gt;
				return scheme;													-- return scheme only making domain nil which will cause an error message&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			if not scheme:match ('^news:') then									-- except for news:..., MediaWiki won't link URLs that do not have authority indicator; TODO: a better way to do this test?&lt;br /&gt;
				return scheme;													-- return scheme only making domain nil which will cause an error message&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		domain = domain:gsub ('(%a):%d+', '%1');								-- strip port number if present&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return scheme, domain;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; L I N K _ P A R A M _ O K &amp;gt;---------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
checks the content of |title-link=, |series-link=, |author-link=, etc. for properly formatted content: no wikilinks, no URLs&lt;br /&gt;
&lt;br /&gt;
Link parameters are to hold the title of a Wikipedia article, so none of the WP:TITLESPECIALCHARACTERS are allowed:&lt;br /&gt;
	# &amp;lt; &amp;gt; [ ] | { } _&lt;br /&gt;
except the underscore which is used as a space in wiki URLs and # which is used for section links&lt;br /&gt;
&lt;br /&gt;
returns false when the value contains any of these characters.&lt;br /&gt;
&lt;br /&gt;
When there are no illegal characters, this function returns TRUE if value DOES NOT appear to be a valid URL (the&lt;br /&gt;
|&amp;lt;param&amp;gt;-link= parameter is ok); else false when value appears to be a valid URL (the |&amp;lt;param&amp;gt;-link= parameter is NOT ok).&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function link_param_ok (value)&lt;br /&gt;
	local scheme, domain;&lt;br /&gt;
	if value:find ('[&amp;lt;&amp;gt;%[%]|{}]') then											-- if any prohibited characters&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	scheme, domain = split_url (value);											-- get scheme or nil and domain or nil from URL; &lt;br /&gt;
	return not is_url (scheme, domain);											-- return true if value DOES NOT appear to be a valid URL&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; L I N K _ T I T L E _ O K &amp;gt;---------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Use link_param_ok() to validate |&amp;lt;param&amp;gt;-link= value and its matching |&amp;lt;title&amp;gt;= value.&lt;br /&gt;
&lt;br /&gt;
|&amp;lt;title&amp;gt;= may be wiki-linked but not when |&amp;lt;param&amp;gt;-link= has a value.  This function emits an error message when&lt;br /&gt;
that condition exists&lt;br /&gt;
&lt;br /&gt;
check &amp;lt;link&amp;gt; for inter-language interwiki-link prefix.  prefix must be a MediaWiki-recognized language&lt;br /&gt;
code and must begin with a colon.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function link_title_ok (link, lorig, title, torig)&lt;br /&gt;
local orig;&lt;br /&gt;
	if utilities.is_set (link) then 											-- don't bother if &amp;lt;param&amp;gt;-link doesn't have a value&lt;br /&gt;
		if not link_param_ok (link) then										-- check |&amp;lt;param&amp;gt;-link= markup&lt;br /&gt;
			orig = lorig;														-- identify the failing link parameter&lt;br /&gt;
		elseif title:find ('%[%[') then											-- check |title= for wikilink markup&lt;br /&gt;
			orig = torig;														-- identify the failing |title= parameter&lt;br /&gt;
		elseif link:match ('^%a+:') then										-- if the link is what looks like an interwiki&lt;br /&gt;
			local prefix = link:match ('^(%a+):'):lower();						-- get the interwiki prefix&lt;br /&gt;
&lt;br /&gt;
			if cfg.inter_wiki_map[prefix] then									-- if prefix is in the map, must have preceding colon&lt;br /&gt;
				orig = lorig;													-- flag as error&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (orig) then&lt;br /&gt;
		link = '';																-- unset&lt;br /&gt;
		utilities.set_message ('err_bad_paramlink', orig);						-- URL or wikilink in |title= with |title-link=;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return link;																-- link if ok, empty string else&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C H E C K _ U R L &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Determines whether a URL string appears to be valid.&lt;br /&gt;
&lt;br /&gt;
First we test for space characters.  If any are found, return false.  Then split the URL into scheme and domain&lt;br /&gt;
portions, or for protocol-relative (//example.com) URLs, just the domain.  Use is_url() to validate the two&lt;br /&gt;
portions of the URL.  If both are valid, or for protocol-relative if domain is valid, return true, else false.&lt;br /&gt;
&lt;br /&gt;
Because it is different from a standard URL, and because this module used external_link() to make external links&lt;br /&gt;
that work for standard and news: links, we validate newsgroup names here.  The specification for a newsgroup name&lt;br /&gt;
is at https://tools.ietf.org/html/rfc5536#section-3.1.4&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function check_url( url_str )&lt;br /&gt;
	if nil == url_str:match (&amp;quot;^%S+$&amp;quot;) then										-- if there are any spaces in |url=value it can't be a proper URL&lt;br /&gt;
		return false;&lt;br /&gt;
	end&lt;br /&gt;
	local scheme, domain;&lt;br /&gt;
&lt;br /&gt;
	scheme, domain = split_url (url_str);										-- get scheme or nil and domain or nil from URL;&lt;br /&gt;
	&lt;br /&gt;
	if 'news:' == scheme then													-- special case for newsgroups&lt;br /&gt;
		return domain:match('^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$');&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return is_url (scheme, domain);												-- return true if value appears to be a valid URL&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; I S _ P A R A M E T E R _ E X T _ W I K I L I N K &amp;gt;----------------------------&lt;br /&gt;
&lt;br /&gt;
Return true if a parameter value has a string that begins and ends with square brackets [ and ] and the first&lt;br /&gt;
non-space characters following the opening bracket appear to be a URL.  The test will also find external wikilinks&lt;br /&gt;
that use protocol-relative URLs. Also finds bare URLs.&lt;br /&gt;
&lt;br /&gt;
The frontier pattern prevents a match on interwiki-links which are similar to scheme:path URLs.  The tests that&lt;br /&gt;
find bracketed URLs are required because the parameters that call this test (currently |title=, |chapter=, |work=,&lt;br /&gt;
and |publisher=) may have wikilinks and there are articles or redirects like '//Hus' so, while uncommon, |title=[[//Hus]]&lt;br /&gt;
is possible as might be [[en://Hus]].&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function is_parameter_ext_wikilink (value)&lt;br /&gt;
local scheme, domain;&lt;br /&gt;
&lt;br /&gt;
	if value:match ('%f[%[]%[%a%S*:%S+.*%]') then								-- if ext. wikilink with scheme and domain: [xxxx://yyyyy.zzz]&lt;br /&gt;
		scheme, domain = split_url (value:match ('%f[%[]%[(%a%S*:%S+).*%]'));&lt;br /&gt;
	elseif value:match ('%f[%[]%[//%S+.*%]') then								-- if protocol-relative ext. wikilink: [//yyyyy.zzz]&lt;br /&gt;
		scheme, domain = split_url (value:match ('%f[%[]%[(//%S+).*%]'));&lt;br /&gt;
	elseif value:match ('%a%S*:%S+') then										-- if bare URL with scheme; may have leading or trailing plain text&lt;br /&gt;
		scheme, domain = split_url (value:match ('(%a%S*:%S+)'));&lt;br /&gt;
	elseif value:match ('//%S+') then											-- if protocol-relative bare URL: //yyyyy.zzz; may have leading or trailing plain text&lt;br /&gt;
		scheme, domain = split_url (value:match ('(//%S+)'));					-- what is left should be the domain&lt;br /&gt;
	else&lt;br /&gt;
		return false;															-- didn't find anything that is obviously a URL&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return is_url (scheme, domain);												-- return true if value appears to be a valid URL&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------------&amp;lt; C H E C K _ F O R _ U R L &amp;gt;-----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
loop through a list of parameters and their values.  Look at the value and if it has an external link, emit an error message.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function check_for_url (parameter_list, error_list)&lt;br /&gt;
	for k, v in pairs (parameter_list) do										-- for each parameter in the list&lt;br /&gt;
		if is_parameter_ext_wikilink (v) then									-- look at the value; if there is a URL add an error message&lt;br /&gt;
			table.insert (error_list, utilities.wrap_style ('parameter', k));&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S A F E _ F O R _ U R L &amp;gt;------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Escape sequences for content that will be used for URL descriptions&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function safe_for_url( str )&lt;br /&gt;
	if str:match( &amp;quot;%[%[.-%]%]&amp;quot; ) ~= nil then &lt;br /&gt;
		utilities.set_message ('err_wikilink_in_url', {});&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return str:gsub( '[%[%]\n]', {	&lt;br /&gt;
		['['] = '&amp;amp;#91;',&lt;br /&gt;
		[']'] = '&amp;amp;#93;',&lt;br /&gt;
		['\n'] = ' ' } );&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X T E R N A L _ L I N K &amp;gt;----------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Format an external link with error checking&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function external_link (URL, label, source, access)&lt;br /&gt;
	local err_msg = '';&lt;br /&gt;
	local domain;&lt;br /&gt;
	local path;&lt;br /&gt;
	local base_url;&lt;br /&gt;
&lt;br /&gt;
	if not utilities.is_set (label) then&lt;br /&gt;
		label = URL;&lt;br /&gt;
		if utilities.is_set (source) then&lt;br /&gt;
			utilities.set_message ('err_bare_url_missing_title', {utilities.wrap_style ('parameter', source)});&lt;br /&gt;
		else&lt;br /&gt;
			error (cfg.messages[&amp;quot;bare_url_no_origin&amp;quot;]);&lt;br /&gt;
		end			&lt;br /&gt;
	end&lt;br /&gt;
	if not check_url (URL) then&lt;br /&gt;
		utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)});&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	domain, path = URL:match ('^([/%.%-%+:%a%d]+)([/%?#].*)$');					-- split the URL into scheme plus domain and path&lt;br /&gt;
	if path then																-- if there is a path portion&lt;br /&gt;
		path = path:gsub ('[%[%]]', {['['] = '%5b', [']'] = '%5d'});			-- replace '[' and ']' with their percent-encoded values&lt;br /&gt;
		URL = table.concat ({domain, path});									-- and reassemble&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	base_url = table.concat ({ &amp;quot;[&amp;quot;, URL, &amp;quot; &amp;quot;, safe_for_url (label), &amp;quot;]&amp;quot; });		-- assemble a wiki-markup URL&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (access) then											-- access level (subscription, registration, limited)&lt;br /&gt;
		base_url = utilities.substitute (cfg.presentation['ext-link-access-signal'], {cfg.presentation[access].class, cfg.presentation[access].title, base_url});	-- add the appropriate icon&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return base_url;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; D E P R E C A T E D _ P A R A M E T E R &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
Categorize and emit an error message when the citation contains one or more deprecated parameters.  The function includes the&lt;br /&gt;
offending parameter name to the error message.  Only one error message is emitted regardless of the number of deprecated&lt;br /&gt;
parameters in the citation.&lt;br /&gt;
&lt;br /&gt;
added_deprecated_cat is a Boolean declared in page scope variables above&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function deprecated_parameter(name)&lt;br /&gt;
	if not added_deprecated_cat then&lt;br /&gt;
		added_deprecated_cat = true;											-- note that we've added this category&lt;br /&gt;
		utilities.set_message ('err_deprecated_params', {name});				-- add error message&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; K E R N _ Q U O T E S &amp;gt;--------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Apply kerning to open the space between the quote mark provided by the module and a leading or trailing quote&lt;br /&gt;
mark contained in a |title= or |chapter= parameter's value.&lt;br /&gt;
&lt;br /&gt;
This function will positive kern either single or double quotes:&lt;br /&gt;
	&amp;quot;'Unkerned title with leading and trailing single quote marks'&amp;quot;&lt;br /&gt;
	&amp;quot; 'Kerned title with leading and trailing single quote marks' &amp;quot; (in real life the kerning isn't as wide as this example)&lt;br /&gt;
Double single quotes (italic or bold wiki-markup) are not kerned.&lt;br /&gt;
&lt;br /&gt;
Replaces Unicode quote marks in plain text or in the label portion of a [[L|D]] style wikilink with typewriter&lt;br /&gt;
quote marks regardless of the need for kerning.  Unicode quote marks are not replaced in simple [[D]] wikilinks.&lt;br /&gt;
&lt;br /&gt;
Call this function for chapter titles, for website titles, etc.; not for book titles.&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function kern_quotes (str)&lt;br /&gt;
	local cap = '';&lt;br /&gt;
	local wl_type, label, link;&lt;br /&gt;
&lt;br /&gt;
	wl_type, label, link = utilities.is_wikilink (str);							-- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]&lt;br /&gt;
	&lt;br /&gt;
	if 1 == wl_type then														-- [[D]] simple wikilink with or without quote marks&lt;br /&gt;
		if mw.ustring.match (str, '%[%[[\&amp;quot;“”\'‘’].+[\&amp;quot;“”\'‘’]%]%]') then		-- leading and trailing quote marks&lt;br /&gt;
			str = utilities.substitute (cfg.presentation['kern-left'], str);&lt;br /&gt;
			str = utilities.substitute (cfg.presentation['kern-right'], str);&lt;br /&gt;
		elseif mw.ustring.match (str, '%[%[[\&amp;quot;“”\'‘’].+%]%]')	then			-- leading quote marks&lt;br /&gt;
			str = utilities.substitute (cfg.presentation['kern-left'], str);&lt;br /&gt;
		elseif mw.ustring.match (str, '%[%[.+[\&amp;quot;“”\'‘’]%]%]') then				-- trailing quote marks&lt;br /&gt;
			str = utilities.substitute (cfg.presentation['kern-right'], str);&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	else																		-- plain text or [[L|D]]; text in label variable&lt;br /&gt;
		label = mw.ustring.gsub (label, '[“”]', '\&amp;quot;');							-- replace “” (U+201C &amp;amp; U+201D) with &amp;quot; (typewriter double quote mark)&lt;br /&gt;
		label = mw.ustring.gsub (label, '[‘’]', '\'');							-- replace ‘’ (U+2018 &amp;amp; U+2019) with ' (typewriter single quote mark)&lt;br /&gt;
&lt;br /&gt;
		cap = mw.ustring.match (label, &amp;quot;^([\&amp;quot;\'][^\'].+)&amp;quot;);						-- match leading double or single quote but not doubled single quotes (italic markup)&lt;br /&gt;
		if utilities.is_set (cap) then&lt;br /&gt;
			label = utilities.substitute (cfg.presentation['kern-left'], cap);&lt;br /&gt;
		end&lt;br /&gt;
	&lt;br /&gt;
		cap = mw.ustring.match (label, &amp;quot;^(.+[^\'][\&amp;quot;\'])$&amp;quot;)						-- match trailing double or single quote but not doubled single quotes (italic markup)&lt;br /&gt;
		if utilities.is_set (cap) then&lt;br /&gt;
			label = utilities.substitute (cfg.presentation['kern-right'], cap);&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if 2 == wl_type then&lt;br /&gt;
			str = utilities.make_wikilink (link, label);						-- reassemble the wikilink&lt;br /&gt;
		else&lt;br /&gt;
			str = label;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return str;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; F O R M A T _ S C R I P T _ V A L U E &amp;gt;----------------------------------------&lt;br /&gt;
&lt;br /&gt;
|script-title= holds title parameters that are not written in Latin-based scripts: Chinese, Japanese, Arabic, Hebrew, etc. These scripts should&lt;br /&gt;
not be italicized and may be written right-to-left.  The value supplied by |script-title= is concatenated onto Title after Title has been wrapped&lt;br /&gt;
in italic markup.&lt;br /&gt;
&lt;br /&gt;
Regardless of language, all values provided by |script-title= are wrapped in &amp;lt;bdi&amp;gt;...&amp;lt;/bdi&amp;gt; tags to isolate RTL languages from the English left to right.&lt;br /&gt;
&lt;br /&gt;
|script-title= provides a unique feature.  The value in |script-title= may be prefixed with a two-character ISO 639-1 language code and a colon:&lt;br /&gt;
	|script-title=ja:*** *** (where * represents a Japanese character)&lt;br /&gt;
Spaces between the two-character code and the colon and the colon and the first script character are allowed:&lt;br /&gt;
	|script-title=ja : *** ***&lt;br /&gt;
	|script-title=ja: *** ***&lt;br /&gt;
	|script-title=ja :*** ***&lt;br /&gt;
Spaces preceding the prefix are allowed: |script-title = ja:*** ***&lt;br /&gt;
&lt;br /&gt;
The prefix is checked for validity.  If it is a valid ISO 639-1 language code, the lang attribute (lang=&amp;quot;ja&amp;quot;) is added to the &amp;lt;bdi&amp;gt; tag so that browsers can&lt;br /&gt;
know the language the tag contains.  This may help the browser render the script more correctly.  If the prefix is invalid, the lang attribute&lt;br /&gt;
is not added.  At this time there is no error message for this condition.&lt;br /&gt;
&lt;br /&gt;
Supports |script-title=, |script-chapter=, |script-&amp;lt;periodical&amp;gt;=&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function format_script_value (script_value, script_param)&lt;br /&gt;
	local lang='';																-- initialize to empty string&lt;br /&gt;
	local name;&lt;br /&gt;
	if script_value:match('^%l%l%l?%s*:') then									-- if first 3 or 4 non-space characters are script language prefix&lt;br /&gt;
		lang = script_value:match('^(%l%l%l?)%s*:%s*%S.*');						-- get the language prefix or nil if there is no script&lt;br /&gt;
		if not utilities.is_set (lang) then&lt;br /&gt;
			utilities.set_message ('err_script_parameter', {script_param, 'missing title part'});		-- prefix without 'title'; add error message&lt;br /&gt;
			return '';															-- script_value was just the prefix so return empty string&lt;br /&gt;
		end&lt;br /&gt;
																				-- if we get this far we have prefix and script&lt;br /&gt;
		name = cfg.lang_code_remap[lang] or mw.language.fetchLanguageName( lang, cfg.this_wiki_code );	-- get language name so that we can use it to categorize&lt;br /&gt;
		if utilities.is_set (name) then											-- is prefix a proper ISO 639-1 language code?&lt;br /&gt;
			script_value = script_value:gsub ('^%l+%s*:%s*', '');				-- strip prefix from script&lt;br /&gt;
																				-- is prefix one of these language codes?&lt;br /&gt;
			if utilities.in_array (lang, cfg.script_lang_codes) then&lt;br /&gt;
				utilities.add_prop_cat ('script', {name, lang})&lt;br /&gt;
			else&lt;br /&gt;
				utilities.set_message ('err_script_parameter', {script_param, 'unknown language code'});	-- unknown script-language; add error message&lt;br /&gt;
			end&lt;br /&gt;
			lang = ' lang=&amp;quot;' .. lang .. '&amp;quot; ';									-- convert prefix into a lang attribute&lt;br /&gt;
		else&lt;br /&gt;
			utilities.set_message ('err_script_parameter', {script_param, 'invalid language code'});		-- invalid language code; add error message&lt;br /&gt;
			lang = '';															-- invalid so set lang to empty string&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		utilities.set_message ('err_script_parameter', {script_param, 'missing prefix'});				-- no language code prefix; add error message&lt;br /&gt;
	end&lt;br /&gt;
	script_value = utilities.substitute (cfg.presentation['bdi'], {lang, script_value});	-- isolate in case script is RTL&lt;br /&gt;
&lt;br /&gt;
	return script_value;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S C R I P T _ C O N C A T E N A T E &amp;gt;------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Initially for |title= and |script-title=, this function concatenates those two parameter values after the script&lt;br /&gt;
value has been wrapped in &amp;lt;bdi&amp;gt; tags.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function script_concatenate (title, script, script_param)&lt;br /&gt;
	if utilities.is_set (script) then&lt;br /&gt;
		script = format_script_value (script, script_param);					-- &amp;lt;bdi&amp;gt; tags, lang attribute, categorization, etc.; returns empty string on error&lt;br /&gt;
		if utilities.is_set (script) then&lt;br /&gt;
			title = title .. ' ' .. script;										-- concatenate title and script title&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return title;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; W R A P _ M S G &amp;gt;--------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Applies additional message text to various parameter values. Supplied string is wrapped using a message_list&lt;br /&gt;
configuration taking one argument.  Supports lower case text for {{citation}} templates.  Additional text taken&lt;br /&gt;
from citation_config.messages - the reason this function is similar to but separate from wrap_style().&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function wrap_msg (key, str, lower)&lt;br /&gt;
	if not utilities.is_set ( str ) then&lt;br /&gt;
		return &amp;quot;&amp;quot;;&lt;br /&gt;
	end&lt;br /&gt;
	if true == lower then&lt;br /&gt;
		local msg;&lt;br /&gt;
		msg = cfg.messages[key]:lower();										-- set the message to lower case before &lt;br /&gt;
		return utilities.substitute ( msg, str );								-- including template text&lt;br /&gt;
	else&lt;br /&gt;
		return utilities.substitute ( cfg.messages[key], str );&lt;br /&gt;
	end		&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[----------------&amp;lt; W I K I S O U R C E _ U R L _ M A K E &amp;gt;-------------------&lt;br /&gt;
&lt;br /&gt;
Makes a Wikisource URL from Wikisource interwiki-link.  Returns the URL and appropriate&lt;br /&gt;
label; nil else.&lt;br /&gt;
&lt;br /&gt;
str is the value assigned to |chapter= (or aliases) or |title= or |title-link=&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function wikisource_url_make (str)&lt;br /&gt;
	local wl_type, D, L;&lt;br /&gt;
	local ws_url, ws_label;&lt;br /&gt;
	local wikisource_prefix = table.concat ({'https://', cfg.this_wiki_code, '.wikisource.org/wiki/'});&lt;br /&gt;
&lt;br /&gt;
	wl_type, D, L = utilities.is_wikilink (str);								-- wl_type is 0 (not a wikilink), 1 (simple wikilink), 2 (complex wikilink)&lt;br /&gt;
&lt;br /&gt;
	if 0 == wl_type then														-- not a wikilink; might be from |title-link=&lt;br /&gt;
		str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)');		-- article title from interwiki link with long-form or short-form namespace&lt;br /&gt;
		if utilities.is_set (str) then&lt;br /&gt;
			ws_url = table.concat ({											-- build a Wikisource URL&lt;br /&gt;
				wikisource_prefix,												-- prefix&lt;br /&gt;
				str,															-- article title&lt;br /&gt;
				});&lt;br /&gt;
			ws_label = str;														-- label for the URL&lt;br /&gt;
		end&lt;br /&gt;
	elseif 1 == wl_type then													-- simple wikilink: [[Wikisource:ws article]]&lt;br /&gt;
		str = D:match ('^[Ww]ikisource:(.+)') or D:match ('^[Ss]:(.+)');		-- article title from interwiki link with long-form or short-form namespace&lt;br /&gt;
		if utilities.is_set (str) then&lt;br /&gt;
			ws_url = table.concat ({											-- build a Wikisource URL&lt;br /&gt;
				wikisource_prefix,												-- prefix&lt;br /&gt;
				str,															-- article title&lt;br /&gt;
				});&lt;br /&gt;
			ws_label = str;														-- label for the URL&lt;br /&gt;
		end&lt;br /&gt;
	elseif 2 == wl_type then													-- non-so-simple wikilink: [[Wikisource:ws article|displayed text]] ([[L|D]])&lt;br /&gt;
		str = L:match ('^[Ww]ikisource:(.+)') or L:match ('^[Ss]:(.+)');		-- article title from interwiki link with long-form or short-form namespace&lt;br /&gt;
		if utilities.is_set (str) then&lt;br /&gt;
			ws_label = D;														-- get ws article name from display portion of interwiki link&lt;br /&gt;
			ws_url = table.concat ({											-- build a Wikisource URL&lt;br /&gt;
				wikisource_prefix,												-- prefix&lt;br /&gt;
				str,															-- article title without namespace from link portion of wikilink&lt;br /&gt;
				});&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if ws_url then&lt;br /&gt;
		ws_url = mw.uri.encode (ws_url, 'WIKI');								-- make a usable URL&lt;br /&gt;
		ws_url = ws_url:gsub ('%%23', '#');										-- undo percent-encoding of fragment marker&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return ws_url, ws_label, L or D;											-- return proper URL or nil and a label or nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[----------------&amp;lt; F O R M A T _ P E R I O D I C A L &amp;gt;-----------------------&lt;br /&gt;
&lt;br /&gt;
Format the three periodical parameters: |script-&amp;lt;periodical&amp;gt;=, |&amp;lt;periodical&amp;gt;=,&lt;br /&gt;
and |trans-&amp;lt;periodical&amp;gt;= into a single Periodical meta-parameter.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function format_periodical (script_periodical, script_periodical_source, periodical, trans_periodical)&lt;br /&gt;
&lt;br /&gt;
	if not utilities.is_set (periodical) then&lt;br /&gt;
		periodical = '';														-- to be safe for concatenation&lt;br /&gt;
	else&lt;br /&gt;
		periodical = utilities.wrap_style ('italic-title', periodical);			-- style &lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	periodical = script_concatenate (periodical, script_periodical, script_periodical_source);	-- &amp;lt;bdi&amp;gt; tags, lang attribute, categorization, etc.; must be done after title is wrapped&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (trans_periodical) then&lt;br /&gt;
		trans_periodical = utilities.wrap_style ('trans-italic-title', trans_periodical);&lt;br /&gt;
		if utilities.is_set (periodical) then&lt;br /&gt;
			periodical = periodical .. ' ' .. trans_periodical;&lt;br /&gt;
		else																	-- here when trans-periodical without periodical or script-periodical&lt;br /&gt;
			periodical = trans_periodical;&lt;br /&gt;
			utilities.set_message ('err_trans_missing_title', {'periodical'});&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return periodical;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[------------------&amp;lt; F O R M A T _ C H A P T E R _ T I T L E &amp;gt;---------------&lt;br /&gt;
&lt;br /&gt;
Format the four chapter parameters: |script-chapter=, |chapter=, |trans-chapter=,&lt;br /&gt;
and |chapter-url= into a single chapter meta- parameter (chapter_url_source used&lt;br /&gt;
for error messages).&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function format_chapter_title (script_chapter, script_chapter_source, chapter, chapter_source, trans_chapter, trans_chapter_source, chapter_url, chapter_url_source, no_quotes, access)&lt;br /&gt;
	local ws_url, ws_label, L = wikisource_url_make (chapter);					-- make a wikisource URL and label from a wikisource interwiki link&lt;br /&gt;
	if ws_url then&lt;br /&gt;
		ws_label = ws_label:gsub ('_', ' ');									-- replace underscore separators with space characters&lt;br /&gt;
		chapter = ws_label;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not utilities.is_set (chapter) then&lt;br /&gt;
		chapter = '';															-- to be safe for concatenation&lt;br /&gt;
	else&lt;br /&gt;
		if false == no_quotes then&lt;br /&gt;
			chapter = kern_quotes (chapter);									-- if necessary, separate chapter title's leading and trailing quote marks from module provided quote marks&lt;br /&gt;
			chapter = utilities.wrap_style ('quoted-title', chapter);&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	chapter = script_concatenate (chapter, script_chapter, script_chapter_source);	-- &amp;lt;bdi&amp;gt; tags, lang attribute, categorization, etc.; must be done after title is wrapped&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (chapter_url) then&lt;br /&gt;
		chapter = external_link (chapter_url, chapter, chapter_url_source, access);	-- adds bare_url_missing_title error if appropriate&lt;br /&gt;
	elseif ws_url then&lt;br /&gt;
		chapter = external_link (ws_url, chapter .. '&amp;amp;nbsp;', 'ws link in chapter');	-- adds bare_url_missing_title error if appropriate; space char to move icon away from chap text; TODO: better way to do this?&lt;br /&gt;
		chapter = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, chapter});				&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (trans_chapter) then&lt;br /&gt;
		trans_chapter = utilities.wrap_style ('trans-quoted-title', trans_chapter);&lt;br /&gt;
		if utilities.is_set (chapter) then&lt;br /&gt;
			chapter = chapter .. ' ' .. trans_chapter;&lt;br /&gt;
		else																	-- here when trans_chapter without chapter or script-chapter&lt;br /&gt;
			chapter = trans_chapter;&lt;br /&gt;
			chapter_source = trans_chapter_source:match ('trans%-?(.+)');		-- when no chapter, get matching name from trans-&amp;lt;param&amp;gt;&lt;br /&gt;
			utilities.set_message ('err_trans_missing_title', {chapter_source});&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return chapter;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[----------------&amp;lt; H A S _ I N V I S I B L E _ C H A R S &amp;gt;-------------------&lt;br /&gt;
&lt;br /&gt;
This function searches a parameter's value for non-printable or invisible characters.&lt;br /&gt;
The search stops at the first match.&lt;br /&gt;
&lt;br /&gt;
This function will detect the visible replacement character when it is part of the Wikisource.&lt;br /&gt;
&lt;br /&gt;
Detects but ignores nowiki and math stripmarkers.  Also detects other named stripmarkers&lt;br /&gt;
(gallery, math, pre, ref) and identifies them with a slightly different error message.&lt;br /&gt;
See also coins_cleanup().&lt;br /&gt;
&lt;br /&gt;
Output of this function is an error message that identifies the character or the&lt;br /&gt;
Unicode group, or the stripmarker that was detected along with its position (or,&lt;br /&gt;
for multi-byte characters, the position of its first byte) in the parameter value.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function has_invisible_chars (param, v)&lt;br /&gt;
	local position = '';														-- position of invisible char or starting position of stripmarker&lt;br /&gt;
	local capture;																-- used by stripmarker detection to hold name of the stripmarker&lt;br /&gt;
	local stripmarker;															-- boolean set true when a stripmarker is found&lt;br /&gt;
&lt;br /&gt;
	capture = string.match (v, '[%w%p ]*');										-- test for values that are simple ASCII text and bypass other tests if true&lt;br /&gt;
	if capture == v then														-- if same there are no Unicode characters&lt;br /&gt;
		return;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for _, invisible_char in ipairs (cfg.invisible_chars) do&lt;br /&gt;
		local char_name = invisible_char[1];									-- the character or group name&lt;br /&gt;
		local pattern = invisible_char[2];										-- the pattern used to find it&lt;br /&gt;
		position, _, capture = mw.ustring.find (v, pattern);					-- see if the parameter value contains characters that match the pattern&lt;br /&gt;
		&lt;br /&gt;
		if position and (cfg.invisible_defs.zwj == capture) then				-- if we found a zero-width joiner character&lt;br /&gt;
			if mw.ustring.find (v, cfg.indic_script) then						-- it's ok if one of the Indic scripts&lt;br /&gt;
				position = nil;													-- unset position&lt;br /&gt;
			elseif cfg.emoji[mw.ustring.codepoint (v, position+1)] then			-- is zwj followed by a character listed in emoji{}?&lt;br /&gt;
				position = nil;													-- unset position&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if position then&lt;br /&gt;
			if 'nowiki' == capture or 'math' == capture or						-- nowiki and math stripmarkers (not an error condition)&lt;br /&gt;
				('templatestyles' == capture and utilities.in_array (param, {'id', 'quote'})) then	-- templatestyles stripmarker allowed in these parameters&lt;br /&gt;
					stripmarker = true;											-- set a flag&lt;br /&gt;
			elseif true == stripmarker and cfg.invisible_defs.del == capture then	-- because stripmakers begin and end with the delete char, assume that we've found one end of a stripmarker&lt;br /&gt;
				position = nil;													-- unset&lt;br /&gt;
			else&lt;br /&gt;
				local err_msg;&lt;br /&gt;
				if capture and not (cfg.invisible_defs.del == capture or cfg.invisible_defs.zwj == capture) then&lt;br /&gt;
					err_msg = capture .. ' ' .. char_name;&lt;br /&gt;
				else&lt;br /&gt;
					err_msg = char_name .. ' ' .. 'character';&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				utilities.set_message ('err_invisible_char', {err_msg, utilities.wrap_style ('parameter', param), position});	-- add error message&lt;br /&gt;
				return;															-- and done with this parameter&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------&amp;lt; A R G U M E N T _ W R A P P E R &amp;gt;----------------------&lt;br /&gt;
&lt;br /&gt;
Argument wrapper.  This function provides support for argument mapping defined&lt;br /&gt;
in the configuration file so that multiple names can be transparently aliased to&lt;br /&gt;
single internal variable.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function argument_wrapper ( args )&lt;br /&gt;
	local origin = {};&lt;br /&gt;
	&lt;br /&gt;
	return setmetatable({&lt;br /&gt;
		ORIGIN = function ( self, k )&lt;br /&gt;
			local dummy = self[k];												-- force the variable to be loaded.&lt;br /&gt;
			return origin[k];&lt;br /&gt;
		end&lt;br /&gt;
	},&lt;br /&gt;
	{&lt;br /&gt;
		__index = function ( tbl, k )&lt;br /&gt;
			if origin[k] ~= nil then&lt;br /&gt;
				return nil;&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			local args, list, v = args, cfg.aliases[k];&lt;br /&gt;
			&lt;br /&gt;
			if type( list ) == 'table' then&lt;br /&gt;
				v, origin[k] = utilities.select_one ( args, list, 'err_redundant_parameters' );&lt;br /&gt;
				if origin[k] == nil then&lt;br /&gt;
					origin[k] = '';												-- Empty string, not nil&lt;br /&gt;
				end&lt;br /&gt;
			elseif list ~= nil then&lt;br /&gt;
				v, origin[k] = args[list], list;&lt;br /&gt;
			else&lt;br /&gt;
				-- maybe let through instead of raising an error?&lt;br /&gt;
				-- v, origin[k] = args[k], k;&lt;br /&gt;
				error( cfg.messages['unknown_argument_map'] .. ': ' .. k);&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			-- Empty strings, not nil;&lt;br /&gt;
			if v == nil then&lt;br /&gt;
				v = '';&lt;br /&gt;
				origin[k] = '';&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			tbl = rawset( tbl, k, v );&lt;br /&gt;
			return v;&lt;br /&gt;
		end,&lt;br /&gt;
	});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; N O W R A P _ D A T E &amp;gt;-------------------------&lt;br /&gt;
&lt;br /&gt;
When date is YYYY-MM-DD format wrap in nowrap span: &amp;lt;span ...&amp;gt;YYYY-MM-DD&amp;lt;/span&amp;gt;.&lt;br /&gt;
When date is DD MMMM YYYY or is MMMM DD, YYYY then wrap in nowrap span:&lt;br /&gt;
&amp;lt;span ...&amp;gt;DD MMMM&amp;lt;/span&amp;gt; YYYY or &amp;lt;span ...&amp;gt;MMMM DD,&amp;lt;/span&amp;gt; YYYY&lt;br /&gt;
&lt;br /&gt;
DOES NOT yet support MMMM YYYY or any of the date ranges.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function nowrap_date (date)&lt;br /&gt;
	local cap = '';&lt;br /&gt;
	local cap2 = '';&lt;br /&gt;
&lt;br /&gt;
	if date:match(&amp;quot;^%d%d%d%d%-%d%d%-%d%d$&amp;quot;) then&lt;br /&gt;
		date = utilities.substitute (cfg.presentation['nowrap1'], date);&lt;br /&gt;
	&lt;br /&gt;
	elseif date:match(&amp;quot;^%a+%s*%d%d?,%s+%d%d%d%d$&amp;quot;) or date:match (&amp;quot;^%d%d?%s*%a+%s+%d%d%d%d$&amp;quot;) then&lt;br /&gt;
		cap, cap2 = string.match (date, &amp;quot;^(.*)%s+(%d%d%d%d)$&amp;quot;);&lt;br /&gt;
		date = utilities.substitute (cfg.presentation['nowrap2'], {cap, cap2});&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return date;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E T _ T I T L E T Y P E &amp;gt;---------------------&lt;br /&gt;
&lt;br /&gt;
This function sets default title types (equivalent to the citation including&lt;br /&gt;
|type=&amp;lt;default value&amp;gt;) for those templates that have defaults. Also handles the&lt;br /&gt;
special case where it is desirable to omit the title type from the rendered citation&lt;br /&gt;
(|type=none).&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function set_titletype (cite_class, title_type)&lt;br /&gt;
	if utilities.is_set (title_type) then&lt;br /&gt;
		if 'none' == cfg.keywords_xlate[title_type] then&lt;br /&gt;
			title_type = '';													-- if |type=none then type parameter not displayed&lt;br /&gt;
		end&lt;br /&gt;
		return title_type;														-- if |type= has been set to any other value use that value&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return cfg.title_types [cite_class] or '';									-- set template's default title type; else empty string for concatenation&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S A F E _ J O I N &amp;gt;-----------------------------&lt;br /&gt;
&lt;br /&gt;
Joins a sequence of strings together while checking for duplicate separation characters.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function safe_join( tbl, duplicate_char )&lt;br /&gt;
	local f = {};																-- create a function table appropriate to type of 'duplicate character'&lt;br /&gt;
		if 1 == #duplicate_char then											-- for single byte ASCII characters use the string library functions&lt;br /&gt;
			f.gsub = string.gsub&lt;br /&gt;
			f.match = string.match&lt;br /&gt;
			f.sub = string.sub&lt;br /&gt;
		else																	-- for multi-byte characters use the ustring library functions&lt;br /&gt;
			f.gsub = mw.ustring.gsub&lt;br /&gt;
			f.match = mw.ustring.match&lt;br /&gt;
			f.sub = mw.ustring.sub&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	local str = '';																-- the output string&lt;br /&gt;
	local comp = '';															-- what does 'comp' mean?&lt;br /&gt;
	local end_chr = '';&lt;br /&gt;
	local trim;&lt;br /&gt;
	for _, value in ipairs( tbl ) do&lt;br /&gt;
		if value == nil then value = ''; end&lt;br /&gt;
		&lt;br /&gt;
		if str == '' then														-- if output string is empty&lt;br /&gt;
			str = value;														-- assign value to it (first time through the loop)&lt;br /&gt;
		elseif value ~= '' then&lt;br /&gt;
			if value:sub(1, 1) == '&amp;lt;' then										-- special case of values enclosed in spans and other markup.&lt;br /&gt;
				comp = value:gsub( &amp;quot;%b&amp;lt;&amp;gt;&amp;quot;, &amp;quot;&amp;quot; );								-- remove HTML markup (&amp;lt;span&amp;gt;string&amp;lt;/span&amp;gt; -&amp;gt; string)&lt;br /&gt;
			else&lt;br /&gt;
				comp = value;&lt;br /&gt;
			end&lt;br /&gt;
																				-- typically duplicate_char is sepc&lt;br /&gt;
			if f.sub(comp, 1, 1) == duplicate_char then							-- is first character same as duplicate_char? why test first character?&lt;br /&gt;
																				--   Because individual string segments often (always?) begin with terminal punct for the&lt;br /&gt;
																				--   preceding segment: 'First element' .. 'sepc next element' .. etc.?&lt;br /&gt;
				trim = false;&lt;br /&gt;
				end_chr = f.sub(str, -1, -1);									-- get the last character of the output string&lt;br /&gt;
				-- str = str .. &amp;quot;&amp;lt;HERE(enchr=&amp;quot; .. end_chr .. &amp;quot;)&amp;quot;				-- debug stuff?&lt;br /&gt;
				if end_chr == duplicate_char then								-- if same as separator&lt;br /&gt;
					str = f.sub(str, 1, -2);									-- remove it&lt;br /&gt;
				elseif end_chr == &amp;quot;'&amp;quot; then										-- if it might be wiki-markup&lt;br /&gt;
					if f.sub(str, -3, -1) == duplicate_char .. &amp;quot;''&amp;quot; then		-- if last three chars of str are sepc'' &lt;br /&gt;
						str = f.sub(str, 1, -4) .. &amp;quot;''&amp;quot;;						-- remove them and add back ''&lt;br /&gt;
					elseif  f.sub(str, -5, -1) == duplicate_char .. &amp;quot;]]''&amp;quot; then	-- if last five chars of str are sepc]]'' &lt;br /&gt;
						trim = true;											-- why? why do this and next differently from previous?&lt;br /&gt;
					elseif f.sub(str, -4, -1) == duplicate_char .. &amp;quot;]''&amp;quot; then	-- if last four chars of str are sepc]'' &lt;br /&gt;
						trim = true;											-- same question&lt;br /&gt;
					end&lt;br /&gt;
				elseif end_chr == &amp;quot;]&amp;quot; then										-- if it might be wiki-markup&lt;br /&gt;
					if f.sub(str, -3, -1) == duplicate_char .. &amp;quot;]]&amp;quot; then		-- if last three chars of str are sepc]] wikilink &lt;br /&gt;
						trim = true;&lt;br /&gt;
					elseif f.sub(str, -3, -1) == duplicate_char .. '&amp;quot;]' then	-- if last three chars of str are sepc&amp;quot;] quoted external link &lt;br /&gt;
						trim = true;&lt;br /&gt;
					elseif  f.sub(str, -2, -1) == duplicate_char .. &amp;quot;]&amp;quot; then	-- if last two chars of str are sepc] external link&lt;br /&gt;
						trim = true;&lt;br /&gt;
					elseif f.sub(str, -4, -1) == duplicate_char .. &amp;quot;'']&amp;quot; then	-- normal case when |url=something &amp;amp; |title=Title.&lt;br /&gt;
						trim = true;&lt;br /&gt;
					end&lt;br /&gt;
				elseif end_chr == &amp;quot; &amp;quot; then										-- if last char of output string is a space&lt;br /&gt;
					if f.sub(str, -2, -1) == duplicate_char .. &amp;quot; &amp;quot; then			-- if last two chars of str are &amp;lt;sepc&amp;gt;&amp;lt;space&amp;gt;&lt;br /&gt;
						str = f.sub(str, 1, -3);								-- remove them both&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				if trim then&lt;br /&gt;
					if value ~= comp then 										-- value does not equal comp when value contains HTML markup&lt;br /&gt;
						local dup2 = duplicate_char;&lt;br /&gt;
						if f.match(dup2, &amp;quot;%A&amp;quot; ) then dup2 = &amp;quot;%&amp;quot; .. dup2; end	-- if duplicate_char not a letter then escape it&lt;br /&gt;
						&lt;br /&gt;
						value = f.gsub(value, &amp;quot;(%b&amp;lt;&amp;gt;)&amp;quot; .. dup2, &amp;quot;%1&amp;quot;, 1 )		-- remove duplicate_char if it follows HTML markup&lt;br /&gt;
					else&lt;br /&gt;
						value = f.sub(value, 2, -1 );							-- remove duplicate_char when it is first character&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			str = str .. value; 												-- add it to the output string&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return str;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ S U F F I X &amp;gt;-----------------------------&lt;br /&gt;
&lt;br /&gt;
returns true if suffix is properly formed Jr, Sr, or ordinal in the range 1–9.&lt;br /&gt;
Puncutation not allowed.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_suffix (suffix)&lt;br /&gt;
	if utilities.in_array (suffix, {'Jr', 'Sr', 'Jnr', 'Snr', '1st', '2nd', '3rd'}) or suffix:match ('^%dth$') then&lt;br /&gt;
		return true;&lt;br /&gt;
	end&lt;br /&gt;
	return false;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------&amp;lt; I S _ G O O D _ V A N C _ N A M E &amp;gt;-------------------&lt;br /&gt;
&lt;br /&gt;
For Vancouver style, author/editor names are supposed to be rendered in Latin&lt;br /&gt;
(read ASCII) characters.  When a name uses characters that contain diacritical&lt;br /&gt;
marks, those characters are to be converted to the corresponding Latin&lt;br /&gt;
character. When a name is written using a non-Latin alphabet or logogram, that&lt;br /&gt;
name is to be transliterated into Latin characters. The module doesn't do this&lt;br /&gt;
so editors may/must.&lt;br /&gt;
&lt;br /&gt;
This test allows |first= and |last= names to contain any of the letters defined&lt;br /&gt;
in the four Unicode Latin character sets&lt;br /&gt;
	[http://www.unicode.org/charts/PDF/U0000.pdf C0 Controls and Basic Latin] 0041–005A, 0061–007A&lt;br /&gt;
	[http://www.unicode.org/charts/PDF/U0080.pdf C1 Controls and Latin-1 Supplement] 00C0–00D6, 00D8–00F6, 00F8–00FF&lt;br /&gt;
	[http://www.unicode.org/charts/PDF/U0100.pdf Latin Extended-A] 0100–017F&lt;br /&gt;
	[http://www.unicode.org/charts/PDF/U0180.pdf Latin Extended-B] 0180–01BF, 01C4–024F&lt;br /&gt;
&lt;br /&gt;
|lastn= also allowed to contain hyphens, spaces, and apostrophes.&lt;br /&gt;
	(http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)&lt;br /&gt;
|firstn= also allowed to contain hyphens, spaces, apostrophes, and periods&lt;br /&gt;
&lt;br /&gt;
This original test:&lt;br /&gt;
	if nil == mw.ustring.find (last, &amp;quot;^[A-Za-zÀ-ÖØ-öø-ƿǄ-ɏ%-%s%']*$&amp;quot;)&lt;br /&gt;
	or nil == mw.ustring.find (first, &amp;quot;^[A-Za-zÀ-ÖØ-öø-ƿǄ-ɏ%-%s%'%.]+[2-6%a]*$&amp;quot;) then&lt;br /&gt;
was written outside of the code editor and pasted here because the code editor&lt;br /&gt;
gets confused between character insertion point and cursor position. The test has&lt;br /&gt;
been rewritten to use decimal character escape sequence for the individual bytes&lt;br /&gt;
of the Unicode characters so that it is not necessary to use an external editor&lt;br /&gt;
to maintain this code.&lt;br /&gt;
&lt;br /&gt;
	\195\128-\195\150 – À-Ö (U+00C0–U+00D6 – C0 controls)&lt;br /&gt;
	\195\152-\195\182 – Ø-ö (U+00D8-U+00F6 – C0 controls)&lt;br /&gt;
	\195\184-\198\191 – ø-ƿ (U+00F8-U+01BF – C0 controls, Latin extended A &amp;amp; B)&lt;br /&gt;
	\199\132-\201\143 – Ǆ-ɏ (U+01C4-U+024F – Latin extended B)&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_good_vanc_name (last, first, suffix, position)&lt;br /&gt;
	if not suffix then&lt;br /&gt;
		if first:find ('[,%s]') then											-- when there is a space or comma, might be first name/initials + generational suffix&lt;br /&gt;
			first = first:match ('(.-)[,%s]+');									-- get name/initials&lt;br /&gt;
			suffix = first:match ('[,%s]+(.+)$');								-- get generational suffix&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if utilities.is_set (suffix) then&lt;br /&gt;
		if not is_suffix (suffix) then&lt;br /&gt;
			add_vanc_error (cfg.err_msg_supl.suffix, position);&lt;br /&gt;
			return false;														-- not a name with an appropriate suffix&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if nil == mw.ustring.find (last, &amp;quot;^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%']*$&amp;quot;) or&lt;br /&gt;
		nil == mw.ustring.find (first, &amp;quot;^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%'%.]*$&amp;quot;) then&lt;br /&gt;
			add_vanc_error (cfg.err_msg_supl['non-Latin char'], position);&lt;br /&gt;
			return false;														-- not a string of Latin characters; Vancouver requires Romanization&lt;br /&gt;
	end;&lt;br /&gt;
	return true;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; R E D U C E _ T O _ I N I T I A L S &amp;gt;------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Attempts to convert names to initials in support of |name-list-style=vanc.  &lt;br /&gt;
&lt;br /&gt;
Names in |firstn= may be separated by spaces or hyphens, or for initials, a period.&lt;br /&gt;
See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35062/.&lt;br /&gt;
&lt;br /&gt;
Vancouver style requires family rank designations (Jr, II, III, etc.) to be rendered&lt;br /&gt;
as Jr, 2nd, 3rd, etc.  See http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35085/.&lt;br /&gt;
This code only accepts and understands generational suffix in the Vancouver format&lt;br /&gt;
because Roman numerals look like, and can be mistaken for, initials.&lt;br /&gt;
&lt;br /&gt;
This function uses ustring functions because firstname initials may be any of the&lt;br /&gt;
Unicode Latin characters accepted by is_good_vanc_name ().&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function reduce_to_initials(first, position)&lt;br /&gt;
	local name, suffix = mw.ustring.match(first, &amp;quot;^(%u+) ([%dJS][%drndth]+)$&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
	if not name then															-- if not initials and a suffix&lt;br /&gt;
		name = mw.ustring.match(first, &amp;quot;^(%u+)$&amp;quot;);								-- is it just initials?&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if name then																-- if first is initials with or without suffix&lt;br /&gt;
		if 3 &amp;gt; mw.ustring.len (name) then										-- if one or two initials&lt;br /&gt;
			if suffix then														-- if there is a suffix&lt;br /&gt;
				if is_suffix (suffix) then										-- is it legitimate?&lt;br /&gt;
					return first;												-- one or two initials and a valid suffix so nothing to do&lt;br /&gt;
				else&lt;br /&gt;
					add_vanc_error (cfg.err_msg_supl.suffix, position);			-- one or two initials with invalid suffix so error message&lt;br /&gt;
					return first;												-- and return first unmolested&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				return first;													-- one or two initials without suffix; nothing to do&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end																			-- if here then name has 3 or more uppercase letters so treat them as a word&lt;br /&gt;
&lt;br /&gt;
	local initials, names = {}, {};												-- tables to hold name parts and initials&lt;br /&gt;
	local i = 1;																-- counter for number of initials&lt;br /&gt;
&lt;br /&gt;
	names = mw.text.split (first, '[%s,]+');									-- split into a table of names and possible suffix&lt;br /&gt;
&lt;br /&gt;
	while names[i] do															-- loop through the table&lt;br /&gt;
		if 1 &amp;lt; i and names[i]:match ('[%dJS][%drndth]+%.?$') then				-- if not the first name, and looks like a suffix (may have trailing dot)&lt;br /&gt;
			names[i] = names[i]:gsub ('%.', '');								-- remove terminal dot if present&lt;br /&gt;
			if is_suffix (names[i]) then										-- if a legitimate suffix&lt;br /&gt;
				table.insert (initials, ' ' .. names[i]);						-- add a separator space, insert at end of initials table&lt;br /&gt;
				break;															-- and done because suffix must fall at the end of a name&lt;br /&gt;
			end																	-- no error message if not a suffix; possibly because of Romanization&lt;br /&gt;
		end&lt;br /&gt;
		if 3 &amp;gt; i then&lt;br /&gt;
			table.insert (initials, mw.ustring.sub(names[i], 1, 1));			-- insert the initial at end of initials table&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1;																-- bump the counter&lt;br /&gt;
	end&lt;br /&gt;
			&lt;br /&gt;
	return table.concat(initials)												-- Vancouver format does not include spaces.&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; L I S T _ P E O P L E &amp;gt;--------------------------&lt;br /&gt;
&lt;br /&gt;
Formats a list of people (authors, contributors, editors, interviewers, translators) &lt;br /&gt;
&lt;br /&gt;
names in the list will be linked when&lt;br /&gt;
	|&amp;lt;name&amp;gt;-link= has a value&lt;br /&gt;
	|&amp;lt;name&amp;gt;-mask- does NOT have a value; masked names are presumed to have been&lt;br /&gt;
		rendered previously so should have been linked there&lt;br /&gt;
&lt;br /&gt;
when |&amp;lt;name&amp;gt;-mask=0, the associated name is not rendered&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function list_people (control, people, etal)&lt;br /&gt;
	local sep;&lt;br /&gt;
	local namesep;&lt;br /&gt;
	local format = control.format;&lt;br /&gt;
	local maximum = control.maximum;&lt;br /&gt;
	local name_list = {};&lt;br /&gt;
&lt;br /&gt;
	if 'vanc' == format then													-- Vancouver-like name styling?&lt;br /&gt;
		sep = cfg.presentation['sep_nl_vanc'];									-- name-list separator between names is a comma&lt;br /&gt;
		namesep = cfg.presentation['sep_name_vanc'];							-- last/first separator is a space&lt;br /&gt;
	else&lt;br /&gt;
		sep = cfg.presentation['sep_nl'];										-- name-list separator between names is a semicolon&lt;br /&gt;
		namesep = cfg.presentation['sep_name'];									-- last/first separator is &amp;lt;comma&amp;gt;&amp;lt;space&amp;gt;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if sep:sub (-1, -1) ~= &amp;quot; &amp;quot; then sep = sep .. &amp;quot; &amp;quot; end&lt;br /&gt;
	if utilities.is_set (maximum) and maximum &amp;lt; 1 then return &amp;quot;&amp;quot;, 0; end		-- returned 0 is for EditorCount; not used for other names&lt;br /&gt;
	&lt;br /&gt;
	for i, person in ipairs (people) do&lt;br /&gt;
		if utilities.is_set (person.last) then&lt;br /&gt;
			local mask = person.mask;&lt;br /&gt;
			local one;&lt;br /&gt;
			local sep_one = sep;&lt;br /&gt;
&lt;br /&gt;
			if utilities.is_set (maximum) and i &amp;gt; maximum then&lt;br /&gt;
				etal = true;&lt;br /&gt;
				break;&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
			if mask then&lt;br /&gt;
				local n = tonumber (mask);										-- convert to a number if it can be converted; nil else&lt;br /&gt;
				if n then&lt;br /&gt;
					one = 0 ~= n and string.rep(&amp;quot;&amp;amp;mdash;&amp;quot;, n) or nil;			-- make a string of (n &amp;gt; 0) mdashes, nil else, to replace name&lt;br /&gt;
					person.link = nil;											-- don't create link to name if name is replaces with mdash string or has been set nil&lt;br /&gt;
				else&lt;br /&gt;
					one = mask;													-- replace name with mask text (must include name-list separator)&lt;br /&gt;
					sep_one = &amp;quot; &amp;quot;;												-- modify name-list separator&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				one = person.last;												-- get surname&lt;br /&gt;
				local first = person.first										-- get given name&lt;br /&gt;
				if utilities.is_set (first) then&lt;br /&gt;
					if (&amp;quot;vanc&amp;quot; == format) then									-- if Vancouver format&lt;br /&gt;
						one = one:gsub ('%.', '');								-- remove periods from surnames (http://www.ncbi.nlm.nih.gov/books/NBK7271/box/A35029/)&lt;br /&gt;
						if not person.corporate and is_good_vanc_name (one, first, nil, i) then		-- and name is all Latin characters; corporate authors not tested&lt;br /&gt;
							first = reduce_to_initials (first, i);				-- attempt to convert first name(s) to initials&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
					one = one .. namesep .. first;&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if utilities.is_set (person.link) then&lt;br /&gt;
				one = utilities.make_wikilink (person.link, one);				-- link author/editor&lt;br /&gt;
			end&lt;br /&gt;
			if one then															-- if &amp;lt;one&amp;gt; has a value (name, mdash replacement, or mask text replacement)&lt;br /&gt;
				table.insert (name_list, one);									-- add it to the list of names&lt;br /&gt;
				table.insert (name_list, sep_one);								-- add the proper name-list separator&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local count = #name_list / 2;												-- (number of names + number of separators) divided by 2&lt;br /&gt;
	if 0 &amp;lt; count then &lt;br /&gt;
		if 1 &amp;lt; count and not etal then&lt;br /&gt;
			if 'amp' == format then&lt;br /&gt;
				name_list[#name_list-2] = &amp;quot; &amp;amp; &amp;quot;;								-- replace last separator with ampersand text&lt;br /&gt;
			elseif 'and' == format then&lt;br /&gt;
				if 2 == count then&lt;br /&gt;
					name_list[#name_list-2] = cfg.presentation.sep_nl_and;		-- replace last separator with 'and' text&lt;br /&gt;
				else&lt;br /&gt;
					name_list[#name_list-2] = cfg.presentation.sep_nl_end;		-- replace last separator with '(sep) and' text&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		name_list[#name_list] = nil;											-- erase the last separator&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local result = table.concat (name_list);									-- construct list&lt;br /&gt;
	if etal and utilities.is_set (result) then									-- etal may be set by |display-authors=etal but we might not have a last-first list&lt;br /&gt;
		result = result .. sep .. ' ' .. cfg.messages['et al'];					-- we've got a last-first list and etal so add et al.&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return result, count;														-- return name-list string and count of number of names (count used for editor names only)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------&amp;lt; M A K E _ C I T E R E F _ I D &amp;gt;-----------------------&lt;br /&gt;
&lt;br /&gt;
Generates a CITEREF anchor ID if we have at least one name or a date.  Otherwise&lt;br /&gt;
returns an empty string.&lt;br /&gt;
&lt;br /&gt;
namelist is one of the contributor-, author-, or editor-name lists chosen in that&lt;br /&gt;
order.  year is Year or anchor_year.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function make_citeref_id (namelist, year)&lt;br /&gt;
	local names={};							-- a table for the one to four names and year&lt;br /&gt;
	for i,v in ipairs (namelist) do			-- loop through the list and take up to the first four last names&lt;br /&gt;
		names[i] = v.last&lt;br /&gt;
		if i == 4 then break end			-- if four then done&lt;br /&gt;
	end&lt;br /&gt;
	table.insert (names, year);				-- add the year at the end&lt;br /&gt;
	local id = table.concat(names);			-- concatenate names and year for CITEREF id&lt;br /&gt;
	if utilities.is_set (id) then			-- if concatenation is not an empty string&lt;br /&gt;
		return &amp;quot;CITEREF&amp;quot; .. id;				-- add the CITEREF portion&lt;br /&gt;
	else&lt;br /&gt;
		return '';							-- return an empty string; no reason to include CITEREF id in this citation&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C I T E _ C L A S S _A T T R I B U T E _M A K E &amp;gt;------------------------------&lt;br /&gt;
&lt;br /&gt;
construct &amp;lt;cite&amp;gt; tag class attribute for this citation.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;cite_class&amp;gt; – config.CitationClass from calling template&lt;br /&gt;
&amp;lt;mode&amp;gt; – value from |mode= parameter&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function cite_class_attribute_make (cite_class, mode)&lt;br /&gt;
	local class_t = {};&lt;br /&gt;
	table.insert (class_t, 'citation');											-- required for blue highlight&lt;br /&gt;
	if 'citation' ~= cite_class then&lt;br /&gt;
		table.insert (class_t, cite_class);										-- identify this template for user css&lt;br /&gt;
		table.insert (class_t, utilities.is_set (mode) and mode or 'cs1');		-- identify the citation style for user css or javascript&lt;br /&gt;
	else&lt;br /&gt;
		table.insert (class_t, utilities.is_set (mode) and mode or 'cs2');		-- identify the citation style for user css or javascript&lt;br /&gt;
	end&lt;br /&gt;
	for _, prop_key in ipairs (z.prop_keys_t) do&lt;br /&gt;
		table.insert (class_t, prop_key);										-- identify various properties for user css or javascript&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat (class_t, ' ');											-- make a big string and done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[---------------------&amp;lt; N A M E _ H A S _ E T A L &amp;gt;--------------------------&lt;br /&gt;
&lt;br /&gt;
Evaluates the content of name parameters (author, editor, etc.) for variations on&lt;br /&gt;
the theme of et al.  If found, the et al. is removed, a flag is set to true and&lt;br /&gt;
the function returns the modified name and the flag.&lt;br /&gt;
&lt;br /&gt;
This function never sets the flag to false but returns its previous state because&lt;br /&gt;
it may have been set by previous passes through this function or by the associated&lt;br /&gt;
|display-&amp;lt;names&amp;gt;=etal parameter&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function name_has_etal (name, etal, nocat, param)&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (name) then												-- name can be nil in which case just return&lt;br /&gt;
		local patterns = cfg.et_al_patterns; 									-- get patterns from configuration&lt;br /&gt;
		&lt;br /&gt;
		for _, pattern in ipairs (patterns) do									-- loop through all of the patterns&lt;br /&gt;
			if name:match (pattern) then										-- if this 'et al' pattern is found in name&lt;br /&gt;
				name = name:gsub (pattern, '');									-- remove the offending text&lt;br /&gt;
				etal = true;													-- set flag (may have been set previously here or by |display-&amp;lt;names&amp;gt;=etal)&lt;br /&gt;
				if not nocat then												-- no categorization for |vauthors=&lt;br /&gt;
					utilities.set_message ('err_etal', {param});				-- and set an error if not added&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return name, etal;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[---------------------&amp;lt; N A M E _ I S _ N U M E R I C &amp;gt;----------------------&lt;br /&gt;
&lt;br /&gt;
Add maint cat when name parameter value does not contain letters.  Does not catch&lt;br /&gt;
mixed alphanumeric names so |last=A. Green (1922-1987) does not get caught in the&lt;br /&gt;
current version of this test but |first=(1888) is caught.&lt;br /&gt;
&lt;br /&gt;
returns nothing&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function name_is_numeric (name, list_name)&lt;br /&gt;
	if utilities.is_set (name) then&lt;br /&gt;
		if mw.ustring.match (name, '^[%A]+$') then								-- when name does not contain any letters&lt;br /&gt;
			utilities.set_message ('maint_numeric_names', cfg.special_case_translation [list_name]);	-- add a maint cat for this template&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-----------------&amp;lt; N A M E _ H A S _ M U L T _ N A M E S &amp;gt;------------------&lt;br /&gt;
&lt;br /&gt;
Evaluates the content of last/surname (authors etc.) parameters for multiple names.&lt;br /&gt;
Multiple names are indicated if there is more than one comma or any &amp;quot;unescaped&amp;quot;&lt;br /&gt;
semicolons. Escaped semicolons are ones used as part of selected HTML entities.&lt;br /&gt;
If the condition is met, the function adds the multiple name maintenance category.&lt;br /&gt;
&lt;br /&gt;
returns nothing&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function name_has_mult_names (name, list_name)&lt;br /&gt;
	local _, commas, semicolons, nbsps;&lt;br /&gt;
	if utilities.is_set (name) then&lt;br /&gt;
		_, commas = name:gsub (',', '');										-- count the number of commas&lt;br /&gt;
		_, semicolons = name:gsub (';', '');									-- count the number of semicolons&lt;br /&gt;
		-- nbsps probably should be its own separate count rather than merged in&lt;br /&gt;
		-- some way with semicolons because Lua patterns do not support the&lt;br /&gt;
		-- grouping operator that regex does, which means there is no way to add&lt;br /&gt;
		-- more entities to escape except by adding more counts with the new&lt;br /&gt;
		-- entities&lt;br /&gt;
		_, nbsps = name:gsub ('&amp;amp;nbsp;','');										-- count nbsps&lt;br /&gt;
		&lt;br /&gt;
		-- There is exactly 1 semicolon per &amp;amp;nbsp; entity, so subtract nbsps&lt;br /&gt;
		-- from semicolons to 'escape' them. If additional entities are added,&lt;br /&gt;
		-- they also can be subtracted.&lt;br /&gt;
		if 1 &amp;lt; commas or 0 &amp;lt; (semicolons - nbsps) then&lt;br /&gt;
			utilities.set_message ('maint_mult_names', cfg.special_case_translation [list_name]);	-- add a maint message&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ G E N E R I C &amp;gt;----------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Compares values assigned to various parameter according to the string provided as &amp;lt;item&amp;gt; in the function call:&lt;br /&gt;
	'generic_names': |last=, |first=, |editor-last=, etc value against list of known generic name patterns&lt;br /&gt;
	'generic_titles': |title=&lt;br /&gt;
Returns true when pattern matches; nil else&lt;br /&gt;
&lt;br /&gt;
The k/v pairs in cfg.special_case_translation[item] each contain two tables, one for English and one for another&lt;br /&gt;
'local' language.Each of those tables contain another table that holds the string or pattern (whole or fragment)&lt;br /&gt;
in index [1]. index [2] is a Boolean that tells string.find() or mw.ustring.find() to do plain-text search (true)&lt;br /&gt;
or a pattern search (false).  The intent of all this complexity is to make these searches as fast as possible so&lt;br /&gt;
that we don't run out of processing time on very large articles.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_generic (item, value)&lt;br /&gt;
	local test_val;&lt;br /&gt;
&lt;br /&gt;
	for _, generic_value in ipairs (cfg.special_case_translation[item]) do		-- spin through the list of known generic value fragments&lt;br /&gt;
		test_val = generic_value['en'][2] and value:lower() or value;			-- when set to 'true', plaintext search using lowercase value&lt;br /&gt;
&lt;br /&gt;
		if test_val:find (generic_value['en'][1], 1, generic_value['en'][2]) then&lt;br /&gt;
			return true;														-- found English generic value so done&lt;br /&gt;
&lt;br /&gt;
		elseif generic_value['local'] then										-- to keep work load down, generic_&amp;lt;value&amp;gt;['local'] should be nil except when there is a local version of the generic value&lt;br /&gt;
			test_val = generic_value['local'][2] and mw.ustring.lower(value) or value;	-- when set to 'true', plaintext search using lowercase value&lt;br /&gt;
&lt;br /&gt;
			if mw.ustring.find (test_val, generic_value['local'][1], 1, generic_value['local'][2]) then	-- mw.ustring() because might not be Latin script&lt;br /&gt;
				return true;													-- found local generic value so done&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; N A M E _ I S _ G E N E R I C &amp;gt;------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
calls is_generic() to determine if &amp;lt;name&amp;gt; is a 'generic name' listed in cfg.generic_names; &amp;lt;name_alias&amp;gt; is the&lt;br /&gt;
parameter name used in error messaging&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function name_is_generic (name, name_alias)&lt;br /&gt;
	if not added_generic_name_errs  and is_generic ('generic_names', name) then&lt;br /&gt;
		utilities.set_message ('err_generic_name', name_alias);					-- set an error message&lt;br /&gt;
		added_generic_name_errs = true;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; N A M E _ C H E C K S &amp;gt;--------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
This function calls various name checking functions used to validate the content of the various name-holding parameters.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function name_checks (last, first, list_name, last_alias, first_alias)&lt;br /&gt;
	local accept_name;&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (last) then&lt;br /&gt;
		last, accept_name = utilities.has_accept_as_written (last);				-- remove accept-this-as-written markup when it wraps all of &amp;lt;last&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		if not accept_name then													-- &amp;lt;last&amp;gt; not wrapped in accept-as-written markup&lt;br /&gt;
			name_has_mult_names (last, list_name);								-- check for multiple names in the parameter (last only)&lt;br /&gt;
			name_is_numeric (last, list_name);									-- check for names that are composed of digits and punctuation&lt;br /&gt;
			name_is_generic (last, last_alias);									-- check for names found in the generic names list&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (first) then&lt;br /&gt;
		first, accept_name = utilities.has_accept_as_written (first);			-- remove accept-this-as-written markup when it wraps all of &amp;lt;first&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		if not accept_name then													-- &amp;lt;first&amp;gt; not wrapped in accept-as-written markup&lt;br /&gt;
			name_is_numeric (first, list_name);									-- check for names that are composed of digits and punctuation&lt;br /&gt;
			name_is_generic (first, first_alias);								-- check for names found in the generic names list&lt;br /&gt;
		end&lt;br /&gt;
		local wl_type, D = utilities.is_wikilink (first);&lt;br /&gt;
		if 0 ~= wl_type then&lt;br /&gt;
			first = D;&lt;br /&gt;
			utilities.set_message ('err_bad_paramlink', first_alias);&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return last, first;															-- done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[----------------------&amp;lt; E X T R A C T _ N A M E S &amp;gt;-------------------------&lt;br /&gt;
&lt;br /&gt;
Gets name list from the input arguments&lt;br /&gt;
&lt;br /&gt;
Searches through args in sequential order to find |lastn= and |firstn= parameters&lt;br /&gt;
(or their aliases), and their matching link and mask parameters. Stops searching&lt;br /&gt;
when both |lastn= and |firstn= are not found in args after two sequential attempts:&lt;br /&gt;
found |last1=, |last2=, and |last3= but doesn't find |last4= and |last5= then the&lt;br /&gt;
search is done.&lt;br /&gt;
&lt;br /&gt;
This function emits an error message when there is a |firstn= without a matching&lt;br /&gt;
|lastn=.  When there are 'holes' in the list of last names, |last1= and |last3=&lt;br /&gt;
are present but |last2= is missing, an error message is emitted. |lastn= is not&lt;br /&gt;
required to have a matching |firstn=.&lt;br /&gt;
&lt;br /&gt;
When an author or editor parameter contains some form of 'et al.', the 'et al.'&lt;br /&gt;
is stripped from the parameter and a flag (etal) returned that will cause list_people()&lt;br /&gt;
to add the static 'et al.' text from Module:Citation/CS1/Configuration.  This keeps&lt;br /&gt;
'et al.' out of the template's metadata.  When this occurs, an error is emitted.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function extract_names(args, list_name)&lt;br /&gt;
	local names = {};															-- table of names&lt;br /&gt;
	local last;																	-- individual name components&lt;br /&gt;
	local first;&lt;br /&gt;
	local link;&lt;br /&gt;
	local mask;&lt;br /&gt;
	local i = 1;																-- loop counter/indexer&lt;br /&gt;
	local n = 1;																-- output table indexer&lt;br /&gt;
	local count = 0;															-- used to count the number of times we haven't found a |last= (or alias for authors, |editor-last or alias for editors)&lt;br /&gt;
	local etal = false;															-- return value set to true when we find some form of et al. in an author parameter&lt;br /&gt;
&lt;br /&gt;
	local last_alias, first_alias, link_alias;									-- selected parameter aliases used in error messaging&lt;br /&gt;
	while true do&lt;br /&gt;
		last, last_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'err_redundant_parameters', i );		-- search through args for name components beginning at 1&lt;br /&gt;
		first, first_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'err_redundant_parameters', i );&lt;br /&gt;
		link, link_alias = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i );&lt;br /&gt;
		mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );&lt;br /&gt;
&lt;br /&gt;
		last, etal = name_has_etal (last, etal, false, last_alias);				-- find and remove variations on et al.&lt;br /&gt;
		first, etal = name_has_etal (first, etal, false, first_alias);			-- find and remove variations on et al.&lt;br /&gt;
		last, first = name_checks (last, first, list_name, last_alias, first_alias);						-- multiple names, extraneous annotation, etc. checks&lt;br /&gt;
&lt;br /&gt;
		if first and not last then												-- if there is a firstn without a matching lastn&lt;br /&gt;
			local alias = first_alias:find ('given', 1, true) and 'given' or 'first';	-- get first or given form of the alias&lt;br /&gt;
			utilities.set_message ('err_first_missing_last', {&lt;br /&gt;
				first_alias,													-- param name of alias missing its mate&lt;br /&gt;
				first_alias:gsub (alias, {['first'] = 'last', ['given'] = 'surname'}),	-- make param name appropriate to the alias form&lt;br /&gt;
				});																-- add this error message&lt;br /&gt;
		elseif not first and not last then										-- if both firstn and lastn aren't found, are we done?&lt;br /&gt;
			count = count + 1;													-- number of times we haven't found last and first&lt;br /&gt;
			if 2 &amp;lt;= count then													-- two missing names and we give up&lt;br /&gt;
				break;															-- normal exit or there is a two-name hole in the list; can't tell which&lt;br /&gt;
			end&lt;br /&gt;
		else																	-- we have last with or without a first&lt;br /&gt;
			local result;&lt;br /&gt;
			link = link_title_ok (link, link_alias, last, last_alias);			-- check for improper wiki-markup&lt;br /&gt;
&lt;br /&gt;
			if first then&lt;br /&gt;
				link = link_title_ok (link, link_alias, first, first_alias);	-- check for improper wiki-markup&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			names[n] = {last = last, first = first, link = link, mask = mask, corporate = false};	-- add this name to our names list (corporate for |vauthors= only)&lt;br /&gt;
			n = n + 1;															-- point to next location in the names table&lt;br /&gt;
			if 1 == count then													-- if the previous name was missing&lt;br /&gt;
				utilities.set_message ('err_missing_name', {list_name:match (&amp;quot;(%w+)List&amp;quot;):lower(), i - 1});	-- add this error message&lt;br /&gt;
			end&lt;br /&gt;
			count = 0;															-- reset the counter, we're looking for two consecutive missing names&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1;																-- point to next args location&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return names, etal;															-- all done, return our list of names and the etal flag&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; N A M E _ T A G _ G E T &amp;gt;------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
attempt to decode |language=&amp;lt;lang_param&amp;gt; and return language name and matching tag; nil else.&lt;br /&gt;
&lt;br /&gt;
This function looks for:&lt;br /&gt;
	&amp;lt;lang_param&amp;gt; as a tag in cfg.lang_code_remap{}&lt;br /&gt;
	&amp;lt;lang_param&amp;gt; as a name in cfg.lang_name_remap{}&lt;br /&gt;
	&lt;br /&gt;
	&amp;lt;lang_param&amp;gt; as a name in cfg.mw_languages_by_name_t&lt;br /&gt;
	&amp;lt;lang_param&amp;gt; as a tag in cfg.mw_languages_by_tag_t&lt;br /&gt;
when those fail, presume that &amp;lt;lang_param&amp;gt; is an IETF-like tag that MediaWiki does not recognize.  Strip all&lt;br /&gt;
script, region, variant, whatever subtags from &amp;lt;lang_param&amp;gt; to leave just a two or three character language tag&lt;br /&gt;
and look for the new &amp;lt;lang_param&amp;gt; in cfg.mw_languages_by_tag_t{}&lt;br /&gt;
&lt;br /&gt;
on success, return name and matching tag; on failure return nil&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function name_tag_get (lang_param)&lt;br /&gt;
	local lang_param_lc = mw.ustring.lower (lang_param);						-- use lowercase as an index into the various tables&lt;br /&gt;
	local name;&lt;br /&gt;
	local tag;&lt;br /&gt;
&lt;br /&gt;
	name = cfg.lang_code_remap[lang_param_lc];									-- assume &amp;lt;lang_param_lc&amp;gt; is a tag; attempt to get remapped language name &lt;br /&gt;
	if name then																-- when &amp;lt;name&amp;gt;, &amp;lt;lang_param&amp;gt; is a tag for a remapped language name&lt;br /&gt;
		return name, lang_param;												-- so return &amp;lt;name&amp;gt; from remap and &amp;lt;lang_param&amp;gt;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	tag = lang_param_lc:match ('^(%a%a%a?)%-.*');								-- still assuming that &amp;lt;lang_param_lc&amp;gt; is a tag; strip script, region, variant subtags&lt;br /&gt;
	name = cfg.lang_code_remap[tag];											-- attempt to get remapped language name with language subtag only&lt;br /&gt;
	if name then																-- when &amp;lt;name&amp;gt;, &amp;lt;tag&amp;gt; is a tag for a remapped language name&lt;br /&gt;
		return name, tag;														-- so return &amp;lt;name&amp;gt; from remap and &amp;lt;tag&amp;gt;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if cfg.lang_name_remap[lang_param_lc] then									-- not a tag, assume &amp;lt;lang_param_lc&amp;gt; is a name; attempt to get remapped language tag &lt;br /&gt;
		return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2];	-- for this &amp;lt;lang_param_lc&amp;gt;, return a (possibly) new name and appropriate tag&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	tag = cfg.mw_languages_by_name_t[lang_param_lc];							-- assume that &amp;lt;lang_param_lc&amp;gt; is a language name; attempt to get its matching tag&lt;br /&gt;
	&lt;br /&gt;
	if tag then&lt;br /&gt;
		return cfg.mw_languages_by_tag_t[tag], tag;								-- &amp;lt;lang_param_lc&amp;gt; is a name so return the name from the table and &amp;lt;tag&amp;gt;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	name = cfg.mw_languages_by_tag_t[lang_param_lc];							-- assume that &amp;lt;lang_param_lc&amp;gt; is a tag; attempt to get its matching language name&lt;br /&gt;
	&lt;br /&gt;
	if name then&lt;br /&gt;
		return name, lang_param;												-- &amp;lt;lang_param_lc&amp;gt; is a tag so return &amp;lt;name&amp;gt; and the tag&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	tag = lang_param_lc:match ('^(%a%a%a?)%-.*');								-- is &amp;lt;lang_param_lc&amp;gt; an IETF-like tag that MediaWiki doesn't recognize? &amp;lt;tag&amp;gt; gets the language subtag; nil else&lt;br /&gt;
&lt;br /&gt;
	if tag then&lt;br /&gt;
		name = cfg.mw_languages_by_tag_t[tag];									-- attempt to get a language name using the shortened &amp;lt;tag&amp;gt;&lt;br /&gt;
		if name then&lt;br /&gt;
			return name, tag;													-- &amp;lt;lang_param_lc&amp;gt; is an unrecognized IETF-like tag so return &amp;lt;name&amp;gt; and language subtag&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------&amp;lt; L A N G U A G E _ P A R A M E T E R &amp;gt;------------------&lt;br /&gt;
&lt;br /&gt;
Gets language name from a provided two- or three-character ISO 639 code.  If a code&lt;br /&gt;
is recognized by MediaWiki, use the returned name; if not, then use the value that&lt;br /&gt;
was provided with the language parameter.&lt;br /&gt;
&lt;br /&gt;
When |language= contains a recognized language (either code or name), the page is&lt;br /&gt;
assigned to the category for that code: Category:Norwegian-language sources (no).&lt;br /&gt;
For valid three-character code languages, the page is assigned to the single category&lt;br /&gt;
for '639-2' codes: Category:CS1 ISO 639-2 language sources.&lt;br /&gt;
&lt;br /&gt;
Languages that are the same as the local wiki are not categorized.  MediaWiki does&lt;br /&gt;
not recognize three-character equivalents of two-character codes: code 'ar' is&lt;br /&gt;
recognized but code 'ara' is not.&lt;br /&gt;
&lt;br /&gt;
This function supports multiple languages in the form |language=nb, French, th&lt;br /&gt;
where the language names or codes are separated from each other by commas with&lt;br /&gt;
optional space characters.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function language_parameter (lang)&lt;br /&gt;
	local tag;																	-- some form of IETF-like language tag; language subtag with optional region, sript, vatiant, etc subtags&lt;br /&gt;
	local lang_subtag;															-- ve populates |language= with mostly unecessary region subtags the MediaWiki does not recognize; this is the base language subtag&lt;br /&gt;
	local name;																	-- the language name&lt;br /&gt;
	local language_list = {};													-- table of language names to be rendered&lt;br /&gt;
	local names_t = {};															-- table made from the value assigned to |language=&lt;br /&gt;
&lt;br /&gt;
	local this_wiki_name = mw.language.fetchLanguageName (cfg.this_wiki_code, cfg.this_wiki_code);	-- get this wiki's language name&lt;br /&gt;
&lt;br /&gt;
	names_t = mw.text.split (lang, '%s*,%s*');									-- names should be a comma separated list&lt;br /&gt;
&lt;br /&gt;
	for _, lang in ipairs (names_t) do											-- reuse lang here because we don't yet know if lang is a language name or a language tag&lt;br /&gt;
		name, tag = name_tag_get (lang);										-- attempt to get name/tag pair for &amp;lt;lang&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (tag) then&lt;br /&gt;
			lang_subtag = tag:lower():gsub ('^(%a%a%a?)%-.*', '%1');			-- for categorization, strip any IETF-like tags from language tag&lt;br /&gt;
&lt;br /&gt;
			if cfg.this_wiki_code ~= lang_subtag then							-- when the language is not the same as this wiki's language&lt;br /&gt;
				if 2 == lang_subtag:len() then									-- and is a two-character tag&lt;br /&gt;
					utilities.add_prop_cat ('foreign-lang-source', {name, lang_subtag}, lang_subtag);		-- categorize it; tag appended to allow for multiple language categorization&lt;br /&gt;
				else															-- or is a recognized language (but has a three-character tag)&lt;br /&gt;
					utilities.add_prop_cat ('foreign-lang-source-2', {lang_subtag}, lang_subtag);			-- categorize it differently TODO: support multiple three-character tag categories per cs1|2 template?&lt;br /&gt;
				end&lt;br /&gt;
			elseif cfg.local_lang_cat_enable then								-- when the language and this wiki's language are the same and categorization is enabled&lt;br /&gt;
				utilities.add_prop_cat ('local-lang-source', {name, lang_subtag});		-- categorize it&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			name = lang;														-- return whatever &amp;lt;lang&amp;gt; has so that we show something&lt;br /&gt;
			utilities.set_message ('maint_unknown_lang');						-- add maint category if not already added&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		table.insert (language_list, name);&lt;br /&gt;
		name = '';																-- so we can reuse it&lt;br /&gt;
	end&lt;br /&gt;
 &lt;br /&gt;
	name = utilities.make_sep_list (#language_list, language_list);&lt;br /&gt;
	if (1 == #language_list) and (lang_subtag == cfg.this_wiki_code) then		-- when only one language, find lang name in this wiki lang name; for |language=en-us, 'English' in 'American English'&lt;br /&gt;
		return '';																-- if one language and that language is this wiki's return an empty string (no annotation)&lt;br /&gt;
	end&lt;br /&gt;
	return (&amp;quot; &amp;quot; .. wrap_msg ('language', name));								-- otherwise wrap with '(in ...)'&lt;br /&gt;
	--[[ TODO: should only return blank or name rather than full list&lt;br /&gt;
	so we can clean up the bunched parenthetical elements Language, Type, Format&lt;br /&gt;
	]]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-----------------------&amp;lt; S E T _ C S _ S T Y L E &amp;gt;--------------------------&lt;br /&gt;
&lt;br /&gt;
Gets the default CS style configuration for the given mode.&lt;br /&gt;
Returns default separator and either postscript as passed in or the default.&lt;br /&gt;
In CS1, the default postscript and separator are '.'.&lt;br /&gt;
In CS2, the default postscript is the empty string and the default separator is ','.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function set_cs_style (postscript, mode)&lt;br /&gt;
	if utilities.is_set(postscript) then&lt;br /&gt;
		-- emit a maintenance message if user postscript is the default cs1 postscript&lt;br /&gt;
		-- we catch the opposite case for cs2 in set_style&lt;br /&gt;
		if mode == 'cs1' and postscript == cfg.presentation['ps_' .. mode] then&lt;br /&gt;
			utilities.set_message ('maint_postscript');&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		postscript = cfg.presentation['ps_' .. mode];&lt;br /&gt;
	end&lt;br /&gt;
	return cfg.presentation['sep_' .. mode], postscript;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E T _ S T Y L E &amp;gt;-----------------------------&lt;br /&gt;
&lt;br /&gt;
Sets the separator and postscript styles. Checks the |mode= first and the&lt;br /&gt;
#invoke CitationClass second. Removes the postscript if postscript == none.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function set_style (mode, postscript, cite_class)&lt;br /&gt;
	local sep;&lt;br /&gt;
	if 'cs2' == mode then&lt;br /&gt;
		sep, postscript = set_cs_style (postscript, 'cs2');&lt;br /&gt;
	elseif 'cs1' == mode then&lt;br /&gt;
		sep, postscript = set_cs_style (postscript, 'cs1');&lt;br /&gt;
	elseif 'citation' == cite_class	then&lt;br /&gt;
		sep, postscript = set_cs_style (postscript, 'cs2');&lt;br /&gt;
	else&lt;br /&gt;
		sep, postscript = set_cs_style (postscript, 'cs1');&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if cfg.keywords_xlate[postscript:lower()] == 'none' then&lt;br /&gt;
		-- emit a maintenance message if user postscript is the default cs2 postscript&lt;br /&gt;
		-- we catch the opposite case for cs1 in set_cs_style&lt;br /&gt;
		if 'cs2' == mode or 'citation' == cite_class then&lt;br /&gt;
			utilities.set_message ('maint_postscript');&lt;br /&gt;
		end&lt;br /&gt;
		postscript = '';&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return sep, postscript&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; I S _ P D F &amp;gt;-----------------------------------&lt;br /&gt;
&lt;br /&gt;
Determines if a URL has the file extension that is one of the PDF file extensions&lt;br /&gt;
used by [[MediaWiki:Common.css]] when applying the PDF icon to external links.&lt;br /&gt;
&lt;br /&gt;
returns true if file extension is one of the recognized extensions, else false&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function is_pdf (url)&lt;br /&gt;
	return url:match ('%.pdf$') or url:match ('%.PDF$') or&lt;br /&gt;
		url:match ('%.pdf[%?#]') or url:match ('%.PDF[%?#]') or&lt;br /&gt;
		url:match ('%.PDF&amp;amp;#035') or url:match ('%.pdf&amp;amp;#035');&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S T Y L E _ F O R M A T &amp;gt;-----------------------&lt;br /&gt;
&lt;br /&gt;
Applies CSS style to |format=, |chapter-format=, etc.  Also emits an error message&lt;br /&gt;
if the format parameter does not have a matching URL parameter.  If the format parameter&lt;br /&gt;
is not set and the URL contains a file extension that is recognized as a PDF document&lt;br /&gt;
by MediaWiki's commons.css, this code will set the format parameter to (PDF) with&lt;br /&gt;
the appropriate styling.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function style_format (format, url, fmt_param, url_param)&lt;br /&gt;
	if utilities.is_set (format) then&lt;br /&gt;
		format = utilities.wrap_style ('format', format);						-- add leading space, parentheses, resize&lt;br /&gt;
		if not utilities.is_set (url) then&lt;br /&gt;
			utilities.set_message ('err_format_missing_url', {fmt_param, url_param});	-- add an error message&lt;br /&gt;
		end&lt;br /&gt;
	elseif is_pdf (url) then													-- format is not set so if URL is a PDF file then&lt;br /&gt;
		format = utilities.wrap_style ('format', 'PDF');						-- set format to PDF&lt;br /&gt;
	else&lt;br /&gt;
		format = '';															-- empty string for concatenation&lt;br /&gt;
	end&lt;br /&gt;
	return format;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[---------------------&amp;lt; G E T _ D I S P L A Y _ N A M E S &amp;gt;------------------&lt;br /&gt;
&lt;br /&gt;
Returns a number that defines the number of names displayed for author and editor&lt;br /&gt;
name lists and a Boolean flag to indicate when et al. should be appended to the name list.&lt;br /&gt;
&lt;br /&gt;
When the value assigned to |display-xxxxors= is a number greater than or equal to zero,&lt;br /&gt;
return the number and the previous state of the 'etal' flag (false by default&lt;br /&gt;
but may have been set to true if the name list contains some variant of the text 'et al.').&lt;br /&gt;
&lt;br /&gt;
When the value assigned to |display-xxxxors= is the keyword 'etal', return a number&lt;br /&gt;
that is one greater than the number of authors in the list and set the 'etal' flag true.&lt;br /&gt;
This will cause the list_people() to display all of the names in the name list followed by 'et al.'&lt;br /&gt;
&lt;br /&gt;
In all other cases, returns nil and the previous state of the 'etal' flag.&lt;br /&gt;
&lt;br /&gt;
inputs:&lt;br /&gt;
	max: A['DisplayAuthors'] or A['DisplayEditors']; a number or some flavor of etal&lt;br /&gt;
	count: #a or #e&lt;br /&gt;
	list_name: 'authors' or 'editors'&lt;br /&gt;
	etal: author_etal or editor_etal&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function get_display_names (max, count, list_name, etal, param)&lt;br /&gt;
	if utilities.is_set (max) then&lt;br /&gt;
		if 'etal' == max:lower():gsub(&amp;quot;[ '%.]&amp;quot;, '') then						-- the :gsub() portion makes 'etal' from a variety of 'et al.' spellings and stylings&lt;br /&gt;
			max = count + 1;													-- number of authors + 1 so display all author name plus et al.&lt;br /&gt;
			etal = true;														-- overrides value set by extract_names()&lt;br /&gt;
		elseif max:match ('^%d+$') then											-- if is a string of numbers&lt;br /&gt;
			max = tonumber (max);												-- make it a number&lt;br /&gt;
			if max &amp;gt;= count then												-- if |display-xxxxors= value greater than or equal to number of authors/editors&lt;br /&gt;
				utilities.set_message ('err_disp_name', {param, max});			-- add error message&lt;br /&gt;
				max = nil;&lt;br /&gt;
			end&lt;br /&gt;
		else																	-- not a valid keyword or number&lt;br /&gt;
			utilities.set_message ('err_disp_name', {param, max});		-- add error message&lt;br /&gt;
			max = nil;															-- unset; as if |display-xxxxors= had not been set&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return max, etal;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[----------&amp;lt; E X T R A _ T E X T _ I N _ P A G E _ C H E C K &amp;gt;---------------&lt;br /&gt;
&lt;br /&gt;
Adds error if |page=, |pages=, |quote-page=, |quote-pages= has what appears to be&lt;br /&gt;
some form of p. or pp. abbreviation in the first characters of the parameter content.&lt;br /&gt;
&lt;br /&gt;
check page for extraneous p, p., pp, pp., pg, pg. at start of parameter value:&lt;br /&gt;
	good pattern: '^P[^%.P%l]' matches when page begins PX or P# but not Px&lt;br /&gt;
		      where x and X are letters and # is a digit&lt;br /&gt;
	bad pattern:  '^[Pp][PpGg]' matches when page begins pp, pP, Pp, PP, pg, pG, Pg, PG&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function extra_text_in_page_check (val, name)&lt;br /&gt;
	if not val:match (cfg.vol_iss_pg_patterns.good_ppattern) then&lt;br /&gt;
		for _, pattern in ipairs (cfg.vol_iss_pg_patterns.bad_ppatterns) do		-- spin through the selected sequence table of patterns&lt;br /&gt;
			if val:match (pattern) then											-- when a match, error so&lt;br /&gt;
				utilities.set_message ('err_extra_text_pages', name);	 		-- add error message&lt;br /&gt;
				return;															-- and done&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end		&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X T R A _ T E X T _ I N _ V O L _ I S S _ C H E C K &amp;gt;------------------------&lt;br /&gt;
&lt;br /&gt;
Adds error if |volume= or |issue= has what appears to be some form of redundant 'type' indicator.&lt;br /&gt;
&lt;br /&gt;
For |volume=:&lt;br /&gt;
	'V.', or 'Vol.' (with or without the dot) abbreviations or 'Volume' in the first characters of the parameter&lt;br /&gt;
	content (all case insensitive). 'V' and 'v' (without the dot) are presumed to be roman numerals so&lt;br /&gt;
	are allowed.&lt;br /&gt;
&lt;br /&gt;
For |issue=:&lt;br /&gt;
	'No.', 'I.', 'Iss.' (with or without the dot) abbreviations, or 'Issue' in the first characters of the&lt;br /&gt;
	parameter content (all case insensitive).&lt;br /&gt;
	&lt;br /&gt;
Single character values ('v', 'i', 'n') allowed when not followed by separator character ('.', ':', '=', or&lt;br /&gt;
whitespace character) – param values are trimmed of whitespace by MediaWiki before delivered to the module.&lt;br /&gt;
	&lt;br /&gt;
&amp;lt;val&amp;gt; is |volume= or |issue= parameter value&lt;br /&gt;
&amp;lt;name&amp;gt; is |volume= or |issue= parameter name for error message&lt;br /&gt;
&amp;lt;selector&amp;gt; is 'v' for |volume=, 'i' for |issue=&lt;br /&gt;
&lt;br /&gt;
sets error message on failure; returns nothing&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function extra_text_in_vol_iss_check (val, name, selector)&lt;br /&gt;
	if not utilities.is_set (val) then&lt;br /&gt;
		return;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local patterns = 'v' == selector and cfg.vol_iss_pg_patterns.vpatterns or cfg.vol_iss_pg_patterns.ipatterns;&lt;br /&gt;
&lt;br /&gt;
	local handler = 'v' == selector and 'err_extra_text_volume' or 'err_extra_text_issue';&lt;br /&gt;
	val = val:lower();															-- force parameter value to lower case&lt;br /&gt;
	for _, pattern in ipairs (patterns) do										-- spin through the selected sequence table of patterns&lt;br /&gt;
		if val:match (pattern) then												-- when a match, error so&lt;br /&gt;
			utilities.set_message (handler, name);								-- add error message&lt;br /&gt;
			return;																-- and done&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; G E T _ V _ N A M E _ T A B L E &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
split apart a |vauthors= or |veditors= parameter.  This function allows for corporate names, wrapped in doubled&lt;br /&gt;
parentheses to also have commas; in the old version of the code, the doubled parentheses were included in the&lt;br /&gt;
rendered citation and in the metadata.  Individual author names may be wikilinked&lt;br /&gt;
&lt;br /&gt;
	|vauthors=Jones AB, [[E. B. White|White EB]], ((Black, Brown, and Co.))&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function get_v_name_table (vparam, output_table, output_link_table)&lt;br /&gt;
	local name_table = mw.text.split(vparam, &amp;quot;%s*,%s*&amp;quot;);						-- names are separated by commas&lt;br /&gt;
	local wl_type, label, link;													-- wl_type not used here; just a placeholder&lt;br /&gt;
	&lt;br /&gt;
	local i = 1;&lt;br /&gt;
	&lt;br /&gt;
	while name_table[i] do&lt;br /&gt;
		if name_table[i]:match ('^%(%(.*[^%)][^%)]$') then						-- first segment of corporate with one or more commas; this segment has the opening doubled parentheses&lt;br /&gt;
			local name = name_table[i];&lt;br /&gt;
			i = i + 1;															-- bump indexer to next segment&lt;br /&gt;
			while name_table[i] do&lt;br /&gt;
				name = name .. ', ' .. name_table[i];							-- concatenate with previous segments&lt;br /&gt;
				if name_table[i]:match ('^.*%)%)$') then						-- if this table member has the closing doubled parentheses&lt;br /&gt;
					break;														-- and done reassembling so&lt;br /&gt;
				end&lt;br /&gt;
				i = i + 1;														-- bump indexer&lt;br /&gt;
			end&lt;br /&gt;
			table.insert (output_table, name);									-- and add corporate name to the output table&lt;br /&gt;
			table.insert (output_link_table, '');								-- no wikilink&lt;br /&gt;
		else&lt;br /&gt;
			wl_type, label, link = utilities.is_wikilink (name_table[i]);		-- wl_type is: 0, no wl (text in label variable); 1, [[D]]; 2, [[L|D]]&lt;br /&gt;
			table.insert (output_table, label);									-- add this name&lt;br /&gt;
			if 1 == wl_type then&lt;br /&gt;
				table.insert (output_link_table, label);						-- simple wikilink [[D]]&lt;br /&gt;
			else&lt;br /&gt;
				table.insert (output_link_table, link);							-- no wikilink or [[L|D]]; add this link if there is one, else empty string&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		i = i + 1;&lt;br /&gt;
	end	&lt;br /&gt;
	return output_table;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P A R S E _ V A U T H O R S _ V E D I T O R S &amp;gt;--------------------------------&lt;br /&gt;
&lt;br /&gt;
This function extracts author / editor names from |vauthors= or |veditors= and finds matching |xxxxor-maskn= and&lt;br /&gt;
|xxxxor-linkn= in args.  It then returns a table of assembled names just as extract_names() does.&lt;br /&gt;
&lt;br /&gt;
Author / editor names in |vauthors= or |veditors= must be in Vancouver system style. Corporate or institutional names&lt;br /&gt;
may sometimes be required and because such names will often fail the is_good_vanc_name() and other format compliance&lt;br /&gt;
tests, are wrapped in doubled parentheses ((corporate name)) to suppress the format tests.&lt;br /&gt;
&lt;br /&gt;
Supports generational suffixes Jr, 2nd, 3rd, 4th–6th.&lt;br /&gt;
&lt;br /&gt;
This function sets the Vancouver error when a required comma is missing and when there is a space between an author's initials.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function parse_vauthors_veditors (args, vparam, list_name)&lt;br /&gt;
	local names = {};															-- table of names assembled from |vauthors=, |author-maskn=, |author-linkn=&lt;br /&gt;
	local v_name_table = {};&lt;br /&gt;
	local v_link_table = {};													-- when name is wikilinked, targets go in this table&lt;br /&gt;
	local etal = false;															-- return value set to true when we find some form of et al. vauthors parameter&lt;br /&gt;
	local last, first, link, mask, suffix;&lt;br /&gt;
	local corporate = false;&lt;br /&gt;
&lt;br /&gt;
	vparam, etal = name_has_etal (vparam, etal, true);							-- find and remove variations on et al. do not categorize (do it here because et al. might have a period)&lt;br /&gt;
	v_name_table = get_v_name_table (vparam, v_name_table, v_link_table);		-- names are separated by commas&lt;br /&gt;
&lt;br /&gt;
	for i, v_name in ipairs(v_name_table) do&lt;br /&gt;
		first = '';																-- set to empty string for concatenation and because it may have been set for previous author/editor&lt;br /&gt;
		local accept_name;&lt;br /&gt;
		v_name, accept_name = utilities.has_accept_as_written (v_name);			-- remove accept-this-as-written markup when it wraps all of &amp;lt;v_name&amp;gt;&lt;br /&gt;
&lt;br /&gt;
		if accept_name then&lt;br /&gt;
			last = v_name;&lt;br /&gt;
			corporate = true;													-- flag used in list_people()&lt;br /&gt;
		elseif string.find(v_name, &amp;quot;%s&amp;quot;) then&lt;br /&gt;
			if v_name:find('[;%.]') then										-- look for commonly occurring punctuation characters; &lt;br /&gt;
				add_vanc_error (cfg.err_msg_supl.punctuation, i);&lt;br /&gt;
			end&lt;br /&gt;
			local lastfirstTable = {}&lt;br /&gt;
			lastfirstTable = mw.text.split(v_name, &amp;quot;%s+&amp;quot;)&lt;br /&gt;
			first = table.remove(lastfirstTable);								-- removes and returns value of last element in table which should be initials or generational suffix&lt;br /&gt;
&lt;br /&gt;
			if not mw.ustring.match (first, '^%u+$') then						-- mw.ustring here so that later we will catch non-Latin characters&lt;br /&gt;
				suffix = first;													-- not initials so assume that whatever we got is a generational suffix&lt;br /&gt;
				first = table.remove(lastfirstTable);							-- get what should be the initials from the table&lt;br /&gt;
			end&lt;br /&gt;
			last = table.concat(lastfirstTable, ' ')							-- returns a string that is the concatenation of all other names that are not initials and generational suffix&lt;br /&gt;
			if not utilities.is_set (last) then&lt;br /&gt;
				first = '';														-- unset&lt;br /&gt;
				last = v_name;													-- last empty because something wrong with first&lt;br /&gt;
				add_vanc_error (cfg.err_msg_supl.name, i);&lt;br /&gt;
			end&lt;br /&gt;
			if mw.ustring.match (last, '%a+%s+%u+%s+%a+') then&lt;br /&gt;
				add_vanc_error (cfg.err_msg_supl['missing comma'], i);			-- matches last II last; the case when a comma is missing&lt;br /&gt;
			end&lt;br /&gt;
			if mw.ustring.match (v_name, ' %u %u$') then						-- this test is in the wrong place TODO: move or replace with a more appropriate test&lt;br /&gt;
				add_vanc_error (cfg.err_msg_supl.initials, i);					-- matches a space between two initials&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			last = v_name;														-- last name or single corporate name?  Doesn't support multiword corporate names? do we need this?&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if utilities.is_set (first) then&lt;br /&gt;
			if not mw.ustring.match (first, &amp;quot;^%u?%u$&amp;quot;) then						-- first shall contain one or two upper-case letters, nothing else&lt;br /&gt;
				add_vanc_error (cfg.err_msg_supl.initials, i);					-- too many initials; mixed case initials (which may be ok Romanization); hyphenated initials&lt;br /&gt;
			end&lt;br /&gt;
			is_good_vanc_name (last, first, suffix, i);							-- check first and last before restoring the suffix which may have a non-Latin digit&lt;br /&gt;
			if utilities.is_set (suffix) then&lt;br /&gt;
				first = first .. ' ' .. suffix;									-- if there was a suffix concatenate with the initials&lt;br /&gt;
				suffix = '';													-- unset so we don't add this suffix to all subsequent names&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			if not corporate then&lt;br /&gt;
				is_good_vanc_name (last, '', nil, i);&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		link = utilities.select_one ( args, cfg.aliases[list_name .. '-Link'], 'err_redundant_parameters', i ) or v_link_table[i];&lt;br /&gt;
		mask = utilities.select_one ( args, cfg.aliases[list_name .. '-Mask'], 'err_redundant_parameters', i );&lt;br /&gt;
		names[i] = {last = last, first = first, link = link, mask = mask, corporate = corporate};		-- add this assembled name to our names list&lt;br /&gt;
	end&lt;br /&gt;
	return names, etal;															-- all done, return our list of names&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; S E L E C T _ A U T H O R _ E D I T O R _ S O U R C E &amp;gt;------------------------&lt;br /&gt;
&lt;br /&gt;
Select one of |authors=, |authorn= / |lastn / firstn=, or |vauthors= as the source of the author name list or&lt;br /&gt;
select one of |editorn= / editor-lastn= / |editor-firstn= or |veditors= as the source of the editor name list.&lt;br /&gt;
&lt;br /&gt;
Only one of these appropriate three will be used.  The hierarchy is: |authorn= (and aliases) highest and |authors= lowest;&lt;br /&gt;
|editorn= (and aliases) highest and |veditors= lowest (support for |editors= withdrawn)&lt;br /&gt;
&lt;br /&gt;
When looking for |authorn= / |editorn= parameters, test |xxxxor1= and |xxxxor2= (and all of their aliases); stops after the second&lt;br /&gt;
test which mimicks the test used in extract_names() when looking for a hole in the author name list.  There may be a better&lt;br /&gt;
way to do this, I just haven't discovered what that way is.&lt;br /&gt;
&lt;br /&gt;
Emits an error message when more than one xxxxor name source is provided.&lt;br /&gt;
&lt;br /&gt;
In this function, vxxxxors = vauthors or veditors; xxxxors = authors as appropriate.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function select_author_editor_source (vxxxxors, xxxxors, args, list_name)&lt;br /&gt;
	local lastfirst = false;&lt;br /&gt;
	if utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 1 ) or		-- do this twice in case we have a |first1= without a |last1=; this ...&lt;br /&gt;
		utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 1 ) or		-- ... also catches the case where |first= is used with |vauthors=&lt;br /&gt;
		utilities.select_one ( args, cfg.aliases[list_name .. '-Last'], 'none', 2 ) or&lt;br /&gt;
		utilities.select_one ( args, cfg.aliases[list_name .. '-First'], 'none', 2 ) then&lt;br /&gt;
			lastfirst = true;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (utilities.is_set (vxxxxors) and true == lastfirst) or					-- these are the three error conditions&lt;br /&gt;
		(utilities.is_set (vxxxxors) and utilities.is_set (xxxxors)) or&lt;br /&gt;
		(true == lastfirst and utilities.is_set (xxxxors)) then&lt;br /&gt;
			local err_name;&lt;br /&gt;
			if 'AuthorList' == list_name then									-- figure out which name should be used in error message&lt;br /&gt;
				err_name = 'author';&lt;br /&gt;
			else&lt;br /&gt;
				err_name = 'editor';&lt;br /&gt;
			end&lt;br /&gt;
			utilities.set_message ('err_redundant_parameters', err_name .. '-name-list parameters');	-- add error message&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if true == lastfirst then return 1 end;										-- return a number indicating which author name source to use&lt;br /&gt;
	if utilities.is_set (vxxxxors) then return 2 end;&lt;br /&gt;
	if utilities.is_set (xxxxors) then return 3 end;&lt;br /&gt;
	return 1;																	-- no authors so return 1; this allows missing author name test to run in case there is a first without last &lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ V A L I D _ P A R A M E T E R _ V A L U E &amp;gt;------------------------------&lt;br /&gt;
&lt;br /&gt;
This function is used to validate a parameter's assigned value for those parameters that have only a limited number&lt;br /&gt;
of allowable values (yes, y, true, live, dead, etc.).  When the parameter value has not been assigned a value (missing&lt;br /&gt;
or empty in the source template) the function returns the value specified by ret_val.  If the parameter value is one&lt;br /&gt;
of the list of allowed values returns the translated value; else, emits an error message and returns the value&lt;br /&gt;
specified by ret_val.&lt;br /&gt;
&lt;br /&gt;
TODO: explain &amp;lt;invert&amp;gt;&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_valid_parameter_value (value, name, possible, ret_val, invert)&lt;br /&gt;
	if not utilities.is_set (value) then&lt;br /&gt;
		return ret_val;															-- an empty parameter is ok&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (not invert and utilities.in_array (value, possible)) then				-- normal; &amp;lt;value&amp;gt; is in &amp;lt;possible&amp;gt; table&lt;br /&gt;
		return cfg.keywords_xlate[value];										-- return translation of parameter keyword&lt;br /&gt;
	elseif invert and not utilities.in_array (value, possible) then				-- invert; &amp;lt;value&amp;gt; is not in &amp;lt;possible&amp;gt; table&lt;br /&gt;
		return value;															-- return &amp;lt;value&amp;gt; as it is&lt;br /&gt;
	else&lt;br /&gt;
		utilities.set_message ('err_invalid_param_val', {name, value});			-- not an allowed value so add error message&lt;br /&gt;
		return ret_val;&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; T E R M I N A T E _ N A M E _ L I S T &amp;gt;----------------------------------------&lt;br /&gt;
&lt;br /&gt;
This function terminates a name list (author, contributor, editor) with a separator character (sepc) and a space&lt;br /&gt;
when the last character is not a sepc character or when the last three characters are not sepc followed by two&lt;br /&gt;
closing square brackets (close of a wikilink).  When either of these is true, the name_list is terminated with a&lt;br /&gt;
single space character.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function terminate_name_list (name_list, sepc)&lt;br /&gt;
	if (string.sub (name_list, -3, -1) == sepc .. '. ') then					-- if already properly terminated&lt;br /&gt;
		return name_list;														-- just return the name list&lt;br /&gt;
	elseif (string.sub (name_list, -1, -1) == sepc) or (string.sub (name_list, -3, -1) == sepc .. ']]') then	-- if last name in list ends with sepc char&lt;br /&gt;
		return name_list .. &amp;quot; &amp;quot;;												-- don't add another&lt;br /&gt;
	else&lt;br /&gt;
		return name_list .. sepc .. ' ';										-- otherwise terminate the name list&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------------&amp;lt; F O R M A T _ V O L U M E _ I S S U E &amp;gt;----------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns the concatenation of the formatted volume and issue parameters as a single string; or formatted volume&lt;br /&gt;
or formatted issue, or an empty string if neither are set.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
	&lt;br /&gt;
local function format_volume_issue (volume, issue, cite_class, origin, sepc, lower)&lt;br /&gt;
	if not utilities.is_set (volume) and not utilities.is_set (issue) then&lt;br /&gt;
		return '';&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- same condition as in format_pages_sheets()&lt;br /&gt;
	local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);&lt;br /&gt;
&lt;br /&gt;
	local is_numeric_vol = volume and (volume:match ('^[MDCLXVI]+$') or volume:match ('^%d+$'));	-- is only uppercase roman numerals or only digits?&lt;br /&gt;
	local is_long_vol = volume and (4 &amp;lt; mw.ustring.len(volume));				-- is |volume= value longer than 4 characters?&lt;br /&gt;
	&lt;br /&gt;
	if volume and (not is_numeric_vol and is_long_vol) then						-- when not all digits or Roman numerals, is |volume= longer than 4 characters?&lt;br /&gt;
		utilities.add_prop_cat ('long-vol');									-- yes, add properties cat&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if is_journal then															-- journal-style formatting&lt;br /&gt;
		local vol = '';&lt;br /&gt;
		&lt;br /&gt;
		if utilities.is_set (volume) then&lt;br /&gt;
			if is_numeric_vol then												-- |volume= value all digits or all uppercase Roman numerals?&lt;br /&gt;
				vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, volume});	-- render in bold face&lt;br /&gt;
			elseif is_long_vol then												-- not all digits or Roman numerals; longer than 4 characters?&lt;br /&gt;
				vol = utilities.substitute (cfg.messages['j-vol'], {sepc, utilities.hyphen_to_dash (volume)});	-- not bold&lt;br /&gt;
			else																-- four or fewer characters&lt;br /&gt;
				vol = utilities.substitute (cfg.presentation['vol-bold'], {sepc, utilities.hyphen_to_dash (volume)});	-- bold&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if utilities.is_set (issue) then&lt;br /&gt;
			return vol .. utilities.substitute (cfg.messages['j-issue'], issue);&lt;br /&gt;
		end&lt;br /&gt;
		return vol;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if 'podcast' == cite_class and utilities.is_set (issue) then&lt;br /&gt;
		return wrap_msg ('issue', {sepc, issue}, lower);&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- all other types of citation&lt;br /&gt;
	if utilities.is_set (volume) and utilities.is_set (issue) then&lt;br /&gt;
		return wrap_msg ('vol-no', {sepc, utilities.hyphen_to_dash (volume), issue}, lower);&lt;br /&gt;
	elseif utilities.is_set (volume) then&lt;br /&gt;
		return wrap_msg ('vol', {sepc, utilities.hyphen_to_dash (volume)}, lower);&lt;br /&gt;
	else&lt;br /&gt;
		return wrap_msg ('issue', {sepc, issue}, lower);&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[-------------------------&amp;lt; F O R M A T _ P A G E S _ S H E E T S &amp;gt;-----------------------------------------&lt;br /&gt;
&lt;br /&gt;
adds static text to one of |page(s)= or |sheet(s)= values and returns it with all of the others set to empty strings.&lt;br /&gt;
The return order is:&lt;br /&gt;
	page, pages, sheet, sheets&lt;br /&gt;
&lt;br /&gt;
Singular has priority over plural when both are provided.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function format_pages_sheets (page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower)&lt;br /&gt;
	if 'map' == cite_class then													-- only cite map supports sheet(s) as in-source locators&lt;br /&gt;
		if utilities.is_set (sheet) then&lt;br /&gt;
			if 'journal' == origin then&lt;br /&gt;
				return '', '', wrap_msg ('j-sheet', sheet, lower), '';&lt;br /&gt;
			else&lt;br /&gt;
				return '', '', wrap_msg ('sheet', {sepc, sheet}, lower), '';&lt;br /&gt;
			end&lt;br /&gt;
		elseif utilities.is_set (sheets) then&lt;br /&gt;
			if 'journal' == origin then&lt;br /&gt;
				return '', '', '', wrap_msg ('j-sheets', sheets, lower);&lt;br /&gt;
			else&lt;br /&gt;
				return '', '', '', wrap_msg ('sheets', {sepc, sheets}, lower);&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local is_journal = 'journal' == cite_class or (utilities.in_array (cite_class, {'citation', 'map', 'interview'}) and 'journal' == origin);&lt;br /&gt;
	&lt;br /&gt;
	if utilities.is_set (page) then&lt;br /&gt;
		if is_journal then&lt;br /&gt;
			return utilities.substitute (cfg.messages['j-page(s)'], page), '', '', '';&lt;br /&gt;
		elseif not nopp then&lt;br /&gt;
			return utilities.substitute (cfg.messages['p-prefix'], {sepc, page}), '', '', '';&lt;br /&gt;
		else&lt;br /&gt;
			return utilities.substitute (cfg.messages['nopp'], {sepc, page}), '', '', '';&lt;br /&gt;
		end&lt;br /&gt;
	elseif utilities.is_set (pages) then&lt;br /&gt;
		if is_journal then&lt;br /&gt;
			return utilities.substitute (cfg.messages['j-page(s)'], pages), '', '', '';&lt;br /&gt;
		elseif tonumber(pages) ~= nil and not nopp then							-- if pages is only digits, assume a single page number&lt;br /&gt;
			return '', utilities.substitute (cfg.messages['p-prefix'], {sepc, pages}), '', '';&lt;br /&gt;
		elseif not nopp then&lt;br /&gt;
			return '', utilities.substitute (cfg.messages['pp-prefix'], {sepc, pages}), '', '';&lt;br /&gt;
		else&lt;br /&gt;
			return '', utilities.substitute (cfg.messages['nopp'], {sepc, pages}), '', '';&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return '', '', '', '';														-- return empty strings&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I N S O U R C E _ L O C _ G E T &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
returns one of the in-source locators: page, pages, or at. &lt;br /&gt;
&lt;br /&gt;
If any of these are interwiki links to Wikisource, returns the label portion of the interwiki-link as plain text&lt;br /&gt;
for use in COinS.  This COinS thing is done because here we convert an interwiki-link to an external link and&lt;br /&gt;
add an icon span around that; get_coins_pages() doesn't know about the span.  TODO: should it?  &lt;br /&gt;
&lt;br /&gt;
TODO: add support for sheet and sheets?; streamline;&lt;br /&gt;
&lt;br /&gt;
TODO: make it so that this function returns only one of the three as the single in-source (the return value assigned&lt;br /&gt;
to a new name)?&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function insource_loc_get (page, page_orig, pages, pages_orig, at)&lt;br /&gt;
	local ws_url, ws_label, coins_pages, L;										-- for Wikisource interwiki-links; TODO: this corrupts page metadata (span remains in place after cleanup; fix there?)&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (page) then&lt;br /&gt;
		if utilities.is_set (pages) or utilities.is_set (at) then&lt;br /&gt;
			pages = '';															-- unset the others&lt;br /&gt;
			at = '';&lt;br /&gt;
		end&lt;br /&gt;
		extra_text_in_page_check (page, page_orig);								-- emit error message when |page= value begins with what looks like p., pp., etc.&lt;br /&gt;
&lt;br /&gt;
		ws_url, ws_label, L = wikisource_url_make (page);						-- make ws URL from |page= interwiki link; link portion L becomes tooltip label&lt;br /&gt;
		if ws_url then&lt;br /&gt;
			page = external_link (ws_url, ws_label .. '&amp;amp;nbsp;', 'ws link in page');	-- space char after label to move icon away from in-source text; TODO: a better way to do this?&lt;br /&gt;
			page = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, page});&lt;br /&gt;
			coins_pages = ws_label;&lt;br /&gt;
		end&lt;br /&gt;
	elseif utilities.is_set (pages) then&lt;br /&gt;
		if utilities.is_set (at) then&lt;br /&gt;
			at = '';															-- unset&lt;br /&gt;
		end&lt;br /&gt;
		extra_text_in_page_check (pages, pages_orig);							-- emit error message when |page= value begins with what looks like p., pp., etc.&lt;br /&gt;
&lt;br /&gt;
		ws_url, ws_label, L = wikisource_url_make (pages);						-- make ws URL from |pages= interwiki link; link portion L becomes tooltip label&lt;br /&gt;
		if ws_url then&lt;br /&gt;
			pages = external_link (ws_url, ws_label .. '&amp;amp;nbsp;', 'ws link in pages');	-- space char after label to move icon away from in-source text; TODO: a better way to do this?&lt;br /&gt;
			pages = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, pages});&lt;br /&gt;
			coins_pages = ws_label;&lt;br /&gt;
		end&lt;br /&gt;
	elseif utilities.is_set (at) then&lt;br /&gt;
		ws_url, ws_label, L = wikisource_url_make (at);							-- make ws URL from |at= interwiki link; link portion L becomes tooltip label&lt;br /&gt;
		if ws_url then&lt;br /&gt;
			at = external_link (ws_url, ws_label .. '&amp;amp;nbsp;', 'ws link in at');	-- space char after label to move icon away from in-source text; TODO: a better way to do this?&lt;br /&gt;
			at = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, at});&lt;br /&gt;
			coins_pages = ws_label;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return page, pages, at, coins_pages;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ U N I Q U E _ A R C H I V E _ U R L &amp;gt;------------------------------------&lt;br /&gt;
&lt;br /&gt;
add error message when |archive-url= value is same as |url= or chapter-url= (or alias...) value&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_unique_archive_url (archive, url, c_url, source, date)&lt;br /&gt;
	if utilities.is_set (archive) then&lt;br /&gt;
		if archive == url or archive == c_url then&lt;br /&gt;
			utilities.set_message ('err_bad_url', {utilities.wrap_style ('parameter', source)});	-- add error message&lt;br /&gt;
			return '', '';														-- unset |archive-url= and |archive-date= because same as |url= or |chapter-url=&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return archive, date;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; A R C H I V E _ U R L _ C H E C K &amp;gt;--------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Check archive.org URLs to make sure they at least look like they are pointing at valid archives and not to the &lt;br /&gt;
save snapshot URL or to calendar pages.  When the archive URL is 'https://web.archive.org/save/' (or http://...)&lt;br /&gt;
archive.org saves a snapshot of the target page in the URL.  That is something that Wikipedia should not allow&lt;br /&gt;
unwitting readers to do.&lt;br /&gt;
&lt;br /&gt;
When the archive.org URL does not have a complete timestamp, archive.org chooses a snapshot according to its own&lt;br /&gt;
algorithm or provides a calendar 'search' result.  [[WP:ELNO]] discourages links to search results.&lt;br /&gt;
&lt;br /&gt;
This function looks at the value assigned to |archive-url= and returns empty strings for |archive-url= and&lt;br /&gt;
|archive-date= and an error message when:&lt;br /&gt;
	|archive-url= holds an archive.org save command URL&lt;br /&gt;
	|archive-url= is an archive.org URL that does not have a complete timestamp (YYYYMMDDhhmmss 14 digits) in the&lt;br /&gt;
		correct place&lt;br /&gt;
otherwise returns |archive-url= and |archive-date=&lt;br /&gt;
&lt;br /&gt;
There are two mostly compatible archive.org URLs:&lt;br /&gt;
	//web.archive.org/&amp;lt;timestamp&amp;gt;...		-- the old form&lt;br /&gt;
	//web.archive.org/web/&amp;lt;timestamp&amp;gt;...	-- the new form&lt;br /&gt;
&lt;br /&gt;
The old form does not support or map to the new form when it contains a display flag.  There are four identified flags&lt;br /&gt;
('id_', 'js_', 'cs_', 'im_') but since archive.org ignores others following the same form (two letters and an underscore)&lt;br /&gt;
we don't check for these specific flags but we do check the form.&lt;br /&gt;
&lt;br /&gt;
This function supports a preview mode.  When the article is rendered in preview mode, this function may return a modified&lt;br /&gt;
archive URL:&lt;br /&gt;
	for save command errors, return undated wildcard (/*/)&lt;br /&gt;
	for timestamp errors when the timestamp has a wildcard, return the URL unmodified&lt;br /&gt;
	for timestamp errors when the timestamp does not have a wildcard, return with timestamp limited to six digits plus wildcard (/yyyymm*/)&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function archive_url_check (url, date)&lt;br /&gt;
	local err_msg = '';															-- start with the error message empty&lt;br /&gt;
	local path, timestamp, flag;												-- portions of the archive.org URL&lt;br /&gt;
	&lt;br /&gt;
	if (not url:match('//web%.archive%.org/')) and (not url:match('//liveweb%.archive%.org/')) then		-- also deprecated liveweb Wayback machine URL&lt;br /&gt;
		return url, date;														-- not an archive.org archive, return ArchiveURL and ArchiveDate&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if url:match('//web%.archive%.org/save/') then								-- if a save command URL, we don't want to allow saving of the target page &lt;br /&gt;
		err_msg = cfg.err_msg_supl.save;&lt;br /&gt;
		url = url:gsub ('(//web%.archive%.org)/save/', '%1/*/', 1);				-- for preview mode: modify ArchiveURL&lt;br /&gt;
	elseif url:match('//liveweb%.archive%.org/') then&lt;br /&gt;
		err_msg = cfg.err_msg_supl.liveweb;&lt;br /&gt;
	else&lt;br /&gt;
		path, timestamp, flag = url:match('//web%.archive%.org/([^%d]*)(%d+)([^/]*)/');	-- split out some of the URL parts for evaluation&lt;br /&gt;
		if not path then														-- malformed in some way; pattern did not match&lt;br /&gt;
			err_msg = cfg.err_msg_supl.timestamp;&lt;br /&gt;
		elseif 14 ~= timestamp:len() then										-- path and flag optional, must have 14-digit timestamp here&lt;br /&gt;
			err_msg = cfg.err_msg_supl.timestamp;&lt;br /&gt;
			if '*' ~= flag then&lt;br /&gt;
				local replacement = timestamp:match ('^%d%d%d%d%d%d') or timestamp:match ('^%d%d%d%d');	-- get the first 6 (YYYYMM) or first 4 digits (YYYY)&lt;br /&gt;
				if replacement then												-- nil if there aren't at least 4 digits (year)&lt;br /&gt;
					replacement = replacement .. string.rep ('0', 14 - replacement:len());	-- year or yearmo (4 or 6 digits) zero-fill to make 14-digit timestamp&lt;br /&gt;
					url=url:gsub ('(//web%.archive%.org/[^%d]*)%d[^/]*', '%1' .. replacement .. '*', 1)	-- for preview, modify ts to 14 digits plus splat for calendar display&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		elseif utilities.is_set (path) and 'web/' ~= path then					-- older archive URLs do not have the extra 'web/' path element&lt;br /&gt;
			err_msg = cfg.err_msg_supl.path;&lt;br /&gt;
		elseif utilities.is_set (flag) and not utilities.is_set (path) then		-- flag not allowed with the old form URL (without the 'web/' path element)&lt;br /&gt;
			err_msg = cfg.err_msg_supl.flag;&lt;br /&gt;
		elseif utilities.is_set (flag) and not flag:match ('%a%a_') then		-- flag if present must be two alpha characters and underscore (requires 'web/' path element)&lt;br /&gt;
			err_msg = cfg.err_msg_supl.flag;&lt;br /&gt;
		else&lt;br /&gt;
			return url, date;													-- return ArchiveURL and ArchiveDate&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
																				-- if here, something not right so&lt;br /&gt;
	utilities.set_message ('err_archive_url', {err_msg});						-- add error message and&lt;br /&gt;
&lt;br /&gt;
	if is_preview_mode then&lt;br /&gt;
		return url, date;														-- preview mode so return ArchiveURL and ArchiveDate&lt;br /&gt;
	else&lt;br /&gt;
		return '', '';															-- return empty strings for ArchiveURL and ArchiveDate&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; P L A C E _ C H E C K &amp;gt;--------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
check |place=, |publication-place=, |location= to see if these params include digits.  This function added because&lt;br /&gt;
many editors misuse location to specify the in-source location (|page(s)= and |at= are supposed to do that)&lt;br /&gt;
&lt;br /&gt;
returns the original parameter value without modification; added maint cat when parameter value contains digits&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function place_check (param_val)&lt;br /&gt;
	if not utilities.is_set (param_val) then									-- parameter empty or omitted&lt;br /&gt;
		return param_val;														-- return that empty state&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if mw.ustring.find (param_val, '%d') then									-- not empty, are there digits in the parameter value&lt;br /&gt;
		utilities.set_message ('maint_location');								-- yep, add maint cat&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return param_val;															-- and done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; I S _ A R C H I V E D _ C O P Y &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
compares |title= to 'Archived copy' (placeholder added by bots that can't find proper title); if matches, return true; nil else&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function is_archived_copy (title)&lt;br /&gt;
	title = mw.ustring.lower(title);											-- switch title to lower case&lt;br /&gt;
	if title:find (cfg.special_case_translation.archived_copy.en) then			-- if title is 'Archived copy'&lt;br /&gt;
		return true;&lt;br /&gt;
	elseif cfg.special_case_translation.archived_copy['local'] then&lt;br /&gt;
		if mw.ustring.find (title, cfg.special_case_translation.archived_copy['local']) then	-- mw.ustring() because might not be Latin script&lt;br /&gt;
			return true;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C I T A T I O N 0 &amp;gt;------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
This is the main function doing the majority of the citation formatting.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function citation0( config, args )&lt;br /&gt;
	--[[ &lt;br /&gt;
	Load Input Parameters&lt;br /&gt;
	The argument_wrapper facilitates the mapping of multiple aliases to single internal variable.&lt;br /&gt;
	]]&lt;br /&gt;
	local A = argument_wrapper ( args );&lt;br /&gt;
	local i &lt;br /&gt;
&lt;br /&gt;
	-- Pick out the relevant fields from the arguments.  Different citation templates&lt;br /&gt;
	-- define different field names for the same underlying things.	&lt;br /&gt;
&lt;br /&gt;
	local author_etal;&lt;br /&gt;
	local a	= {};																-- authors list from |lastn= / |firstn= pairs or |vauthors=&lt;br /&gt;
	local Authors;&lt;br /&gt;
	local NameListStyle = is_valid_parameter_value (A['NameListStyle'], A:ORIGIN('NameListStyle'), cfg.keywords_lists['name-list-style'], '');&lt;br /&gt;
	local Collaboration = A['Collaboration'];&lt;br /&gt;
&lt;br /&gt;
	do																			-- to limit scope of selected&lt;br /&gt;
		local selected = select_author_editor_source (A['Vauthors'], A['Authors'], args, 'AuthorList');&lt;br /&gt;
		if 1 == selected then&lt;br /&gt;
			a, author_etal = extract_names (args, 'AuthorList');				-- fetch author list from |authorn= / |lastn= / |firstn=, |author-linkn=, and |author-maskn=&lt;br /&gt;
		elseif 2 == selected then&lt;br /&gt;
			NameListStyle = 'vanc';												-- override whatever |name-list-style= might be&lt;br /&gt;
			a, author_etal = parse_vauthors_veditors (args, args.vauthors, 'AuthorList');	-- fetch author list from |vauthors=, |author-linkn=, and |author-maskn=&lt;br /&gt;
		elseif 3 == selected then&lt;br /&gt;
			Authors = A['Authors'];												-- use content of |authors=&lt;br /&gt;
			if 'authors' == A:ORIGIN('Authors') then							-- but add a maint cat if the parameter is |authors=&lt;br /&gt;
				utilities.set_message ('maint_authors');						-- because use of this parameter is discouraged; what to do about the aliases is a TODO:&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if utilities.is_set (Collaboration) then&lt;br /&gt;
			author_etal = true;													-- so that |display-authors=etal not required&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local editor_etal;&lt;br /&gt;
	local e	= {};																-- editors list from |editor-lastn= / |editor-firstn= pairs or |veditors=&lt;br /&gt;
&lt;br /&gt;
	do																			-- to limit scope of selected&lt;br /&gt;
		local selected = select_author_editor_source (A['Veditors'], nil, args, 'EditorList');	-- support for |editors= withdrawn&lt;br /&gt;
		if 1 == selected then&lt;br /&gt;
			e, editor_etal = extract_names (args, 'EditorList');				-- fetch editor list from |editorn= / |editor-lastn= / |editor-firstn=, |editor-linkn=, and |editor-maskn=&lt;br /&gt;
		elseif 2 == selected then&lt;br /&gt;
			NameListStyle = 'vanc';												-- override whatever |name-list-style= might be&lt;br /&gt;
			e, editor_etal = parse_vauthors_veditors (args, args.veditors, 'EditorList');	-- fetch editor list from |veditors=, |editor-linkn=, and |editor-maskn=&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
							&lt;br /&gt;
	local Chapter = A['Chapter'];												-- done here so that we have access to |contribution= from |chapter= aliases&lt;br /&gt;
	local Chapter_origin = A:ORIGIN ('Chapter');&lt;br /&gt;
	local Contribution;															-- because contribution is required for contributor(s)&lt;br /&gt;
		if 'contribution' == Chapter_origin then&lt;br /&gt;
			Contribution = Chapter;												-- get the name of the contribution&lt;br /&gt;
		end&lt;br /&gt;
	local c = {};																-- contributors list from |contributor-lastn= / contributor-firstn= pairs&lt;br /&gt;
	&lt;br /&gt;
	if utilities.in_array (config.CitationClass, {&amp;quot;book&amp;quot;, &amp;quot;citation&amp;quot;}) and not utilities.is_set (A['Periodical']) then	-- |contributor= and |contribution= only supported in book cites&lt;br /&gt;
		c = extract_names (args, 'ContributorList');							-- fetch contributor list from |contributorn= / |contributor-lastn=, -firstn=, -linkn=, -maskn=&lt;br /&gt;
		&lt;br /&gt;
		if 0 &amp;lt; #c then&lt;br /&gt;
			if not utilities.is_set (Contribution) then							-- |contributor= requires |contribution=&lt;br /&gt;
				utilities.set_message ('err_contributor_missing_required_param', 'contribution');	-- add missing contribution error message&lt;br /&gt;
				c = {};															-- blank the contributors' table; it is used as a flag later&lt;br /&gt;
			end&lt;br /&gt;
			if 0 == #a then														-- |contributor= requires |author=&lt;br /&gt;
				utilities.set_message ('err_contributor_missing_required_param', 'author');	-- add missing author error message&lt;br /&gt;
				c = {};															-- blank the contributors' table; it is used as a flag later&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else																		-- if not a book cite&lt;br /&gt;
		if utilities.select_one (args, cfg.aliases['ContributorList-Last'], 'err_redundant_parameters', 1 ) then	-- are there contributor name list parameters?&lt;br /&gt;
			utilities.set_message ('err_contributor_ignored');					-- add contributor ignored error message&lt;br /&gt;
		end&lt;br /&gt;
		Contribution = nil;														-- unset&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local Title = A['Title'];&lt;br /&gt;
	local TitleLink = A['TitleLink'];&lt;br /&gt;
&lt;br /&gt;
	local auto_select = ''; -- default is auto&lt;br /&gt;
	local accept_link;&lt;br /&gt;
	TitleLink, accept_link = utilities.has_accept_as_written (TitleLink, true);	-- test for accept-this-as-written markup&lt;br /&gt;
	if (not accept_link) and utilities.in_array (TitleLink, {'none', 'pmc', 'doi'}) then -- check for special keywords&lt;br /&gt;
		auto_select = TitleLink;												-- remember selection for later&lt;br /&gt;
		TitleLink = '';															-- treat as if |title-link= would have been empty&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	TitleLink = link_title_ok (TitleLink, A:ORIGIN ('TitleLink'), Title, 'title');	-- check for wiki-markup in |title-link= or wiki-markup in |title= when |title-link= is set&lt;br /&gt;
&lt;br /&gt;
	local Section = '';															-- {{cite map}} only; preset to empty string for concatenation if not used&lt;br /&gt;
	if 'map' == config.CitationClass and 'section' == Chapter_origin then&lt;br /&gt;
		Section = A['Chapter'];													-- get |section= from |chapter= alias list; |chapter= and the other aliases not supported in {{cite map}}&lt;br /&gt;
		Chapter = '';															-- unset for now; will be reset later from |map= if present&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local Periodical = A['Periodical'];&lt;br /&gt;
	local Periodical_origin = '';&lt;br /&gt;
	if utilities.is_set (Periodical) then&lt;br /&gt;
		Periodical_origin = A:ORIGIN('Periodical');								-- get the name of the periodical parameter&lt;br /&gt;
		local i;&lt;br /&gt;
		Periodical, i = utilities.strip_apostrophe_markup (Periodical);			-- strip apostrophe markup so that metadata isn't contaminated &lt;br /&gt;
		if i then																-- non-zero when markup was stripped so emit an error message&lt;br /&gt;
			utilities.set_message ('err_apostrophe_markup', {Periodical_origin});&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if 'mailinglist' == config.CitationClass then								-- special case for {{cite mailing list}}&lt;br /&gt;
		if utilities.is_set (Periodical) and utilities.is_set (A ['MailingList']) then	-- both set emit an error TODO: make a function for this and similar?&lt;br /&gt;
			utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', Periodical_origin) .. ' and ' .. utilities.wrap_style ('parameter', 'mailinglist')});&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		Periodical = A ['MailingList'];											-- error or no, set Periodical to |mailinglist= value because this template is {{cite mailing list}}&lt;br /&gt;
		Periodical_origin = A:ORIGIN('MailingList');&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ScriptPeriodical = A['ScriptPeriodical'];&lt;br /&gt;
&lt;br /&gt;
	-- web and news not tested for now because of &lt;br /&gt;
	-- Wikipedia:Administrators%27_noticeboard#Is_there_a_semi-automated_tool_that_could_fix_these_annoying_&amp;quot;Cite_Web&amp;quot;_errors?&lt;br /&gt;
	if not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) then	-- 'periodical' templates require periodical parameter&lt;br /&gt;
	--	local p = {['journal'] = 'journal', ['magazine'] = 'magazine', ['news'] = 'newspaper', ['web'] = 'website'};	-- for error message&lt;br /&gt;
		local p = {['journal'] = 'journal', ['magazine'] = 'magazine'};			-- for error message&lt;br /&gt;
		if p[config.CitationClass]  then&lt;br /&gt;
			utilities.set_message ('err_missing_periodical', {config.CitationClass, p[config.CitationClass]});&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local Volume;&lt;br /&gt;
	local ScriptPeriodical_origin = A:ORIGIN('ScriptPeriodical');&lt;br /&gt;
	if 'citation' == config.CitationClass then&lt;br /&gt;
		if utilities.is_set (Periodical) then&lt;br /&gt;
			if not utilities.in_array (Periodical_origin, {'website', 'mailinglist'}) then	-- {{citation}} does not render volume for these 'periodicals' --TODO: move 'array' to ~/Configuration&lt;br /&gt;
				Volume = A['Volume'];											-- but does for all other 'periodicals'&lt;br /&gt;
			end&lt;br /&gt;
		elseif utilities.is_set (ScriptPeriodical) then&lt;br /&gt;
			if 'script-website' ~= ScriptPeriodical_origin then					-- {{citation}} does not render volume for |script-website=&lt;br /&gt;
				Volume = A['Volume'];											-- but does for all other 'periodicals'&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			Volume = A['Volume'];												-- and does for non-'periodical' cites&lt;br /&gt;
		end&lt;br /&gt;
	elseif utilities.in_array (config.CitationClass, cfg.templates_using_volume) then	-- render |volume= for cs1 according to the configuration settings&lt;br /&gt;
		Volume = A['Volume'];&lt;br /&gt;
	end	&lt;br /&gt;
	extra_text_in_vol_iss_check (Volume, A:ORIGIN ('Volume'), 'v');	&lt;br /&gt;
&lt;br /&gt;
	local Issue;&lt;br /&gt;
	if 'citation' == config.CitationClass then&lt;br /&gt;
		if utilities.is_set (Periodical) and utilities.in_array (Periodical_origin, {'journal', 'magazine', 'newspaper', 'periodical', 'work'}) or	-- {{citation}} renders issue for these 'periodicals'--TODO: move 'array' to ~/Configuration&lt;br /&gt;
			utilities.is_set (ScriptPeriodical) and utilities.in_array (ScriptPeriodical_origin, {'script-journal', 'script-magazine', 'script-newspaper', 'script-periodical', 'script-work'}) then -- and these 'script-periodicals'&lt;br /&gt;
				Issue = utilities.hyphen_to_dash (A['Issue']);&lt;br /&gt;
		end&lt;br /&gt;
	elseif utilities.in_array (config.CitationClass, cfg.templates_using_issue) then	-- conference &amp;amp; map books do not support issue; {{citation}} listed here because included in settings table&lt;br /&gt;
		if not (utilities.in_array (config.CitationClass, {'conference', 'map', 'citation'}) and not (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical))) then&lt;br /&gt;
			Issue = utilities.hyphen_to_dash (A['Issue']);&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	extra_text_in_vol_iss_check (Issue, A:ORIGIN ('Issue'), 'i');	&lt;br /&gt;
&lt;br /&gt;
	local Page;&lt;br /&gt;
	local Pages;&lt;br /&gt;
	local At;&lt;br /&gt;
	if not utilities.in_array (config.CitationClass, cfg.templates_not_using_page) then&lt;br /&gt;
		Page = A['Page'];&lt;br /&gt;
		Pages = utilities.hyphen_to_dash (A['Pages']);	&lt;br /&gt;
		At = A['At'];&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local Edition = A['Edition'];&lt;br /&gt;
	local PublicationPlace = place_check (A['PublicationPlace'], A:ORIGIN('PublicationPlace'));&lt;br /&gt;
	local Place = place_check (A['Place'], A:ORIGIN('Place'));&lt;br /&gt;
	&lt;br /&gt;
	local PublisherName = A['PublisherName'];&lt;br /&gt;
	local PublisherName_origin = A:ORIGIN('PublisherName');&lt;br /&gt;
	if utilities.is_set (PublisherName) then&lt;br /&gt;
		local i = 0;&lt;br /&gt;
		PublisherName, i = utilities.strip_apostrophe_markup (PublisherName);	-- strip apostrophe markup so that metadata isn't contaminated; publisher is never italicized&lt;br /&gt;
		if i then																-- non-zero when markup was stripped so emit an error message&lt;br /&gt;
			utilities.set_message ('err_apostrophe_markup', {PublisherName_origin});&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local Newsgroup = A['Newsgroup'];											-- TODO: strip apostrophe markup?&lt;br /&gt;
	local Newsgroup_origin = A:ORIGIN('Newsgroup');	&lt;br /&gt;
&lt;br /&gt;
	if 'newsgroup' == config.CitationClass then&lt;br /&gt;
		if utilities.is_set (PublisherName) then								-- general use parameter |publisher= not allowed in cite newsgroup&lt;br /&gt;
			utilities.set_message ('err_parameter_ignored', {PublisherName_origin});&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		PublisherName = nil;													-- ensure that this parameter is unset for the time being; will be used again after COinS&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local URL = A['URL'];														-- TODO: better way to do this for URL, ChapterURL, and MapURL?&lt;br /&gt;
	local UrlAccess = is_valid_parameter_value (A['UrlAccess'], A:ORIGIN('UrlAccess'), cfg.keywords_lists['url-access'], nil);&lt;br /&gt;
	&lt;br /&gt;
	if not utilities.is_set (URL) and utilities.is_set (UrlAccess) then&lt;br /&gt;
		UrlAccess = nil;&lt;br /&gt;
		utilities.set_message ('err_param_access_requires_param', 'url');&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local ChapterURL = A['ChapterURL'];&lt;br /&gt;
	local ChapterUrlAccess = is_valid_parameter_value (A['ChapterUrlAccess'], A:ORIGIN('ChapterUrlAccess'), cfg.keywords_lists['url-access'], nil);&lt;br /&gt;
	if not utilities.is_set (ChapterURL) and utilities.is_set (ChapterUrlAccess) then&lt;br /&gt;
		ChapterUrlAccess = nil;&lt;br /&gt;
		utilities.set_message ('err_param_access_requires_param', {A:ORIGIN('ChapterUrlAccess'):gsub ('%-access', '')});&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local MapUrlAccess = is_valid_parameter_value (A['MapUrlAccess'], A:ORIGIN('MapUrlAccess'), cfg.keywords_lists['url-access'], nil);&lt;br /&gt;
	if not utilities.is_set (A['MapURL']) and utilities.is_set (MapUrlAccess) then&lt;br /&gt;
		MapUrlAccess = nil;&lt;br /&gt;
		utilities.set_message ('err_param_access_requires_param', {'map-url'});&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local this_page = mw.title.getCurrentTitle();								-- also used for COinS and for language&lt;br /&gt;
	local no_tracking_cats = is_valid_parameter_value (A['NoTracking'], A:ORIGIN('NoTracking'), cfg.keywords_lists['yes_true_y'], nil);&lt;br /&gt;
&lt;br /&gt;
	-- check this page to see if it is in one of the namespaces that cs1 is not supposed to add to the error categories&lt;br /&gt;
	if not utilities.is_set (no_tracking_cats) then								-- ignore if we are already not going to categorize this page&lt;br /&gt;
		if utilities.in_array (this_page.nsText, cfg.uncategorized_namespaces) then&lt;br /&gt;
			no_tracking_cats = &amp;quot;true&amp;quot;;											-- set no_tracking_cats&lt;br /&gt;
		end&lt;br /&gt;
		for _, v in ipairs (cfg.uncategorized_subpages) do						-- cycle through page name patterns&lt;br /&gt;
			if this_page.text:match (v) then									-- test page name against each pattern&lt;br /&gt;
				no_tracking_cats = &amp;quot;true&amp;quot;;										-- set no_tracking_cats&lt;br /&gt;
				break;															-- bail out if one is found&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
																				-- check for extra |page=, |pages= or |at= parameters. (also sheet and sheets while we're at it)&lt;br /&gt;
	utilities.select_one (args, {'page', 'p', 'pp', 'pages', 'at', 'sheet', 'sheets'}, 'err_redundant_parameters');	-- this is a dummy call simply to get the error message and category&lt;br /&gt;
&lt;br /&gt;
	local coins_pages;&lt;br /&gt;
	&lt;br /&gt;
	Page, Pages, At, coins_pages = insource_loc_get (Page, A:ORIGIN('Page'), Pages, A:ORIGIN('Pages'), At);&lt;br /&gt;
&lt;br /&gt;
	local NoPP = is_valid_parameter_value (A['NoPP'], A:ORIGIN('NoPP'), cfg.keywords_lists['yes_true_y'], nil);&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (PublicationPlace) and utilities.is_set (Place) then	-- both |publication-place= and |place= (|location=) allowed if different&lt;br /&gt;
		utilities.add_prop_cat ('location-test');								-- add property cat to evaluate how often PublicationPlace and Place are used together&lt;br /&gt;
		if PublicationPlace == Place then&lt;br /&gt;
			Place = '';															-- unset; don't need both if they are the same&lt;br /&gt;
		end&lt;br /&gt;
	elseif not utilities.is_set (PublicationPlace) and utilities.is_set (Place) then	-- when only |place= (|location=) is set ...&lt;br /&gt;
		PublicationPlace = Place;												-- promote |place= (|location=) to |publication-place&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if PublicationPlace == Place then Place = ''; end							-- don't need both if they are the same&lt;br /&gt;
	&lt;br /&gt;
	local URL_origin = A:ORIGIN('URL');											-- get name of parameter that holds URL&lt;br /&gt;
	local ChapterURL_origin = A:ORIGIN('ChapterURL');							-- get name of parameter that holds ChapterURL&lt;br /&gt;
	local ScriptChapter = A['ScriptChapter'];&lt;br /&gt;
	local ScriptChapter_origin = A:ORIGIN ('ScriptChapter');&lt;br /&gt;
	local Format = A['Format'];&lt;br /&gt;
	local ChapterFormat = A['ChapterFormat'];&lt;br /&gt;
	local TransChapter = A['TransChapter'];&lt;br /&gt;
	local TransChapter_origin = A:ORIGIN ('TransChapter');&lt;br /&gt;
	local TransTitle = A['TransTitle'];&lt;br /&gt;
	local ScriptTitle = A['ScriptTitle'];&lt;br /&gt;
	&lt;br /&gt;
	--[[&lt;br /&gt;
	Parameter remapping for cite encyclopedia:&lt;br /&gt;
	When the citation has these parameters:&lt;br /&gt;
		|encyclopedia= and |title= then map |title= to |article= and |encyclopedia= to |title=&lt;br /&gt;
		|encyclopedia= and |article= then map |encyclopedia= to |title=&lt;br /&gt;
&lt;br /&gt;
		|trans-title= maps to |trans-chapter= when |title= is re-mapped&lt;br /&gt;
		|url= maps to |chapter-url= when |title= is remapped&lt;br /&gt;
	&lt;br /&gt;
	All other combinations of |encyclopedia=, |title=, and |article= are not modified&lt;br /&gt;
	&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local Encyclopedia = A['Encyclopedia'];										-- used as a flag by this module and by ~/COinS&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (Encyclopedia) then										-- emit error message when Encyclopedia set but template is other than {{cite encyclopedia}} or {{citation}}&lt;br /&gt;
		if 'encyclopaedia' ~= config.CitationClass and 'citation' ~= config.CitationClass then&lt;br /&gt;
			utilities.set_message ('err_parameter_ignored', {A:ORIGIN ('Encyclopedia')});&lt;br /&gt;
			Encyclopedia = nil;													-- unset because not supported by this template&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if ('encyclopaedia' == config.CitationClass) or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then&lt;br /&gt;
		if utilities.is_set (Periodical) and utilities.is_set (Encyclopedia) then	-- when both set emit an error TODO: make a function for this and similar?&lt;br /&gt;
			utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', A:ORIGIN ('Encyclopedia')) .. ' and ' .. utilities.wrap_style ('parameter', Periodical_origin)});&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (Encyclopedia) then&lt;br /&gt;
			Periodical = Encyclopedia;											-- error or no, set Periodical to Encyclopedia; allow periodical without encyclopedia&lt;br /&gt;
			Periodical_origin = A:ORIGIN ('Encyclopedia');&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (Periodical) then									-- Periodical is set when |encyclopedia= is set&lt;br /&gt;
			if utilities.is_set (Title) or utilities.is_set (ScriptTitle) then&lt;br /&gt;
				if not utilities.is_set (Chapter) then&lt;br /&gt;
					Chapter = Title;											-- |encyclopedia= and |title= are set so map |title= to |article= and |encyclopedia= to |title=&lt;br /&gt;
					ScriptChapter = ScriptTitle;&lt;br /&gt;
					ScriptChapter_origin = A:ORIGIN('ScriptTitle')&lt;br /&gt;
					TransChapter = TransTitle;&lt;br /&gt;
					ChapterURL = URL;&lt;br /&gt;
					ChapterURL_origin = URL_origin;&lt;br /&gt;
&lt;br /&gt;
					ChapterUrlAccess = UrlAccess;&lt;br /&gt;
&lt;br /&gt;
					if not utilities.is_set (ChapterURL) and utilities.is_set (TitleLink) then&lt;br /&gt;
						Chapter = utilities.make_wikilink (TitleLink, Chapter);&lt;br /&gt;
					end&lt;br /&gt;
					Title = Periodical;&lt;br /&gt;
					ChapterFormat = Format;&lt;br /&gt;
					Periodical = '';											-- redundant so unset&lt;br /&gt;
					TransTitle = '';&lt;br /&gt;
					URL = '';&lt;br /&gt;
					Format = '';&lt;br /&gt;
					TitleLink = '';&lt;br /&gt;
					ScriptTitle = '';&lt;br /&gt;
				end&lt;br /&gt;
			elseif utilities.is_set (Chapter) or utilities.is_set (ScriptChapter) then								-- |title= not set&lt;br /&gt;
				Title = Periodical;												-- |encyclopedia= set and |article= set so map |encyclopedia= to |title=&lt;br /&gt;
				Periodical = '';												-- redundant so unset&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- special case for cite techreport.&lt;br /&gt;
	local ID = A['ID'];&lt;br /&gt;
	if (config.CitationClass == &amp;quot;techreport&amp;quot;) then								-- special case for cite techreport&lt;br /&gt;
		if utilities.is_set (A['Number']) then									-- cite techreport uses 'number', which other citations alias to 'issue'&lt;br /&gt;
			if not utilities.is_set (ID) then									-- can we use ID for the &amp;quot;number&amp;quot;?&lt;br /&gt;
				ID = A['Number'];												-- yes, use it&lt;br /&gt;
			else																-- ID has a value so emit error message&lt;br /&gt;
				utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'id') .. ' and ' .. utilities.wrap_style ('parameter', 'number')});&lt;br /&gt;
			end&lt;br /&gt;
		end	&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Account for the oddity that is {{cite conference}}, before generation of COinS data.&lt;br /&gt;
	local ChapterLink -- = A['ChapterLink'];									-- deprecated as a parameter but still used internally by cite episode&lt;br /&gt;
	local Conference = A['Conference'];&lt;br /&gt;
	local BookTitle = A['BookTitle'];&lt;br /&gt;
	local TransTitle_origin = A:ORIGIN ('TransTitle');&lt;br /&gt;
	if 'conference' == config.CitationClass then&lt;br /&gt;
		if utilities.is_set (BookTitle) then&lt;br /&gt;
			Chapter = Title;&lt;br /&gt;
			Chapter_origin = 'title';&lt;br /&gt;
	--		ChapterLink = TitleLink;											-- |chapter-link= is deprecated&lt;br /&gt;
			ChapterURL = URL;&lt;br /&gt;
			ChapterUrlAccess = UrlAccess;&lt;br /&gt;
			ChapterURL_origin = URL_origin;&lt;br /&gt;
			URL_origin = '';&lt;br /&gt;
			ChapterFormat = Format;&lt;br /&gt;
			TransChapter = TransTitle;&lt;br /&gt;
			TransChapter_origin = TransTitle_origin;&lt;br /&gt;
			Title = BookTitle;&lt;br /&gt;
			Format = '';&lt;br /&gt;
	--		TitleLink = '';&lt;br /&gt;
			TransTitle = '';&lt;br /&gt;
			URL = '';&lt;br /&gt;
		end&lt;br /&gt;
	elseif 'speech' ~= config.CitationClass then&lt;br /&gt;
		Conference = '';														-- not cite conference or cite speech so make sure this is empty string&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- CS1/2 mode&lt;br /&gt;
	local Mode = is_valid_parameter_value (A['Mode'], A:ORIGIN('Mode'), cfg.keywords_lists['mode'], '');&lt;br /&gt;
	-- separator character and postscript&lt;br /&gt;
	local sepc, PostScript = set_style (Mode:lower(), A['PostScript'], config.CitationClass);&lt;br /&gt;
	-- controls capitalization of certain static text&lt;br /&gt;
	local use_lowercase = ( sepc == ',' );&lt;br /&gt;
	&lt;br /&gt;
	-- cite map oddities&lt;br /&gt;
	local Cartography = &amp;quot;&amp;quot;;&lt;br /&gt;
	local Scale = &amp;quot;&amp;quot;;&lt;br /&gt;
	local Sheet = A['Sheet'] or '';&lt;br /&gt;
	local Sheets = A['Sheets'] or '';&lt;br /&gt;
	if config.CitationClass == &amp;quot;map&amp;quot; then&lt;br /&gt;
		if utilities.is_set (Chapter) then										--TODO: make a function for this and similar?&lt;br /&gt;
			utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'map') .. ' and ' .. utilities.wrap_style ('parameter', Chapter_origin)});	-- add error message&lt;br /&gt;
		end&lt;br /&gt;
		Chapter = A['Map'];&lt;br /&gt;
		Chapter_origin = A:ORIGIN('Map');&lt;br /&gt;
		ChapterURL = A['MapURL'];&lt;br /&gt;
		ChapterURL_origin = A:ORIGIN('MapURL');&lt;br /&gt;
		TransChapter = A['TransMap'];&lt;br /&gt;
		ScriptChapter = A['ScriptMap']&lt;br /&gt;
		ScriptChapter_origin = A:ORIGIN('ScriptMap')&lt;br /&gt;
&lt;br /&gt;
		ChapterUrlAccess = MapUrlAccess;&lt;br /&gt;
		ChapterFormat = A['MapFormat'];&lt;br /&gt;
&lt;br /&gt;
		Cartography = A['Cartography'];&lt;br /&gt;
		if utilities.is_set ( Cartography ) then&lt;br /&gt;
			Cartography = sepc .. &amp;quot; &amp;quot; .. wrap_msg ('cartography', Cartography, use_lowercase);&lt;br /&gt;
		end		&lt;br /&gt;
		Scale = A['Scale'];&lt;br /&gt;
		if utilities.is_set ( Scale ) then&lt;br /&gt;
			Scale = sepc .. &amp;quot; &amp;quot; .. Scale;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Account for the oddities that are {{cite episode}} and {{cite serial}}, before generation of COinS data.&lt;br /&gt;
	local Series = A['Series'];&lt;br /&gt;
	if 'episode' == config.CitationClass or 'serial' == config.CitationClass then&lt;br /&gt;
		local SeriesLink = A['SeriesLink'];&lt;br /&gt;
&lt;br /&gt;
		SeriesLink = link_title_ok (SeriesLink, A:ORIGIN ('SeriesLink'), Series, 'series');	-- check for wiki-markup in |series-link= or wiki-markup in |series= when |series-link= is set&lt;br /&gt;
&lt;br /&gt;
		local Network = A['Network'];&lt;br /&gt;
		local Station = A['Station'];&lt;br /&gt;
		local s, n = {}, {};&lt;br /&gt;
																				-- do common parameters first&lt;br /&gt;
		if utilities.is_set (Network) then table.insert(n, Network); end&lt;br /&gt;
		if utilities.is_set (Station) then table.insert(n, Station); end&lt;br /&gt;
		ID = table.concat(n, sepc .. ' ');&lt;br /&gt;
		&lt;br /&gt;
		if 'episode' == config.CitationClass then								-- handle the oddities that are strictly {{cite episode}}&lt;br /&gt;
			local Season = A['Season'];&lt;br /&gt;
			local SeriesNumber = A['SeriesNumber'];&lt;br /&gt;
&lt;br /&gt;
			if utilities.is_set (Season) and utilities.is_set (SeriesNumber) then	-- these are mutually exclusive so if both are set TODO: make a function for this and similar?&lt;br /&gt;
				utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'season') .. ' and ' .. utilities.wrap_style ('parameter', 'seriesno')});		-- add error message&lt;br /&gt;
				SeriesNumber = '';												-- unset; prefer |season= over |seriesno=&lt;br /&gt;
			end&lt;br /&gt;
																				-- assemble a table of parts concatenated later into Series&lt;br /&gt;
			if utilities.is_set (Season) then table.insert(s, wrap_msg ('season', Season, use_lowercase)); end&lt;br /&gt;
			if utilities.is_set (SeriesNumber) then table.insert(s, wrap_msg ('seriesnum', SeriesNumber, use_lowercase)); end&lt;br /&gt;
			if utilities.is_set (Issue) then table.insert(s, wrap_msg ('episode', Issue, use_lowercase)); end&lt;br /&gt;
			Issue = '';															-- unset because this is not a unique parameter&lt;br /&gt;
	&lt;br /&gt;
			Chapter = Title;													-- promote title parameters to chapter&lt;br /&gt;
			ScriptChapter = ScriptTitle;&lt;br /&gt;
			ScriptChapter_origin = A:ORIGIN('ScriptTitle');&lt;br /&gt;
			ChapterLink = TitleLink;											-- alias |episode-link=&lt;br /&gt;
			TransChapter = TransTitle;&lt;br /&gt;
			ChapterURL = URL;&lt;br /&gt;
			ChapterUrlAccess = UrlAccess;&lt;br /&gt;
			ChapterURL_origin = URL_origin;&lt;br /&gt;
			&lt;br /&gt;
			Title = Series;														-- promote series to title&lt;br /&gt;
			TitleLink = SeriesLink;&lt;br /&gt;
			Series = table.concat(s, sepc .. ' ');								-- this is concatenation of season, seriesno, episode number&lt;br /&gt;
&lt;br /&gt;
			if utilities.is_set (ChapterLink) and not utilities.is_set (ChapterURL) then	-- link but not URL&lt;br /&gt;
				Chapter = utilities.make_wikilink (ChapterLink, Chapter);&lt;br /&gt;
			elseif utilities.is_set (ChapterLink) and utilities.is_set (ChapterURL) then	-- if both are set, URL links episode;&lt;br /&gt;
				Series = utilities.make_wikilink (ChapterLink, Series);&lt;br /&gt;
			end&lt;br /&gt;
			URL = '';															-- unset&lt;br /&gt;
			TransTitle = '';&lt;br /&gt;
			ScriptTitle = '';&lt;br /&gt;
			&lt;br /&gt;
		else																	-- now oddities that are cite serial&lt;br /&gt;
			Issue = '';															-- unset because this parameter no longer supported by the citation/core version of cite serial&lt;br /&gt;
			Chapter = A['Episode'];												-- TODO: make |episode= available to cite episode someday?&lt;br /&gt;
			if utilities.is_set (Series) and utilities.is_set (SeriesLink) then&lt;br /&gt;
				Series = utilities.make_wikilink (SeriesLink, Series);&lt;br /&gt;
			end&lt;br /&gt;
			Series = utilities.wrap_style ('italic-title', Series);				-- series is italicized&lt;br /&gt;
		end	&lt;br /&gt;
	end&lt;br /&gt;
	-- end of {{cite episode}} stuff&lt;br /&gt;
&lt;br /&gt;
	-- handle type parameter for those CS1 citations that have default values&lt;br /&gt;
	local TitleType = A['TitleType'];&lt;br /&gt;
	local Degree = A['Degree'];&lt;br /&gt;
	if utilities.in_array (config.CitationClass, {'AV-media-notes', 'interview', 'mailinglist', 'map', 'podcast', 'pressrelease', 'report', 'speech', 'techreport', 'thesis'}) then&lt;br /&gt;
		TitleType = set_titletype (config.CitationClass, TitleType);&lt;br /&gt;
		if utilities.is_set (Degree) and &amp;quot;Thesis&amp;quot; == TitleType then				-- special case for cite thesis&lt;br /&gt;
			TitleType = Degree .. ' ' .. cfg.title_types ['thesis']:lower();&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (TitleType) then										-- if type parameter is specified&lt;br /&gt;
		TitleType = utilities.substitute ( cfg.messages['type'], TitleType);	-- display it in parentheses&lt;br /&gt;
	-- TODO: Hack on TitleType to fix bunched parentheses problem&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- legacy: promote PublicationDate to Date if neither Date nor Year are set.&lt;br /&gt;
	local Date = A['Date'];&lt;br /&gt;
 	local Date_origin;															-- to hold the name of parameter promoted to Date; required for date error messaging&lt;br /&gt;
	local PublicationDate = A['PublicationDate'];&lt;br /&gt;
	local Year = A['Year'];&lt;br /&gt;
&lt;br /&gt;
	if not utilities.is_set (Date) then&lt;br /&gt;
		Date = Year;															-- promote Year to Date&lt;br /&gt;
		Year = nil;																-- make nil so Year as empty string isn't used for CITEREF&lt;br /&gt;
		if not utilities.is_set (Date) and utilities.is_set (PublicationDate) then	-- use PublicationDate when |date= and |year= are not set&lt;br /&gt;
			Date = PublicationDate;												-- promote PublicationDate to Date&lt;br /&gt;
			PublicationDate = '';												-- unset, no longer needed&lt;br /&gt;
			Date_origin = A:ORIGIN('PublicationDate');							-- save the name of the promoted parameter&lt;br /&gt;
		else&lt;br /&gt;
			Date_origin = A:ORIGIN('Year');										-- save the name of the promoted parameter&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		Date_origin = A:ORIGIN('Date');											-- not a promotion; name required for error messaging&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if PublicationDate == Date then PublicationDate = ''; end					-- if PublicationDate is same as Date, don't display in rendered citation&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	Go test all of the date-holding parameters for valid MOS:DATE format and make sure that dates are real dates. This must be done before we do COinS because here is where&lt;br /&gt;
	we get the date used in the metadata.&lt;br /&gt;
	&lt;br /&gt;
	Date validation supporting code is in Module:Citation/CS1/Date_validation&lt;br /&gt;
	]]&lt;br /&gt;
&lt;br /&gt;
	local DF = is_valid_parameter_value (A['DF'], A:ORIGIN('DF'), cfg.keywords_lists['df'], '');&lt;br /&gt;
	if not utilities.is_set (DF) then&lt;br /&gt;
		DF = cfg.global_df;														-- local |df= if present overrides global df set by {{use xxx date}} template&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ArchiveURL;&lt;br /&gt;
	local ArchiveDate;&lt;br /&gt;
	local ArchiveFormat = A['ArchiveFormat'];&lt;br /&gt;
&lt;br /&gt;
	ArchiveURL, ArchiveDate = archive_url_check (A['ArchiveURL'], A['ArchiveDate'])&lt;br /&gt;
	ArchiveFormat = style_format (ArchiveFormat, ArchiveURL, 'archive-format', 'archive-url');&lt;br /&gt;
	&lt;br /&gt;
	ArchiveURL, ArchiveDate = is_unique_archive_url (ArchiveURL, URL, ChapterURL, A:ORIGIN('ArchiveURL'), ArchiveDate);		-- add error message when URL or ChapterURL == ArchiveURL&lt;br /&gt;
&lt;br /&gt;
	&lt;br /&gt;
	local AccessDate = A['AccessDate'];&lt;br /&gt;
	local LayDate = A['LayDate'];&lt;br /&gt;
	local COinS_date = {};														-- holds date info extracted from |date= for the COinS metadata by Module:Date verification&lt;br /&gt;
	local DoiBroken = A['DoiBroken'];&lt;br /&gt;
	local Embargo = A['Embargo'];&lt;br /&gt;
	local anchor_year;															-- used in the CITEREF identifier&lt;br /&gt;
	do	-- create defined block to contain local variables error_message, date_parameters_list, mismatch&lt;br /&gt;
		local error_message = '';&lt;br /&gt;
																				-- AirDate has been promoted to Date so not necessary to check it&lt;br /&gt;
		local date_parameters_list = {&lt;br /&gt;
			['access-date'] = {val = AccessDate, name = A:ORIGIN ('AccessDate')},&lt;br /&gt;
			['archive-date'] = {val = ArchiveDate, name = A:ORIGIN ('ArchiveDate')},&lt;br /&gt;
			['date'] = {val = Date, name = Date_origin},&lt;br /&gt;
			['doi-broken-date'] = {val = DoiBroken, name = A:ORIGIN ('DoiBroken')},&lt;br /&gt;
			['pmc-embargo-date'] = {val = Embargo, name = A:ORIGIN ('Embargo')},&lt;br /&gt;
			['lay-date'] = {val = LayDate, name = A:ORIGIN ('LayDate')},&lt;br /&gt;
			['publication-date'] = {val = PublicationDate, name = A:ORIGIN ('PublicationDate')},&lt;br /&gt;
			['year'] = {val = Year, name = A:ORIGIN ('Year')},&lt;br /&gt;
			};&lt;br /&gt;
&lt;br /&gt;
		local error_list = {};&lt;br /&gt;
		anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list);&lt;br /&gt;
&lt;br /&gt;
-- start temporary Julian / Gregorian calendar uncertainty categorization&lt;br /&gt;
		if COinS_date.inter_cal_cat then&lt;br /&gt;
			utilities.add_prop_cat ('jul-greg-uncertainty');&lt;br /&gt;
		end&lt;br /&gt;
-- end temporary Julian / Gregorian calendar uncertainty categorization&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (Year) and utilities.is_set (Date) then				-- both |date= and |year= not normally needed; &lt;br /&gt;
			validation.year_date_check (Year, A:ORIGIN ('Year'), Date, A:ORIGIN ('Date'), error_list);&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		if 0 == #error_list then												-- error free dates only; 0 when error_list is empty&lt;br /&gt;
			local modified = false;												-- flag&lt;br /&gt;
			&lt;br /&gt;
			if utilities.is_set (DF) then										-- if we need to reformat dates&lt;br /&gt;
				modified = validation.reformat_dates (date_parameters_list, DF);	-- reformat to DF format, use long month names if appropriate&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if true == validation.date_hyphen_to_dash (date_parameters_list) then	-- convert hyphens to dashes where appropriate&lt;br /&gt;
				modified = true;&lt;br /&gt;
				utilities.set_message ('maint_date_format');					-- hyphens were converted so add maint category&lt;br /&gt;
			end&lt;br /&gt;
			&lt;br /&gt;
	-- for those wikis that can and want to have English date names translated to the local language; not supported at en.wiki&lt;br /&gt;
			if cfg.date_name_auto_xlate_enable and validation.date_name_xlate (date_parameters_list, cfg.date_digit_auto_xlate_enable ) then&lt;br /&gt;
				utilities.set_message ('maint_date_auto_xlated');				-- add maint cat&lt;br /&gt;
				modified = true;&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if modified then													-- if the date_parameters_list values were modified&lt;br /&gt;
				AccessDate = date_parameters_list['access-date'].val;			-- overwrite date holding parameters with modified values&lt;br /&gt;
				ArchiveDate = date_parameters_list['archive-date'].val;&lt;br /&gt;
				Date = date_parameters_list['date'].val;&lt;br /&gt;
				DoiBroken = date_parameters_list['doi-broken-date'].val;&lt;br /&gt;
				LayDate = date_parameters_list['lay-date'].val;&lt;br /&gt;
				PublicationDate = date_parameters_list['publication-date'].val;&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			utilities.set_message ('err_bad_date', {utilities.make_sep_list (#error_list, error_list)});	-- add this error message&lt;br /&gt;
		end&lt;br /&gt;
	end	-- end of do&lt;br /&gt;
&lt;br /&gt;
	local ID_list = {};															-- sequence table of rendered identifiers&lt;br /&gt;
	local ID_list_coins = {};													-- table of identifiers and their values from args; key is same as cfg.id_handlers's key&lt;br /&gt;
	local Class = A['Class'];													-- arxiv class identifier&lt;br /&gt;
	&lt;br /&gt;
	local ID_support = {&lt;br /&gt;
		{A['ASINTLD'], 'ASIN', 'err_asintld_missing_asin', A:ORIGIN ('ASINTLD')},				&lt;br /&gt;
		{DoiBroken, 'DOI', 'err_doibroken_missing_doi', A:ORIGIN ('DoiBroken')},&lt;br /&gt;
		{Embargo, 'PMC', 'err_embargo_missing_pmc', A:ORIGIN ('Embargo')},&lt;br /&gt;
		}&lt;br /&gt;
&lt;br /&gt;
	ID_list, ID_list_coins = identifiers.identifier_lists_get (args, {DoiBroken = DoiBroken, ASINTLD = A['ASINTLD'], Embargo = Embargo, Class = Class}, ID_support);&lt;br /&gt;
&lt;br /&gt;
	-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, {{cite ssrn}}, before generation of COinS data.&lt;br /&gt;
	if utilities.in_array (config.CitationClass, whitelist.preprint_template_list) then&lt;br /&gt;
		if not utilities.is_set (ID_list_coins[config.CitationClass:upper()]) then 	-- |arxiv= or |eprint= required for cite arxiv; |biorxiv= &amp;amp; |citeseerx= required for their templates&lt;br /&gt;
			utilities.set_message ('err_' .. config.CitationClass .. '_missing');	-- add error message&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		Periodical = ({['arxiv'] = 'arXiv', ['biorxiv'] = 'bioRxiv', ['citeseerx'] = 'CiteSeerX', ['ssrn'] = 'Social Science Research Network'})[config.CitationClass];&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Link the title of the work if no |url= was provided, but we have a |pmc= or a |doi= with |doi-access=free&lt;br /&gt;
&lt;br /&gt;
	if config.CitationClass == &amp;quot;journal&amp;quot; and not utilities.is_set (URL) and not utilities.is_set (TitleLink) and not utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) then -- TODO: remove 'none' once existing citations have been switched to 'off', so 'none' can be used as token for &amp;quot;no title&amp;quot; instead&lt;br /&gt;
		if 'none' ~= cfg.keywords_xlate[auto_select] then						-- if auto-linking not disabled&lt;br /&gt;
 	 		if identifiers.auto_link_urls[auto_select] then						-- manual selection&lt;br /&gt;
		 		URL = identifiers.auto_link_urls[auto_select];					-- set URL to be the same as identifier's external link&lt;br /&gt;
 				URL_origin = cfg.id_handlers[auto_select:upper()].parameters[1];	-- set URL_origin to parameter name for use in error message if citation is missing a |title=&lt;br /&gt;
			elseif identifiers.auto_link_urls['pmc'] then						-- auto-select PMC&lt;br /&gt;
				URL = identifiers.auto_link_urls['pmc'];						-- set URL to be the same as the PMC external link if not embargoed&lt;br /&gt;
				URL_origin = cfg.id_handlers['PMC'].parameters[1];				-- set URL_origin to parameter name for use in error message if citation is missing a |title=&lt;br /&gt;
			elseif identifiers.auto_link_urls['doi'] then						-- auto-select DOI&lt;br /&gt;
				URL = identifiers.auto_link_urls['doi'];&lt;br /&gt;
				URL_origin = cfg.id_handlers['DOI'].parameters[1];&lt;br /&gt;
			end&lt;br /&gt;
 	 	end&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (URL) and utilities.is_set (AccessDate) then		-- access date requires |url=; identifier-created URL is not |url=&lt;br /&gt;
			utilities.set_message ('err_accessdate_missing_url');				-- add an error message&lt;br /&gt;
			AccessDate = '';													-- unset&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- At this point fields may be nil if they weren't specified in the template use.  We can use that fact.&lt;br /&gt;
	-- Test if citation has no title&lt;br /&gt;
	if	not utilities.is_set (Title) and not utilities.is_set (TransTitle) and not utilities.is_set (ScriptTitle) then	-- has special case for cite episode&lt;br /&gt;
		utilities.set_message ('err_citation_missing_title', {'episode' == config.CitationClass and 'series' or 'title'});&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.in_array (cfg.keywords_xlate[Title], {'off', 'none'}) and&lt;br /&gt;
			utilities.in_array (config.CitationClass, {'journal', 'citation'}) and&lt;br /&gt;
			(utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and&lt;br /&gt;
			('journal' == Periodical_origin or 'script-journal' == ScriptPeriodical_origin) then	-- special case for journal cites&lt;br /&gt;
				Title = '';														-- set title to empty string&lt;br /&gt;
				utilities.set_message ('maint_untitled');						-- add maint cat&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- COinS metadata (see &amp;lt;http://ocoins.info/&amp;gt;) for automated parsing of citation information.&lt;br /&gt;
	-- handle the oddity that is cite encyclopedia and {{citation |encyclopedia=something}}. Here we presume that&lt;br /&gt;
	-- when Periodical, Title, and Chapter are all set, then Periodical is the book (encyclopedia) title, Title&lt;br /&gt;
	-- is the article title, and Chapter is a section within the article.  So, we remap &lt;br /&gt;
	&lt;br /&gt;
	local coins_chapter = Chapter;												-- default assuming that remapping not required&lt;br /&gt;
	local coins_title = Title;													-- et tu&lt;br /&gt;
	if 'encyclopaedia' == config.CitationClass or ('citation' == config.CitationClass and utilities.is_set (Encyclopedia)) then&lt;br /&gt;
		if utilities.is_set (Chapter) and utilities.is_set (Title) and utilities.is_set (Periodical) then		-- if all are used then&lt;br /&gt;
			coins_chapter = Title;												-- remap&lt;br /&gt;
			coins_title = Periodical;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local coins_author = a;														-- default for coins rft.au &lt;br /&gt;
	if 0 &amp;lt; #c then																-- but if contributor list&lt;br /&gt;
		coins_author = c;														-- use that instead&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local QuotePage = A['QuotePage'];&lt;br /&gt;
	local QuotePages = utilities.hyphen_to_dash (A['QuotePages']);&lt;br /&gt;
&lt;br /&gt;
	-- this is the function call to COinS()&lt;br /&gt;
	local OCinSoutput = metadata.COinS({&lt;br /&gt;
		['Periodical'] = utilities.strip_apostrophe_markup (Periodical),		-- no markup in the metadata&lt;br /&gt;
		['Encyclopedia'] = Encyclopedia,										-- just a flag; content ignored by ~/COinS&lt;br /&gt;
		['Chapter'] = metadata.make_coins_title (coins_chapter, ScriptChapter),	-- Chapter and ScriptChapter stripped of bold / italic / accept-as-written markup&lt;br /&gt;
		['Degree'] = Degree;													-- cite thesis only&lt;br /&gt;
		['Title'] = metadata.make_coins_title (coins_title, ScriptTitle),		-- Title and ScriptTitle stripped of bold / italic / accept-as-written markup&lt;br /&gt;
		['PublicationPlace'] = PublicationPlace,&lt;br /&gt;
		['Date'] = COinS_date.rftdate,											-- COinS_date has correctly formatted date if Date is valid;&lt;br /&gt;
		['Season'] = COinS_date.rftssn,&lt;br /&gt;
		['Quarter'] = COinS_date.rftquarter,&lt;br /&gt;
		['Chron'] =  COinS_date.rftchron or (not COinS_date.rftdate and Date) or '',	-- chron but if not set and invalid date format use Date; keep this last bit?&lt;br /&gt;
		['Series'] = Series,&lt;br /&gt;
		['Volume'] = Volume,&lt;br /&gt;
		['Issue'] = Issue,&lt;br /&gt;
		['Pages'] = coins_pages or metadata.get_coins_pages (first_set ({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)),	-- pages stripped of external links&lt;br /&gt;
		['Edition'] = Edition,&lt;br /&gt;
		['PublisherName'] = PublisherName or Newsgroup,							-- any apostrophe markup already removed from PublisherName&lt;br /&gt;
		['URL'] = first_set ({ChapterURL, URL}, 2),&lt;br /&gt;
		['Authors'] = coins_author,&lt;br /&gt;
		['ID_list'] = ID_list_coins,&lt;br /&gt;
		['RawPage'] = this_page.prefixedText,&lt;br /&gt;
	}, config.CitationClass);&lt;br /&gt;
&lt;br /&gt;
	-- Account for the oddities that are {{cite arxiv}}, {{cite biorxiv}}, {{cite citeseerx}}, and {{cite ssrn}} AFTER generation of COinS data.&lt;br /&gt;
	if utilities.in_array (config.CitationClass, whitelist.preprint_template_list) then	-- we have set rft.jtitle in COinS to arXiv, bioRxiv, CiteSeerX, or ssrn now unset so it isn't displayed&lt;br /&gt;
		Periodical = '';														-- periodical not allowed in these templates; if article has been published, use cite journal&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- special case for cite newsgroup.  Do this after COinS because we are modifying Publishername to include some static text&lt;br /&gt;
	if 'newsgroup' == config.CitationClass and utilities.is_set (Newsgroup) then&lt;br /&gt;
		PublisherName = utilities.substitute (cfg.messages['newsgroup'], external_link( 'news:' .. Newsgroup, Newsgroup, Newsgroup_origin, nil ));&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local Editors;&lt;br /&gt;
	local EditorCount;															-- used only for choosing {ed.) or (eds.) annotation at end of editor name-list&lt;br /&gt;
	local Contributors;															-- assembled contributors name list&lt;br /&gt;
	local contributor_etal;&lt;br /&gt;
	local Translators;															-- assembled translators name list&lt;br /&gt;
	local translator_etal;&lt;br /&gt;
	local t = {};																-- translators list from |translator-lastn= / translator-firstn= pairs&lt;br /&gt;
	t = extract_names (args, 'TranslatorList');									-- fetch translator list from |translatorn= / |translator-lastn=, -firstn=, -linkn=, -maskn=&lt;br /&gt;
	local Interviewers;															&lt;br /&gt;
	local interviewers_list = {};					&lt;br /&gt;
	interviewers_list = extract_names (args, 'InterviewerList');				-- process preferred interviewers parameters&lt;br /&gt;
	local interviewer_etal;&lt;br /&gt;
	&lt;br /&gt;
	-- Now perform various field substitutions.&lt;br /&gt;
	-- We also add leading spaces and surrounding markup and punctuation to the&lt;br /&gt;
	-- various parts of the citation, but only when they are non-nil.&lt;br /&gt;
	do&lt;br /&gt;
		local last_first_list;&lt;br /&gt;
		local control = { &lt;br /&gt;
			format = NameListStyle,												-- empty string or 'vanc'&lt;br /&gt;
			maximum = nil,														-- as if display-authors or display-editors not set&lt;br /&gt;
			mode = Mode&lt;br /&gt;
		};&lt;br /&gt;
&lt;br /&gt;
		do																		-- do editor name list first because the now unsupported coauthors used to modify control table&lt;br /&gt;
			control.maximum , editor_etal = get_display_names (A['DisplayEditors'], #e, 'editors', editor_etal, A:ORIGIN ('DisplayEditors'));&lt;br /&gt;
			Editors, EditorCount = list_people (control, e, editor_etal);&lt;br /&gt;
&lt;br /&gt;
			if 1 == EditorCount and (true == editor_etal or 1 &amp;lt; #e) then		-- only one editor displayed but includes etal then &lt;br /&gt;
				EditorCount = 2;												-- spoof to display (eds.) annotation&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		do																		-- now do interviewers&lt;br /&gt;
			control.maximum, interviewer_etal = get_display_names (A['DisplayInterviewers'], #interviewers_list, 'interviewers', interviewer_etal, A:ORIGIN ('DisplayInterviewers'));&lt;br /&gt;
			Interviewers = list_people (control, interviewers_list, interviewer_etal);&lt;br /&gt;
		end&lt;br /&gt;
		do																		-- now do translators&lt;br /&gt;
			control.maximum, translator_etal = get_display_names (A['DisplayTranslators'], #t, 'translators', translator_etal, A:ORIGIN ('DisplayTranslators'));&lt;br /&gt;
			Translators = list_people (control, t, translator_etal);&lt;br /&gt;
		end&lt;br /&gt;
		do																		-- now do contributors&lt;br /&gt;
			control.maximum, contributor_etal = get_display_names (A['DisplayContributors'], #c, 'contributors', contributor_etal, A:ORIGIN ('DisplayContributors'));&lt;br /&gt;
			Contributors = list_people (control, c, contributor_etal);&lt;br /&gt;
		end&lt;br /&gt;
		do																		-- now do authors&lt;br /&gt;
			control.maximum, author_etal = get_display_names (A['DisplayAuthors'], #a, 'authors', author_etal, A:ORIGIN ('DisplayAuthors'));&lt;br /&gt;
&lt;br /&gt;
			last_first_list = list_people (control, a, author_etal);&lt;br /&gt;
&lt;br /&gt;
			if utilities.is_set (Authors) then&lt;br /&gt;
				Authors, author_etal = name_has_etal (Authors, author_etal, false, 'authors');	-- find and remove variations on et al.&lt;br /&gt;
				if author_etal then&lt;br /&gt;
					Authors = Authors .. ' ' .. cfg.messages['et al'];			-- add et al. to authors parameter&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				Authors = last_first_list;										-- either an author name list or an empty string&lt;br /&gt;
			end&lt;br /&gt;
		end																		-- end of do&lt;br /&gt;
	&lt;br /&gt;
		if utilities.is_set (Authors) and utilities.is_set (Collaboration) then&lt;br /&gt;
			Authors = Authors .. ' (' .. Collaboration .. ')';					-- add collaboration after et al.&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ConferenceFormat = A['ConferenceFormat'];&lt;br /&gt;
	local ConferenceURL = A['ConferenceURL'];&lt;br /&gt;
	ConferenceFormat = style_format (ConferenceFormat, ConferenceURL, 'conference-format', 'conference-url');&lt;br /&gt;
	Format = style_format (Format, URL, 'format', 'url');&lt;br /&gt;
&lt;br /&gt;
	-- special case for chapter format so no error message or cat when chapter not supported&lt;br /&gt;
	if not (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or&lt;br /&gt;
		('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia))) then&lt;br /&gt;
			ChapterFormat = style_format (ChapterFormat, ChapterURL, 'chapter-format', 'chapter-url');&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not utilities.is_set (URL) then&lt;br /&gt;
		if utilities.in_array (config.CitationClass, {&amp;quot;web&amp;quot;, &amp;quot;podcast&amp;quot;, &amp;quot;mailinglist&amp;quot;}) or		-- |url= required for cite web, cite podcast, and cite mailinglist&lt;br /&gt;
			('citation' == config.CitationClass and ('website' == Periodical_origin or 'script-website' == ScriptPeriodical_origin)) then	-- and required for {{citation}} with |website= or |script-website=&lt;br /&gt;
				utilities.set_message ('err_cite_web_url');&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		-- do we have |accessdate= without either |url= or |chapter-url=?&lt;br /&gt;
		if utilities.is_set (AccessDate) and not utilities.is_set (ChapterURL) then		-- ChapterURL may be set when URL is not set;&lt;br /&gt;
			utilities.set_message ('err_accessdate_missing_url');&lt;br /&gt;
			AccessDate = '';&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local UrlStatus = is_valid_parameter_value (A['UrlStatus'], A:ORIGIN('UrlStatus'), cfg.keywords_lists['url-status'], '');&lt;br /&gt;
	local OriginalURL&lt;br /&gt;
	local OriginalURL_origin&lt;br /&gt;
	local OriginalFormat&lt;br /&gt;
	local OriginalAccess;&lt;br /&gt;
	UrlStatus = UrlStatus:lower();												-- used later when assembling archived text&lt;br /&gt;
	if utilities.is_set ( ArchiveURL ) then&lt;br /&gt;
		if utilities.is_set (ChapterURL) then 									-- if chapter-url= is set apply archive url to it&lt;br /&gt;
			OriginalURL = ChapterURL;											-- save copy of source chapter's url for archive text&lt;br /&gt;
			OriginalURL_origin = ChapterURL_origin;								-- name of |chapter-url= parameter for error messages&lt;br /&gt;
			OriginalFormat = ChapterFormat;										-- and original |chapter-format=&lt;br /&gt;
&lt;br /&gt;
			if 'live' ~= UrlStatus then&lt;br /&gt;
				ChapterURL = ArchiveURL											-- swap-in the archive's URL&lt;br /&gt;
				ChapterURL_origin = A:ORIGIN('ArchiveURL')						-- name of |archive-url= parameter for error messages&lt;br /&gt;
				ChapterFormat = ArchiveFormat or '';							-- swap in archive's format&lt;br /&gt;
				ChapterUrlAccess = nil;											-- restricted access levels do not make sense for archived URLs&lt;br /&gt;
			end&lt;br /&gt;
		elseif utilities.is_set (URL) then&lt;br /&gt;
			OriginalURL = URL;													-- save copy of original source URL&lt;br /&gt;
			OriginalURL_origin = URL_origin;									-- name of URL parameter for error messages&lt;br /&gt;
			OriginalFormat = Format; 											-- and original |format=&lt;br /&gt;
			OriginalAccess = UrlAccess;&lt;br /&gt;
&lt;br /&gt;
			if 'live' ~= UrlStatus then											-- if URL set then |archive-url= applies to it&lt;br /&gt;
				URL = ArchiveURL												-- swap-in the archive's URL&lt;br /&gt;
				URL_origin = A:ORIGIN('ArchiveURL')								-- name of archive URL parameter for error messages&lt;br /&gt;
				Format = ArchiveFormat or '';									-- swap in archive's format&lt;br /&gt;
				UrlAccess = nil;												-- restricted access levels do not make sense for archived URLs&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	elseif utilities.is_set (UrlStatus) then									-- if |url-status= is set when |archive-url= is not set&lt;br /&gt;
 		utilities.set_message ('maint_url_status');								-- add maint cat&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or	-- if any of the 'periodical' cites except encyclopedia&lt;br /&gt;
		('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) then&lt;br /&gt;
			local chap_param;&lt;br /&gt;
			if utilities.is_set (Chapter) then									-- get a parameter name from one of these chapter related meta-parameters&lt;br /&gt;
				chap_param = A:ORIGIN ('Chapter')&lt;br /&gt;
			elseif utilities.is_set (TransChapter) then&lt;br /&gt;
				chap_param = A:ORIGIN ('TransChapter')&lt;br /&gt;
			elseif utilities.is_set (ChapterURL) then&lt;br /&gt;
				chap_param = A:ORIGIN ('ChapterURL')&lt;br /&gt;
			elseif utilities.is_set (ScriptChapter) then&lt;br /&gt;
				chap_param = ScriptChapter_origin;&lt;br /&gt;
			else utilities.is_set (ChapterFormat)&lt;br /&gt;
				chap_param = A:ORIGIN ('ChapterFormat')&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if utilities.is_set (chap_param) then								-- if we found one&lt;br /&gt;
				utilities.set_message ('err_chapter_ignored', {chap_param});	-- add error message&lt;br /&gt;
				Chapter = '';													-- and set them to empty string to be safe with concatenation&lt;br /&gt;
				TransChapter = '';&lt;br /&gt;
				ChapterURL = '';&lt;br /&gt;
				ScriptChapter = '';&lt;br /&gt;
				ChapterFormat = '';&lt;br /&gt;
			end&lt;br /&gt;
	else																		-- otherwise, format chapter / article title&lt;br /&gt;
		local no_quotes = false;												-- default assume that we will be quoting the chapter parameter value&lt;br /&gt;
		if utilities.is_set (Contribution) and 0 &amp;lt; #c then						-- if this is a contribution with contributor(s)&lt;br /&gt;
			if utilities.in_array (Contribution:lower(), cfg.keywords_lists.contribution) then	-- and a generic contribution title&lt;br /&gt;
				no_quotes = true;												-- then render it unquoted&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		Chapter = format_chapter_title (ScriptChapter, ScriptChapter_origin, Chapter, Chapter_origin, TransChapter, TransChapter_origin, ChapterURL, ChapterURL_origin, no_quotes, ChapterUrlAccess);		-- Contribution is also in Chapter&lt;br /&gt;
		if utilities.is_set (Chapter) then&lt;br /&gt;
			Chapter = Chapter .. ChapterFormat ;&lt;br /&gt;
			if 'map' == config.CitationClass and utilities.is_set (TitleType) then&lt;br /&gt;
				Chapter = Chapter .. ' ' .. TitleType;							-- map annotation here; not after title&lt;br /&gt;
			end&lt;br /&gt;
			Chapter = Chapter .. sepc .. ' ';&lt;br /&gt;
		elseif utilities.is_set (ChapterFormat) then							-- |chapter= not set but |chapter-format= is so ...&lt;br /&gt;
			Chapter = ChapterFormat .. sepc .. ' ';								-- ... ChapterFormat has error message, we want to see it&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Format main title&lt;br /&gt;
	local plain_title = false;&lt;br /&gt;
	local accept_title;&lt;br /&gt;
	Title, accept_title = utilities.has_accept_as_written (Title, true);		-- remove accept-this-as-written markup when it wraps all of &amp;lt;Title&amp;gt;&lt;br /&gt;
	if accept_title and ('' == Title) then										-- only support forced empty for now &amp;quot;(())&amp;quot;&lt;br /&gt;
		Title = cfg.messages['notitle'];										-- replace by predefined &amp;quot;No title&amp;quot; message&lt;br /&gt;
			-- TODO: utilities.set_message ( 'err_redundant_parameters', ...);	-- issue proper error message instead of muting	 &lt;br /&gt;
			ScriptTitle = '';													-- just mute for now	 &lt;br /&gt;
			TransTitle = '';													-- just mute for now&lt;br /&gt;
 		plain_title = true;														-- suppress text decoration for descriptive title&lt;br /&gt;
		utilities.set_message ('maint_untitled');								-- add maint cat&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not accept_title then													-- &amp;lt;Title&amp;gt; not wrapped in accept-as-written markup&lt;br /&gt;
		if '...' == Title:sub (-3) then											-- if ellipsis is the last three characters of |title=&lt;br /&gt;
			Title = Title:gsub ('(%.%.%.)%.+$', '%1');							-- limit the number of dots to three&lt;br /&gt;
		elseif not mw.ustring.find (Title, '%.%s*%a%.$') and					-- end of title is not a 'dot-(optional space-)letter-dot' initialism ...&lt;br /&gt;
			not mw.ustring.find (Title, '%s+%a%.$') then						-- ...and not a 'space-letter-dot' initial (''Allium canadense'' L.)&lt;br /&gt;
				Title = mw.ustring.gsub(Title, '%' .. sepc .. '$', '');			-- remove any trailing separator character; sepc and ms.ustring() here for languages that use multibyte separator characters&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (ArchiveURL) and is_archived_copy (Title) then&lt;br /&gt;
			utilities.set_message ('maint_archived_copy');						-- add maintenance category before we modify the content of Title&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if is_generic ('generic_titles', Title) then&lt;br /&gt;
			utilities.set_message ('err_generic_title');						-- set an error message&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if (not plain_title) and (utilities.in_array (config.CitationClass, {'web', 'news', 'journal', 'magazine', 'pressrelease', 'podcast', 'newsgroup', 'mailinglist', 'interview', 'arxiv', 'biorxiv', 'citeseerx', 'ssrn'}) or&lt;br /&gt;
		('citation' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)) and not utilities.is_set (Encyclopedia)) or&lt;br /&gt;
		('map' == config.CitationClass and (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical)))) then		-- special case for cite map when the map is in a periodical treat as an article&lt;br /&gt;
			Title = kern_quotes (Title);										-- if necessary, separate title's leading and trailing quote marks from module provided quote marks&lt;br /&gt;
			Title = utilities.wrap_style ('quoted-title', Title);&lt;br /&gt;
			Title = script_concatenate (Title, ScriptTitle, 'script-title');	-- &amp;lt;bdi&amp;gt; tags, lang attribute, categorization, etc.; must be done after title is wrapped&lt;br /&gt;
			TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle );&lt;br /&gt;
	elseif plain_title or ('report' == config.CitationClass) then				-- no styling for cite report and descriptive titles (otherwise same as above)&lt;br /&gt;
		Title = script_concatenate (Title, ScriptTitle, 'script-title');		-- &amp;lt;bdi&amp;gt; tags, lang attribute, categorization, etc.; must be done after title is wrapped&lt;br /&gt;
		TransTitle = utilities.wrap_style ('trans-quoted-title', TransTitle );	-- for cite report, use this form for trans-title&lt;br /&gt;
	else&lt;br /&gt;
		Title = utilities.wrap_style ('italic-title', Title);&lt;br /&gt;
		Title = script_concatenate (Title, ScriptTitle, 'script-title');		-- &amp;lt;bdi&amp;gt; tags, lang attribute, categorization, etc.; must be done after title is wrapped&lt;br /&gt;
		TransTitle = utilities.wrap_style ('trans-italic-title', TransTitle);&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (TransTitle) then&lt;br /&gt;
		if utilities.is_set (Title) then&lt;br /&gt;
			TransTitle = &amp;quot; &amp;quot; .. TransTitle;&lt;br /&gt;
		else&lt;br /&gt;
			utilities.set_message ('err_trans_missing_title', {'title'});&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (Title) then											-- TODO: is this the right place to be making Wikisource URLs?&lt;br /&gt;
		if utilities.is_set (TitleLink) and utilities.is_set (URL) then&lt;br /&gt;
			utilities.set_message ('err_wikilink_in_url');						-- set an error message because we can't have both&lt;br /&gt;
			TitleLink = '';														-- unset&lt;br /&gt;
		end&lt;br /&gt;
	&lt;br /&gt;
		if not utilities.is_set (TitleLink) and utilities.is_set (URL) then&lt;br /&gt;
			Title = external_link (URL, Title, URL_origin, UrlAccess) .. TransTitle .. Format;&lt;br /&gt;
			URL = '';															-- unset these because no longer needed&lt;br /&gt;
			Format = &amp;quot;&amp;quot;;&lt;br /&gt;
		elseif utilities.is_set (TitleLink) and not utilities.is_set (URL) then&lt;br /&gt;
			local ws_url;&lt;br /&gt;
			ws_url = wikisource_url_make (TitleLink);							-- ignore ws_label return; not used here&lt;br /&gt;
			if ws_url then&lt;br /&gt;
				Title = external_link (ws_url, Title .. '&amp;amp;nbsp;', 'ws link in title-link');	-- space char after Title to move icon away from italic text; TODO: a better way to do this?&lt;br /&gt;
				Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], TitleLink, Title});				&lt;br /&gt;
				Title = Title .. TransTitle;&lt;br /&gt;
			else&lt;br /&gt;
				Title = utilities.make_wikilink (TitleLink, Title) .. TransTitle;&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			local ws_url, ws_label, L;											-- Title has italic or quote markup by the time we get here which causes is_wikilink() to return 0 (not a wikilink)&lt;br /&gt;
			ws_url, ws_label, L = wikisource_url_make (Title:gsub('^[\'&amp;quot;]*(.-)[\'&amp;quot;]*$', '%1'));	-- make ws URL from |title= interwiki link (strip italic or quote markup); link portion L becomes tooltip label&lt;br /&gt;
			if ws_url then&lt;br /&gt;
				Title = Title:gsub ('%b[]', ws_label);							-- replace interwiki link with ws_label to retain markup&lt;br /&gt;
				Title = external_link (ws_url, Title .. '&amp;amp;nbsp;', 'ws link in title');	-- space char after Title to move icon away from italic text; TODO: a better way to do this?&lt;br /&gt;
				Title = utilities.substitute (cfg.presentation['interwiki-icon'], {cfg.presentation['class-wikisource'], L, Title});				&lt;br /&gt;
				Title = Title .. TransTitle;&lt;br /&gt;
			else&lt;br /&gt;
				Title = Title .. TransTitle;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		Title = TransTitle;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (Place) then&lt;br /&gt;
		Place = &amp;quot; &amp;quot; .. wrap_msg ('written', Place, use_lowercase) .. sepc .. &amp;quot; &amp;quot;;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ConferenceURL_origin = A:ORIGIN('ConferenceURL');						-- get name of parameter that holds ConferenceURL&lt;br /&gt;
	if utilities.is_set (Conference) then&lt;br /&gt;
		if utilities.is_set (ConferenceURL) then&lt;br /&gt;
			Conference = external_link( ConferenceURL, Conference, ConferenceURL_origin, nil );&lt;br /&gt;
		end&lt;br /&gt;
		Conference = sepc .. &amp;quot; &amp;quot; .. Conference .. ConferenceFormat;&lt;br /&gt;
	elseif utilities.is_set (ConferenceURL) then&lt;br /&gt;
		Conference = sepc .. &amp;quot; &amp;quot; .. external_link( ConferenceURL, nil, ConferenceURL_origin, nil );&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local Position = '';&lt;br /&gt;
	if not utilities.is_set (Position) then&lt;br /&gt;
		local Minutes = A['Minutes'];&lt;br /&gt;
		local Time = A['Time'];&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (Minutes) then&lt;br /&gt;
			if utilities.is_set (Time) then		--TODO: make a function for this and similar?&lt;br /&gt;
				utilities.set_message ('err_redundant_parameters', {utilities.wrap_style ('parameter', 'minutes') .. ' and ' .. utilities.wrap_style ('parameter', 'time')});&lt;br /&gt;
			end&lt;br /&gt;
			Position = &amp;quot; &amp;quot; .. Minutes .. &amp;quot; &amp;quot; .. cfg.messages['minutes'];&lt;br /&gt;
		else&lt;br /&gt;
			if utilities.is_set (Time) then&lt;br /&gt;
				local TimeCaption = A['TimeCaption']&lt;br /&gt;
				if not utilities.is_set (TimeCaption) then&lt;br /&gt;
					TimeCaption = cfg.messages['event'];&lt;br /&gt;
					if sepc ~= '.' then&lt;br /&gt;
						TimeCaption = TimeCaption:lower();&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
				Position = &amp;quot; &amp;quot; .. TimeCaption .. &amp;quot; &amp;quot; .. Time;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		Position = &amp;quot; &amp;quot; .. Position;&lt;br /&gt;
		At = '';&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	Page, Pages, Sheet, Sheets = format_pages_sheets (Page, Pages, Sheet, Sheets, config.CitationClass, Periodical_origin, sepc, NoPP, use_lowercase);&lt;br /&gt;
&lt;br /&gt;
	At = utilities.is_set (At) and (sepc .. &amp;quot; &amp;quot; .. At) or &amp;quot;&amp;quot;;&lt;br /&gt;
	Position = utilities.is_set (Position) and (sepc .. &amp;quot; &amp;quot; .. Position) or &amp;quot;&amp;quot;;&lt;br /&gt;
	if config.CitationClass == 'map' then&lt;br /&gt;
		local Sections = A['Sections'];											-- Section (singular) is an alias of Chapter so set earlier&lt;br /&gt;
		local Inset = A['Inset'];&lt;br /&gt;
		&lt;br /&gt;
		if utilities.is_set ( Inset ) then&lt;br /&gt;
			Inset = sepc .. &amp;quot; &amp;quot; .. wrap_msg ('inset', Inset, use_lowercase);&lt;br /&gt;
		end			&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set ( Sections ) then&lt;br /&gt;
			Section = sepc .. &amp;quot; &amp;quot; .. wrap_msg ('sections', Sections, use_lowercase);&lt;br /&gt;
		elseif utilities.is_set ( Section ) then&lt;br /&gt;
			Section = sepc .. &amp;quot; &amp;quot; .. wrap_msg ('section', Section, use_lowercase);&lt;br /&gt;
		end&lt;br /&gt;
		At = At .. Inset .. Section;		&lt;br /&gt;
	end	&lt;br /&gt;
&lt;br /&gt;
	local Others = A['Others'];&lt;br /&gt;
	if utilities.is_set (Others) and 0 == #a and 0 == #e then					-- add maint cat when |others= has value and used without |author=, |editor=&lt;br /&gt;
		if config.CitationClass == &amp;quot;AV-media-notes&amp;quot;&lt;br /&gt;
		or config.CitationClass == &amp;quot;audio-visual&amp;quot; then							-- special maint for AV/M which has a lot of 'false' positives right now&lt;br /&gt;
			utilities.set_message ('maint_others_avm')&lt;br /&gt;
		else&lt;br /&gt;
			utilities.set_message ('maint_others');&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	Others = utilities.is_set (Others) and (sepc .. &amp;quot; &amp;quot; .. Others) or &amp;quot;&amp;quot;;&lt;br /&gt;
	&lt;br /&gt;
	if utilities.is_set (Translators) then&lt;br /&gt;
		Others = safe_join ({sepc .. ' ', wrap_msg ('translated', Translators, use_lowercase), Others}, sepc);&lt;br /&gt;
	end&lt;br /&gt;
	if utilities.is_set (Interviewers) then&lt;br /&gt;
		Others = safe_join ({sepc .. ' ', wrap_msg ('interview', Interviewers, use_lowercase), Others}, sepc);&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local TitleNote = A['TitleNote'];&lt;br /&gt;
	TitleNote = utilities.is_set (TitleNote) and (sepc .. &amp;quot; &amp;quot; .. TitleNote) or &amp;quot;&amp;quot;;&lt;br /&gt;
	if utilities.is_set (Edition) then&lt;br /&gt;
		if Edition:match ('%f[%a][Ee]d%n?%.?$') or Edition:match ('%f[%a][Ee]dition$') then -- Ed, ed, Ed., ed., Edn, edn, Edn., edn.&lt;br /&gt;
			utilities.set_message ('err_extra_text_edition');					 -- add error message&lt;br /&gt;
		end&lt;br /&gt;
		Edition = &amp;quot; &amp;quot; .. wrap_msg ('edition', Edition);&lt;br /&gt;
	else&lt;br /&gt;
		Edition = '';&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	Series = utilities.is_set (Series) and wrap_msg ('series', {sepc, Series}) or &amp;quot;&amp;quot;;	-- not the same as SeriesNum&lt;br /&gt;
	local Agency = A['Agency'];&lt;br /&gt;
	Agency = utilities.is_set (Agency) and wrap_msg ('agency', {sepc, Agency}) or &amp;quot;&amp;quot;;&lt;br /&gt;
	Volume = format_volume_issue (Volume, Issue, config.CitationClass, Periodical_origin, sepc, use_lowercase);&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (AccessDate) then&lt;br /&gt;
		local retrv_text = &amp;quot; &amp;quot; .. cfg.messages['retrieved']&lt;br /&gt;
&lt;br /&gt;
		AccessDate = nowrap_date (AccessDate);									-- wrap in nowrap span if date in appropriate format&lt;br /&gt;
		if (sepc ~= &amp;quot;.&amp;quot;) then retrv_text = retrv_text:lower() end				-- if mode is cs2, lower case&lt;br /&gt;
		AccessDate = utilities.substitute (retrv_text, AccessDate);				-- add retrieved text&lt;br /&gt;
&lt;br /&gt;
		AccessDate = utilities.substitute (cfg.presentation['accessdate'], {sepc, AccessDate});	-- allow editors to hide accessdates&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if utilities.is_set (ID) then ID = sepc .. &amp;quot; &amp;quot; .. ID; end&lt;br /&gt;
	&lt;br /&gt;
	local Docket = A['Docket'];&lt;br /&gt;
   	if &amp;quot;thesis&amp;quot; == config.CitationClass and utilities.is_set (Docket) then&lt;br /&gt;
		ID = sepc .. &amp;quot; Docket &amp;quot; .. Docket .. ID;&lt;br /&gt;
	end&lt;br /&gt;
   	if &amp;quot;report&amp;quot; == config.CitationClass and utilities.is_set (Docket) then		-- for cite report when |docket= is set&lt;br /&gt;
		ID = sepc .. ' ' .. Docket;												-- overwrite ID even if |id= is set&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (URL) then&lt;br /&gt;
		URL = &amp;quot; &amp;quot; .. external_link( URL, nil, URL_origin, UrlAccess );&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local Quote = A['Quote'];&lt;br /&gt;
	local TransQuote = A['TransQuote'];&lt;br /&gt;
	local ScriptQuote = A['ScriptQuote'];&lt;br /&gt;
	if utilities.is_set (Quote) or utilities.is_set (TransQuote) or utilities.is_set (ScriptQuote) then&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (Quote) then&lt;br /&gt;
			if Quote:sub(1, 1) == '&amp;quot;' and Quote:sub(-1, -1) == '&amp;quot;' then			-- if first and last characters of quote are quote marks&lt;br /&gt;
				Quote = Quote:sub(2, -2);										-- strip them off&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		Quote = utilities.wrap_style ('quoted-text', Quote );					-- wrap in &amp;lt;q&amp;gt;...&amp;lt;/q&amp;gt; tags&lt;br /&gt;
	&lt;br /&gt;
		if utilities.is_set (ScriptQuote) then&lt;br /&gt;
			Quote = script_concatenate (Quote, ScriptQuote, 'script-quote');	-- &amp;lt;bdi&amp;gt; tags, lang attribute, categorization, etc.; must be done after quote is wrapped&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (TransQuote) then&lt;br /&gt;
			if TransQuote:sub(1, 1) == '&amp;quot;' and TransQuote:sub(-1, -1) == '&amp;quot;' then -- if first and last characters of |trans-quote are quote marks&lt;br /&gt;
				TransQuote = TransQuote:sub(2, -2); -- strip them off&lt;br /&gt;
			end&lt;br /&gt;
			Quote = Quote .. &amp;quot; &amp;quot; .. utilities.wrap_style ('trans-quoted-title', TransQuote );&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (QuotePage) or utilities.is_set (QuotePages) then	-- add page prefix&lt;br /&gt;
			local quote_prefix = '';&lt;br /&gt;
			if utilities.is_set (QuotePage) then&lt;br /&gt;
				extra_text_in_page_check (QuotePage, 'quote-page');				-- add to maint cat if |quote-page= value begins with what looks like p., pp., etc.&lt;br /&gt;
				if not NoPP then&lt;br /&gt;
					quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, QuotePage}), '', '', '';&lt;br /&gt;
				else&lt;br /&gt;
					quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, QuotePage}), '', '', '';&lt;br /&gt;
				end&lt;br /&gt;
			elseif utilities.is_set (QuotePages) then&lt;br /&gt;
				extra_text_in_page_check (QuotePages, 'quote-pages');			-- add to maint cat if |quote-pages= value begins with what looks like p., pp., etc.&lt;br /&gt;
				if tonumber(QuotePages) ~= nil and not NoPP then				-- if only digits, assume single page&lt;br /&gt;
					quote_prefix = utilities.substitute (cfg.messages['p-prefix'], {sepc, QuotePages}), '', '';&lt;br /&gt;
				elseif not NoPP then&lt;br /&gt;
					quote_prefix = utilities.substitute (cfg.messages['pp-prefix'], {sepc, QuotePages}), '', '';&lt;br /&gt;
				else&lt;br /&gt;
					quote_prefix = utilities.substitute (cfg.messages['nopp'], {sepc, QuotePages}), '', '';&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
                        &lt;br /&gt;
			Quote = quote_prefix .. &amp;quot;: &amp;quot; .. Quote;&lt;br /&gt;
		else&lt;br /&gt;
			Quote = sepc .. &amp;quot; &amp;quot; .. Quote;&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		PostScript = &amp;quot;&amp;quot;;														-- cs1|2 does not supply terminal punctuation when |quote= is set&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- We check length of PostScript here because it will have been nuked by&lt;br /&gt;
	-- the quote parameters. We'd otherwise emit a message even if there wasn't&lt;br /&gt;
	-- a displayed postscript.&lt;br /&gt;
	-- TODO: Should the max size (1) be configurable?&lt;br /&gt;
	-- TODO: Should we check a specific pattern?&lt;br /&gt;
	if utilities.is_set(PostScript) and mw.ustring.len(PostScript) &amp;gt; 1 then&lt;br /&gt;
		utilities.set_message ('maint_postscript')&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local Archived;&lt;br /&gt;
	if utilities.is_set (ArchiveURL) then&lt;br /&gt;
		local arch_text;&lt;br /&gt;
		if not utilities.is_set (ArchiveDate) then&lt;br /&gt;
			utilities.set_message ('err_archive_missing_date');&lt;br /&gt;
			ArchiveDate = '';													-- empty string for concatenation&lt;br /&gt;
		end&lt;br /&gt;
		if &amp;quot;live&amp;quot; == UrlStatus then&lt;br /&gt;
			arch_text = cfg.messages['archived'];&lt;br /&gt;
			if sepc ~= &amp;quot;.&amp;quot; then arch_text = arch_text:lower() end&lt;br /&gt;
			if utilities.is_set (ArchiveDate) then&lt;br /&gt;
				Archived = sepc .. ' ' .. utilities.substitute ( cfg.messages['archived-live'],&lt;br /&gt;
					{external_link( ArchiveURL, arch_text, A:ORIGIN('ArchiveURL'), nil) .. ArchiveFormat, ArchiveDate } );&lt;br /&gt;
			else&lt;br /&gt;
				Archived = '';&lt;br /&gt;
			end&lt;br /&gt;
			if not utilities.is_set (OriginalURL) then&lt;br /&gt;
				utilities.set_message ('err_archive_missing_url');&lt;br /&gt;
				Archived = '';													-- empty string for concatenation&lt;br /&gt;
			end&lt;br /&gt;
		elseif utilities.is_set (OriginalURL) then								-- UrlStatus is empty, 'dead', 'unfit', 'usurped', 'bot: unknown'&lt;br /&gt;
			if utilities.in_array (UrlStatus, {'unfit', 'usurped', 'bot: unknown'}) then&lt;br /&gt;
				arch_text = cfg.messages['archived-unfit'];&lt;br /&gt;
				if sepc ~= &amp;quot;.&amp;quot; then arch_text = arch_text:lower() end&lt;br /&gt;
				Archived = sepc .. ' ' .. arch_text .. ArchiveDate;				-- format already styled&lt;br /&gt;
				if 'bot: unknown' == UrlStatus then&lt;br /&gt;
					utilities.set_message ('maint_bot_unknown');				-- and add a category if not already added&lt;br /&gt;
				else&lt;br /&gt;
					utilities.set_message ('maint_unfit');						-- and add a category if not already added&lt;br /&gt;
				end&lt;br /&gt;
			else																-- UrlStatus is empty, 'dead'&lt;br /&gt;
				arch_text = cfg.messages['archived-dead'];&lt;br /&gt;
				if sepc ~= &amp;quot;.&amp;quot; then arch_text = arch_text:lower() end&lt;br /&gt;
				if utilities.is_set (ArchiveDate) then&lt;br /&gt;
					Archived = sepc .. &amp;quot; &amp;quot; .. utilities.substitute ( arch_text,&lt;br /&gt;
						{ external_link( OriginalURL, cfg.messages['original'], OriginalURL_origin, OriginalAccess ) .. OriginalFormat, ArchiveDate } );	-- format already styled&lt;br /&gt;
				else&lt;br /&gt;
					Archived = '';												-- unset for concatenation&lt;br /&gt;
				end&lt;br /&gt;
			end	&lt;br /&gt;
		else																	-- OriginalUrl not set&lt;br /&gt;
			arch_text = cfg.messages['archived-missing'];&lt;br /&gt;
			if sepc ~= &amp;quot;.&amp;quot; then arch_text = arch_text:lower() end&lt;br /&gt;
			utilities.set_message ('err_archive_missing_url');&lt;br /&gt;
			Archived = '';														-- empty string for concatenation&lt;br /&gt;
		end&lt;br /&gt;
	elseif utilities.is_set (ArchiveFormat) then&lt;br /&gt;
		Archived = ArchiveFormat;												-- if set and ArchiveURL not set ArchiveFormat has error message&lt;br /&gt;
	else&lt;br /&gt;
		Archived = '';&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local Lay = '';&lt;br /&gt;
	local LaySource = A['LaySource'];&lt;br /&gt;
	local LayURL = A['LayURL'];&lt;br /&gt;
	local LayFormat = A['LayFormat'];&lt;br /&gt;
	LayFormat = style_format (LayFormat, LayURL, 'lay-format', 'lay-url');&lt;br /&gt;
	if utilities.is_set (LayURL) then&lt;br /&gt;
		if utilities.is_set (LayDate) then LayDate = &amp;quot; (&amp;quot; .. LayDate .. &amp;quot;)&amp;quot; end&lt;br /&gt;
		if utilities.is_set (LaySource) then &lt;br /&gt;
			LaySource = &amp;quot; &amp;amp;ndash; ''&amp;quot; .. utilities.safe_for_italics (LaySource) .. &amp;quot;''&amp;quot;;&lt;br /&gt;
		else&lt;br /&gt;
			LaySource = &amp;quot;&amp;quot;;&lt;br /&gt;
		end&lt;br /&gt;
		if sepc == '.' then&lt;br /&gt;
			Lay = sepc .. &amp;quot; &amp;quot; .. external_link( LayURL, cfg.messages['lay summary'], A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate&lt;br /&gt;
		else&lt;br /&gt;
			Lay = sepc .. &amp;quot; &amp;quot; .. external_link( LayURL, cfg.messages['lay summary']:lower(), A:ORIGIN('LayURL'), nil ) .. LayFormat .. LaySource .. LayDate&lt;br /&gt;
		end			&lt;br /&gt;
	elseif utilities.is_set (LayFormat) then									-- Test if |lay-format= is given without giving a |lay-url=&lt;br /&gt;
		Lay = sepc .. LayFormat;												-- if set and LayURL not set, then LayFormat has error message&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local TranscriptURL = A['TranscriptURL']&lt;br /&gt;
	local TranscriptFormat = A['TranscriptFormat'];&lt;br /&gt;
	TranscriptFormat = style_format (TranscriptFormat, TranscriptURL, 'transcript-format', 'transcripturl');&lt;br /&gt;
	local Transcript = A['Transcript'];&lt;br /&gt;
	local TranscriptURL_origin = A:ORIGIN('TranscriptURL');						-- get name of parameter that holds TranscriptURL&lt;br /&gt;
	if utilities.is_set (Transcript) then&lt;br /&gt;
		if utilities.is_set (TranscriptURL) then&lt;br /&gt;
			Transcript = external_link( TranscriptURL, Transcript, TranscriptURL_origin, nil );&lt;br /&gt;
		end&lt;br /&gt;
		Transcript = sepc .. ' ' .. Transcript .. TranscriptFormat;&lt;br /&gt;
	elseif utilities.is_set (TranscriptURL) then&lt;br /&gt;
		Transcript = external_link( TranscriptURL, nil, TranscriptURL_origin, nil );&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local Publisher;&lt;br /&gt;
	if utilities.is_set (PublicationDate) then&lt;br /&gt;
		PublicationDate = wrap_msg ('published', PublicationDate);&lt;br /&gt;
	end&lt;br /&gt;
	if utilities.is_set (PublisherName) then&lt;br /&gt;
		if utilities.is_set (PublicationPlace) then&lt;br /&gt;
			Publisher = sepc .. &amp;quot; &amp;quot; .. PublicationPlace .. &amp;quot;: &amp;quot; .. PublisherName .. PublicationDate;&lt;br /&gt;
		else&lt;br /&gt;
			Publisher = sepc .. &amp;quot; &amp;quot; .. PublisherName .. PublicationDate;  &lt;br /&gt;
		end			&lt;br /&gt;
	elseif utilities.is_set (PublicationPlace) then &lt;br /&gt;
		Publisher= sepc .. &amp;quot; &amp;quot; .. PublicationPlace .. PublicationDate;&lt;br /&gt;
	else &lt;br /&gt;
		Publisher = PublicationDate;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local TransPeriodical =  A['TransPeriodical'];&lt;br /&gt;
	local TransPeriodical_origin =  A:ORIGIN ('TransPeriodical');&lt;br /&gt;
	-- Several of the above rely upon detecting this as nil, so do it last.&lt;br /&gt;
	if (utilities.is_set (Periodical) or utilities.is_set (ScriptPeriodical) or utilities.is_set (TransPeriodical)) then&lt;br /&gt;
		if utilities.is_set (Title) or utilities.is_set (TitleNote) then &lt;br /&gt;
			Periodical = sepc .. &amp;quot; &amp;quot; .. format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);&lt;br /&gt;
		else &lt;br /&gt;
			Periodical = format_periodical (ScriptPeriodical, ScriptPeriodical_origin, Periodical, TransPeriodical, TransPeriodical_origin);&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local Language = A['Language'];&lt;br /&gt;
	if utilities.is_set (Language) then&lt;br /&gt;
		Language = language_parameter (Language);								-- format, categories, name from ISO639-1, etc.&lt;br /&gt;
	else&lt;br /&gt;
		Language='';															-- language not specified so make sure this is an empty string;&lt;br /&gt;
	--[[ TODO: need to extract the wrap_msg from language_parameter&lt;br /&gt;
	so that we can solve parentheses bunching problem with Format/Language/TitleType&lt;br /&gt;
	]]&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	Handle the oddity that is cite speech.  This code overrides whatever may be the value assigned to TitleNote (through |department=) and forces it to be &amp;quot; (Speech)&amp;quot; so that&lt;br /&gt;
	the annotation directly follows the |title= parameter value in the citation rather than the |event= parameter value (if provided).&lt;br /&gt;
	]]&lt;br /&gt;
	if &amp;quot;speech&amp;quot; == config.CitationClass then									-- cite speech only&lt;br /&gt;
		TitleNote = TitleType;													-- move TitleType to TitleNote so that it renders ahead of |event=&lt;br /&gt;
		TitleType = '';															-- and unset&lt;br /&gt;
&lt;br /&gt;
		if utilities.is_set (Periodical) then									-- if Periodical, perhaps because of an included |website= or |journal= parameter &lt;br /&gt;
			if utilities.is_set (Conference) then								-- and if |event= is set&lt;br /&gt;
				Conference = Conference .. sepc .. &amp;quot; &amp;quot;;							-- then add appropriate punctuation to the end of the Conference variable before rendering&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Piece all bits together at last.  Here, all should be non-nil.&lt;br /&gt;
	-- We build things this way because it is more efficient in LUA&lt;br /&gt;
	-- not to keep reassigning to the same string variable over and over.&lt;br /&gt;
&lt;br /&gt;
	local tcommon;&lt;br /&gt;
	local tcommon2;																-- used for book cite when |contributor= is set&lt;br /&gt;
	&lt;br /&gt;
	if utilities.in_array (config.CitationClass, {&amp;quot;journal&amp;quot;, &amp;quot;citation&amp;quot;}) and utilities.is_set (Periodical) then&lt;br /&gt;
		if utilities.is_set (Others) then Others = safe_join ({Others, sepc .. &amp;quot; &amp;quot;}, sepc) end		-- add terminal punctuation &amp;amp; space; check for dup sepc; TODO why do we need to do this here?&lt;br /&gt;
		tcommon = safe_join( {Others, Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Edition, Publisher, Agency, Volume}, sepc );&lt;br /&gt;
	elseif utilities.in_array (config.CitationClass, {&amp;quot;book&amp;quot;, &amp;quot;citation&amp;quot;}) and not utilities.is_set (Periodical) then		-- special cases for book cites&lt;br /&gt;
		if utilities.is_set (Contributors) then									-- when we are citing foreword, preface, introduction, etc.&lt;br /&gt;
			tcommon = safe_join( {Title, TitleNote}, sepc );					-- author and other stuff will come after this and before tcommon2&lt;br /&gt;
			tcommon2 = safe_join( {Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );&lt;br /&gt;
		else&lt;br /&gt;
			tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, Volume, Others, Edition, Publisher, Agency}, sepc );&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
	elseif 'map' == config.CitationClass then									-- special cases for cite map&lt;br /&gt;
		if utilities.is_set (Chapter) then										-- map in a book; TitleType is part of Chapter&lt;br /&gt;
			tcommon = safe_join( {Title, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc );&lt;br /&gt;
		elseif utilities.is_set (Periodical) then								-- map in a periodical&lt;br /&gt;
			tcommon = safe_join( {Title, TitleType, Format, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume}, sepc );&lt;br /&gt;
		else																	-- a sheet or stand-alone map&lt;br /&gt;
			tcommon = safe_join( {Title, TitleType, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher}, sepc );&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
	elseif 'episode' == config.CitationClass then								-- special case for cite episode&lt;br /&gt;
		tcommon = safe_join( {Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc );&lt;br /&gt;
&lt;br /&gt;
	else																		-- all other CS1 templates&lt;br /&gt;
		tcommon = safe_join( {Title, TitleNote, Conference, Periodical, Format, TitleType, Series, Language, &lt;br /&gt;
			Volume, Others, Edition, Publisher, Agency}, sepc );&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if #ID_list &amp;gt; 0 then&lt;br /&gt;
		ID_list = safe_join( { sepc .. &amp;quot; &amp;quot;,  table.concat( ID_list, sepc .. &amp;quot; &amp;quot; ), ID }, sepc );&lt;br /&gt;
	else&lt;br /&gt;
		ID_list = ID;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local Via = A['Via'];&lt;br /&gt;
	Via = utilities.is_set (Via) and  wrap_msg ('via', Via) or '';&lt;br /&gt;
	local idcommon;&lt;br /&gt;
	if 'audio-visual' == config.CitationClass or 'episode' == config.CitationClass then	-- special case for cite AV media &amp;amp; cite episode position transcript&lt;br /&gt;
		idcommon = safe_join( { ID_list, URL, Archived, Transcript, AccessDate, Via, Lay, Quote }, sepc );&lt;br /&gt;
	else&lt;br /&gt;
		idcommon = safe_join( { ID_list, URL, Archived, AccessDate, Via, Lay, Quote }, sepc );&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local text;&lt;br /&gt;
	local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At;&lt;br /&gt;
&lt;br /&gt;
	local OrigDate = A['OrigDate'];&lt;br /&gt;
	OrigDate = utilities.is_set (OrigDate) and wrap_msg ('origdate', OrigDate) or '';&lt;br /&gt;
	if utilities.is_set (Date) then&lt;br /&gt;
		if utilities.is_set (Authors) or utilities.is_set (Editors) then		-- date follows authors or editors when authors not set&lt;br /&gt;
			Date = &amp;quot; (&amp;quot; .. Date .. &amp;quot;)&amp;quot; .. OrigDate .. sepc .. &amp;quot; &amp;quot;;				-- in parentheses&lt;br /&gt;
		else																	-- neither of authors and editors set&lt;br /&gt;
			if (string.sub(tcommon, -1, -1) == sepc) then						-- if the last character of tcommon is sepc&lt;br /&gt;
				Date = &amp;quot; &amp;quot; .. Date .. OrigDate;									-- Date does not begin with sepc&lt;br /&gt;
			else&lt;br /&gt;
				Date = sepc .. &amp;quot; &amp;quot; .. Date .. OrigDate;							-- Date begins with sepc&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end	&lt;br /&gt;
	if utilities.is_set (Authors) then&lt;br /&gt;
		if (not utilities.is_set (Date)) then									-- when date is set it's in parentheses; no Authors termination&lt;br /&gt;
			Authors = terminate_name_list (Authors, sepc);						-- when no date, terminate with 0 or 1 sepc and a space&lt;br /&gt;
		end&lt;br /&gt;
		if utilities.is_set (Editors) then&lt;br /&gt;
			local in_text = &amp;quot; &amp;quot;;&lt;br /&gt;
			local post_text = &amp;quot;&amp;quot;;&lt;br /&gt;
			if utilities.is_set (Chapter) and 0 == #c then&lt;br /&gt;
				in_text = in_text .. cfg.messages['in'] .. &amp;quot; &amp;quot;&lt;br /&gt;
				if (sepc ~= '.') then&lt;br /&gt;
					in_text = in_text:lower()									-- lowercase for cs2&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if EditorCount &amp;lt;= 1 then&lt;br /&gt;
				post_text = &amp;quot; (&amp;quot; .. cfg.messages['editor'] .. &amp;quot;)&amp;quot;;				-- be consistent with no-author, no-date case&lt;br /&gt;
			else&lt;br /&gt;
				post_text = &amp;quot; (&amp;quot; .. cfg.messages['editors'] .. &amp;quot;)&amp;quot;;&lt;br /&gt;
			end&lt;br /&gt;
			Editors = terminate_name_list (in_text .. Editors .. post_text, sepc);	-- terminate with 0 or 1 sepc and a space&lt;br /&gt;
		end&lt;br /&gt;
		if utilities.is_set (Contributors) then									-- book cite and we're citing the intro, preface, etc.&lt;br /&gt;
			local by_text = sepc .. ' ' .. cfg.messages['by'] .. ' ';&lt;br /&gt;
			if (sepc ~= '.') then by_text = by_text:lower() end					-- lowercase for cs2&lt;br /&gt;
			Authors = by_text .. Authors;										-- author follows title so tweak it here&lt;br /&gt;
			if utilities.is_set (Editors) and utilities.is_set (Date) then		-- when Editors make sure that Authors gets terminated&lt;br /&gt;
				Authors = terminate_name_list (Authors, sepc);					-- terminate with 0 or 1 sepc and a space&lt;br /&gt;
			end&lt;br /&gt;
			if (not utilities.is_set (Date)) then								-- when date is set it's in parentheses; no Contributors termination&lt;br /&gt;
				Contributors = terminate_name_list (Contributors, sepc);		-- terminate with 0 or 1 sepc and a space&lt;br /&gt;
			end&lt;br /&gt;
			text = safe_join( {Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon }, sepc );&lt;br /&gt;
		else&lt;br /&gt;
			text = safe_join( {Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon }, sepc );&lt;br /&gt;
		end&lt;br /&gt;
	elseif utilities.is_set (Editors) then&lt;br /&gt;
		if utilities.is_set (Date) then&lt;br /&gt;
			if EditorCount &amp;lt;= 1 then&lt;br /&gt;
				Editors = Editors .. &amp;quot;, &amp;quot; .. cfg.messages['editor'];&lt;br /&gt;
			else&lt;br /&gt;
				Editors = Editors .. &amp;quot;, &amp;quot; .. cfg.messages['editors'];&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			if EditorCount &amp;lt;= 1 then&lt;br /&gt;
				Editors = Editors .. &amp;quot; (&amp;quot; .. cfg.messages['editor'] .. &amp;quot;)&amp;quot; .. sepc .. &amp;quot; &amp;quot;&lt;br /&gt;
			else&lt;br /&gt;
				Editors = Editors .. &amp;quot; (&amp;quot; .. cfg.messages['editors'] .. &amp;quot;)&amp;quot; .. sepc .. &amp;quot; &amp;quot;&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		text = safe_join( {Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc );&lt;br /&gt;
	else&lt;br /&gt;
		if utilities.in_array (config.CitationClass, {&amp;quot;journal&amp;quot;, &amp;quot;citation&amp;quot;}) and utilities.is_set (Periodical) then&lt;br /&gt;
			text = safe_join( {Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc );&lt;br /&gt;
		else&lt;br /&gt;
			text = safe_join( {Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc );&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if utilities.is_set (PostScript) and PostScript ~= sepc then&lt;br /&gt;
		text = safe_join( {text, sepc}, sepc ); 								-- Deals with italics, spaces, etc.&lt;br /&gt;
		text = text:sub(1, -sepc:len() - 1);&lt;br /&gt;
	end	&lt;br /&gt;
	&lt;br /&gt;
	text = safe_join( {text, PostScript}, sepc );&lt;br /&gt;
&lt;br /&gt;
	-- Now enclose the whole thing in a &amp;lt;cite&amp;gt; element&lt;br /&gt;
	local options_t = {};&lt;br /&gt;
	options_t.class = cite_class_attribute_make (config.CitationClass, Mode);&lt;br /&gt;
&lt;br /&gt;
	local Ref = is_valid_parameter_value (A['Ref'], A:ORIGIN('Ref'), cfg.keywords_lists['ref'], nil, true);	-- nil when |ref=harv; A['Ref'] else&lt;br /&gt;
&lt;br /&gt;
	if 'none' ~= cfg.keywords_xlate[(Ref and Ref:lower()) or ''] then&lt;br /&gt;
		local namelist_t = {};													-- holds selected contributor, author, editor name list&lt;br /&gt;
		local year = first_set ({Year, anchor_year}, 2);						-- Year first for legacy citations and for YMD dates that require disambiguation&lt;br /&gt;
&lt;br /&gt;
		if #c &amp;gt; 0 then															-- if there is a contributor list&lt;br /&gt;
			namelist_t = c;														-- select it&lt;br /&gt;
		elseif #a &amp;gt; 0 then														-- or an author list&lt;br /&gt;
			namelist_t = a;&lt;br /&gt;
		elseif #e &amp;gt; 0 then														-- or an editor list&lt;br /&gt;
			namelist_t = e;&lt;br /&gt;
		end&lt;br /&gt;
		local citeref_id;&lt;br /&gt;
		if #namelist_t &amp;gt; 0 then													-- if there are names in namelist_t&lt;br /&gt;
			citeref_id = make_citeref_id (namelist_t, year);					-- go make the CITEREF anchor&lt;br /&gt;
			if mw.uri.anchorEncode (citeref_id) == ((Ref and mw.uri.anchorEncode (Ref)) or '') then	-- Ref may already be encoded (by {{sfnref}}) so citeref_id must be encoded before comparison&lt;br /&gt;
				utilities.set_message ('maint_ref_duplicates_default');&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			citeref_id = '';													-- unset&lt;br /&gt;
		end&lt;br /&gt;
		options_t.id = Ref or citeref_id;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if string.len (text:gsub('%b&amp;lt;&amp;gt;', '')) &amp;lt;= 2 then								-- remove html and html-like tags; then get length of what remains; &lt;br /&gt;
		z.error_cats_t = {};													-- blank the categories list&lt;br /&gt;
		z.error_msgs_t = {};													-- blank the error messages list&lt;br /&gt;
		OCinSoutput = nil;														-- blank the metadata string&lt;br /&gt;
		text = '';																-- blank the the citation&lt;br /&gt;
		utilities.set_message ('err_empty_citation');							-- set empty citation message and category&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local render_t = {};														-- here we collect the final bits for concatenation into the rendered citation&lt;br /&gt;
&lt;br /&gt;
	if utilities.is_set (options_t.id) then										-- here we wrap the rendered citation in &amp;lt;cite ...&amp;gt;...&amp;lt;/cite&amp;gt; tags&lt;br /&gt;
		table.insert (render_t, utilities.substitute (cfg.presentation['cite-id'], {mw.uri.anchorEncode(options_t.id), mw.text.nowiki(options_t.class), text}));	-- when |ref= is set or when there is a namelist&lt;br /&gt;
	else&lt;br /&gt;
		table.insert (render_t, utilities.substitute (cfg.presentation['cite'], {mw.text.nowiki(options_t.class), text}));	-- when |ref=none or when namelist_t empty and |ref= is missing or is empty&lt;br /&gt;
	end		&lt;br /&gt;
&lt;br /&gt;
	if OCinSoutput then															-- blanked when citation is 'empty' so don't bother to add boilerplate metadata span&lt;br /&gt;
		table.insert (render_t, utilities.substitute (cfg.presentation['ocins'], OCinSoutput));	-- format and append metadata to the citation&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local template_name = ('citation' == config.CitationClass) and 'citation' or 'cite ' .. (cfg.citation_class_map_t[config.CitationClass] or config.CitationClass);&lt;br /&gt;
	local template_link = '[[Template:' .. template_name .. '|' .. template_name .. ']]';	-- TODO: if kept, these require some sort of i18n&lt;br /&gt;
	local msg_prefix = '&amp;lt;code class=&amp;quot;cs1-code&amp;quot;&amp;gt;{{' .. template_link .. '}}&amp;lt;/code&amp;gt;: ';&lt;br /&gt;
&lt;br /&gt;
	if 0 ~= #z.error_msgs_t then&lt;br /&gt;
		mw.addWarning (utilities.substitute (cfg.messages.warning_msg_e, template_link));&lt;br /&gt;
&lt;br /&gt;
		table.insert (render_t, ' ');											-- insert a space between citation and its error messages&lt;br /&gt;
		table.sort (z.error_msgs_t);											-- sort the error messages list; sorting includes wrapping &amp;lt;span&amp;gt; and &amp;lt;code&amp;gt; tags; hidden-error sorts ahead of visible-error&lt;br /&gt;
&lt;br /&gt;
		local hidden = true;													-- presume that the only error messages emited by this template are hidden&lt;br /&gt;
		for _, v in ipairs (z.error_msgs_t) do									-- spin through the list of error messages&lt;br /&gt;
			if v:find ('cs1-visible-error', 1, true) then						-- look for the visible error class name&lt;br /&gt;
				hidden = false;													-- found one; so don't hide the error message prefix&lt;br /&gt;
				break;															-- and done because no need to look further&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		z.error_msgs_t[1] = table.concat ({utilities.error_comment (msg_prefix, hidden), z.error_msgs_t[1]});	-- add error message prefix to first error message to prevent extraneous punctuation&lt;br /&gt;
		table.insert (render_t, table.concat (z.error_msgs_t, '; '));			-- make a big string of error messages and add it to the rendering&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if 0 ~= #z.maint_cats_t then&lt;br /&gt;
		mw.addWarning (utilities.substitute (cfg.messages.warning_msg_m, template_link));&lt;br /&gt;
&lt;br /&gt;
		table.sort (z.maint_cats_t);											-- sort the maintenance messages list&lt;br /&gt;
&lt;br /&gt;
		local maint_msgs_t = {};												-- here we collect all of the maint messages&lt;br /&gt;
&lt;br /&gt;
		if 0 == #z.error_msgs_t then											-- if no error messages&lt;br /&gt;
			table.insert (maint_msgs_t, msg_prefix);							-- insert message prefix in maint message livery&lt;br /&gt;
		end&lt;br /&gt;
		&lt;br /&gt;
		for _, v in ipairs( z.maint_cats_t ) do									-- append maintenance categories&lt;br /&gt;
			table.insert (maint_msgs_t, 										-- assemble new maint message and add it to the maint_msgs_t table&lt;br /&gt;
				table.concat ({v, ' (', utilities.substitute (cfg.messages[':cat wikilink'], v), ')'})&lt;br /&gt;
				);&lt;br /&gt;
		end&lt;br /&gt;
		table.insert (render_t, utilities.substitute (cfg.presentation['hidden-maint'], table.concat (maint_msgs_t, ' ')));	-- wrap the group of maint messages with proper presentation and save&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not no_tracking_cats then&lt;br /&gt;
		for _, v in ipairs (z.error_cats_t) do									-- append error categories&lt;br /&gt;
			table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v));&lt;br /&gt;
		end&lt;br /&gt;
		for _, v in ipairs (z.maint_cats_t) do									-- append maintenance categories&lt;br /&gt;
			table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v));&lt;br /&gt;
		end&lt;br /&gt;
		for _, v in ipairs (z.prop_cats_t) do									-- append properties categories&lt;br /&gt;
			table.insert (render_t, utilities.substitute (cfg.messages['cat wikilink'], v));&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat (render_t);												-- make a big string and done&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; V A L I D A T E &amp;gt;--------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Looks for a parameter's name in one of several whitelists.&lt;br /&gt;
&lt;br /&gt;
Parameters in the whitelist can have three values:&lt;br /&gt;
	true - active, supported parameters&lt;br /&gt;
	false - deprecated, supported parameters&lt;br /&gt;
	nil - unsupported parameters&lt;br /&gt;
	&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function validate (name, cite_class, empty)&lt;br /&gt;
	local name = tostring (name);&lt;br /&gt;
	local enum_name;															-- for enumerated parameters, is name with enumerator replaced with '#'&lt;br /&gt;
	local state;&lt;br /&gt;
	local function state_test (state, name)										-- local function to do testing of state values&lt;br /&gt;
		if true == state then return true; end									-- valid actively supported parameter&lt;br /&gt;
		if false == state then&lt;br /&gt;
			if empty then return nil; end										-- empty deprecated parameters are treated as unknowns&lt;br /&gt;
			deprecated_parameter (name);										-- parameter is deprecated but still supported&lt;br /&gt;
			return true;&lt;br /&gt;
		end&lt;br /&gt;
		if 'tracked' == state then&lt;br /&gt;
			local base_name = name:gsub ('%d', '');								-- strip enumerators from parameter names that have them to get the base name&lt;br /&gt;
			utilities.add_prop_cat ('tracked-param', {base_name}, base_name);	-- add a properties category; &amp;lt;base_name&amp;gt; modifies &amp;lt;key&amp;gt;&lt;br /&gt;
			return true;&lt;br /&gt;
		end&lt;br /&gt;
		return nil;&lt;br /&gt;
	end		&lt;br /&gt;
&lt;br /&gt;
	if name:find ('#') then														-- # is a cs1|2 reserved character so parameters with # not permitted&lt;br /&gt;
		return nil;&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if utilities.in_array (cite_class, whitelist.preprint_template_list ) then	-- limited parameter sets allowed for these templates&lt;br /&gt;
		state = whitelist.limited_basic_arguments[name];&lt;br /&gt;
		if true == state_test (state, name) then return true; end&lt;br /&gt;
&lt;br /&gt;
		state = whitelist.preprint_arguments[cite_class][name];					-- look in the parameter-list for the template identified by cite_class&lt;br /&gt;
		if true == state_test (state, name) then return true; end&lt;br /&gt;
&lt;br /&gt;
																				-- limited enumerated parameters list&lt;br /&gt;
		enum_name = name:gsub(&amp;quot;%d+&amp;quot;, &amp;quot;#&amp;quot; );										-- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits)&lt;br /&gt;
		state = whitelist.limited_numbered_arguments[enum_name];&lt;br /&gt;
		if true == state_test (state, name) then return true; end&lt;br /&gt;
&lt;br /&gt;
		return false;															-- not supported because not found or name is set to nil&lt;br /&gt;
	end																			-- end limited parameter-set templates&lt;br /&gt;
&lt;br /&gt;
	if utilities.in_array (cite_class, whitelist.unique_param_template_list) then 	-- experiment for template-specific parameters for templates that accept parameters from the basic argument list&lt;br /&gt;
		state = whitelist.unique_arguments[cite_class][name];					-- look in the template-specific parameter-lists for the template identified by cite_class&lt;br /&gt;
		if true == state_test (state, name) then return true; end&lt;br /&gt;
	end																			-- if here, fall into general validation&lt;br /&gt;
		&lt;br /&gt;
	state = whitelist.basic_arguments[name];									-- all other templates; all normal parameters allowed&lt;br /&gt;
	if true == state_test (state, name) then return true; end&lt;br /&gt;
&lt;br /&gt;
																				-- all enumerated parameters allowed&lt;br /&gt;
	enum_name = name:gsub(&amp;quot;%d+&amp;quot;, &amp;quot;#&amp;quot; );											-- replace digit(s) with # (last25 becomes last#) (mw.ustring because non-Western 'local' digits)&lt;br /&gt;
	state = whitelist.numbered_arguments[enum_name];&lt;br /&gt;
	if true == state_test (state, name) then return true; end&lt;br /&gt;
&lt;br /&gt;
	return false;																-- not supported because not found or name is set to nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[=[-------------------------&amp;lt; I N T E R _ W I K I _ C H E C K &amp;gt;----------------------------------------------&lt;br /&gt;
&lt;br /&gt;
check &amp;lt;value&amp;gt; for inter-language interwiki-link markup.  &amp;lt;prefix&amp;gt; must be a MediaWiki-recognized language&lt;br /&gt;
code.  when these values have the form (without leading colon):&lt;br /&gt;
	[[&amp;lt;prefix&amp;gt;:link|label]] return label as plain-text&lt;br /&gt;
	[[&amp;lt;prefix&amp;gt;:link]] return &amp;lt;prefix&amp;gt;:link as plain-text&lt;br /&gt;
&lt;br /&gt;
return value as is else&lt;br /&gt;
&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local function inter_wiki_check (parameter, value)&lt;br /&gt;
	local prefix = value:match ('%[%[(%a+):');									-- get an interwiki prefix if one exists&lt;br /&gt;
	local _;&lt;br /&gt;
	&lt;br /&gt;
	if prefix and cfg.inter_wiki_map[prefix:lower()] then						-- if prefix is in the map, needs preceding colon so&lt;br /&gt;
		utilities.set_message ('err_bad_paramlink', parameter);					-- emit an error message&lt;br /&gt;
		_, value, _ = utilities.is_wikilink (value);							-- extract label portion from wikilink&lt;br /&gt;
	end&lt;br /&gt;
	return value;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; M I S S I N G _ P I P E _ C H E C K &amp;gt;------------------------------------------&lt;br /&gt;
&lt;br /&gt;
Look at the contents of a parameter. If the content has a string of characters and digits followed by an equal&lt;br /&gt;
sign, compare the alphanumeric string to the list of cs1|2 parameters.  If found, then the string is possibly a&lt;br /&gt;
parameter that is missing its pipe.  There are two tests made:&lt;br /&gt;
	{{cite ... |title=Title access-date=2016-03-17}}	-- the first parameter has a value and whitespace separates that value from the missing pipe parameter name&lt;br /&gt;
	{{cite ... |title=access-date=2016-03-17}}			-- the first parameter has no value (whitespace after the first = is trimmed by MediaWiki)&lt;br /&gt;
cs1|2 shares some parameter names with XML/HTML attributes: class=, title=, etc.  To prevent false positives XML/HTML&lt;br /&gt;
tags are removed before the search.&lt;br /&gt;
&lt;br /&gt;
If a missing pipe is detected, this function adds the missing pipe maintenance category.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function missing_pipe_check (parameter, value)&lt;br /&gt;
	local capture;&lt;br /&gt;
	value = value:gsub ('%b&amp;lt;&amp;gt;', '');											-- remove XML/HTML tags because attributes: class=, title=, etc.&lt;br /&gt;
&lt;br /&gt;
	capture = value:match ('%s+(%a[%w%-]+)%s*=') or value:match ('^(%a[%w%-]+)%s*=');	-- find and categorize parameters with possible missing pipes&lt;br /&gt;
	if capture and validate (capture) then										-- if the capture is a valid parameter name&lt;br /&gt;
		utilities.set_message ('err_missing_pipe', parameter);&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; H A S _ E X T R A N E O U S _ P U N C T &amp;gt;--------------------------------------&lt;br /&gt;
&lt;br /&gt;
look for extraneous terminal punctuation in most parameter values; parameters listed in skip table are not checked&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function has_extraneous_punc (param, value)&lt;br /&gt;
	if 'number' == type (param) then&lt;br /&gt;
		return;&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	param = param:gsub ('%d+', '#');											-- enumerated name-list mask params allow terminal punct; normalize &lt;br /&gt;
	if cfg.punct_skip[param] then&lt;br /&gt;
		return;																	-- parameter name found in the skip table so done&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if value:match ('[,;:]$') then&lt;br /&gt;
		utilities.set_message ('maint_extra_punct');							-- has extraneous punctuation; add maint cat&lt;br /&gt;
	end&lt;br /&gt;
	if value:match ('^=') then													-- sometimes an extraneous '=' character appears ...&lt;br /&gt;
		utilities.set_message ('maint_extra_punct');							-- has extraneous punctuation; add maint cat&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; H A S _ E X T R A N E O U S _ U R L &amp;gt;------------------------------------------&lt;br /&gt;
&lt;br /&gt;
look for extraneous url parameter values; parameters listed in skip table are not checked&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function has_extraneous_url (url_param_t)&lt;br /&gt;
	local url_error_t = {};&lt;br /&gt;
	&lt;br /&gt;
	check_for_url (url_param_t, url_error_t);									-- extraneous url check&lt;br /&gt;
	if 0 ~= #url_error_t then													-- non-zero when there are errors&lt;br /&gt;
		table.sort (url_error_t);&lt;br /&gt;
		utilities.set_message ('err_param_has_ext_link', {utilities.make_sep_list (#url_error_t, url_error_t)});	-- add this error message&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; C I T A T I O N &amp;gt;--------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
This is used by templates such as {{cite book}} to create the actual citation text.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
local function citation(frame)&lt;br /&gt;
	Frame = frame;																-- save a copy in case we need to display an error message in preview mode&lt;br /&gt;
	is_sandbox = nil ~= string.find (frame:getTitle(), 'sandbox', 1, true);&lt;br /&gt;
	local pframe = frame:getParent()&lt;br /&gt;
	local styles;&lt;br /&gt;
	&lt;br /&gt;
	if is_sandbox then															-- did the {{#invoke:}} use sandbox version?&lt;br /&gt;
		cfg = mw.loadData ('Module:Citation/CS1/Configuration/sandbox');		-- load sandbox versions of support modules&lt;br /&gt;
		whitelist = mw.loadData ('Module:Citation/CS1/Whitelist/sandbox');&lt;br /&gt;
		utilities = require ('Module:Citation/CS1/Utilities/sandbox');&lt;br /&gt;
		validation = require ('Module:Citation/CS1/Date_validation/sandbox');&lt;br /&gt;
		identifiers = require ('Module:Citation/CS1/Identifiers/sandbox');&lt;br /&gt;
		metadata = require ('Module:Citation/CS1/COinS/sandbox');&lt;br /&gt;
		styles = 'Module:Citation/CS1/sandbox/styles.css';&lt;br /&gt;
		&lt;br /&gt;
	else																		-- otherwise&lt;br /&gt;
		cfg = mw.loadData ('Module:Citation/CS1/Configuration');				-- load live versions of support modules&lt;br /&gt;
		whitelist = mw.loadData ('Module:Citation/CS1/Whitelist');&lt;br /&gt;
		utilities = require ('Module:Citation/CS1/Utilities');&lt;br /&gt;
		validation = require ('Module:Citation/CS1/Date_validation');&lt;br /&gt;
		identifiers = require ('Module:Citation/CS1/Identifiers');&lt;br /&gt;
		metadata = require ('Module:Citation/CS1/COinS');&lt;br /&gt;
		styles = 'Module:Citation/CS1/styles.css';&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	utilities.set_selected_modules (cfg);										-- so that functions in Utilities can see the selected cfg tables&lt;br /&gt;
	identifiers.set_selected_modules (cfg, utilities);							-- so that functions in Identifiers can see the selected cfg tables and selected Utilities module&lt;br /&gt;
	validation.set_selected_modules (cfg, utilities);							-- so that functions in Date validataion can see selected cfg tables and the selected Utilities module&lt;br /&gt;
	metadata.set_selected_modules (cfg, utilities);								-- so that functions in COinS can see the selected cfg tables and selected Utilities module&lt;br /&gt;
&lt;br /&gt;
	z = utilities.z;															-- table of error and category tables in Module:Citation/CS1/Utilities&lt;br /&gt;
&lt;br /&gt;
	is_preview_mode = not utilities.is_set (frame:preprocess ('{{REVISIONID}}'));&lt;br /&gt;
&lt;br /&gt;
	local args = {};															-- table where we store all of the template's arguments&lt;br /&gt;
	local suggestions = {};														-- table where we store suggestions if we need to loadData them&lt;br /&gt;
	local error_text;															-- used as a flag&lt;br /&gt;
&lt;br /&gt;
	local config = {};															-- table to store parameters from the module {{#invoke:}}&lt;br /&gt;
	for k, v in pairs( frame.args ) do											-- get parameters from the {{#invoke}} frame&lt;br /&gt;
		config[k] = v;&lt;br /&gt;
	--	args[k] = v;															-- crude debug support that allows us to render a citation from module {{#invoke:}}; skips parameter validation; TODO: keep?&lt;br /&gt;
	end	&lt;br /&gt;
&lt;br /&gt;
	local capture;																-- the single supported capture when matching unknown parameters using patterns&lt;br /&gt;
	local empty_unknowns = {};													-- sequence table to hold empty unknown params for error message listing&lt;br /&gt;
	for k, v in pairs( pframe.args ) do											-- get parameters from the parent (template) frame&lt;br /&gt;
		v = mw.ustring.gsub (v, '^%s*(.-)%s*$', '%1');							-- trim leading/trailing whitespace; when v is only whitespace, becomes empty string&lt;br /&gt;
		if v ~= '' then&lt;br /&gt;
			if ('string' == type (k)) then&lt;br /&gt;
				k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits);		-- for enumerated parameters, translate 'local' digits to Western 0-9&lt;br /&gt;
			end&lt;br /&gt;
			if not validate( k, config.CitationClass ) then			&lt;br /&gt;
				if type (k) ~= 'string' then									-- exclude empty numbered parameters&lt;br /&gt;
					if v:match(&amp;quot;%S+&amp;quot;) ~= nil then&lt;br /&gt;
						error_text = utilities.set_message ('err_text_ignored', {v});&lt;br /&gt;
					end&lt;br /&gt;
				elseif validate (k:lower(), config.CitationClass) then &lt;br /&gt;
					error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, k:lower()});	-- suggest the lowercase version of the parameter&lt;br /&gt;
				else&lt;br /&gt;
					if nil == suggestions.suggestions then						-- if this table is nil then we need to load it&lt;br /&gt;
						if is_sandbox then										-- did the {{#invoke:}} use sandbox version?&lt;br /&gt;
							suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions/sandbox' );	-- use the sandbox version&lt;br /&gt;
						else&lt;br /&gt;
							suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );			-- use the live version&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
					for pattern, param in pairs (suggestions.patterns) do		-- loop through the patterns to see if we can suggest a proper parameter&lt;br /&gt;
						capture = k:match (pattern);							-- the whole match if no capture in pattern else the capture if a match&lt;br /&gt;
						if capture then											-- if the pattern matches &lt;br /&gt;
							param = utilities.substitute (param, capture);		-- add the capture to the suggested parameter (typically the enumerator)&lt;br /&gt;
							if validate (param, config.CitationClass) then		-- validate the suggestion to make sure that the suggestion is supported by this template (necessary for limited parameter lists)&lt;br /&gt;
								error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, param});	-- set the suggestion error message&lt;br /&gt;
							else&lt;br /&gt;
								error_text = utilities.set_message ('err_parameter_ignored', {k});	-- suggested param not supported by this template&lt;br /&gt;
								v = '';											-- unset&lt;br /&gt;
							end&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
					if not utilities.is_set (error_text) then					-- couldn't match with a pattern, is there an explicit suggestion?						&lt;br /&gt;
						if (suggestions.suggestions[ k:lower() ] ~= nil) and validate (suggestions.suggestions[ k:lower() ], config.CitationClass) then&lt;br /&gt;
							utilities.set_message ('err_parameter_ignored_suggest', {k, suggestions.suggestions[ k:lower() ]});&lt;br /&gt;
						else&lt;br /&gt;
							utilities.set_message ('err_parameter_ignored', {k});&lt;br /&gt;
							v = '';												-- unset value assigned to unrecognized parameters (this for the limited parameter lists)&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end				  &lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			args[k] = v;														-- save this parameter and its value&lt;br /&gt;
&lt;br /&gt;
		elseif not utilities.is_set (v) then									-- for empty parameters&lt;br /&gt;
			if not validate (k, config.CitationClass, true) then				-- is this empty parameter a valid parameter&lt;br /&gt;
				k = ('' == k) and '(empty string)' or k;						-- when k is empty string (or was space(s) trimmed to empty string), replace with descriptive text&lt;br /&gt;
				table.insert (empty_unknowns, utilities.wrap_style ('parameter', k));	-- format for error message and add to the list&lt;br /&gt;
			end&lt;br /&gt;
																				-- crude debug support that allows us to render a citation from module {{#invoke:}} TODO: keep?&lt;br /&gt;
	--	elseif args[k] ~= nil or (k == 'postscript') then						-- when args[k] has a value from {{#invoke}} frame (we don't normally do that)&lt;br /&gt;
	--		args[k] = v;														-- overwrite args[k] with empty string from pframe.args[k] (template frame); v is empty string here&lt;br /&gt;
		end																		-- not sure about the postscript bit; that gets handled in parameter validation; historical artifact?&lt;br /&gt;
	end	&lt;br /&gt;
&lt;br /&gt;
	if 0 ~= #empty_unknowns then												-- create empty unknown error message&lt;br /&gt;
		utilities.set_message ('err_param_unknown_empty', {&lt;br /&gt;
			1 == #empty_unknowns and '' or 's',&lt;br /&gt;
			utilities.make_sep_list (#empty_unknowns, empty_unknowns)&lt;br /&gt;
			});&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local url_param_t = {};&lt;br /&gt;
&lt;br /&gt;
	for k, v in pairs( args ) do&lt;br /&gt;
		if 'string' == type (k) then											-- don't evaluate positional parameters&lt;br /&gt;
			has_invisible_chars (k, v);											-- look for invisible characters&lt;br /&gt;
		end&lt;br /&gt;
		has_extraneous_punc (k, v);												-- look for extraneous terminal punctuation in parameter values&lt;br /&gt;
		missing_pipe_check (k, v);												-- do we think that there is a parameter that is missing a pipe?&lt;br /&gt;
		args[k] = inter_wiki_check (k, v);										-- when language interwiki-linked parameter missing leading colon replace with wiki-link label&lt;br /&gt;
&lt;br /&gt;
		if 'string' == type (k) and not cfg.url_skip[k] then					-- when parameter k is not positional and not in url skip table&lt;br /&gt;
			url_param_t[k] = v;													-- make a parameter/value list for extraneous url check&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	has_extraneous_url (url_param_t);											-- look for url in parameter values where a url does not belong&lt;br /&gt;
&lt;br /&gt;
	return table.concat ({&lt;br /&gt;
		frame:extensionTag ('templatestyles', '', {src=styles}),&lt;br /&gt;
		citation0( config, args)&lt;br /&gt;
	});&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--[[--------------------------&amp;lt; E X P O R T E D   F U N C T I O N S &amp;gt;------------------------------------------&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
return {citation = citation};&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Template:Citation&amp;diff=259</id>
		<title>Template:Citation</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Template:Citation&amp;diff=259"/>
		<updated>2022-04-04T14:00:54Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;&amp;lt;includeonly&amp;gt;{{#invoke:citation/CS1|citation |CitationClass=citation }}&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt; {{Documentation}} &amp;lt;/noinclude&amp;gt;&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;includeonly&amp;gt;{{#invoke:citation/CS1|citation&lt;br /&gt;
|CitationClass=citation&lt;br /&gt;
}}&amp;lt;/includeonly&amp;gt;&amp;lt;noinclude&amp;gt;&lt;br /&gt;
{{Documentation}}&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Template:TOC_limit/styles.css&amp;diff=240</id>
		<title>Template:TOC limit/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Template:TOC_limit/styles.css&amp;diff=240"/>
		<updated>2022-03-30T18:44:01Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Zoran changed the content model of the page Template:TOC limit/styles.css from &amp;quot;wikitext&amp;quot; to &amp;quot;Sanitized CSS&amp;quot;: Styling related page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* {{pp-template}} &lt;br /&gt;
   Allow limiting of which header levels are shown in a TOC;&lt;br /&gt;
   &amp;lt;div class=&amp;quot;toclimit-3&amp;quot;&amp;gt;, for instance, will limit to&lt;br /&gt;
   showing ==headings== and ===headings=== but no further&lt;br /&gt;
   (as long as there are no =headings= on the page, which&lt;br /&gt;
   there shouldn't be according to the MoS). */&lt;br /&gt;
.toclimit-2 .toclevel-1 ul,&lt;br /&gt;
.toclimit-3 .toclevel-2 ul,&lt;br /&gt;
.toclimit-4 .toclevel-3 ul,&lt;br /&gt;
.toclimit-5 .toclevel-4 ul,&lt;br /&gt;
.toclimit-6 .toclevel-5 ul,&lt;br /&gt;
.toclimit-7 .toclevel-6 ul {&lt;br /&gt;
	display: none;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Documentation/config&amp;diff=239</id>
		<title>Module:Documentation/config</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Documentation/config&amp;diff=239"/>
		<updated>2022-03-30T18:41:49Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;---------------------------------------------------------------------------------------------------- -- --                               Configuration for Module:Documentation...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;----------------------------------------------------------------------------------------------------&lt;br /&gt;
--&lt;br /&gt;
--                               Configuration for Module:Documentation&lt;br /&gt;
--&lt;br /&gt;
-- Here you can set the values of the parameters and messages used in Module:Documentation to&lt;br /&gt;
-- localise it to your wiki and your language. Unless specified otherwise, values given here&lt;br /&gt;
-- should be string values.&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local cfg = {} -- Do not edit this line.&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Protection template configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg['protection-reason-edit']&lt;br /&gt;
-- The protection reason for edit-protected templates to pass to&lt;br /&gt;
-- [[Module:Protection banner]].&lt;br /&gt;
cfg['protection-reason-edit'] = 'template'&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Sandbox notice configuration&lt;br /&gt;
--&lt;br /&gt;
-- On sandbox pages the module can display a template notifying users that the current page is a&lt;br /&gt;
-- sandbox, and the location of test cases pages, etc. The module decides whether the page is a&lt;br /&gt;
-- sandbox or not based on the value of cfg['sandbox-subpage']. The following settings configure the&lt;br /&gt;
-- messages that the notices contains.&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- cfg['sandbox-notice-image']&lt;br /&gt;
-- The image displayed in the sandbox notice.&lt;br /&gt;
cfg['sandbox-notice-image'] = '[[File:Sandbox.svg|50px|alt=|link=]]'&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg['sandbox-notice-pagetype-template']&lt;br /&gt;
-- cfg['sandbox-notice-pagetype-module']&lt;br /&gt;
-- cfg['sandbox-notice-pagetype-other']&lt;br /&gt;
-- The page type of the sandbox page. The message that is displayed depends on the current subject&lt;br /&gt;
-- namespace. This message is used in either cfg['sandbox-notice-blurb'] or&lt;br /&gt;
-- cfg['sandbox-notice-diff-blurb'].&lt;br /&gt;
--]]&lt;br /&gt;
cfg['sandbox-notice-pagetype-template'] = '[[Wikipedia:Template test cases|template sandbox]] page'&lt;br /&gt;
cfg['sandbox-notice-pagetype-module'] = '[[Wikipedia:Template test cases|module sandbox]] page'&lt;br /&gt;
cfg['sandbox-notice-pagetype-other'] = 'sandbox page'&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg['sandbox-notice-blurb']&lt;br /&gt;
-- cfg['sandbox-notice-diff-blurb']&lt;br /&gt;
-- cfg['sandbox-notice-diff-display']&lt;br /&gt;
-- Either cfg['sandbox-notice-blurb'] or cfg['sandbox-notice-diff-blurb'] is the opening sentence&lt;br /&gt;
-- of the sandbox notice. The latter has a diff link, but the former does not. $1 is the page&lt;br /&gt;
-- type, which is either cfg['sandbox-notice-pagetype-template'],&lt;br /&gt;
-- cfg['sandbox-notice-pagetype-module'] or cfg['sandbox-notice-pagetype-other'] depending what&lt;br /&gt;
-- namespace we are in. $2 is a link to the main template page, and $3 is a diff link between&lt;br /&gt;
-- the sandbox and the main template. The display value of the diff link is set by &lt;br /&gt;
-- cfg['sandbox-notice-compare-link-display'].&lt;br /&gt;
--]]&lt;br /&gt;
cfg['sandbox-notice-blurb'] = 'This is the $1 for $2.'&lt;br /&gt;
cfg['sandbox-notice-diff-blurb'] = 'This is the $1 for $2 ($3).'&lt;br /&gt;
cfg['sandbox-notice-compare-link-display'] = 'diff'&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg['sandbox-notice-testcases-blurb']&lt;br /&gt;
-- cfg['sandbox-notice-testcases-link-display']&lt;br /&gt;
-- cfg['sandbox-notice-testcases-run-blurb']&lt;br /&gt;
-- cfg['sandbox-notice-testcases-run-link-display']&lt;br /&gt;
-- cfg['sandbox-notice-testcases-blurb'] is a sentence notifying the user that there is a test cases page&lt;br /&gt;
-- corresponding to this sandbox that they can edit. $1 is a link to the test cases page.&lt;br /&gt;
-- cfg['sandbox-notice-testcases-link-display'] is the display value for that link.&lt;br /&gt;
-- cfg['sandbox-notice-testcases-run-blurb'] is a sentence notifying the user that there is a test cases page&lt;br /&gt;
-- corresponding to this sandbox that they can edit, along with a link to run it. $1 is a link to the test&lt;br /&gt;
-- cases page, and $2 is a link to the page to run it.&lt;br /&gt;
-- cfg['sandbox-notice-testcases-run-link-display'] is the display value for the link to run the test&lt;br /&gt;
-- cases.&lt;br /&gt;
--]]&lt;br /&gt;
cfg['sandbox-notice-testcases-blurb'] = 'See also the companion subpage for $1.'&lt;br /&gt;
cfg['sandbox-notice-testcases-link-display'] = 'test cases'&lt;br /&gt;
cfg['sandbox-notice-testcases-run-blurb'] = 'See also the companion subpage for $1 ($2).'&lt;br /&gt;
cfg['sandbox-notice-testcases-run-link-display'] = 'run'&lt;br /&gt;
&lt;br /&gt;
-- cfg['sandbox-category']&lt;br /&gt;
-- A category to add to all template sandboxes.&lt;br /&gt;
cfg['sandbox-category'] = 'Template sandboxes'&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Start box configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg['documentation-icon-wikitext']&lt;br /&gt;
-- The wikitext for the icon shown at the top of the template.&lt;br /&gt;
cfg['documentation-icon-wikitext'] = '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]'&lt;br /&gt;
&lt;br /&gt;
-- cfg['template-namespace-heading']&lt;br /&gt;
-- The heading shown in the template namespace.&lt;br /&gt;
cfg['template-namespace-heading'] = 'Template documentation'&lt;br /&gt;
&lt;br /&gt;
-- cfg['module-namespace-heading']&lt;br /&gt;
-- The heading shown in the module namespace.&lt;br /&gt;
cfg['module-namespace-heading'] = 'Module documentation'&lt;br /&gt;
&lt;br /&gt;
-- cfg['file-namespace-heading']&lt;br /&gt;
-- The heading shown in the file namespace.&lt;br /&gt;
cfg['file-namespace-heading'] = 'Summary'&lt;br /&gt;
&lt;br /&gt;
-- cfg['other-namespaces-heading']&lt;br /&gt;
-- The heading shown in other namespaces.&lt;br /&gt;
cfg['other-namespaces-heading'] = 'Documentation'&lt;br /&gt;
&lt;br /&gt;
-- cfg['view-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;view&amp;quot; links.&lt;br /&gt;
cfg['view-link-display'] = 'view'&lt;br /&gt;
&lt;br /&gt;
-- cfg['edit-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;edit&amp;quot; links.&lt;br /&gt;
cfg['edit-link-display'] = 'edit'&lt;br /&gt;
&lt;br /&gt;
-- cfg['history-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;history&amp;quot; links.&lt;br /&gt;
cfg['history-link-display'] = 'history'&lt;br /&gt;
&lt;br /&gt;
-- cfg['purge-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;purge&amp;quot; links.&lt;br /&gt;
cfg['purge-link-display'] = 'purge'&lt;br /&gt;
&lt;br /&gt;
-- cfg['create-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;create&amp;quot; links.&lt;br /&gt;
cfg['create-link-display'] = 'create'&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Link box (end box) configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg['transcluded-from-blurb']&lt;br /&gt;
-- Notice displayed when the docs are transcluded from another page. $1 is a wikilink to that page.&lt;br /&gt;
cfg['transcluded-from-blurb'] = 'The above [[Wikipedia:Template documentation|documentation]] is [[Help:Transclusion|transcluded]] from $1.'&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg['create-module-doc-blurb']&lt;br /&gt;
-- Notice displayed in the module namespace when the documentation subpage does not exist.&lt;br /&gt;
-- $1 is a link to create the documentation page with the preload cfg['module-preload'] and the&lt;br /&gt;
-- display cfg['create-link-display'].&lt;br /&gt;
--]]&lt;br /&gt;
cfg['create-module-doc-blurb'] = 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].'&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Experiment blurb configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg['experiment-blurb-template']&lt;br /&gt;
-- cfg['experiment-blurb-module']&lt;br /&gt;
-- The experiment blurb is the text inviting editors to experiment in sandbox and test cases pages.&lt;br /&gt;
-- It is only shown in the template and module namespaces. With the default English settings, it&lt;br /&gt;
-- might look like this:&lt;br /&gt;
--&lt;br /&gt;
-- Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages.&lt;br /&gt;
--&lt;br /&gt;
-- In this example, &amp;quot;sandbox&amp;quot;, &amp;quot;edit&amp;quot;, &amp;quot;diff&amp;quot;, &amp;quot;testcases&amp;quot;, and &amp;quot;edit&amp;quot; would all be links.&lt;br /&gt;
--&lt;br /&gt;
-- There are two versions, cfg['experiment-blurb-template'] and cfg['experiment-blurb-module'], depending&lt;br /&gt;
-- on what namespace we are in.&lt;br /&gt;
-- &lt;br /&gt;
-- Parameters:&lt;br /&gt;
--&lt;br /&gt;
-- $1 is a link to the sandbox page. If the sandbox exists, it is in the following format:&lt;br /&gt;
--&lt;br /&gt;
--     cfg['sandbox-link-display'] (cfg['sandbox-edit-link-display'] | cfg['compare-link-display'])&lt;br /&gt;
-- &lt;br /&gt;
-- If the sandbox doesn't exist, it is in the format:&lt;br /&gt;
--&lt;br /&gt;
--     cfg['sandbox-link-display'] (cfg['sandbox-create-link-display'] | cfg['mirror-link-display'])&lt;br /&gt;
-- &lt;br /&gt;
-- The link for cfg['sandbox-create-link-display'] link preloads the page with cfg['template-sandbox-preload']&lt;br /&gt;
-- or cfg['module-sandbox-preload'], depending on the current namespace. The link for cfg['mirror-link-display']&lt;br /&gt;
-- loads a default edit summary of cfg['mirror-edit-summary'].&lt;br /&gt;
--&lt;br /&gt;
-- $2 is a link to the test cases page. If the test cases page exists, it is in the following format:&lt;br /&gt;
--&lt;br /&gt;
--     cfg['testcases-link-display'] (cfg['testcases-edit-link-display'] | cfg['testcases-run-link-display'])&lt;br /&gt;
--&lt;br /&gt;
-- If the test cases page doesn't exist, it is in the format:&lt;br /&gt;
-- &lt;br /&gt;
--     cfg['testcases-link-display'] (cfg['testcases-create-link-display'])&lt;br /&gt;
--&lt;br /&gt;
-- If the test cases page doesn't exist, the link for cfg['testcases-create-link-display'] preloads the&lt;br /&gt;
-- page with cfg['template-testcases-preload'] or cfg['module-testcases-preload'], depending on the current&lt;br /&gt;
-- namespace.&lt;br /&gt;
--]]&lt;br /&gt;
cfg['experiment-blurb-template'] = &amp;quot;Editors can experiment in this template's $1 and $2 pages.&amp;quot;&lt;br /&gt;
cfg['experiment-blurb-module'] = &amp;quot;Editors can experiment in this module's $1 and $2 pages.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Sandbox link configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg['sandbox-subpage']&lt;br /&gt;
-- The name of the template subpage typically used for sandboxes.&lt;br /&gt;
cfg['sandbox-subpage'] = 'sandbox'&lt;br /&gt;
&lt;br /&gt;
-- cfg['template-sandbox-preload']&lt;br /&gt;
-- Preload file for template sandbox pages.&lt;br /&gt;
cfg['template-sandbox-preload'] = 'Template:Documentation/preload-sandbox'&lt;br /&gt;
&lt;br /&gt;
-- cfg['module-sandbox-preload']&lt;br /&gt;
-- Preload file for Lua module sandbox pages.&lt;br /&gt;
cfg['module-sandbox-preload'] = 'Template:Documentation/preload-module-sandbox'&lt;br /&gt;
&lt;br /&gt;
-- cfg['sandbox-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;sandbox&amp;quot; links.&lt;br /&gt;
cfg['sandbox-link-display'] = 'sandbox'&lt;br /&gt;
&lt;br /&gt;
-- cfg['sandbox-edit-link-display']&lt;br /&gt;
-- The text to display for sandbox &amp;quot;edit&amp;quot; links.&lt;br /&gt;
cfg['sandbox-edit-link-display'] = 'edit'&lt;br /&gt;
&lt;br /&gt;
-- cfg['sandbox-create-link-display']&lt;br /&gt;
-- The text to display for sandbox &amp;quot;create&amp;quot; links.&lt;br /&gt;
cfg['sandbox-create-link-display'] = 'create'&lt;br /&gt;
&lt;br /&gt;
-- cfg['compare-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;compare&amp;quot; links.&lt;br /&gt;
cfg['compare-link-display'] = 'diff'&lt;br /&gt;
&lt;br /&gt;
-- cfg['mirror-edit-summary']&lt;br /&gt;
-- The default edit summary to use when a user clicks the &amp;quot;mirror&amp;quot; link. $1 is a wikilink to the&lt;br /&gt;
-- template page.&lt;br /&gt;
cfg['mirror-edit-summary'] = 'Create sandbox version of $1'&lt;br /&gt;
&lt;br /&gt;
-- cfg['mirror-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;mirror&amp;quot; links.&lt;br /&gt;
cfg['mirror-link-display'] = 'mirror'&lt;br /&gt;
&lt;br /&gt;
-- cfg['mirror-link-preload']&lt;br /&gt;
-- The page to preload when a user clicks the &amp;quot;mirror&amp;quot; link.&lt;br /&gt;
cfg['mirror-link-preload'] = 'Template:Documentation/mirror'&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Test cases link configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg['testcases-subpage']&lt;br /&gt;
-- The name of the template subpage typically used for test cases.&lt;br /&gt;
cfg['testcases-subpage'] = 'testcases'&lt;br /&gt;
&lt;br /&gt;
-- cfg['template-testcases-preload']&lt;br /&gt;
-- Preload file for template test cases pages.&lt;br /&gt;
cfg['template-testcases-preload'] = 'Template:Documentation/preload-testcases'&lt;br /&gt;
&lt;br /&gt;
-- cfg['module-testcases-preload']&lt;br /&gt;
-- Preload file for Lua module test cases pages.&lt;br /&gt;
cfg['module-testcases-preload'] = 'Template:Documentation/preload-module-testcases'&lt;br /&gt;
&lt;br /&gt;
-- cfg['testcases-link-display']&lt;br /&gt;
-- The text to display for &amp;quot;testcases&amp;quot; links.&lt;br /&gt;
cfg['testcases-link-display'] = 'testcases'&lt;br /&gt;
&lt;br /&gt;
-- cfg['testcases-edit-link-display']&lt;br /&gt;
-- The text to display for test cases &amp;quot;edit&amp;quot; links.&lt;br /&gt;
cfg['testcases-edit-link-display'] = 'edit'&lt;br /&gt;
&lt;br /&gt;
-- cfg['testcases-run-link-display']&lt;br /&gt;
-- The text to display for test cases &amp;quot;run&amp;quot; links.&lt;br /&gt;
cfg['testcases-run-link-display'] = 'run'&lt;br /&gt;
&lt;br /&gt;
-- cfg['testcases-create-link-display']&lt;br /&gt;
-- The text to display for test cases &amp;quot;create&amp;quot; links.&lt;br /&gt;
cfg['testcases-create-link-display'] = 'create'&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Add categories blurb configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg['add-categories-blurb']&lt;br /&gt;
-- Text to direct users to add categories to the /doc subpage. Not used if the &amp;quot;content&amp;quot; or&lt;br /&gt;
-- &amp;quot;docname fed&amp;quot; arguments are set, as then it is not clear where to add the categories. $1 is a&lt;br /&gt;
-- link to the /doc subpage with a display value of cfg['doc-link-display'].&lt;br /&gt;
--]]&lt;br /&gt;
cfg['add-categories-blurb'] = 'Add categories to the $1 subpage.'&lt;br /&gt;
&lt;br /&gt;
-- cfg['doc-link-display']&lt;br /&gt;
-- The text to display when linking to the /doc subpage.&lt;br /&gt;
cfg['doc-link-display'] = '/doc'&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Subpages link configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg['subpages-blurb']&lt;br /&gt;
-- The &amp;quot;Subpages of this template&amp;quot; blurb. $1 is a link to the main template's subpages with a&lt;br /&gt;
-- display value of cfg['subpages-link-display']. In the English version this blurb is simply&lt;br /&gt;
-- the link followed by a period, and the link display provides the actual text.&lt;br /&gt;
--]]&lt;br /&gt;
cfg['subpages-blurb'] = '$1.'&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- cfg['subpages-link-display']&lt;br /&gt;
-- The text to display for the &amp;quot;subpages of this page&amp;quot; link. $1 is cfg['template-pagetype'],&lt;br /&gt;
-- cfg['module-pagetype'] or cfg['default-pagetype'], depending on whether the current page is in&lt;br /&gt;
-- the template namespace, the module namespace, or another namespace.&lt;br /&gt;
--]]&lt;br /&gt;
cfg['subpages-link-display'] = 'Subpages of this $1'&lt;br /&gt;
&lt;br /&gt;
-- cfg['template-pagetype']&lt;br /&gt;
-- The pagetype to display for template pages.&lt;br /&gt;
cfg['template-pagetype'] = 'template'&lt;br /&gt;
&lt;br /&gt;
-- cfg['module-pagetype']&lt;br /&gt;
-- The pagetype to display for Lua module pages.&lt;br /&gt;
cfg['module-pagetype'] = 'module'&lt;br /&gt;
&lt;br /&gt;
-- cfg['default-pagetype']&lt;br /&gt;
-- The pagetype to display for pages other than templates or Lua modules.&lt;br /&gt;
cfg['default-pagetype'] = 'page'&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Doc link configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg['doc-subpage']&lt;br /&gt;
-- The name of the subpage typically used for documentation pages.&lt;br /&gt;
cfg['doc-subpage'] = 'doc'&lt;br /&gt;
&lt;br /&gt;
-- cfg['docpage-preload']&lt;br /&gt;
-- Preload file for template documentation pages in all namespaces.&lt;br /&gt;
cfg['docpage-preload'] = 'Template:Documentation/preload'&lt;br /&gt;
&lt;br /&gt;
-- cfg['module-preload']&lt;br /&gt;
-- Preload file for Lua module documentation pages.&lt;br /&gt;
cfg['module-preload'] = 'Template:Documentation/preload-module-doc'&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- HTML and CSS configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg['templatestyles']&lt;br /&gt;
-- The name of the TemplateStyles page where CSS is kept.&lt;br /&gt;
-- Sandbox CSS will be at Module:Documentation/sandbox/styles.css when needed.&lt;br /&gt;
cfg['templatestyles'] = 'Module:Documentation/styles.css'&lt;br /&gt;
&lt;br /&gt;
-- cfg['container']&lt;br /&gt;
-- Class which can be used to set flex or grid CSS on the&lt;br /&gt;
-- two child divs documentation and documentation-metadata&lt;br /&gt;
cfg['container'] = 'documentation-container'&lt;br /&gt;
&lt;br /&gt;
-- cfg['main-div-classes']&lt;br /&gt;
-- Classes added to the main HTML &amp;quot;div&amp;quot; tag.&lt;br /&gt;
cfg['main-div-classes'] = 'documentation'&lt;br /&gt;
&lt;br /&gt;
-- cfg['main-div-heading-class']&lt;br /&gt;
-- Class for the main heading for templates and modules and assoc. talk spaces&lt;br /&gt;
cfg['main-div-heading-class'] = 'documentation-heading'&lt;br /&gt;
&lt;br /&gt;
-- cfg['start-box-class']&lt;br /&gt;
-- Class for the start box&lt;br /&gt;
cfg['start-box-class'] = 'documentation-startbox'&lt;br /&gt;
&lt;br /&gt;
-- cfg['start-box-link-classes']&lt;br /&gt;
-- Classes used for the [view][edit][history] or [create] links in the start box.&lt;br /&gt;
-- mw-editsection-like is per [[Wikipedia:Village pump (technical)/Archive 117]]&lt;br /&gt;
cfg['start-box-link-classes'] = 'mw-editsection-like plainlinks'&lt;br /&gt;
&lt;br /&gt;
-- cfg['end-box-class']&lt;br /&gt;
-- Class for the end box.&lt;br /&gt;
cfg['end-box-class'] = 'documentation-metadata'&lt;br /&gt;
&lt;br /&gt;
-- cfg['end-box-plainlinks']&lt;br /&gt;
-- Plainlinks&lt;br /&gt;
cfg['end-box-plainlinks'] = 'plainlinks'&lt;br /&gt;
&lt;br /&gt;
-- cfg['toolbar-class']&lt;br /&gt;
-- Class added for toolbar links.&lt;br /&gt;
cfg['toolbar-class'] = 'documentation-toolbar'&lt;br /&gt;
&lt;br /&gt;
-- cfg['clear']&lt;br /&gt;
-- Just used to clear things.&lt;br /&gt;
cfg['clear'] = 'documentation-clear'&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- Tracking category configuration&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- cfg['display-strange-usage-category']&lt;br /&gt;
-- Set to true to enable output of cfg['strange-usage-category'] if the module is used on a /doc subpage&lt;br /&gt;
-- or a /testcases subpage. This should be a boolean value (either true or false).&lt;br /&gt;
cfg['display-strange-usage-category'] = true&lt;br /&gt;
&lt;br /&gt;
-- cfg['strange-usage-category']&lt;br /&gt;
-- Category to output if cfg['display-strange-usage-category'] is set to true and the module is used on a&lt;br /&gt;
-- /doc subpage or a /testcases subpage.&lt;br /&gt;
cfg['strange-usage-category'] = 'Wikipedia pages with strange ((documentation)) usage'&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
-- End configuration&lt;br /&gt;
--&lt;br /&gt;
-- Don't edit anything below this line.&lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
return cfg&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Documentation&amp;diff=238</id>
		<title>Module:Documentation</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Documentation&amp;diff=238"/>
		<updated>2022-03-30T18:41:01Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- This module implements {{documentation}}.  -- Get required modules. local getArgs = require('Module:Arguments').getArgs  -- Get the config table. local cfg = mw.loadData('M...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module implements {{documentation}}.&lt;br /&gt;
&lt;br /&gt;
-- Get required modules.&lt;br /&gt;
local getArgs = require('Module:Arguments').getArgs&lt;br /&gt;
&lt;br /&gt;
-- Get the config table.&lt;br /&gt;
local cfg = mw.loadData('Module:Documentation/config')&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Often-used functions.&lt;br /&gt;
local ugsub = mw.ustring.gsub&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Helper functions&lt;br /&gt;
--&lt;br /&gt;
-- These are defined as local functions, but are made available in the p&lt;br /&gt;
-- table for testing purposes.&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function message(cfgKey, valArray, expectType)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Gets a message from the cfg table and formats it if appropriate.&lt;br /&gt;
	-- The function raises an error if the value from the cfg table is not&lt;br /&gt;
	-- of the type expectType. The default type for expectType is 'string'.&lt;br /&gt;
	-- If the table valArray is present, strings such as $1, $2 etc. in the&lt;br /&gt;
	-- message are substituted with values from the table keys [1], [2] etc.&lt;br /&gt;
	-- For example, if the message &amp;quot;foo-message&amp;quot; had the value 'Foo $2 bar $1.',&lt;br /&gt;
	-- message('foo-message', {'baz', 'qux'}) would return &amp;quot;Foo qux bar baz.&amp;quot;&lt;br /&gt;
	--]]&lt;br /&gt;
	local msg = cfg[cfgKey]&lt;br /&gt;
	expectType = expectType or 'string'&lt;br /&gt;
	if type(msg) ~= expectType then&lt;br /&gt;
		error('message: type error in message cfg.' .. cfgKey .. ' (' .. expectType .. ' expected, got ' .. type(msg) .. ')', 2)&lt;br /&gt;
	end&lt;br /&gt;
	if not valArray then&lt;br /&gt;
		return msg&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local function getMessageVal(match)&lt;br /&gt;
		match = tonumber(match)&lt;br /&gt;
		return valArray[match] or error('message: no value found for key $' .. match .. ' in message cfg.' .. cfgKey, 4)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return ugsub(msg, '$([1-9][0-9]*)', getMessageVal)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.message = message&lt;br /&gt;
&lt;br /&gt;
local function makeWikilink(page, display)&lt;br /&gt;
	if display then&lt;br /&gt;
		return mw.ustring.format('[[%s|%s]]', page, display)&lt;br /&gt;
	else&lt;br /&gt;
		return mw.ustring.format('[[%s]]', page)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.makeWikilink = makeWikilink&lt;br /&gt;
&lt;br /&gt;
local function makeCategoryLink(cat, sort)&lt;br /&gt;
	local catns = mw.site.namespaces[14].name&lt;br /&gt;
	return makeWikilink(catns .. ':' .. cat, sort)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.makeCategoryLink = makeCategoryLink&lt;br /&gt;
&lt;br /&gt;
local function makeUrlLink(url, display)&lt;br /&gt;
	return mw.ustring.format('[%s %s]', url, display)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.makeUrlLink = makeUrlLink&lt;br /&gt;
&lt;br /&gt;
local function makeToolbar(...)&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	local lim = select('#', ...)&lt;br /&gt;
	if lim &amp;lt; 1 then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	for i = 1, lim do&lt;br /&gt;
		ret[#ret + 1] = select(i, ...)&lt;br /&gt;
	end&lt;br /&gt;
	-- 'documentation-toolbar'&lt;br /&gt;
	return '&amp;lt;span class=&amp;quot;' .. message('toolbar-class') .. '&amp;quot;&amp;gt;('&lt;br /&gt;
		.. table.concat(ret, ' &amp;amp;#124; ') .. ')&amp;lt;/span&amp;gt;'&lt;br /&gt;
end	&lt;br /&gt;
&lt;br /&gt;
p.makeToolbar = makeToolbar&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Argument processing&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function makeInvokeFunc(funcName)&lt;br /&gt;
	return function (frame)&lt;br /&gt;
		local args = getArgs(frame, {&lt;br /&gt;
			valueFunc = function (key, value)&lt;br /&gt;
				if type(value) == 'string' then&lt;br /&gt;
					value = value:match('^%s*(.-)%s*$') -- Remove whitespace.&lt;br /&gt;
					if key == 'heading' or value ~= '' then&lt;br /&gt;
						return value&lt;br /&gt;
					else&lt;br /&gt;
						return nil&lt;br /&gt;
					end&lt;br /&gt;
				else&lt;br /&gt;
					return value&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		})&lt;br /&gt;
		return p[funcName](args)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Entry points&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
function p.nonexistent(frame)&lt;br /&gt;
	if mw.title.getCurrentTitle().subpageText == 'testcases' then&lt;br /&gt;
		return frame:expandTemplate{title = 'module test cases notice'}&lt;br /&gt;
	else&lt;br /&gt;
		return p.main(frame)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.main = makeInvokeFunc('_main')&lt;br /&gt;
&lt;br /&gt;
function p._main(args)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- This function defines logic flow for the module.&lt;br /&gt;
	-- @args - table of arguments passed by the user&lt;br /&gt;
	--]]&lt;br /&gt;
	local env = p.getEnvironment(args)&lt;br /&gt;
	local root = mw.html.create()&lt;br /&gt;
	root&lt;br /&gt;
		:wikitext(p._getModuleWikitext(args, env))&lt;br /&gt;
		:wikitext(p.protectionTemplate(env))&lt;br /&gt;
		:wikitext(p.sandboxNotice(args, env))&lt;br /&gt;
		:tag('div')&lt;br /&gt;
			-- 'documentation-container'&lt;br /&gt;
			:addClass(message('container'))&lt;br /&gt;
			:attr('role', 'complementary')&lt;br /&gt;
			:attr('aria-labelledby', args.heading ~= '' and 'documentation-heading' or nil)&lt;br /&gt;
			:attr('aria-label', args.heading == '' and 'Documentation' or nil)&lt;br /&gt;
			:newline()&lt;br /&gt;
			:tag('div')&lt;br /&gt;
				-- 'documentation'&lt;br /&gt;
				:addClass(message('main-div-classes'))&lt;br /&gt;
				:newline()&lt;br /&gt;
				:wikitext(p._startBox(args, env))&lt;br /&gt;
				:wikitext(p._content(args, env))&lt;br /&gt;
				:tag('div')&lt;br /&gt;
					-- 'documentation-clear'&lt;br /&gt;
					:addClass(message('clear'))&lt;br /&gt;
					:done()&lt;br /&gt;
				:newline()&lt;br /&gt;
				:done()&lt;br /&gt;
			:wikitext(p._endBox(args, env))&lt;br /&gt;
			:done()&lt;br /&gt;
		:wikitext(p.addTrackingCategories(env))&lt;br /&gt;
	-- 'Module:Documentation/styles.css'&lt;br /&gt;
	return mw.getCurrentFrame():extensionTag (&lt;br /&gt;
		'templatestyles', '', {src=cfg['templatestyles']&lt;br /&gt;
	}) .. tostring(root)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Environment settings&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
function p.getEnvironment(args)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Returns a table with information about the environment, including title&lt;br /&gt;
	-- objects and other namespace- or path-related data.&lt;br /&gt;
	-- @args - table of arguments passed by the user&lt;br /&gt;
	--&lt;br /&gt;
	-- Title objects include:&lt;br /&gt;
	-- env.title - the page we are making documentation for (usually the current title)&lt;br /&gt;
	-- env.templateTitle - the template (or module, file, etc.)&lt;br /&gt;
	-- env.docTitle - the /doc subpage.&lt;br /&gt;
	-- env.sandboxTitle - the /sandbox subpage.&lt;br /&gt;
	-- env.testcasesTitle - the /testcases subpage.&lt;br /&gt;
	--&lt;br /&gt;
	-- Data includes:&lt;br /&gt;
	-- env.protectionLevels - the protection levels table of the title object.&lt;br /&gt;
	-- env.subjectSpace - the number of the title's subject namespace.&lt;br /&gt;
	-- env.docSpace - the number of the namespace the title puts its documentation in.&lt;br /&gt;
	-- env.docpageBase - the text of the base page of the /doc, /sandbox and /testcases pages, with namespace.&lt;br /&gt;
	-- env.compareUrl - URL of the Special:ComparePages page comparing the sandbox with the template.&lt;br /&gt;
	-- &lt;br /&gt;
	-- All table lookups are passed through pcall so that errors are caught. If an error occurs, the value&lt;br /&gt;
	-- returned will be nil.&lt;br /&gt;
	--]]&lt;br /&gt;
	&lt;br /&gt;
	local env, envFuncs = {}, {}&lt;br /&gt;
&lt;br /&gt;
	-- Set up the metatable. If triggered we call the corresponding function in the envFuncs table. The value&lt;br /&gt;
	-- returned by that function is memoized in the env table so that we don't call any of the functions&lt;br /&gt;
	-- more than once. (Nils won't be memoized.)&lt;br /&gt;
	setmetatable(env, {&lt;br /&gt;
		__index = function (t, key)&lt;br /&gt;
			local envFunc = envFuncs[key]&lt;br /&gt;
			if envFunc then&lt;br /&gt;
				local success, val = pcall(envFunc)&lt;br /&gt;
				if success then&lt;br /&gt;
					env[key] = val -- Memoise the value.&lt;br /&gt;
					return val&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	})	&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.title()&lt;br /&gt;
		-- The title object for the current page, or a test page passed with args.page.&lt;br /&gt;
		local title&lt;br /&gt;
		local titleArg = args.page&lt;br /&gt;
		if titleArg then&lt;br /&gt;
			title = mw.title.new(titleArg)&lt;br /&gt;
		else&lt;br /&gt;
			title = mw.title.getCurrentTitle()&lt;br /&gt;
		end&lt;br /&gt;
		return title&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.templateTitle()&lt;br /&gt;
		--[[&lt;br /&gt;
		-- The template (or module, etc.) title object.&lt;br /&gt;
		-- Messages:&lt;br /&gt;
		-- 'sandbox-subpage' --&amp;gt; 'sandbox'&lt;br /&gt;
		-- 'testcases-subpage' --&amp;gt; 'testcases'&lt;br /&gt;
		--]]&lt;br /&gt;
		local subjectSpace = env.subjectSpace&lt;br /&gt;
		local title = env.title&lt;br /&gt;
		local subpage = title.subpageText&lt;br /&gt;
		if subpage == message('sandbox-subpage') or subpage == message('testcases-subpage') then&lt;br /&gt;
			return mw.title.makeTitle(subjectSpace, title.baseText)&lt;br /&gt;
		else&lt;br /&gt;
			return mw.title.makeTitle(subjectSpace, title.text)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.docTitle()&lt;br /&gt;
		--[[&lt;br /&gt;
		-- Title object of the /doc subpage.&lt;br /&gt;
		-- Messages:&lt;br /&gt;
		-- 'doc-subpage' --&amp;gt; 'doc'&lt;br /&gt;
		--]]&lt;br /&gt;
		local title = env.title&lt;br /&gt;
		local docname = args[1] -- User-specified doc page.&lt;br /&gt;
		local docpage&lt;br /&gt;
		if docname then&lt;br /&gt;
			docpage = docname&lt;br /&gt;
		else&lt;br /&gt;
			docpage = env.docpageBase .. '/' .. message('doc-subpage')&lt;br /&gt;
		end&lt;br /&gt;
		return mw.title.new(docpage)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	function envFuncs.sandboxTitle()&lt;br /&gt;
		--[[&lt;br /&gt;
		-- Title object for the /sandbox subpage.&lt;br /&gt;
		-- Messages:&lt;br /&gt;
		-- 'sandbox-subpage' --&amp;gt; 'sandbox'&lt;br /&gt;
		--]]&lt;br /&gt;
		return mw.title.new(env.docpageBase .. '/' .. message('sandbox-subpage'))&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	function envFuncs.testcasesTitle()&lt;br /&gt;
		--[[&lt;br /&gt;
		-- Title object for the /testcases subpage.&lt;br /&gt;
		-- Messages:&lt;br /&gt;
		-- 'testcases-subpage' --&amp;gt; 'testcases'&lt;br /&gt;
		--]]&lt;br /&gt;
		return mw.title.new(env.docpageBase .. '/' .. message('testcases-subpage'))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.protectionLevels()&lt;br /&gt;
		-- The protection levels table of the title object.&lt;br /&gt;
		return env.title.protectionLevels&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.subjectSpace()&lt;br /&gt;
		-- The subject namespace number.&lt;br /&gt;
		return mw.site.namespaces[env.title.namespace].subject.id&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.docSpace()&lt;br /&gt;
		-- The documentation namespace number. For most namespaces this is the&lt;br /&gt;
		-- same as the subject namespace. However, pages in the Article, File,&lt;br /&gt;
		-- MediaWiki or Category namespaces must have their /doc, /sandbox and&lt;br /&gt;
		-- /testcases pages in talk space.&lt;br /&gt;
		local subjectSpace = env.subjectSpace&lt;br /&gt;
		if subjectSpace == 0 or subjectSpace == 6 or subjectSpace == 8 or subjectSpace == 14 then&lt;br /&gt;
			return subjectSpace + 1&lt;br /&gt;
		else&lt;br /&gt;
			return subjectSpace&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	function envFuncs.docpageBase()&lt;br /&gt;
		-- The base page of the /doc, /sandbox, and /testcases subpages.&lt;br /&gt;
		-- For some namespaces this is the talk page, rather than the template page.&lt;br /&gt;
		local templateTitle = env.templateTitle&lt;br /&gt;
		local docSpace = env.docSpace&lt;br /&gt;
		local docSpaceText = mw.site.namespaces[docSpace].name&lt;br /&gt;
		-- Assemble the link. docSpace is never the main namespace, so we can hardcode the colon.&lt;br /&gt;
		return docSpaceText .. ':' .. templateTitle.text&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	function envFuncs.compareUrl()&lt;br /&gt;
		-- Diff link between the sandbox and the main template using [[Special:ComparePages]].&lt;br /&gt;
		local templateTitle = env.templateTitle&lt;br /&gt;
		local sandboxTitle = env.sandboxTitle&lt;br /&gt;
		if templateTitle.exists and sandboxTitle.exists then&lt;br /&gt;
			local compareUrl = mw.uri.fullUrl(&lt;br /&gt;
				'Special:ComparePages',&lt;br /&gt;
				{ page1 = templateTitle.prefixedText, page2 = sandboxTitle.prefixedText}&lt;br /&gt;
			)&lt;br /&gt;
			return tostring(compareUrl)&lt;br /&gt;
		else&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	end		&lt;br /&gt;
&lt;br /&gt;
	return env&lt;br /&gt;
end	&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Auxiliary templates&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
p.getModuleWikitext = makeInvokeFunc('_getModuleWikitext')&lt;br /&gt;
&lt;br /&gt;
function p._getModuleWikitext(args, env)&lt;br /&gt;
	local currentTitle = mw.title.getCurrentTitle()&lt;br /&gt;
	if currentTitle.contentModel ~= 'Scribunto' then return end&lt;br /&gt;
	pcall(require, currentTitle.prefixedText) -- if it fails, we don't care&lt;br /&gt;
	local moduleWikitext =  package.loaded[&amp;quot;Module:Module wikitext&amp;quot;]&lt;br /&gt;
	if moduleWikitext then&lt;br /&gt;
		return moduleWikitext.main()&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.sandboxNotice(args, env)&lt;br /&gt;
	--[=[&lt;br /&gt;
	-- Generates a sandbox notice for display above sandbox pages.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'sandbox-notice-image' --&amp;gt; '[[Image:Sandbox.svg|50px|alt=|link=]]'&lt;br /&gt;
	-- 'sandbox-notice-blurb' --&amp;gt; 'This is the $1 for $2.'&lt;br /&gt;
	-- 'sandbox-notice-diff-blurb' --&amp;gt; 'This is the $1 for $2 ($3).'&lt;br /&gt;
	-- 'sandbox-notice-pagetype-template' --&amp;gt; '[[Wikipedia:Template test cases|template sandbox]] page'&lt;br /&gt;
	-- 'sandbox-notice-pagetype-module' --&amp;gt; '[[Wikipedia:Template test cases|module sandbox]] page'&lt;br /&gt;
	-- 'sandbox-notice-pagetype-other' --&amp;gt; 'sandbox page'&lt;br /&gt;
	-- 'sandbox-notice-compare-link-display' --&amp;gt; 'diff'&lt;br /&gt;
	-- 'sandbox-notice-testcases-blurb' --&amp;gt; 'See also the companion subpage for $1.'&lt;br /&gt;
	-- 'sandbox-notice-testcases-link-display' --&amp;gt; 'test cases'&lt;br /&gt;
	-- 'sandbox-category' --&amp;gt; 'Template sandboxes'&lt;br /&gt;
	--]=]&lt;br /&gt;
	local title = env.title&lt;br /&gt;
	local sandboxTitle = env.sandboxTitle&lt;br /&gt;
	local templateTitle = env.templateTitle&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	if not (subjectSpace and title and sandboxTitle and templateTitle&lt;br /&gt;
		and mw.title.equals(title, sandboxTitle)) then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	-- Build the table of arguments to pass to {{ombox}}. We need just two fields, &amp;quot;image&amp;quot; and &amp;quot;text&amp;quot;.&lt;br /&gt;
	local omargs = {}&lt;br /&gt;
	omargs.image = message('sandbox-notice-image')&lt;br /&gt;
	-- Get the text. We start with the opening blurb, which is something like&lt;br /&gt;
	-- &amp;quot;This is the template sandbox for [[Template:Foo]] (diff).&amp;quot;&lt;br /&gt;
	local text = ''&lt;br /&gt;
	local pagetype&lt;br /&gt;
	if subjectSpace == 10 then&lt;br /&gt;
		pagetype = message('sandbox-notice-pagetype-template')&lt;br /&gt;
	elseif subjectSpace == 828 then&lt;br /&gt;
		pagetype = message('sandbox-notice-pagetype-module')&lt;br /&gt;
	else&lt;br /&gt;
		pagetype = message('sandbox-notice-pagetype-other')&lt;br /&gt;
	end&lt;br /&gt;
	local templateLink = makeWikilink(templateTitle.prefixedText)&lt;br /&gt;
	local compareUrl = env.compareUrl&lt;br /&gt;
	if compareUrl then&lt;br /&gt;
		local compareDisplay = message('sandbox-notice-compare-link-display')&lt;br /&gt;
		local compareLink = makeUrlLink(compareUrl, compareDisplay)&lt;br /&gt;
		text = text .. message('sandbox-notice-diff-blurb', {pagetype, templateLink, compareLink})&lt;br /&gt;
	else&lt;br /&gt;
		text = text .. message('sandbox-notice-blurb', {pagetype, templateLink})&lt;br /&gt;
	end&lt;br /&gt;
	-- Get the test cases page blurb if the page exists. This is something like&lt;br /&gt;
	-- &amp;quot;See also the companion subpage for [[Template:Foo/testcases|test cases]].&amp;quot;&lt;br /&gt;
	local testcasesTitle = env.testcasesTitle&lt;br /&gt;
	if testcasesTitle and testcasesTitle.exists then&lt;br /&gt;
		if testcasesTitle.contentModel == &amp;quot;Scribunto&amp;quot; then&lt;br /&gt;
			local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')&lt;br /&gt;
			local testcasesRunLinkDisplay = message('sandbox-notice-testcases-run-link-display')&lt;br /&gt;
			local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)&lt;br /&gt;
			local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay)&lt;br /&gt;
			text = text .. '&amp;lt;br /&amp;gt;' .. message('sandbox-notice-testcases-run-blurb', {testcasesLink, testcasesRunLink})&lt;br /&gt;
		else&lt;br /&gt;
			local testcasesLinkDisplay = message('sandbox-notice-testcases-link-display')&lt;br /&gt;
			local testcasesLink = makeWikilink(testcasesTitle.prefixedText, testcasesLinkDisplay)&lt;br /&gt;
			text = text .. '&amp;lt;br /&amp;gt;' .. message('sandbox-notice-testcases-blurb', {testcasesLink})&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	-- Add the sandbox to the sandbox category.&lt;br /&gt;
	omargs.text = text .. makeCategoryLink(message('sandbox-category'))&lt;br /&gt;
&lt;br /&gt;
	-- 'documentation-clear'&lt;br /&gt;
	return '&amp;lt;div class=&amp;quot;' .. message('clear') .. '&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;'&lt;br /&gt;
		.. require('Module:Message box').main('ombox', omargs)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.protectionTemplate(env)&lt;br /&gt;
	-- Generates the padlock icon in the top right.&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'protection-template' --&amp;gt; 'pp-template'&lt;br /&gt;
	-- 'protection-template-args' --&amp;gt; {docusage = 'yes'}&lt;br /&gt;
	local protectionLevels = env.protectionLevels&lt;br /&gt;
	if not protectionLevels then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local editProt = protectionLevels.edit and protectionLevels.edit[1]&lt;br /&gt;
	local moveProt = protectionLevels.move and protectionLevels.move[1]&lt;br /&gt;
	if editProt then&lt;br /&gt;
		-- The page is edit-protected.&lt;br /&gt;
		return require('Module:Protection banner')._main{&lt;br /&gt;
			message('protection-reason-edit'), small = true&lt;br /&gt;
		}&lt;br /&gt;
	elseif moveProt and moveProt ~= 'autoconfirmed' then&lt;br /&gt;
		-- The page is move-protected but not edit-protected. Exclude move&lt;br /&gt;
		-- protection with the level &amp;quot;autoconfirmed&amp;quot;, as this is equivalent to&lt;br /&gt;
		-- no move protection at all.&lt;br /&gt;
		return require('Module:Protection banner')._main{&lt;br /&gt;
			action = 'move', small = true&lt;br /&gt;
		}&lt;br /&gt;
	else&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Start box&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
p.startBox = makeInvokeFunc('_startBox')&lt;br /&gt;
&lt;br /&gt;
function p._startBox(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- This function generates the start box.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- The actual work is done by p.makeStartBoxLinksData and p.renderStartBoxLinks which make&lt;br /&gt;
	-- the [view] [edit] [history] [purge] links, and by p.makeStartBoxData and p.renderStartBox&lt;br /&gt;
	-- which generate the box HTML.&lt;br /&gt;
	--]]&lt;br /&gt;
	env = env or p.getEnvironment(args)&lt;br /&gt;
	local links&lt;br /&gt;
	local content = args.content&lt;br /&gt;
	if not content or args[1] then&lt;br /&gt;
		-- No need to include the links if the documentation is on the template page itself.&lt;br /&gt;
		local linksData = p.makeStartBoxLinksData(args, env)&lt;br /&gt;
		if linksData then&lt;br /&gt;
			links = p.renderStartBoxLinks(linksData)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	-- Generate the start box html.&lt;br /&gt;
	local data = p.makeStartBoxData(args, env, links)&lt;br /&gt;
	if data then&lt;br /&gt;
		return p.renderStartBox(data)&lt;br /&gt;
	else&lt;br /&gt;
		-- User specified no heading.&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeStartBoxLinksData(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Does initial processing of data to make the [view] [edit] [history] [purge] links.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'view-link-display' --&amp;gt; 'view'&lt;br /&gt;
	-- 'edit-link-display' --&amp;gt; 'edit'&lt;br /&gt;
	-- 'history-link-display' --&amp;gt; 'history'&lt;br /&gt;
	-- 'purge-link-display' --&amp;gt; 'purge'&lt;br /&gt;
	-- 'module-preload' --&amp;gt; 'Template:Documentation/preload-module-doc'&lt;br /&gt;
	-- 'docpage-preload' --&amp;gt; 'Template:Documentation/preload'&lt;br /&gt;
	-- 'create-link-display' --&amp;gt; 'create'&lt;br /&gt;
	--]]&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	local title = env.title&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not title or not docTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	if docTitle.isRedirect then &lt;br /&gt;
		docTitle = docTitle.redirectTarget&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local data = {}&lt;br /&gt;
	data.title = title&lt;br /&gt;
	data.docTitle = docTitle&lt;br /&gt;
	-- View, display, edit, and purge links if /doc exists.&lt;br /&gt;
	data.viewLinkDisplay = message('view-link-display')&lt;br /&gt;
	data.editLinkDisplay = message('edit-link-display')&lt;br /&gt;
	data.historyLinkDisplay = message('history-link-display')&lt;br /&gt;
	data.purgeLinkDisplay = message('purge-link-display')&lt;br /&gt;
	-- Create link if /doc doesn't exist.&lt;br /&gt;
	local preload = args.preload&lt;br /&gt;
	if not preload then&lt;br /&gt;
		if subjectSpace == 828 then -- Module namespace&lt;br /&gt;
			preload = message('module-preload')&lt;br /&gt;
		else&lt;br /&gt;
			preload = message('docpage-preload')&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	data.preload = preload&lt;br /&gt;
	data.createLinkDisplay = message('create-link-display')&lt;br /&gt;
	return data&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderStartBoxLinks(data)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Generates the [view][edit][history][purge] or [create][purge] links from the data table.&lt;br /&gt;
	-- @data - a table of data generated by p.makeStartBoxLinksData&lt;br /&gt;
	--]]&lt;br /&gt;
	&lt;br /&gt;
	local function escapeBrackets(s)&lt;br /&gt;
		-- Escapes square brackets with HTML entities.&lt;br /&gt;
		s = s:gsub('%[', '&amp;amp;#91;') -- Replace square brackets with HTML entities.&lt;br /&gt;
		s = s:gsub('%]', '&amp;amp;#93;')&lt;br /&gt;
		return s&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ret&lt;br /&gt;
	local docTitle = data.docTitle&lt;br /&gt;
	local title = data.title&lt;br /&gt;
	local purgeLink = makeUrlLink(title:fullUrl{action = 'purge'}, data.purgeLinkDisplay)&lt;br /&gt;
	if docTitle.exists then&lt;br /&gt;
		local viewLink = makeWikilink(docTitle.prefixedText, data.viewLinkDisplay)&lt;br /&gt;
		local editLink = makeUrlLink(docTitle:fullUrl{action = 'edit'}, data.editLinkDisplay)&lt;br /&gt;
		local historyLink = makeUrlLink(docTitle:fullUrl{action = 'history'}, data.historyLinkDisplay)&lt;br /&gt;
		ret = '[%s] [%s] [%s] [%s]'&lt;br /&gt;
		ret = escapeBrackets(ret)&lt;br /&gt;
		ret = mw.ustring.format(ret, viewLink, editLink, historyLink, purgeLink)&lt;br /&gt;
	else&lt;br /&gt;
		local createLink = makeUrlLink(docTitle:fullUrl{action = 'edit', preload = data.preload}, data.createLinkDisplay)&lt;br /&gt;
		ret = '[%s] [%s]'&lt;br /&gt;
		ret = escapeBrackets(ret)&lt;br /&gt;
		ret = mw.ustring.format(ret, createLink, purgeLink)&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeStartBoxData(args, env, links)&lt;br /&gt;
	--[=[&lt;br /&gt;
	-- Does initial processing of data to pass to the start-box render function, p.renderStartBox.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- @links - a string containing the [view][edit][history][purge] links - could be nil if there's an error.&lt;br /&gt;
	--&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'documentation-icon-wikitext' --&amp;gt; '[[File:Test Template Info-Icon - Version (2).svg|50px|link=|alt=]]'&lt;br /&gt;
	-- 'template-namespace-heading' --&amp;gt; 'Template documentation'&lt;br /&gt;
	-- 'module-namespace-heading' --&amp;gt; 'Module documentation'&lt;br /&gt;
	-- 'file-namespace-heading' --&amp;gt; 'Summary'&lt;br /&gt;
	-- 'other-namespaces-heading' --&amp;gt; 'Documentation'&lt;br /&gt;
	-- 'testcases-create-link-display' --&amp;gt; 'create'&lt;br /&gt;
	--]=]&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	if not subjectSpace then&lt;br /&gt;
		-- Default to an &amp;quot;other namespaces&amp;quot; namespace, so that we get at least some output&lt;br /&gt;
		-- if an error occurs.&lt;br /&gt;
		subjectSpace = 2&lt;br /&gt;
	end&lt;br /&gt;
	local data = {}&lt;br /&gt;
	&lt;br /&gt;
	-- Heading&lt;br /&gt;
	local heading = args.heading -- Blank values are not removed.&lt;br /&gt;
	if heading == '' then&lt;br /&gt;
		-- Don't display the start box if the heading arg is defined but blank.&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	if heading then&lt;br /&gt;
		data.heading = heading&lt;br /&gt;
	elseif subjectSpace == 10 then -- Template namespace&lt;br /&gt;
		data.heading = message('documentation-icon-wikitext') .. ' ' .. message('template-namespace-heading')&lt;br /&gt;
	elseif subjectSpace == 828 then -- Module namespace&lt;br /&gt;
		data.heading = message('documentation-icon-wikitext') .. ' ' .. message('module-namespace-heading')&lt;br /&gt;
	elseif subjectSpace == 6 then -- File namespace&lt;br /&gt;
		data.heading = message('file-namespace-heading')&lt;br /&gt;
	else&lt;br /&gt;
		data.heading = message('other-namespaces-heading')&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Heading CSS&lt;br /&gt;
	local headingStyle = args['heading-style']&lt;br /&gt;
	if headingStyle then&lt;br /&gt;
		data.headingStyleText = headingStyle&lt;br /&gt;
	else&lt;br /&gt;
		-- 'documentation-heading'&lt;br /&gt;
		data.headingClass = message('main-div-heading-class')&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Data for the [view][edit][history][purge] or [create] links.&lt;br /&gt;
	if links then&lt;br /&gt;
		-- 'mw-editsection-like plainlinks'&lt;br /&gt;
		data.linksClass = message('start-box-link-classes')&lt;br /&gt;
		data.links = links&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return data&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.renderStartBox(data)&lt;br /&gt;
	-- Renders the start box html.&lt;br /&gt;
	-- @data - a table of data generated by p.makeStartBoxData.&lt;br /&gt;
	local sbox = mw.html.create('div')&lt;br /&gt;
	sbox&lt;br /&gt;
		-- 'documentation-startbox'&lt;br /&gt;
		:addClass(message('start-box-class'))&lt;br /&gt;
		:newline()&lt;br /&gt;
		:tag('span')&lt;br /&gt;
			:addClass(data.headingClass)&lt;br /&gt;
			:attr('id', 'documentation-heading')&lt;br /&gt;
			:cssText(data.headingStyleText)&lt;br /&gt;
			:wikitext(data.heading)&lt;br /&gt;
	local links = data.links&lt;br /&gt;
	if links then&lt;br /&gt;
		sbox:tag('span')&lt;br /&gt;
			:addClass(data.linksClass)&lt;br /&gt;
			:attr('id', data.linksId)&lt;br /&gt;
			:wikitext(links)&lt;br /&gt;
	end&lt;br /&gt;
	return tostring(sbox)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Documentation content&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
p.content = makeInvokeFunc('_content')&lt;br /&gt;
&lt;br /&gt;
function p._content(args, env)&lt;br /&gt;
	-- Displays the documentation contents&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	env = env or p.getEnvironment(args)&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	local content = args.content&lt;br /&gt;
	if not content and docTitle and docTitle.exists then&lt;br /&gt;
		content = args._content or mw.getCurrentFrame():expandTemplate{title = docTitle.prefixedText}&lt;br /&gt;
	end&lt;br /&gt;
	-- The line breaks below are necessary so that &amp;quot;=== Headings ===&amp;quot; at the start and end&lt;br /&gt;
	-- of docs are interpreted correctly.&lt;br /&gt;
	return '\n' .. (content or '') .. '\n' &lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
p.contentTitle = makeInvokeFunc('_contentTitle')&lt;br /&gt;
&lt;br /&gt;
function p._contentTitle(args, env)&lt;br /&gt;
	env = env or p.getEnvironment(args)&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not args.content and docTitle and docTitle.exists then&lt;br /&gt;
		return docTitle.prefixedText&lt;br /&gt;
	else&lt;br /&gt;
		return ''&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- End box&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
p.endBox = makeInvokeFunc('_endBox')&lt;br /&gt;
&lt;br /&gt;
function p._endBox(args, env)&lt;br /&gt;
	--[=[&lt;br /&gt;
	-- This function generates the end box (also known as the link box).&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	--]=]&lt;br /&gt;
	&lt;br /&gt;
	-- Get environment data.&lt;br /&gt;
	env = env or p.getEnvironment(args)&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not subjectSpace or not docTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
		&lt;br /&gt;
	-- Check whether we should output the end box at all. Add the end&lt;br /&gt;
	-- box by default if the documentation exists or if we are in the&lt;br /&gt;
	-- user, module or template namespaces.&lt;br /&gt;
	local linkBox = args['link box']&lt;br /&gt;
	if linkBox == 'off'&lt;br /&gt;
		or not (&lt;br /&gt;
			docTitle.exists&lt;br /&gt;
			or subjectSpace == 2&lt;br /&gt;
			or subjectSpace == 828&lt;br /&gt;
			or subjectSpace == 10&lt;br /&gt;
		)&lt;br /&gt;
	then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Assemble the link box.&lt;br /&gt;
	local text = ''&lt;br /&gt;
	if linkBox then&lt;br /&gt;
		text = text .. linkBox&lt;br /&gt;
	else&lt;br /&gt;
		text = text .. (p.makeDocPageBlurb(args, env) or '') -- &amp;quot;This documentation is transcluded from [[Foo]].&amp;quot; &lt;br /&gt;
		if subjectSpace == 2 or subjectSpace == 10 or subjectSpace == 828 then&lt;br /&gt;
			-- We are in the user, template or module namespaces.&lt;br /&gt;
			-- Add sandbox and testcases links.&lt;br /&gt;
			-- &amp;quot;Editors can experiment in this template's sandbox and testcases pages.&amp;quot;&lt;br /&gt;
			text = text .. (p.makeExperimentBlurb(args, env) or '') .. '&amp;lt;br /&amp;gt;'&lt;br /&gt;
			if not args.content and not args[1] then&lt;br /&gt;
				-- &amp;quot;Please add categories to the /doc subpage.&amp;quot;&lt;br /&gt;
				-- Don't show this message with inline docs or with an explicitly specified doc page,&lt;br /&gt;
				-- as then it is unclear where to add the categories.&lt;br /&gt;
				text = text .. (p.makeCategoriesBlurb(args, env) or '')&lt;br /&gt;
			end&lt;br /&gt;
			text = text .. ' ' .. (p.makeSubpagesBlurb(args, env) or '') --&amp;quot;Subpages of this template&amp;quot;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local box = mw.html.create('div')&lt;br /&gt;
	-- 'documentation-metadata'&lt;br /&gt;
	box:attr('role', 'note')&lt;br /&gt;
		:addClass(message('end-box-class'))&lt;br /&gt;
		-- 'plainlinks'&lt;br /&gt;
		:addClass(message('end-box-plainlinks'))&lt;br /&gt;
		:wikitext(text)&lt;br /&gt;
		:done()&lt;br /&gt;
&lt;br /&gt;
	return '\n' .. tostring(box)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeDocPageBlurb(args, env)&lt;br /&gt;
	--[=[&lt;br /&gt;
	-- Makes the blurb &amp;quot;This documentation is transcluded from [[Template:Foo]] (edit, history)&amp;quot;.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'edit-link-display' --&amp;gt; 'edit'&lt;br /&gt;
	-- 'history-link-display' --&amp;gt; 'history'&lt;br /&gt;
	-- 'transcluded-from-blurb' --&amp;gt; &lt;br /&gt;
	-- 'The above [[Wikipedia:Template documentation|documentation]] &lt;br /&gt;
	-- is [[Help:Transclusion|transcluded]] from $1.'&lt;br /&gt;
	-- 'module-preload' --&amp;gt; 'Template:Documentation/preload-module-doc'&lt;br /&gt;
	-- 'create-link-display' --&amp;gt; 'create'&lt;br /&gt;
	-- 'create-module-doc-blurb' --&amp;gt;&lt;br /&gt;
	-- 'You might want to $1 a documentation page for this [[Wikipedia:Lua|Scribunto module]].'&lt;br /&gt;
	--]=]&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not docTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local ret&lt;br /&gt;
	if docTitle.exists then&lt;br /&gt;
		-- /doc exists; link to it.&lt;br /&gt;
		local docLink = makeWikilink(docTitle.prefixedText)&lt;br /&gt;
		local editUrl = docTitle:fullUrl{action = 'edit'}&lt;br /&gt;
		local editDisplay = message('edit-link-display')&lt;br /&gt;
		local editLink = makeUrlLink(editUrl, editDisplay)&lt;br /&gt;
		local historyUrl = docTitle:fullUrl{action = 'history'}&lt;br /&gt;
		local historyDisplay = message('history-link-display')&lt;br /&gt;
		local historyLink = makeUrlLink(historyUrl, historyDisplay)&lt;br /&gt;
		ret = message('transcluded-from-blurb', {docLink})&lt;br /&gt;
			.. ' '&lt;br /&gt;
			.. makeToolbar(editLink, historyLink)&lt;br /&gt;
			.. '&amp;lt;br /&amp;gt;'&lt;br /&gt;
	elseif env.subjectSpace == 828 then&lt;br /&gt;
		-- /doc does not exist; ask to create it.&lt;br /&gt;
		local createUrl = docTitle:fullUrl{action = 'edit', preload = message('module-preload')}&lt;br /&gt;
		local createDisplay = message('create-link-display')&lt;br /&gt;
		local createLink = makeUrlLink(createUrl, createDisplay)&lt;br /&gt;
		ret = message('create-module-doc-blurb', {createLink})&lt;br /&gt;
			.. '&amp;lt;br /&amp;gt;'&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeExperimentBlurb(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Renders the text &amp;quot;Editors can experiment in this template's sandbox (edit | diff) and testcases (edit) pages.&amp;quot;&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- &lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'sandbox-link-display' --&amp;gt; 'sandbox'&lt;br /&gt;
	-- 'sandbox-edit-link-display' --&amp;gt; 'edit'&lt;br /&gt;
	-- 'compare-link-display' --&amp;gt; 'diff'&lt;br /&gt;
	-- 'module-sandbox-preload' --&amp;gt; 'Template:Documentation/preload-module-sandbox'&lt;br /&gt;
	-- 'template-sandbox-preload' --&amp;gt; 'Template:Documentation/preload-sandbox'&lt;br /&gt;
	-- 'sandbox-create-link-display' --&amp;gt; 'create'&lt;br /&gt;
	-- 'mirror-edit-summary' --&amp;gt; 'Create sandbox version of $1'&lt;br /&gt;
	-- 'mirror-link-display' --&amp;gt; 'mirror'&lt;br /&gt;
	-- 'mirror-link-preload' --&amp;gt; 'Template:Documentation/mirror'&lt;br /&gt;
	-- 'sandbox-link-display' --&amp;gt; 'sandbox'&lt;br /&gt;
	-- 'testcases-link-display' --&amp;gt; 'testcases'&lt;br /&gt;
	-- 'testcases-edit-link-display'--&amp;gt; 'edit'&lt;br /&gt;
	-- 'template-sandbox-preload' --&amp;gt; 'Template:Documentation/preload-sandbox'&lt;br /&gt;
	-- 'testcases-create-link-display' --&amp;gt; 'create'&lt;br /&gt;
	-- 'testcases-link-display' --&amp;gt; 'testcases'&lt;br /&gt;
	-- 'testcases-edit-link-display' --&amp;gt; 'edit'&lt;br /&gt;
	-- 'module-testcases-preload' --&amp;gt; 'Template:Documentation/preload-module-testcases'&lt;br /&gt;
	-- 'template-testcases-preload' --&amp;gt; 'Template:Documentation/preload-testcases'&lt;br /&gt;
	-- 'experiment-blurb-module' --&amp;gt; 'Editors can experiment in this module's $1 and $2 pages.'&lt;br /&gt;
	-- 'experiment-blurb-template' --&amp;gt; 'Editors can experiment in this template's $1 and $2 pages.'&lt;br /&gt;
	--]]&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	local templateTitle = env.templateTitle&lt;br /&gt;
	local sandboxTitle = env.sandboxTitle&lt;br /&gt;
	local testcasesTitle = env.testcasesTitle&lt;br /&gt;
	local templatePage = templateTitle.prefixedText&lt;br /&gt;
	if not subjectSpace or not templateTitle or not sandboxTitle or not testcasesTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	-- Make links.&lt;br /&gt;
	local sandboxLinks, testcasesLinks&lt;br /&gt;
	if sandboxTitle.exists then&lt;br /&gt;
		local sandboxPage = sandboxTitle.prefixedText&lt;br /&gt;
		local sandboxDisplay = message('sandbox-link-display')&lt;br /&gt;
		local sandboxLink = makeWikilink(sandboxPage, sandboxDisplay)&lt;br /&gt;
		local sandboxEditUrl = sandboxTitle:fullUrl{action = 'edit'}&lt;br /&gt;
		local sandboxEditDisplay = message('sandbox-edit-link-display')&lt;br /&gt;
		local sandboxEditLink = makeUrlLink(sandboxEditUrl, sandboxEditDisplay)&lt;br /&gt;
		local compareUrl = env.compareUrl&lt;br /&gt;
		local compareLink&lt;br /&gt;
		if compareUrl then&lt;br /&gt;
			local compareDisplay = message('compare-link-display')&lt;br /&gt;
			compareLink = makeUrlLink(compareUrl, compareDisplay)&lt;br /&gt;
		end&lt;br /&gt;
		sandboxLinks = sandboxLink .. ' ' .. makeToolbar(sandboxEditLink, compareLink)&lt;br /&gt;
	else&lt;br /&gt;
		local sandboxPreload&lt;br /&gt;
		if subjectSpace == 828 then&lt;br /&gt;
			sandboxPreload = message('module-sandbox-preload')&lt;br /&gt;
		else&lt;br /&gt;
			sandboxPreload = message('template-sandbox-preload')&lt;br /&gt;
		end&lt;br /&gt;
		local sandboxCreateUrl = sandboxTitle:fullUrl{action = 'edit', preload = sandboxPreload}&lt;br /&gt;
		local sandboxCreateDisplay = message('sandbox-create-link-display')&lt;br /&gt;
		local sandboxCreateLink = makeUrlLink(sandboxCreateUrl, sandboxCreateDisplay)&lt;br /&gt;
		local mirrorSummary = message('mirror-edit-summary', {makeWikilink(templatePage)})&lt;br /&gt;
		local mirrorPreload = message('mirror-link-preload')&lt;br /&gt;
		local mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = mirrorPreload, summary = mirrorSummary}&lt;br /&gt;
		if subjectSpace == 828 then&lt;br /&gt;
			mirrorUrl = sandboxTitle:fullUrl{action = 'edit', preload = templateTitle.prefixedText, summary = mirrorSummary}&lt;br /&gt;
		end&lt;br /&gt;
		local mirrorDisplay = message('mirror-link-display')&lt;br /&gt;
		local mirrorLink = makeUrlLink(mirrorUrl, mirrorDisplay)&lt;br /&gt;
		sandboxLinks = message('sandbox-link-display') .. ' ' .. makeToolbar(sandboxCreateLink, mirrorLink)&lt;br /&gt;
	end&lt;br /&gt;
	if testcasesTitle.exists then&lt;br /&gt;
		local testcasesPage = testcasesTitle.prefixedText&lt;br /&gt;
		local testcasesDisplay = message('testcases-link-display')&lt;br /&gt;
		local testcasesLink = makeWikilink(testcasesPage, testcasesDisplay)&lt;br /&gt;
		local testcasesEditUrl = testcasesTitle:fullUrl{action = 'edit'}&lt;br /&gt;
		local testcasesEditDisplay = message('testcases-edit-link-display')&lt;br /&gt;
		local testcasesEditLink = makeUrlLink(testcasesEditUrl, testcasesEditDisplay)&lt;br /&gt;
		-- for Modules, add testcases run link if exists&lt;br /&gt;
		if testcasesTitle.contentModel == &amp;quot;Scribunto&amp;quot;  and testcasesTitle.talkPageTitle and testcasesTitle.talkPageTitle.exists then&lt;br /&gt;
			local testcasesRunLinkDisplay = message('testcases-run-link-display')&lt;br /&gt;
			local testcasesRunLink = makeWikilink(testcasesTitle.talkPageTitle.prefixedText, testcasesRunLinkDisplay)&lt;br /&gt;
			testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink, testcasesRunLink)&lt;br /&gt;
		else&lt;br /&gt;
			testcasesLinks = testcasesLink .. ' ' .. makeToolbar(testcasesEditLink)&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		local testcasesPreload&lt;br /&gt;
		if subjectSpace == 828 then&lt;br /&gt;
			testcasesPreload = message('module-testcases-preload')&lt;br /&gt;
		else&lt;br /&gt;
			testcasesPreload = message('template-testcases-preload')&lt;br /&gt;
		end&lt;br /&gt;
		local testcasesCreateUrl = testcasesTitle:fullUrl{action = 'edit', preload = testcasesPreload}&lt;br /&gt;
		local testcasesCreateDisplay = message('testcases-create-link-display')&lt;br /&gt;
		local testcasesCreateLink = makeUrlLink(testcasesCreateUrl, testcasesCreateDisplay)&lt;br /&gt;
		testcasesLinks = message('testcases-link-display') .. ' ' .. makeToolbar(testcasesCreateLink)&lt;br /&gt;
	end&lt;br /&gt;
	local messageName&lt;br /&gt;
	if subjectSpace == 828 then&lt;br /&gt;
		messageName = 'experiment-blurb-module'&lt;br /&gt;
	else&lt;br /&gt;
		messageName = 'experiment-blurb-template'&lt;br /&gt;
	end&lt;br /&gt;
	return message(messageName, {sandboxLinks, testcasesLinks})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeCategoriesBlurb(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Generates the text &amp;quot;Please add categories to the /doc subpage.&amp;quot;&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'doc-link-display' --&amp;gt; '/doc'&lt;br /&gt;
	-- 'add-categories-blurb' --&amp;gt; 'Please add categories to the $1 subpage.'&lt;br /&gt;
	--]]&lt;br /&gt;
	local docTitle = env.docTitle&lt;br /&gt;
	if not docTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local docPathLink = makeWikilink(docTitle.prefixedText, message('doc-link-display'))&lt;br /&gt;
	return message('add-categories-blurb', {docPathLink})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.makeSubpagesBlurb(args, env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Generates the &amp;quot;Subpages of this template&amp;quot; link.&lt;br /&gt;
	-- @args - a table of arguments passed by the user&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'template-pagetype' --&amp;gt; 'template'&lt;br /&gt;
	-- 'module-pagetype' --&amp;gt; 'module'&lt;br /&gt;
	-- 'default-pagetype' --&amp;gt; 'page'&lt;br /&gt;
	-- 'subpages-link-display' --&amp;gt; 'Subpages of this $1'&lt;br /&gt;
	--]]&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	local templateTitle = env.templateTitle&lt;br /&gt;
	if not subjectSpace or not templateTitle then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local pagetype&lt;br /&gt;
	if subjectSpace == 10 then&lt;br /&gt;
		pagetype = message('template-pagetype')&lt;br /&gt;
	elseif subjectSpace == 828 then&lt;br /&gt;
		pagetype = message('module-pagetype')&lt;br /&gt;
	else&lt;br /&gt;
		pagetype = message('default-pagetype')&lt;br /&gt;
	end&lt;br /&gt;
	local subpagesLink = makeWikilink(&lt;br /&gt;
		'Special:PrefixIndex/' .. templateTitle.prefixedText .. '/',&lt;br /&gt;
		message('subpages-link-display', {pagetype})&lt;br /&gt;
	)&lt;br /&gt;
	return message('subpages-blurb', {subpagesLink})&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
-- Tracking categories&lt;br /&gt;
----------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
function p.addTrackingCategories(env)&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Check if {{documentation}} is transcluded on a /doc or /testcases page.&lt;br /&gt;
	-- @env - environment table containing title objects, etc., generated with p.getEnvironment&lt;br /&gt;
	&lt;br /&gt;
	-- Messages:&lt;br /&gt;
	-- 'display-strange-usage-category' --&amp;gt; true&lt;br /&gt;
	-- 'doc-subpage' --&amp;gt; 'doc'&lt;br /&gt;
	-- 'testcases-subpage' --&amp;gt; 'testcases'&lt;br /&gt;
	-- 'strange-usage-category' --&amp;gt; 'Wikipedia pages with strange ((documentation)) usage'&lt;br /&gt;
	-- &lt;br /&gt;
	-- /testcases pages in the module namespace are not categorised, as they may have&lt;br /&gt;
	-- {{documentation}} transcluded automatically.&lt;br /&gt;
	--]]&lt;br /&gt;
	local title = env.title&lt;br /&gt;
	local subjectSpace = env.subjectSpace&lt;br /&gt;
	if not title or not subjectSpace then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local subpage = title.subpageText&lt;br /&gt;
	local ret = ''&lt;br /&gt;
	if message('display-strange-usage-category', nil, 'boolean')&lt;br /&gt;
		and (&lt;br /&gt;
			subpage == message('doc-subpage')&lt;br /&gt;
			or subjectSpace ~= 828 and subpage == message('testcases-subpage')&lt;br /&gt;
		)&lt;br /&gt;
	then&lt;br /&gt;
		ret = ret .. makeCategoryLink(message('strange-usage-category'))&lt;br /&gt;
	end&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Category_handler&amp;diff=237</id>
		<title>Module:Category handler</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Category_handler&amp;diff=237"/>
		<updated>2022-03-30T18:40:39Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-------------------------------------------------------------------------------- --                                                                            -- --...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--------------------------------------------------------------------------------&lt;br /&gt;
--                                                                            --&lt;br /&gt;
--                              CATEGORY HANDLER                              --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
--      This module implements the {{category handler}} template in Lua,      --&lt;br /&gt;
--      with a few improvements: all namespaces and all namespace aliases     --&lt;br /&gt;
--      are supported, and namespace names are detected automatically for     --&lt;br /&gt;
--      the local wiki. This module requires [[Module:Namespace detect]]      --&lt;br /&gt;
--      and [[Module:Yesno]] to be available on the local wiki. It can be     --&lt;br /&gt;
--      configured for different wikis by altering the values in              --&lt;br /&gt;
--      [[Module:Category handler/config]], and pages can be blacklisted      --&lt;br /&gt;
--      from categorisation by using [[Module:Category handler/blacklist]].   --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- Load required modules&lt;br /&gt;
local yesno = require('Module:Yesno')&lt;br /&gt;
&lt;br /&gt;
-- Lazily load things we don't always need&lt;br /&gt;
local mShared, mappings&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Helper functions&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function trimWhitespace(s, removeBlanks)&lt;br /&gt;
	if type(s) ~= 'string' then&lt;br /&gt;
		return s&lt;br /&gt;
	end&lt;br /&gt;
	s = s:match('^%s*(.-)%s*$')&lt;br /&gt;
	if removeBlanks then&lt;br /&gt;
		if s ~= '' then&lt;br /&gt;
			return s&lt;br /&gt;
		else&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		return s&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- CategoryHandler class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local CategoryHandler = {}&lt;br /&gt;
CategoryHandler.__index = CategoryHandler&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler.new(data, args)&lt;br /&gt;
	local obj = setmetatable({ _data = data, _args = args }, CategoryHandler)&lt;br /&gt;
	&lt;br /&gt;
	-- Set the title object&lt;br /&gt;
	do&lt;br /&gt;
		local pagename = obj:parameter('demopage')&lt;br /&gt;
		local success, titleObj&lt;br /&gt;
		if pagename then&lt;br /&gt;
			success, titleObj = pcall(mw.title.new, pagename)&lt;br /&gt;
		end&lt;br /&gt;
		if success and titleObj then&lt;br /&gt;
			obj.title = titleObj&lt;br /&gt;
			if titleObj == mw.title.getCurrentTitle() then&lt;br /&gt;
				obj._usesCurrentTitle = true&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			obj.title = mw.title.getCurrentTitle()&lt;br /&gt;
			obj._usesCurrentTitle = true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set suppression parameter values&lt;br /&gt;
	for _, key in ipairs{'nocat', 'categories'} do&lt;br /&gt;
		local value = obj:parameter(key)&lt;br /&gt;
		value = trimWhitespace(value, true)&lt;br /&gt;
		obj['_' .. key] = yesno(value)&lt;br /&gt;
	end&lt;br /&gt;
	do&lt;br /&gt;
		local subpage = obj:parameter('subpage')&lt;br /&gt;
		local category2 = obj:parameter('category2')&lt;br /&gt;
		if type(subpage) == 'string' then&lt;br /&gt;
			subpage = mw.ustring.lower(subpage)&lt;br /&gt;
		end&lt;br /&gt;
		if type(category2) == 'string' then&lt;br /&gt;
			subpage = mw.ustring.lower(category2)&lt;br /&gt;
		end&lt;br /&gt;
		obj._subpage = trimWhitespace(subpage, true)&lt;br /&gt;
		obj._category2 = trimWhitespace(category2) -- don't remove blank values&lt;br /&gt;
	end&lt;br /&gt;
	return obj&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler:parameter(key)&lt;br /&gt;
	local parameterNames = self._data.parameters[key]&lt;br /&gt;
	local pntype = type(parameterNames)&lt;br /&gt;
	if pntype == 'string' or pntype == 'number' then&lt;br /&gt;
		return self._args[parameterNames]&lt;br /&gt;
	elseif pntype == 'table' then&lt;br /&gt;
		for _, name in ipairs(parameterNames) do&lt;br /&gt;
			local value = self._args[name]&lt;br /&gt;
			if value ~= nil then&lt;br /&gt;
				return value&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		return nil&lt;br /&gt;
	else&lt;br /&gt;
		error(string.format(&lt;br /&gt;
			'invalid config key &amp;quot;%s&amp;quot;',&lt;br /&gt;
			tostring(key)&lt;br /&gt;
		), 2)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler:isSuppressedByArguments()&lt;br /&gt;
	return&lt;br /&gt;
		-- See if a category suppression argument has been set.&lt;br /&gt;
		self._nocat == true&lt;br /&gt;
		or self._categories == false&lt;br /&gt;
		or (&lt;br /&gt;
			self._category2&lt;br /&gt;
			and self._category2 ~= self._data.category2Yes&lt;br /&gt;
			and self._category2 ~= self._data.category2Negative&lt;br /&gt;
		)&lt;br /&gt;
&lt;br /&gt;
		-- Check whether we are on a subpage, and see if categories are&lt;br /&gt;
		-- suppressed based on our subpage status.&lt;br /&gt;
		or self._subpage == self._data.subpageNo and self.title.isSubpage&lt;br /&gt;
		or self._subpage == self._data.subpageOnly and not self.title.isSubpage&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler:shouldSkipBlacklistCheck()&lt;br /&gt;
	-- Check whether the category suppression arguments indicate we&lt;br /&gt;
	-- should skip the blacklist check.&lt;br /&gt;
	return self._nocat == false&lt;br /&gt;
		or self._categories == true&lt;br /&gt;
		or self._category2 == self._data.category2Yes&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler:matchesBlacklist()&lt;br /&gt;
	if self._usesCurrentTitle then&lt;br /&gt;
		return self._data.currentTitleMatchesBlacklist&lt;br /&gt;
	else&lt;br /&gt;
		mShared = mShared or require('Module:Category handler/shared')&lt;br /&gt;
		return mShared.matchesBlacklist(&lt;br /&gt;
			self.title.prefixedText,&lt;br /&gt;
			mw.loadData('Module:Category handler/blacklist')&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler:isSuppressed()&lt;br /&gt;
	-- Find if categories are suppressed by either the arguments or by&lt;br /&gt;
	-- matching the blacklist.&lt;br /&gt;
	return self:isSuppressedByArguments()&lt;br /&gt;
		or not self:shouldSkipBlacklistCheck() and self:matchesBlacklist()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler:getNamespaceParameters()&lt;br /&gt;
	if self._usesCurrentTitle then&lt;br /&gt;
		return self._data.currentTitleNamespaceParameters&lt;br /&gt;
	else&lt;br /&gt;
		if not mappings then&lt;br /&gt;
			mShared = mShared or require('Module:Category handler/shared')&lt;br /&gt;
			mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData&lt;br /&gt;
		end&lt;br /&gt;
		return mShared.getNamespaceParameters(&lt;br /&gt;
			self.title,&lt;br /&gt;
			mappings&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler:namespaceParametersExist()&lt;br /&gt;
	-- Find whether any namespace parameters have been specified.&lt;br /&gt;
	-- We use the order &amp;quot;all&amp;quot; --&amp;gt; namespace params --&amp;gt; &amp;quot;other&amp;quot; as this is what&lt;br /&gt;
	-- the old template did.&lt;br /&gt;
	if self:parameter('all') then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
	if not mappings then&lt;br /&gt;
		mShared = mShared or require('Module:Category handler/shared')&lt;br /&gt;
		mappings = mShared.getParamMappings(true) -- gets mappings with mw.loadData&lt;br /&gt;
	end&lt;br /&gt;
	for ns, params in pairs(mappings) do&lt;br /&gt;
		for i, param in ipairs(params) do&lt;br /&gt;
			if self._args[param] then&lt;br /&gt;
				return true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if self:parameter('other') then&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function CategoryHandler:getCategories()&lt;br /&gt;
	local params = self:getNamespaceParameters()&lt;br /&gt;
	local nsCategory&lt;br /&gt;
	for i, param in ipairs(params) do&lt;br /&gt;
		local value = self._args[param]&lt;br /&gt;
		if value ~= nil then&lt;br /&gt;
			nsCategory = value&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if nsCategory ~= nil or self:namespaceParametersExist() then&lt;br /&gt;
		-- Namespace parameters exist - advanced usage.&lt;br /&gt;
		if nsCategory == nil then&lt;br /&gt;
			nsCategory = self:parameter('other')&lt;br /&gt;
		end&lt;br /&gt;
		local ret = {self:parameter('all')}&lt;br /&gt;
		local numParam = tonumber(nsCategory)&lt;br /&gt;
		if numParam and numParam &amp;gt;= 1 and math.floor(numParam) == numParam then&lt;br /&gt;
			-- nsCategory is an integer&lt;br /&gt;
			ret[#ret + 1] = self._args[numParam]&lt;br /&gt;
		else&lt;br /&gt;
			ret[#ret + 1] = nsCategory&lt;br /&gt;
		end&lt;br /&gt;
		if #ret &amp;lt; 1 then&lt;br /&gt;
			return nil&lt;br /&gt;
		else&lt;br /&gt;
			return table.concat(ret)&lt;br /&gt;
		end&lt;br /&gt;
	elseif self._data.defaultNamespaces[self.title.namespace] then&lt;br /&gt;
		-- Namespace parameters don't exist, simple usage.&lt;br /&gt;
		return self._args[1]&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Exports&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p._exportClasses()&lt;br /&gt;
	-- Used for testing purposes.&lt;br /&gt;
	return {&lt;br /&gt;
		CategoryHandler = CategoryHandler&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._main(args, data)&lt;br /&gt;
	data = data or mw.loadData('Module:Category handler/data')&lt;br /&gt;
	local handler = CategoryHandler.new(data, args)&lt;br /&gt;
	if handler:isSuppressed() then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	return handler:getCategories()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame, data)&lt;br /&gt;
	data = data or mw.loadData('Module:Category handler/data')&lt;br /&gt;
	local args = require('Module:Arguments').getArgs(frame, {&lt;br /&gt;
		wrappers = data.wrappers,&lt;br /&gt;
		valueFunc = function (k, v)&lt;br /&gt;
			v = trimWhitespace(v)&lt;br /&gt;
			if type(k) == 'number' then&lt;br /&gt;
				if v ~= '' then&lt;br /&gt;
					return v&lt;br /&gt;
				else&lt;br /&gt;
					return nil&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				return v&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	})&lt;br /&gt;
	return p._main(args, data)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Message_box/configuration&amp;diff=236</id>
		<title>Module:Message box/configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Message_box/configuration&amp;diff=236"/>
		<updated>2022-03-30T18:39:01Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-------------------------------------------------------------------------------- --                          Message box configuration                         -- --...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--------------------------------------------------------------------------------&lt;br /&gt;
--                          Message box configuration                         --&lt;br /&gt;
--                                                                            --&lt;br /&gt;
-- This module contains configuration data for [[Module:Message box]].        --&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
	ambox = {&lt;br /&gt;
		types = {&lt;br /&gt;
			speedy = {&lt;br /&gt;
				class = 'ambox-speedy',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			delete = {&lt;br /&gt;
				class = 'ambox-delete',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			content = {&lt;br /&gt;
				class = 'ambox-content',&lt;br /&gt;
				image = 'Ambox important.svg'&lt;br /&gt;
			},&lt;br /&gt;
			style = {&lt;br /&gt;
				class = 'ambox-style',&lt;br /&gt;
				image = 'Edit-clear.svg'&lt;br /&gt;
			},&lt;br /&gt;
			move = {&lt;br /&gt;
				class = 'ambox-move',&lt;br /&gt;
				image = 'Merge-split-transwiki default.svg'&lt;br /&gt;
			},&lt;br /&gt;
			protection = {&lt;br /&gt;
				class = 'ambox-protection',&lt;br /&gt;
				image = 'Semi-protection-shackle-keyhole.svg'&lt;br /&gt;
			},&lt;br /&gt;
			notice = {&lt;br /&gt;
				class = 'ambox-notice',&lt;br /&gt;
				image = 'Information icon4.svg'&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		default                     = 'notice',&lt;br /&gt;
		allowBlankParams            = {'talk', 'sect', 'date', 'issue', 'fix', 'subst', 'hidden'},&lt;br /&gt;
		allowSmall                  = true,&lt;br /&gt;
		smallParam                  = 'left',&lt;br /&gt;
		smallClass                  = 'mbox-small-left',&lt;br /&gt;
		substCheck                  = true,&lt;br /&gt;
		classes                     = {'metadata', 'ambox'},&lt;br /&gt;
		imageEmptyCell              = true,&lt;br /&gt;
		imageCheckBlank             = true,&lt;br /&gt;
		imageSmallSize              = '20x20px',&lt;br /&gt;
		imageCellDiv                = true,&lt;br /&gt;
		useCollapsibleTextFields    = true,&lt;br /&gt;
		imageRightNone              = true,&lt;br /&gt;
		sectionDefault              = 'article',&lt;br /&gt;
		allowMainspaceCategories    = true,&lt;br /&gt;
		templateCategory            = 'Article message templates',&lt;br /&gt;
	        templateCategoryRequireName = true,&lt;br /&gt;
		templateErrorCategory       = 'Article message templates with missing parameters',&lt;br /&gt;
		templateErrorParamsToCheck  = {'issue', 'fix', 'subst'},&lt;br /&gt;
		removalNotice               = '&amp;lt;small&amp;gt;[[Help:Maintenance template removal|Learn how and when to remove this template message]]&amp;lt;/small&amp;gt;'&lt;br /&gt;
	},&lt;br /&gt;
	&lt;br /&gt;
	cmbox = {&lt;br /&gt;
		types = {&lt;br /&gt;
			speedy = {&lt;br /&gt;
				class = 'cmbox-speedy',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			delete = {&lt;br /&gt;
				class = 'cmbox-delete',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			content = {&lt;br /&gt;
				class = 'cmbox-content',&lt;br /&gt;
				image = 'Ambox important.svg'&lt;br /&gt;
			},&lt;br /&gt;
			style = {&lt;br /&gt;
				class = 'cmbox-style',&lt;br /&gt;
				image = 'Edit-clear.svg'&lt;br /&gt;
			},&lt;br /&gt;
			move = {&lt;br /&gt;
				class = 'cmbox-move',&lt;br /&gt;
				image = 'Merge-split-transwiki default.svg'&lt;br /&gt;
			},&lt;br /&gt;
			protection = {&lt;br /&gt;
				class = 'cmbox-protection',&lt;br /&gt;
				image = 'Semi-protection-shackle-keyhole.svg'&lt;br /&gt;
			},&lt;br /&gt;
			notice = {&lt;br /&gt;
				class = 'cmbox-notice',&lt;br /&gt;
				image = 'Information icon4.svg'&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		default              = 'notice',&lt;br /&gt;
		showInvalidTypeError = true,&lt;br /&gt;
		classes              = {'cmbox'},&lt;br /&gt;
		imageEmptyCell       = true&lt;br /&gt;
	},&lt;br /&gt;
	&lt;br /&gt;
	fmbox = {&lt;br /&gt;
		types = {&lt;br /&gt;
			warning = {&lt;br /&gt;
				class = 'fmbox-warning',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			editnotice = {&lt;br /&gt;
				class = 'fmbox-editnotice',&lt;br /&gt;
				image = 'Information icon4.svg'&lt;br /&gt;
			},&lt;br /&gt;
			system = {&lt;br /&gt;
				class = 'fmbox-system',&lt;br /&gt;
				image = 'Information icon4.svg'&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		default              = 'system',&lt;br /&gt;
		showInvalidTypeError = true,&lt;br /&gt;
		classes              = {'fmbox'},&lt;br /&gt;
		imageEmptyCell       = false,&lt;br /&gt;
		imageRightNone       = false&lt;br /&gt;
	},&lt;br /&gt;
	&lt;br /&gt;
	imbox = {&lt;br /&gt;
		types = {&lt;br /&gt;
			speedy = {&lt;br /&gt;
				class = 'imbox-speedy',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			delete = {&lt;br /&gt;
				class = 'imbox-delete',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			content = {&lt;br /&gt;
				class = 'imbox-content',&lt;br /&gt;
				image = 'Ambox important.svg'&lt;br /&gt;
			},&lt;br /&gt;
			style = {&lt;br /&gt;
				class = 'imbox-style',&lt;br /&gt;
				image = 'Edit-clear.svg'&lt;br /&gt;
			},&lt;br /&gt;
			move = {&lt;br /&gt;
				class = 'imbox-move',&lt;br /&gt;
				image = 'Merge-split-transwiki default.svg'&lt;br /&gt;
			},&lt;br /&gt;
			protection = {&lt;br /&gt;
				class = 'imbox-protection',&lt;br /&gt;
				image = 'Semi-protection-shackle-keyhole.svg'&lt;br /&gt;
			},&lt;br /&gt;
			license = {&lt;br /&gt;
				class = 'imbox-license licensetpl',&lt;br /&gt;
				image = 'Imbox license.png' -- @todo We need an SVG version of this&lt;br /&gt;
			},&lt;br /&gt;
			featured = {&lt;br /&gt;
				class = 'imbox-featured',&lt;br /&gt;
				image = 'Cscr-featured.svg'&lt;br /&gt;
			},&lt;br /&gt;
			notice = {&lt;br /&gt;
				class = 'imbox-notice',&lt;br /&gt;
				image = 'Information icon4.svg'&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		default              = 'notice',&lt;br /&gt;
		showInvalidTypeError = true,&lt;br /&gt;
		classes              = {'imbox'},&lt;br /&gt;
		imageEmptyCell       = true,&lt;br /&gt;
		below                = true,&lt;br /&gt;
		templateCategory     = 'File message boxes'&lt;br /&gt;
	},&lt;br /&gt;
	&lt;br /&gt;
	ombox = {&lt;br /&gt;
		types = {&lt;br /&gt;
			speedy = {&lt;br /&gt;
				class = 'ombox-speedy',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			delete = {&lt;br /&gt;
				class = 'ombox-delete',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			content = {&lt;br /&gt;
				class = 'ombox-content',&lt;br /&gt;
				image = 'Ambox important.svg'&lt;br /&gt;
			},&lt;br /&gt;
			style = {&lt;br /&gt;
				class = 'ombox-style',&lt;br /&gt;
				image = 'Edit-clear.svg'&lt;br /&gt;
			},&lt;br /&gt;
			move = {&lt;br /&gt;
				class = 'ombox-move',&lt;br /&gt;
				image = 'Merge-split-transwiki default.svg'&lt;br /&gt;
			},&lt;br /&gt;
			protection = {&lt;br /&gt;
				class = 'ombox-protection',&lt;br /&gt;
				image = 'Semi-protection-shackle-keyhole.svg'&lt;br /&gt;
			},&lt;br /&gt;
			notice = {&lt;br /&gt;
				class = 'ombox-notice',&lt;br /&gt;
				image = 'Information icon4.svg'&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		default              = 'notice',&lt;br /&gt;
		showInvalidTypeError = true,&lt;br /&gt;
		classes              = {'ombox'},&lt;br /&gt;
		allowSmall           = true,&lt;br /&gt;
		imageEmptyCell       = true,&lt;br /&gt;
		imageRightNone       = true&lt;br /&gt;
	},&lt;br /&gt;
	&lt;br /&gt;
	tmbox = {&lt;br /&gt;
		types = {&lt;br /&gt;
			speedy = {&lt;br /&gt;
				class = 'tmbox-speedy',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			delete = {&lt;br /&gt;
				class = 'tmbox-delete',&lt;br /&gt;
				image = 'Ambox warning pn.svg'&lt;br /&gt;
			},&lt;br /&gt;
			content = {&lt;br /&gt;
				class = 'tmbox-content',&lt;br /&gt;
				image = 'Ambox important.svg'&lt;br /&gt;
			},&lt;br /&gt;
			style = {&lt;br /&gt;
				class = 'tmbox-style',&lt;br /&gt;
				image = 'Edit-clear.svg'&lt;br /&gt;
			},&lt;br /&gt;
			move = {&lt;br /&gt;
				class = 'tmbox-move',&lt;br /&gt;
				image = 'Merge-split-transwiki default.svg'&lt;br /&gt;
			},&lt;br /&gt;
			protection = {&lt;br /&gt;
				class = 'tmbox-protection',&lt;br /&gt;
				image = 'Semi-protection-shackle-keyhole.svg'&lt;br /&gt;
			},&lt;br /&gt;
			notice = {&lt;br /&gt;
				class = 'tmbox-notice',&lt;br /&gt;
				image = 'Information icon4.svg'&lt;br /&gt;
			}&lt;br /&gt;
		},&lt;br /&gt;
		default              = 'notice',&lt;br /&gt;
		showInvalidTypeError = true,&lt;br /&gt;
		classes              = {'tmbox'},&lt;br /&gt;
		allowSmall           = true,&lt;br /&gt;
		imageRightNone       = true,&lt;br /&gt;
		imageEmptyCell       = true,&lt;br /&gt;
		imageEmptyCellStyle  = true,&lt;br /&gt;
		templateCategory     = 'Talk message boxes'&lt;br /&gt;
	}&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Message_box&amp;diff=235</id>
		<title>Module:Message box</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Message_box&amp;diff=235"/>
		<updated>2022-03-30T18:37:46Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- This is a meta-module for producing message box templates, including -- {{mbox}}, {{ambox}}, {{imbox}}, {{tmbox}}, {{ombox}}, {{cmbox}} and {{fmbox}}.  -- Load necessary mo...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This is a meta-module for producing message box templates, including&lt;br /&gt;
-- {{mbox}}, {{ambox}}, {{imbox}}, {{tmbox}}, {{ombox}}, {{cmbox}} and {{fmbox}}.&lt;br /&gt;
&lt;br /&gt;
-- Load necessary modules.&lt;br /&gt;
require('Module:No globals')&lt;br /&gt;
local getArgs&lt;br /&gt;
local yesno = require('Module:Yesno')&lt;br /&gt;
&lt;br /&gt;
-- Get a language object for formatDate and ucfirst.&lt;br /&gt;
local lang = mw.language.getContentLanguage()&lt;br /&gt;
&lt;br /&gt;
-- Define constants&lt;br /&gt;
local CONFIG_MODULE = 'Module:Message box/configuration'&lt;br /&gt;
local DEMOSPACES = {talk = 'tmbox', image = 'imbox', file = 'imbox', category = 'cmbox', article = 'ambox', main = 'ambox'}&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Helper functions&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function getTitleObject(...)&lt;br /&gt;
	-- Get the title object, passing the function through pcall&lt;br /&gt;
	-- in case we are over the expensive function count limit.&lt;br /&gt;
	local success, title = pcall(mw.title.new, ...)&lt;br /&gt;
	if success then&lt;br /&gt;
		return title&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function union(t1, t2)&lt;br /&gt;
	-- Returns the union of two arrays.&lt;br /&gt;
	local vals = {}&lt;br /&gt;
	for i, v in ipairs(t1) do&lt;br /&gt;
		vals[v] = true&lt;br /&gt;
	end&lt;br /&gt;
	for i, v in ipairs(t2) do&lt;br /&gt;
		vals[v] = true&lt;br /&gt;
	end&lt;br /&gt;
	local ret = {}&lt;br /&gt;
	for k in pairs(vals) do&lt;br /&gt;
		table.insert(ret, k)&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(ret)&lt;br /&gt;
	return ret&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getArgNums(args, prefix)&lt;br /&gt;
	local nums = {}&lt;br /&gt;
	for k, v in pairs(args) do&lt;br /&gt;
		local num = mw.ustring.match(tostring(k), '^' .. prefix .. '([1-9]%d*)$')&lt;br /&gt;
		if num then&lt;br /&gt;
			table.insert(nums, tonumber(num))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(nums)&lt;br /&gt;
	return nums&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Box class definition&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local MessageBox = {}&lt;br /&gt;
MessageBox.__index = MessageBox&lt;br /&gt;
&lt;br /&gt;
function MessageBox.new(boxType, args, cfg)&lt;br /&gt;
	args = args or {}&lt;br /&gt;
	local obj = {}&lt;br /&gt;
&lt;br /&gt;
	-- Set the title object and the namespace.&lt;br /&gt;
	obj.title = getTitleObject(args.page) or mw.title.getCurrentTitle()&lt;br /&gt;
&lt;br /&gt;
	-- Set the config for our box type.&lt;br /&gt;
	obj.cfg = cfg[boxType]&lt;br /&gt;
	if not obj.cfg then&lt;br /&gt;
		local ns = obj.title.namespace&lt;br /&gt;
		-- boxType is &amp;quot;mbox&amp;quot; or invalid input&lt;br /&gt;
		if args.demospace and args.demospace ~= '' then&lt;br /&gt;
			-- implement demospace parameter of mbox&lt;br /&gt;
			local demospace = string.lower(args.demospace)&lt;br /&gt;
			if DEMOSPACES[demospace] then&lt;br /&gt;
				-- use template from DEMOSPACES&lt;br /&gt;
				obj.cfg = cfg[DEMOSPACES[demospace]]&lt;br /&gt;
			elseif string.find( demospace, 'talk' ) then&lt;br /&gt;
				-- demo as a talk page&lt;br /&gt;
				obj.cfg = cfg.tmbox&lt;br /&gt;
			else&lt;br /&gt;
				-- default to ombox&lt;br /&gt;
				obj.cfg = cfg.ombox&lt;br /&gt;
			end&lt;br /&gt;
		elseif ns == 0 then&lt;br /&gt;
			obj.cfg = cfg.ambox -- main namespace&lt;br /&gt;
		elseif ns == 6 then&lt;br /&gt;
			obj.cfg = cfg.imbox -- file namespace&lt;br /&gt;
		elseif ns == 14 then&lt;br /&gt;
			obj.cfg = cfg.cmbox -- category namespace&lt;br /&gt;
		else&lt;br /&gt;
			local nsTable = mw.site.namespaces[ns]&lt;br /&gt;
			if nsTable and nsTable.isTalk then&lt;br /&gt;
				obj.cfg = cfg.tmbox -- any talk namespace&lt;br /&gt;
			else&lt;br /&gt;
				obj.cfg = cfg.ombox -- other namespaces or invalid input&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set the arguments, and remove all blank arguments except for the ones&lt;br /&gt;
	-- listed in cfg.allowBlankParams.&lt;br /&gt;
	do&lt;br /&gt;
		local newArgs = {}&lt;br /&gt;
		for k, v in pairs(args) do&lt;br /&gt;
			if v ~= '' then&lt;br /&gt;
				newArgs[k] = v&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		for i, param in ipairs(obj.cfg.allowBlankParams or {}) do&lt;br /&gt;
			newArgs[param] = args[param]&lt;br /&gt;
		end&lt;br /&gt;
		obj.args = newArgs&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Define internal data structure.&lt;br /&gt;
	obj.categories = {}&lt;br /&gt;
	obj.classes = {}&lt;br /&gt;
	-- For lazy loading of [[Module:Category handler]].&lt;br /&gt;
	obj.hasCategories = false&lt;br /&gt;
&lt;br /&gt;
	return setmetatable(obj, MessageBox)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:addCat(ns, cat, sort)&lt;br /&gt;
	if not cat then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	if sort then&lt;br /&gt;
		cat = string.format('[[Category:%s|%s]]', cat, sort)&lt;br /&gt;
	else&lt;br /&gt;
		cat = string.format('[[Category:%s]]', cat)&lt;br /&gt;
	end&lt;br /&gt;
	self.hasCategories = true&lt;br /&gt;
	self.categories[ns] = self.categories[ns] or {}&lt;br /&gt;
	table.insert(self.categories[ns], cat)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:addClass(class)&lt;br /&gt;
	if not class then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	table.insert(self.classes, class)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:setParameters()&lt;br /&gt;
	local args = self.args&lt;br /&gt;
	local cfg = self.cfg&lt;br /&gt;
&lt;br /&gt;
	-- Get type data.&lt;br /&gt;
	self.type = args.type&lt;br /&gt;
	local typeData = cfg.types[self.type]&lt;br /&gt;
	self.invalidTypeError = cfg.showInvalidTypeError&lt;br /&gt;
		and self.type&lt;br /&gt;
		and not typeData&lt;br /&gt;
	typeData = typeData or cfg.types[cfg.default]&lt;br /&gt;
	self.typeClass = typeData.class&lt;br /&gt;
	self.typeImage = typeData.image&lt;br /&gt;
&lt;br /&gt;
	-- Find if the box has been wrongly substituted.&lt;br /&gt;
	self.isSubstituted = cfg.substCheck and args.subst == 'SUBST'&lt;br /&gt;
&lt;br /&gt;
	-- Find whether we are using a small message box.&lt;br /&gt;
	self.isSmall = cfg.allowSmall and (&lt;br /&gt;
		cfg.smallParam and args.small == cfg.smallParam&lt;br /&gt;
		or not cfg.smallParam and yesno(args.small)&lt;br /&gt;
	)&lt;br /&gt;
&lt;br /&gt;
	-- Add attributes, classes and styles.&lt;br /&gt;
	self.id = args.id&lt;br /&gt;
	self.name = args.name&lt;br /&gt;
	if self.name then&lt;br /&gt;
		self:addClass('box-' .. string.gsub(self.name,' ','_'))&lt;br /&gt;
	end&lt;br /&gt;
	if yesno(args.plainlinks) ~= false then&lt;br /&gt;
		self:addClass('plainlinks')&lt;br /&gt;
	end&lt;br /&gt;
	for _, class in ipairs(cfg.classes or {}) do&lt;br /&gt;
		self:addClass(class)&lt;br /&gt;
	end&lt;br /&gt;
	if self.isSmall then&lt;br /&gt;
		self:addClass(cfg.smallClass or 'mbox-small')&lt;br /&gt;
	end&lt;br /&gt;
	self:addClass(self.typeClass)&lt;br /&gt;
	self:addClass(args.class)&lt;br /&gt;
	self.style = args.style&lt;br /&gt;
	self.attrs = args.attrs&lt;br /&gt;
&lt;br /&gt;
	-- Set text style.&lt;br /&gt;
	self.textstyle = args.textstyle&lt;br /&gt;
&lt;br /&gt;
	-- Find if we are on the template page or not. This functionality is only&lt;br /&gt;
	-- used if useCollapsibleTextFields is set, or if both cfg.templateCategory&lt;br /&gt;
	-- and cfg.templateCategoryRequireName are set.&lt;br /&gt;
	self.useCollapsibleTextFields = cfg.useCollapsibleTextFields&lt;br /&gt;
	if self.useCollapsibleTextFields&lt;br /&gt;
		or cfg.templateCategory&lt;br /&gt;
		and cfg.templateCategoryRequireName&lt;br /&gt;
	then&lt;br /&gt;
		if self.name then&lt;br /&gt;
			local templateName = mw.ustring.match(&lt;br /&gt;
				self.name,&lt;br /&gt;
				'^[tT][eE][mM][pP][lL][aA][tT][eE][%s_]*:[%s_]*(.*)$'&lt;br /&gt;
			) or self.name&lt;br /&gt;
			templateName = 'Template:' .. templateName&lt;br /&gt;
			self.templateTitle = getTitleObject(templateName)&lt;br /&gt;
		end&lt;br /&gt;
		self.isTemplatePage = self.templateTitle&lt;br /&gt;
			and mw.title.equals(self.title, self.templateTitle)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Process data for collapsible text fields. At the moment these are only&lt;br /&gt;
	-- used in {{ambox}}.&lt;br /&gt;
	if self.useCollapsibleTextFields then&lt;br /&gt;
		-- Get the self.issue value.&lt;br /&gt;
		if self.isSmall and args.smalltext then&lt;br /&gt;
			self.issue = args.smalltext&lt;br /&gt;
		else&lt;br /&gt;
			local sect&lt;br /&gt;
			if args.sect == '' then&lt;br /&gt;
				sect = 'This ' .. (cfg.sectionDefault or 'page')&lt;br /&gt;
			elseif type(args.sect) == 'string' then&lt;br /&gt;
				sect = 'This ' .. args.sect&lt;br /&gt;
			end&lt;br /&gt;
			local issue = args.issue&lt;br /&gt;
			issue = type(issue) == 'string' and issue ~= '' and issue or nil&lt;br /&gt;
			local text = args.text&lt;br /&gt;
			text = type(text) == 'string' and text or nil&lt;br /&gt;
			local issues = {}&lt;br /&gt;
			table.insert(issues, sect)&lt;br /&gt;
			table.insert(issues, issue)&lt;br /&gt;
			table.insert(issues, text)&lt;br /&gt;
			self.issue = table.concat(issues, ' ')&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		-- Get the self.talk value.&lt;br /&gt;
		local talk = args.talk&lt;br /&gt;
		-- Show talk links on the template page or template subpages if the talk&lt;br /&gt;
		-- parameter is blank.&lt;br /&gt;
		if talk == ''&lt;br /&gt;
			and self.templateTitle&lt;br /&gt;
			and (&lt;br /&gt;
				mw.title.equals(self.templateTitle, self.title)&lt;br /&gt;
				or self.title:isSubpageOf(self.templateTitle)&lt;br /&gt;
			)&lt;br /&gt;
		then&lt;br /&gt;
			talk = '#'&lt;br /&gt;
		elseif talk == '' then&lt;br /&gt;
			talk = nil&lt;br /&gt;
		end&lt;br /&gt;
		if talk then&lt;br /&gt;
			-- If the talk value is a talk page, make a link to that page. Else&lt;br /&gt;
			-- assume that it's a section heading, and make a link to the talk&lt;br /&gt;
			-- page of the current page with that section heading.&lt;br /&gt;
			local talkTitle = getTitleObject(talk)&lt;br /&gt;
			local talkArgIsTalkPage = true&lt;br /&gt;
			if not talkTitle or not talkTitle.isTalkPage then&lt;br /&gt;
				talkArgIsTalkPage = false&lt;br /&gt;
				talkTitle = getTitleObject(&lt;br /&gt;
					self.title.text,&lt;br /&gt;
					mw.site.namespaces[self.title.namespace].talk.id&lt;br /&gt;
				)&lt;br /&gt;
			end&lt;br /&gt;
			if talkTitle and talkTitle.exists then&lt;br /&gt;
                local talkText&lt;br /&gt;
                if self.isSmall then&lt;br /&gt;
                    local talkLink = talkArgIsTalkPage and talk or (talkTitle.prefixedText .. '#' .. talk)&lt;br /&gt;
                    talkText = string.format('([[%s|talk]])', talkLink)&lt;br /&gt;
                else&lt;br /&gt;
                    talkText = 'Relevant discussion may be found on'&lt;br /&gt;
                    if talkArgIsTalkPage then&lt;br /&gt;
                        talkText = string.format(&lt;br /&gt;
                            '%s [[%s|%s]].',&lt;br /&gt;
                            talkText,&lt;br /&gt;
                            talk,&lt;br /&gt;
                            talkTitle.prefixedText&lt;br /&gt;
                        )&lt;br /&gt;
                    else&lt;br /&gt;
                        talkText = string.format(&lt;br /&gt;
                            '%s the [[%s#%s|talk page]].',&lt;br /&gt;
                            talkText,&lt;br /&gt;
                            talkTitle.prefixedText,&lt;br /&gt;
                            talk&lt;br /&gt;
                        )&lt;br /&gt;
                    end&lt;br /&gt;
                end&lt;br /&gt;
				self.talk = talkText&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
&lt;br /&gt;
		-- Get other values.&lt;br /&gt;
		self.fix = args.fix ~= '' and args.fix or nil&lt;br /&gt;
		local date&lt;br /&gt;
		if args.date and args.date ~= '' then&lt;br /&gt;
			date = args.date&lt;br /&gt;
		elseif args.date == '' and self.isTemplatePage then&lt;br /&gt;
			date = lang:formatDate('F Y')&lt;br /&gt;
		end&lt;br /&gt;
		if date then&lt;br /&gt;
			self.date = string.format(&amp;quot; &amp;lt;span class='date-container'&amp;gt;''(&amp;lt;span class='date'&amp;gt;%s&amp;lt;/span&amp;gt;)''&amp;lt;/span&amp;gt;&amp;quot;, date)&lt;br /&gt;
		end&lt;br /&gt;
		self.info = args.info&lt;br /&gt;
		if yesno(args.removalnotice) then&lt;br /&gt;
			self.removalNotice = cfg.removalNotice&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set the non-collapsible text field. At the moment this is used by all box&lt;br /&gt;
	-- types other than ambox, and also by ambox when small=yes.&lt;br /&gt;
	if self.isSmall then&lt;br /&gt;
		self.text = args.smalltext or args.text&lt;br /&gt;
	else&lt;br /&gt;
		self.text = args.text&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set the below row.&lt;br /&gt;
	self.below = cfg.below and args.below&lt;br /&gt;
&lt;br /&gt;
	-- General image settings.&lt;br /&gt;
	self.imageCellDiv = not self.isSmall and cfg.imageCellDiv&lt;br /&gt;
	self.imageEmptyCell = cfg.imageEmptyCell&lt;br /&gt;
	if cfg.imageEmptyCellStyle then&lt;br /&gt;
		self.imageEmptyCellStyle = 'border:none;padding:0;width:1px'&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Left image settings.&lt;br /&gt;
	local imageLeft = self.isSmall and args.smallimage or args.image&lt;br /&gt;
	if cfg.imageCheckBlank and imageLeft ~= 'blank' and imageLeft ~= 'none'&lt;br /&gt;
		or not cfg.imageCheckBlank and imageLeft ~= 'none'&lt;br /&gt;
	then&lt;br /&gt;
		self.imageLeft = imageLeft&lt;br /&gt;
		if not imageLeft then&lt;br /&gt;
			local imageSize = self.isSmall&lt;br /&gt;
				and (cfg.imageSmallSize or '30x30px')&lt;br /&gt;
				or '40x40px'&lt;br /&gt;
			self.imageLeft = string.format('[[File:%s|%s|link=|alt=]]', self.typeImage&lt;br /&gt;
				or 'Imbox notice.png', imageSize)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Right image settings.&lt;br /&gt;
	local imageRight = self.isSmall and args.smallimageright or args.imageright&lt;br /&gt;
	if not (cfg.imageRightNone and imageRight == 'none') then&lt;br /&gt;
		self.imageRight = imageRight&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:setMainspaceCategories()&lt;br /&gt;
	local args = self.args&lt;br /&gt;
	local cfg = self.cfg&lt;br /&gt;
&lt;br /&gt;
	if not cfg.allowMainspaceCategories then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local nums = {}&lt;br /&gt;
	for _, prefix in ipairs{'cat', 'category', 'all'} do&lt;br /&gt;
		args[prefix .. '1'] = args[prefix]&lt;br /&gt;
		nums = union(nums, getArgNums(args, prefix))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- The following is roughly equivalent to the old {{Ambox/category}}.&lt;br /&gt;
	local date = args.date&lt;br /&gt;
	date = type(date) == 'string' and date&lt;br /&gt;
	local preposition = 'from'&lt;br /&gt;
	for _, num in ipairs(nums) do&lt;br /&gt;
		local mainCat = args['cat' .. tostring(num)]&lt;br /&gt;
			or args['category' .. tostring(num)]&lt;br /&gt;
		local allCat = args['all' .. tostring(num)]&lt;br /&gt;
		mainCat = type(mainCat) == 'string' and mainCat&lt;br /&gt;
		allCat = type(allCat) == 'string' and allCat&lt;br /&gt;
		if mainCat and date and date ~= '' then&lt;br /&gt;
			local catTitle = string.format('%s %s %s', mainCat, preposition, date)&lt;br /&gt;
			self:addCat(0, catTitle)&lt;br /&gt;
			catTitle = getTitleObject('Category:' .. catTitle)&lt;br /&gt;
			if not catTitle or not catTitle.exists then&lt;br /&gt;
				self:addCat(0, 'Articles with invalid date parameter in template')&lt;br /&gt;
			end&lt;br /&gt;
		elseif mainCat and (not date or date == '') then&lt;br /&gt;
			self:addCat(0, mainCat)&lt;br /&gt;
		end&lt;br /&gt;
		if allCat then&lt;br /&gt;
			self:addCat(0, allCat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:setTemplateCategories()&lt;br /&gt;
	local args = self.args&lt;br /&gt;
	local cfg = self.cfg&lt;br /&gt;
&lt;br /&gt;
	-- Add template categories.&lt;br /&gt;
	if cfg.templateCategory then&lt;br /&gt;
		if cfg.templateCategoryRequireName then&lt;br /&gt;
			if self.isTemplatePage then&lt;br /&gt;
				self:addCat(10, cfg.templateCategory)&lt;br /&gt;
			end&lt;br /&gt;
		elseif not self.title.isSubpage then&lt;br /&gt;
			self:addCat(10, cfg.templateCategory)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add template error categories.&lt;br /&gt;
	if cfg.templateErrorCategory then&lt;br /&gt;
		local templateErrorCategory = cfg.templateErrorCategory&lt;br /&gt;
		local templateCat, templateSort&lt;br /&gt;
		if not self.name and not self.title.isSubpage then&lt;br /&gt;
			templateCat = templateErrorCategory&lt;br /&gt;
		elseif self.isTemplatePage then&lt;br /&gt;
			local paramsToCheck = cfg.templateErrorParamsToCheck or {}&lt;br /&gt;
			local count = 0&lt;br /&gt;
			for i, param in ipairs(paramsToCheck) do&lt;br /&gt;
				if not args[param] then&lt;br /&gt;
					count = count + 1&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if count &amp;gt; 0 then&lt;br /&gt;
				templateCat = templateErrorCategory&lt;br /&gt;
				templateSort = tostring(count)&lt;br /&gt;
			end&lt;br /&gt;
			if self.categoryNums and #self.categoryNums &amp;gt; 0 then&lt;br /&gt;
				templateCat = templateErrorCategory&lt;br /&gt;
				templateSort = 'C'&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		self:addCat(10, templateCat, templateSort)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:setAllNamespaceCategories()&lt;br /&gt;
	-- Set categories for all namespaces.&lt;br /&gt;
	if self.invalidTypeError then&lt;br /&gt;
		local allSort = (self.title.namespace == 0 and 'Main:' or '') .. self.title.prefixedText&lt;br /&gt;
		self:addCat('all', 'Wikipedia message box parameter needs fixing', allSort)&lt;br /&gt;
	end&lt;br /&gt;
	if self.isSubstituted then&lt;br /&gt;
		self:addCat('all', 'Pages with incorrectly substituted templates')&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:setCategories()&lt;br /&gt;
	if self.title.namespace == 0 then&lt;br /&gt;
		self:setMainspaceCategories()&lt;br /&gt;
	elseif self.title.namespace == 10 then&lt;br /&gt;
		self:setTemplateCategories()&lt;br /&gt;
	end&lt;br /&gt;
	self:setAllNamespaceCategories()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:renderCategories()&lt;br /&gt;
	if not self.hasCategories then&lt;br /&gt;
		-- No categories added, no need to pass them to Category handler so,&lt;br /&gt;
		-- if it was invoked, it would return the empty string.&lt;br /&gt;
		-- So we shortcut and return the empty string.&lt;br /&gt;
		return &amp;quot;&amp;quot;&lt;br /&gt;
	end&lt;br /&gt;
	-- Convert category tables to strings and pass them through&lt;br /&gt;
	-- [[Module:Category handler]].&lt;br /&gt;
	return require('Module:Category handler')._main{&lt;br /&gt;
		main = table.concat(self.categories[0] or {}),&lt;br /&gt;
		template = table.concat(self.categories[10] or {}),&lt;br /&gt;
		all = table.concat(self.categories.all or {}),&lt;br /&gt;
		nocat = self.args.nocat,&lt;br /&gt;
		page = self.args.page&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MessageBox:export()&lt;br /&gt;
	local root = mw.html.create()&lt;br /&gt;
&lt;br /&gt;
	-- Add the subst check error.&lt;br /&gt;
	if self.isSubstituted and self.name then&lt;br /&gt;
		root:tag('b')&lt;br /&gt;
			:addClass('error')&lt;br /&gt;
			:wikitext(string.format(&lt;br /&gt;
				'Template &amp;lt;code&amp;gt;%s[[Template:%s|%s]]%s&amp;lt;/code&amp;gt; has been incorrectly substituted.',&lt;br /&gt;
				mw.text.nowiki('{{'), self.name, self.name, mw.text.nowiki('}}')&lt;br /&gt;
			))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Create the box table.&lt;br /&gt;
	local boxTable = root:tag('table')&lt;br /&gt;
	boxTable:attr('id', self.id or nil)&lt;br /&gt;
	for i, class in ipairs(self.classes or {}) do&lt;br /&gt;
		boxTable:addClass(class or nil)&lt;br /&gt;
	end&lt;br /&gt;
	boxTable&lt;br /&gt;
		:cssText(self.style or nil)&lt;br /&gt;
		:attr('role', 'presentation')&lt;br /&gt;
&lt;br /&gt;
	if self.attrs then&lt;br /&gt;
		boxTable:attr(self.attrs)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add the left-hand image.&lt;br /&gt;
	local row = boxTable:tag('tr')&lt;br /&gt;
	if self.imageLeft then&lt;br /&gt;
		local imageLeftCell = row:tag('td'):addClass('mbox-image')&lt;br /&gt;
		if self.imageCellDiv then&lt;br /&gt;
			-- If we are using a div, redefine imageLeftCell so that the image&lt;br /&gt;
			-- is inside it. Divs use style=&amp;quot;width: 52px;&amp;quot;, which limits the&lt;br /&gt;
			-- image width to 52px. If any images in a div are wider than that,&lt;br /&gt;
			-- they may overlap with the text or cause other display problems.&lt;br /&gt;
			imageLeftCell = imageLeftCell:tag('div'):css('width', '52px')&lt;br /&gt;
		end&lt;br /&gt;
		imageLeftCell:wikitext(self.imageLeft or nil)&lt;br /&gt;
	elseif self.imageEmptyCell then&lt;br /&gt;
		-- Some message boxes define an empty cell if no image is specified, and&lt;br /&gt;
		-- some don't. The old template code in templates where empty cells are&lt;br /&gt;
		-- specified gives the following hint: &amp;quot;No image. Cell with some width&lt;br /&gt;
		-- or padding necessary for text cell to have 100% width.&amp;quot;&lt;br /&gt;
		row:tag('td')&lt;br /&gt;
			:addClass('mbox-empty-cell')&lt;br /&gt;
			:cssText(self.imageEmptyCellStyle or nil)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add the text.&lt;br /&gt;
	local textCell = row:tag('td'):addClass('mbox-text')&lt;br /&gt;
	if self.useCollapsibleTextFields then&lt;br /&gt;
		-- The message box uses advanced text parameters that allow things to be&lt;br /&gt;
		-- collapsible. At the moment, only ambox uses this.&lt;br /&gt;
		textCell:cssText(self.textstyle or nil)&lt;br /&gt;
		local textCellDiv = textCell:tag('div')&lt;br /&gt;
		textCellDiv&lt;br /&gt;
			:addClass('mbox-text-span')&lt;br /&gt;
			:wikitext(self.issue or nil)&lt;br /&gt;
		if (self.talk or self.fix) then&lt;br /&gt;
			textCellDiv:tag('span')&lt;br /&gt;
				:addClass('hide-when-compact')&lt;br /&gt;
				:wikitext(self.talk and (' ' .. self.talk) or nil)&lt;br /&gt;
				:wikitext(self.fix and (' ' .. self.fix) or nil)&lt;br /&gt;
		end&lt;br /&gt;
		textCellDiv:wikitext(self.date and (' ' .. self.date) or nil)&lt;br /&gt;
		if self.info and not self.isSmall then&lt;br /&gt;
			textCellDiv&lt;br /&gt;
				:tag('span')&lt;br /&gt;
				:addClass('hide-when-compact')&lt;br /&gt;
				:wikitext(self.info and (' ' .. self.info) or nil)&lt;br /&gt;
		end&lt;br /&gt;
		if self.removalNotice then&lt;br /&gt;
			textCellDiv:tag('span')&lt;br /&gt;
				:addClass('hide-when-compact')&lt;br /&gt;
				:tag('i')&lt;br /&gt;
					:wikitext(string.format(&amp;quot; (%s)&amp;quot;, self.removalNotice))&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		-- Default text formatting - anything goes.&lt;br /&gt;
		textCell&lt;br /&gt;
			:cssText(self.textstyle or nil)&lt;br /&gt;
			:wikitext(self.text or nil)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add the right-hand image.&lt;br /&gt;
	if self.imageRight then&lt;br /&gt;
		local imageRightCell = row:tag('td'):addClass('mbox-imageright')&lt;br /&gt;
		if self.imageCellDiv then&lt;br /&gt;
			-- If we are using a div, redefine imageRightCell so that the image&lt;br /&gt;
			-- is inside it.&lt;br /&gt;
			imageRightCell = imageRightCell:tag('div'):css('width', '52px')&lt;br /&gt;
		end&lt;br /&gt;
		imageRightCell&lt;br /&gt;
			:wikitext(self.imageRight or nil)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add the below row.&lt;br /&gt;
	if self.below then&lt;br /&gt;
		boxTable:tag('tr')&lt;br /&gt;
			:tag('td')&lt;br /&gt;
				:attr('colspan', self.imageRight and '3' or '2')&lt;br /&gt;
				:addClass('mbox-text')&lt;br /&gt;
				:cssText(self.textstyle or nil)&lt;br /&gt;
				:wikitext(self.below or nil)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add error message for invalid type parameters.&lt;br /&gt;
	if self.invalidTypeError then&lt;br /&gt;
		root:tag('div')&lt;br /&gt;
			:css('text-align', 'center')&lt;br /&gt;
			:wikitext(string.format(&lt;br /&gt;
				'This message box is using an invalid &amp;quot;type=%s&amp;quot; parameter and needs fixing.',&lt;br /&gt;
				self.type or ''&lt;br /&gt;
			))&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Add categories.&lt;br /&gt;
	root:wikitext(self:renderCategories() or nil)&lt;br /&gt;
&lt;br /&gt;
	return tostring(root)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Exports&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local p, mt = {}, {}&lt;br /&gt;
&lt;br /&gt;
function p._exportClasses()&lt;br /&gt;
	-- For testing.&lt;br /&gt;
	return {&lt;br /&gt;
		MessageBox = MessageBox&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(boxType, args, cfgTables)&lt;br /&gt;
	local box = MessageBox.new(boxType, args, cfgTables or mw.loadData(CONFIG_MODULE))&lt;br /&gt;
	box:setParameters()&lt;br /&gt;
	box:setCategories()&lt;br /&gt;
	return box:export()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function mt.__index(t, k)&lt;br /&gt;
	return function (frame)&lt;br /&gt;
		if not getArgs then&lt;br /&gt;
			getArgs = require('Module:Arguments').getArgs&lt;br /&gt;
		end&lt;br /&gt;
		return t.main(k, getArgs(frame, {trim = false, removeBlanks = false}))&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return setmetatable(p, mt)&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Module_wikitext&amp;diff=234</id>
		<title>Module:Module wikitext</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Module_wikitext&amp;diff=234"/>
		<updated>2022-03-30T18:37:08Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;local p = {}  p.text = ''  function p.main() 	return p.text end  function p._addText(text, preprocessFrame) 	if preprocessFrame ~= false then 		text = (preprocessFrame or mw.g...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
p.text = ''&lt;br /&gt;
&lt;br /&gt;
function p.main()&lt;br /&gt;
	return p.text&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._addText(text, preprocessFrame)&lt;br /&gt;
	if preprocessFrame ~= false then&lt;br /&gt;
		text = (preprocessFrame or mw.getCurrentFrame()):preprocess(text)&lt;br /&gt;
	end&lt;br /&gt;
	p.text = p.text .. text&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Documentation/styles.css&amp;diff=233</id>
		<title>Module:Documentation/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Documentation/styles.css&amp;diff=233"/>
		<updated>2022-03-30T18:36:42Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;/* {{pp|small=yes}} */ .documentation, .documentation-metadata { 	border: 1px solid #a2a9b1; 	background-color: #ecfcf4; 	clear: both; }  .documentation { 	margin: 1em 0 0 0;...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* {{pp|small=yes}} */&lt;br /&gt;
.documentation,&lt;br /&gt;
.documentation-metadata {&lt;br /&gt;
	border: 1px solid #a2a9b1;&lt;br /&gt;
	background-color: #ecfcf4;&lt;br /&gt;
	clear: both;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation {&lt;br /&gt;
	margin: 1em 0 0 0;&lt;br /&gt;
	padding: 1em;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-metadata {&lt;br /&gt;
	margin: 0.2em 0; /* same margin left-right as .documentation */&lt;br /&gt;
    font-style: italic;&lt;br /&gt;
    padding: 0.4em 1em; /* same padding left-right as .documentation */&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-startbox {&lt;br /&gt;
	padding-bottom: 3px;&lt;br /&gt;
	border-bottom: 1px solid #aaa;&lt;br /&gt;
	margin-bottom: 1ex;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-heading {&lt;br /&gt;
	font-weight: bold;&lt;br /&gt;
	font-size: 125%;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-clear { /* Don't want things to stick out where they shouldn't. */&lt;br /&gt;
	clear: both;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.documentation-toolbar {&lt;br /&gt;
	font-style: normal;&lt;br /&gt;
	font-size: 85%;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:If_preview/styles.css&amp;diff=232</id>
		<title>Module:If preview/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:If_preview/styles.css&amp;diff=232"/>
		<updated>2022-03-30T18:35:14Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;/* {{pp|small=yes}} */ .preview-warning { 	font-style: italic; 	/* @noflip */ 	padding-left: 1.6em; 	margin-bottom: 0.5em; 	color: red; }  /* The templatestyles element insert...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* {{pp|small=yes}} */&lt;br /&gt;
.preview-warning {&lt;br /&gt;
	font-style: italic;&lt;br /&gt;
	/* @noflip */&lt;br /&gt;
	padding-left: 1.6em;&lt;br /&gt;
	margin-bottom: 0.5em;&lt;br /&gt;
	color: red;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
/* The templatestyles element inserts a link element before hatnotes.&lt;br /&gt;
 * TODO: Remove link if/when WMF resolves T200206 */&lt;br /&gt;
.preview-warning + link + .preview-warning {&lt;br /&gt;
	margin-top: -0.5em;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:If_preview/configuration&amp;diff=231</id>
		<title>Module:If preview/configuration</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:If_preview/configuration&amp;diff=231"/>
		<updated>2022-03-30T18:34:51Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- We perform the actual check for whether this is a preview here since preprocessing is relatively expensive.  local frame = mw.getCurrentFrame()  local function is_previ...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;--[[&lt;br /&gt;
We perform the actual check for whether this is a preview here since&lt;br /&gt;
preprocessing is relatively expensive.&lt;br /&gt;
]]&lt;br /&gt;
local frame = mw.getCurrentFrame()&lt;br /&gt;
&lt;br /&gt;
local function is_preview()&lt;br /&gt;
	local revision_id = frame:preprocess('{{REVISIONID}}')&lt;br /&gt;
	-- {{REVISIONID}} is usually the empty string when previewed.&lt;br /&gt;
	-- I don't know why we're checking for nil but hey, maybe someday things&lt;br /&gt;
	-- would have broken&lt;br /&gt;
	return revision_id == nil or revision_id == ''&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function templatestyles()&lt;br /&gt;
	return frame:extensionTag{&lt;br /&gt;
		name = 'templatestyles', args = { src = 'Module:If preview/styles.css' }&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
	preview = is_preview(),&lt;br /&gt;
	templatestyles = templatestyles(),&lt;br /&gt;
	warning_infrastructure = '%s&amp;lt;div class=&amp;quot;preview-warning&amp;quot;&amp;gt;&amp;lt;strong&amp;gt;Preview warning:&amp;lt;/strong&amp;gt; %s&amp;lt;/div&amp;gt;',&lt;br /&gt;
	missing_warning = 'The template has no warning text. Please add a warning.'&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:If_preview&amp;diff=230</id>
		<title>Module:If preview</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:If_preview&amp;diff=230"/>
		<updated>2022-03-30T18:34:25Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;local p = {}  local cfg = mw.loadData('Module:If preview/configuration')  --[[ main  This function returns either the first argument or second argument passed to this module,...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
local cfg = mw.loadData('Module:If preview/configuration')&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
main&lt;br /&gt;
&lt;br /&gt;
This function returns either the first argument or second argument passed to&lt;br /&gt;
this module, depending on whether the page is being previewed.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	if cfg.preview then&lt;br /&gt;
		return frame.args[1] or ''&lt;br /&gt;
	else&lt;br /&gt;
		return frame.args[2] or ''&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
pmain&lt;br /&gt;
&lt;br /&gt;
This function returns either the first argument or second argument passed to&lt;br /&gt;
this module's parent (i.e. template using this module), depending on whether it&lt;br /&gt;
is being previewed.&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
function p.pmain(frame)&lt;br /&gt;
	return p.main(frame:getParent())&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function warning_text(warning)&lt;br /&gt;
	return mw.ustring.format(&lt;br /&gt;
		cfg.warning_infrastructure,&lt;br /&gt;
		cfg.templatestyles,&lt;br /&gt;
		warning&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._warning(args)&lt;br /&gt;
	&lt;br /&gt;
	local warning = args[1] and args[1]:match('^%s*(.-)%s*$') or ''&lt;br /&gt;
	if warning == '' then&lt;br /&gt;
		return warning_text(cfg.missing_warning)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if not cfg.preview then return '' end&lt;br /&gt;
	&lt;br /&gt;
	return warning_text(warning)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
warning&lt;br /&gt;
&lt;br /&gt;
This function returns a &amp;quot;preview warning&amp;quot;, which is the first argument marked&lt;br /&gt;
up with HTML and some supporting text, depending on whether the page is being previewed.&lt;br /&gt;
&lt;br /&gt;
disabled since we'll implement the template version in general&lt;br /&gt;
&lt;br /&gt;
]]&lt;br /&gt;
--function p.warning(frame)&lt;br /&gt;
--	return p._warning(frame.args)&lt;br /&gt;
--end&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
warning, but for pass-through templates like {{preview warning}}&lt;br /&gt;
]]&lt;br /&gt;
function p.pwarning(frame)&lt;br /&gt;
	return p._warning(frame:getParent().args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Check_for_unknown_parameters&amp;diff=229</id>
		<title>Module:Check for unknown parameters</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Check_for_unknown_parameters&amp;diff=229"/>
		<updated>2022-03-30T18:33:09Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- This module may be used to compare the arguments passed to the parent -- with a list of arguments, returning a specified result if an argument is -- not on the list local p...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module may be used to compare the arguments passed to the parent&lt;br /&gt;
-- with a list of arguments, returning a specified result if an argument is&lt;br /&gt;
-- not on the list&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
local function trim(s)&lt;br /&gt;
	return s:match('^%s*(.-)%s*$')&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function isnotempty(s)&lt;br /&gt;
	return s and s:match('%S')&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function clean(text)&lt;br /&gt;
	-- Return text cleaned for display and truncated if too long.&lt;br /&gt;
	-- Strip markers are replaced with dummy text representing the original wikitext.&lt;br /&gt;
	local pos, truncated&lt;br /&gt;
	local function truncate(text)&lt;br /&gt;
		if truncated then&lt;br /&gt;
			return ''&lt;br /&gt;
		end&lt;br /&gt;
		if mw.ustring.len(text) &amp;gt; 25 then&lt;br /&gt;
			truncated = true&lt;br /&gt;
			text = mw.ustring.sub(text, 1, 25) .. '...'&lt;br /&gt;
		end&lt;br /&gt;
		return mw.text.nowiki(text)&lt;br /&gt;
	end&lt;br /&gt;
	local parts = {}&lt;br /&gt;
	for before, tag, remainder in text:gmatch('([^\127]*)\127[^\127]*%-(%l+)%-[^\127]*\127()') do&lt;br /&gt;
		pos = remainder&lt;br /&gt;
		table.insert(parts, truncate(before) .. '&amp;amp;lt;' .. tag .. '&amp;amp;gt;...&amp;amp;lt;/' .. tag .. '&amp;amp;gt;')&lt;br /&gt;
	end&lt;br /&gt;
	table.insert(parts, truncate(text:sub(pos or 1)))&lt;br /&gt;
	return table.concat(parts)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._check(args, pargs)&lt;br /&gt;
	if type(args) ~= &amp;quot;table&amp;quot; or type(pargs) ~= &amp;quot;table&amp;quot; then&lt;br /&gt;
		-- TODO: error handling&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- create the list of known args, regular expressions, and the return string&lt;br /&gt;
	local knownargs = {}&lt;br /&gt;
	local regexps = {}&lt;br /&gt;
	for k, v in pairs(args) do&lt;br /&gt;
		if type(k) == 'number' then&lt;br /&gt;
			v = trim(v)&lt;br /&gt;
			knownargs[v] = 1&lt;br /&gt;
		elseif k:find('^regexp[1-9][0-9]*$') then&lt;br /&gt;
			table.insert(regexps, '^' .. v .. '$')&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- loop over the parent args, and make sure they are on the list&lt;br /&gt;
	local ignoreblank = isnotempty(args['ignoreblank'])&lt;br /&gt;
	local showblankpos = isnotempty(args['showblankpositional'])&lt;br /&gt;
	local values = {}&lt;br /&gt;
	for k, v in pairs(pargs) do&lt;br /&gt;
		if type(k) == 'string' and knownargs[k] == nil then&lt;br /&gt;
			local knownflag = false&lt;br /&gt;
			for _, regexp in ipairs(regexps) do&lt;br /&gt;
				if mw.ustring.match(k, regexp) then&lt;br /&gt;
					knownflag = true&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if not knownflag and ( not ignoreblank or isnotempty(v) )  then&lt;br /&gt;
				table.insert(values, clean(k))&lt;br /&gt;
			end&lt;br /&gt;
		elseif type(k) == 'number' and knownargs[tostring(k)] == nil then&lt;br /&gt;
			local knownflag = false&lt;br /&gt;
			for _, regexp in ipairs(regexps) do&lt;br /&gt;
				if mw.ustring.match(tostring(k), regexp) then&lt;br /&gt;
					knownflag = true&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if not knownflag and ( showblankpos or isnotempty(v) ) then&lt;br /&gt;
				table.insert(values, k .. ' = ' .. clean(v))&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- add results to the output tables&lt;br /&gt;
	local res = {}&lt;br /&gt;
	if #values &amp;gt; 0 then&lt;br /&gt;
		local unknown_text = args['unknown'] or 'Found _VALUE_, '&lt;br /&gt;
&lt;br /&gt;
		if mw.getCurrentFrame():preprocess( &amp;quot;{{REVISIONID}}&amp;quot; ) == &amp;quot;&amp;quot; then&lt;br /&gt;
			local preview_text = args['preview']&lt;br /&gt;
			if isnotempty(preview_text) then&lt;br /&gt;
				preview_text = require('Module:If preview')._warning({preview_text})&lt;br /&gt;
			elseif preview == nil then&lt;br /&gt;
				preview_text = unknown_text&lt;br /&gt;
			end&lt;br /&gt;
			unknown_text = preview_text&lt;br /&gt;
		end&lt;br /&gt;
		for _, v in pairs(values) do&lt;br /&gt;
			-- Fix odd bug for | = which gets stripped to the empty string and&lt;br /&gt;
			-- breaks category links&lt;br /&gt;
			if v == '' then v = ' ' end&lt;br /&gt;
&lt;br /&gt;
			-- avoid error with v = 'example%2' (&amp;quot;invalid capture index&amp;quot;)&lt;br /&gt;
			local r = unknown_text:gsub('_VALUE_', {_VALUE_ = v})&lt;br /&gt;
			table.insert(res, r)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return table.concat(res)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.check(frame)&lt;br /&gt;
	local args = frame.args&lt;br /&gt;
	local pargs = frame:getParent().args&lt;br /&gt;
	return p._check(args, pargs)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Template:Blockquote/styles.css&amp;diff=228</id>
		<title>Template:Blockquote/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Template:Blockquote/styles.css&amp;diff=228"/>
		<updated>2022-03-30T18:27:48Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Zoran changed the content model of the page Template:Blockquote/styles.css from &amp;quot;wikitext&amp;quot; to &amp;quot;Sanitized CSS&amp;quot;: Styling related page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* {{pp-template}} */&lt;br /&gt;
.templatequote {&lt;br /&gt;
	overflow: hidden;&lt;br /&gt;
	margin: 1em 0;&lt;br /&gt;
	padding: 0 40px;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
.templatequote .templatequotecite {&lt;br /&gt;
    line-height: 1.5em;&lt;br /&gt;
    /* @noflip */&lt;br /&gt;
    text-align: left;&lt;br /&gt;
    /* @noflip */&lt;br /&gt;
    padding-left: 1.6em;&lt;br /&gt;
    margin-top: 0;&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Template:TOC_right/styles.css&amp;diff=227</id>
		<title>Template:TOC right/styles.css</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Template:TOC_right/styles.css&amp;diff=227"/>
		<updated>2022-03-30T18:18:52Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Zoran changed the content model of the page Template:TOC right/styles.css from &amp;quot;wikitext&amp;quot; to &amp;quot;Sanitized CSS&amp;quot;: Styling related page&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;/* {{pp-template}} */&lt;br /&gt;
/* The TOC is hidden on Minerva (mobile skin) for width &amp;lt; 720px so must also hide this wrapper */&lt;br /&gt;
@media all and (max-width: 720px) {&lt;br /&gt;
	body.skin-minerva .tocright {&lt;br /&gt;
		display: none;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	.tocright {&lt;br /&gt;
		width: 100% !important; /* fix the inline width while at small resolution */&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
@media all and (min-width: 720px) {&lt;br /&gt;
	.tocright {&lt;br /&gt;
		float: right;&lt;br /&gt;
		clear: right;&lt;br /&gt;
		width: auto;&lt;br /&gt;
		margin: 0 0 0.5em 1em; &lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	.tocright-clear-left {&lt;br /&gt;
		clear: left;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	.tocright-clear-both {&lt;br /&gt;
		clear: both;&lt;br /&gt;
	}&lt;br /&gt;
	&lt;br /&gt;
	.tocright-clear-none {&lt;br /&gt;
		clear: none;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Protection_banner/config&amp;diff=219</id>
		<title>Module:Protection banner/config</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Protection_banner/config&amp;diff=219"/>
		<updated>2022-03-29T15:38:05Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- This module provides configuration data for Module:Protection banner.  return {  -------------------------------------------------------------------------------- -- --...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module provides configuration data for [[Module:Protection banner]].&lt;br /&gt;
&lt;br /&gt;
return {&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--&lt;br /&gt;
--                                BANNER DATA&lt;br /&gt;
--&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- Banner data consists of six fields:&lt;br /&gt;
-- * text - the main protection text that appears at the top of protection&lt;br /&gt;
--   banners.&lt;br /&gt;
-- * explanation - the text that appears below the main protection text, used&lt;br /&gt;
--   to explain the details of the protection.&lt;br /&gt;
-- * tooltip - the tooltip text you see when you move the mouse over a small&lt;br /&gt;
--   padlock icon.&lt;br /&gt;
-- * link - the page that the small padlock icon links to.&lt;br /&gt;
-- * alt - the alt text for the small padlock icon. This is also used as tooltip&lt;br /&gt;
--   text for the large protection banners.&lt;br /&gt;
-- * image - the padlock image used in both protection banners and small padlock&lt;br /&gt;
--   icons.&lt;br /&gt;
--&lt;br /&gt;
-- The module checks in three separate tables to find a value for each field.&lt;br /&gt;
-- First it checks the banners table, which has values specific to the reason&lt;br /&gt;
-- for the page being protected. Then the module checks the defaultBanners&lt;br /&gt;
-- table, which has values specific to each protection level. Finally, the&lt;br /&gt;
-- module checks the masterBanner table, which holds data for protection&lt;br /&gt;
-- templates to use if no data has been found in the previous two tables.&lt;br /&gt;
--&lt;br /&gt;
-- The values in the banner data can take parameters. These are specified&lt;br /&gt;
-- using ${TEXTLIKETHIS} (a dollar sign preceding a parameter name&lt;br /&gt;
-- enclosed in curly braces).&lt;br /&gt;
--&lt;br /&gt;
--                          Available parameters:&lt;br /&gt;
--&lt;br /&gt;
-- ${CURRENTVERSION} - a link to the page history or the move log, with the&lt;br /&gt;
-- display message &amp;quot;current-version-edit-display&amp;quot; or&lt;br /&gt;
-- &amp;quot;current-version-move-display&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- ${EDITREQUEST} - a link to create an edit request for the current page.&lt;br /&gt;
--&lt;br /&gt;
-- ${EXPLANATIONBLURB} - an explanation blurb, e.g. &amp;quot;Please discuss any changes&lt;br /&gt;
-- on the talk page; you may submit a request to ask an administrator to make&lt;br /&gt;
-- an edit if it is minor or supported by consensus.&amp;quot;&lt;br /&gt;
--&lt;br /&gt;
-- ${IMAGELINK} - a link to set the image to, depending on the protection&lt;br /&gt;
-- action and protection level.&lt;br /&gt;
--&lt;br /&gt;
-- ${INTROBLURB} - the PROTECTIONBLURB parameter, plus the expiry if an expiry&lt;br /&gt;
-- is set. E.g. &amp;quot;Editing of this page by new or unregistered users is currently &lt;br /&gt;
-- disabled until dd Month YYYY.&amp;quot;&lt;br /&gt;
--&lt;br /&gt;
-- ${INTROFRAGMENT} - the same as ${INTROBLURB}, but without final punctuation&lt;br /&gt;
-- so that it can be used in run-on sentences.&lt;br /&gt;
--&lt;br /&gt;
-- ${PAGETYPE} - the type of the page, e.g. &amp;quot;article&amp;quot; or &amp;quot;template&amp;quot;.&lt;br /&gt;
-- Defined in the cfg.pagetypes table.&lt;br /&gt;
--&lt;br /&gt;
-- ${PROTECTIONBLURB} - a blurb explaining the protection level of the page, e.g.&lt;br /&gt;
-- &amp;quot;Editing of this page by new or unregistered users is currently disabled&amp;quot;&lt;br /&gt;
--&lt;br /&gt;
-- ${PROTECTIONDATE} - the protection date, if it has been supplied to the&lt;br /&gt;
-- template.&lt;br /&gt;
--&lt;br /&gt;
-- ${PROTECTIONLEVEL} - the protection level, e.g. &amp;quot;fully protected&amp;quot; or&lt;br /&gt;
-- &amp;quot;semi-protected&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- ${PROTECTIONLOG} - a link to the protection log or the pending changes log,&lt;br /&gt;
-- depending on the protection action.&lt;br /&gt;
--&lt;br /&gt;
-- ${TALKPAGE} - a link to the talk page. If a section is specified, links&lt;br /&gt;
-- straight to that talk page section.&lt;br /&gt;
--&lt;br /&gt;
-- ${TOOLTIPBLURB} - uses the PAGETYPE, PROTECTIONTYPE and EXPIRY parameters to&lt;br /&gt;
-- create a blurb like &amp;quot;This template is semi-protected&amp;quot;, or &amp;quot;This article is&lt;br /&gt;
-- move-protected until DD Month YYYY&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- ${VANDAL} - links for the specified username (or the root page name)&lt;br /&gt;
-- using Module:Vandal-m.&lt;br /&gt;
--&lt;br /&gt;
--                                 Functions&lt;br /&gt;
--&lt;br /&gt;
-- For advanced users, it is possible to use Lua functions instead of strings&lt;br /&gt;
-- in the banner config tables. Using functions gives flexibility that is not&lt;br /&gt;
-- possible just by using parameters. Functions take two arguments, the&lt;br /&gt;
-- protection object and the template arguments, and they must output a string.&lt;br /&gt;
--&lt;br /&gt;
-- For example:&lt;br /&gt;
--&lt;br /&gt;
-- text = function (protectionObj, args)&lt;br /&gt;
--     if protectionObj.level == 'autoconfirmed' then&lt;br /&gt;
--         return 'foo'&lt;br /&gt;
--     else&lt;br /&gt;
--         return 'bar'&lt;br /&gt;
--     end&lt;br /&gt;
-- end&lt;br /&gt;
--&lt;br /&gt;
-- Some protection object properties and methods that may be useful:&lt;br /&gt;
-- protectionObj.action - the protection action&lt;br /&gt;
-- protectionObj.level - the protection level&lt;br /&gt;
-- protectionObj.reason - the protection reason&lt;br /&gt;
-- protectionObj.expiry - the expiry. Nil if unset, the string &amp;quot;indef&amp;quot; if set&lt;br /&gt;
--     to indefinite, and the protection time in unix time if temporary.&lt;br /&gt;
-- protectionObj.protectionDate - the protection date in unix time, or nil if&lt;br /&gt;
--     unspecified.&lt;br /&gt;
-- protectionObj.bannerConfig - the banner config found by the module. Beware&lt;br /&gt;
--     of editing the config field used by the function, as it could create an&lt;br /&gt;
--     infinite loop.&lt;br /&gt;
-- protectionObj:isProtected - returns a boolean showing whether the page is&lt;br /&gt;
--     protected.&lt;br /&gt;
-- protectionObj:isTemporary - returns a boolean showing whether the expiry is&lt;br /&gt;
--     temporary.&lt;br /&gt;
-- protectionObj:isIncorrect - returns a boolean showing whether the protection&lt;br /&gt;
--     template is incorrect.&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- The master banner data, used if no values have been found in banners or&lt;br /&gt;
-- defaultBanners.&lt;br /&gt;
masterBanner = {&lt;br /&gt;
	text = '${INTROBLURB}',&lt;br /&gt;
	explanation = '${EXPLANATIONBLURB}',&lt;br /&gt;
	tooltip = '${TOOLTIPBLURB}',&lt;br /&gt;
	link = '${IMAGELINK}',&lt;br /&gt;
	alt = 'Page ${PROTECTIONLEVEL}'&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
-- The default banner data. This holds banner data for different protection&lt;br /&gt;
-- levels.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
defaultBanners = {&lt;br /&gt;
	edit = {},&lt;br /&gt;
	move = {},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = {&lt;br /&gt;
			alt = 'Page protected with pending changes',&lt;br /&gt;
			tooltip = 'All edits by unregistered and new users are subject to review prior to becoming visible to unregistered users',&lt;br /&gt;
			image = 'Pending-protection-shackle.svg'&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	upload = {}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
-- The banner data. This holds banner data for different protection reasons.&lt;br /&gt;
-- In fact, the reasons specified in this table control which reasons are&lt;br /&gt;
-- valid inputs to the first positional parameter.&lt;br /&gt;
--&lt;br /&gt;
-- There is also a non-standard &amp;quot;description&amp;quot; field that can be used for items&lt;br /&gt;
-- in this table. This is a description of the protection reason for use in the&lt;br /&gt;
-- module documentation.&lt;br /&gt;
--&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
banners = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		blp = {&lt;br /&gt;
			description = 'For pages protected to promote compliance with the'&lt;br /&gt;
				.. ' [[Wikipedia:Biographies of living persons'&lt;br /&gt;
				.. '|biographies of living persons]] policy',&lt;br /&gt;
			text = '${INTROFRAGMENT} to promote compliance with'&lt;br /&gt;
				.. ' [[Wikipedia:Biographies of living persons'&lt;br /&gt;
				.. &amp;quot;|Wikipedia's&amp;amp;nbsp;policy on&amp;amp;nbsp;the&amp;amp;nbsp;biographies&amp;quot;&lt;br /&gt;
				.. ' of&amp;amp;nbsp;living&amp;amp;nbsp;people]].',&lt;br /&gt;
			tooltip = '${TOOLTIPFRAGMENT} to promote compliance with the policy on'&lt;br /&gt;
				.. ' biographies of living persons',&lt;br /&gt;
		},&lt;br /&gt;
		dmca = {&lt;br /&gt;
			description = 'For pages protected by the Wikimedia Foundation'&lt;br /&gt;
				.. ' due to [[Digital Millennium Copyright Act]] takedown requests',&lt;br /&gt;
			explanation = function (protectionObj, args)&lt;br /&gt;
				local ret = 'Pursuant to a rights owner notice under the Digital'&lt;br /&gt;
					.. ' Millennium Copyright Act (DMCA) regarding some content'&lt;br /&gt;
					.. ' in this article, the Wikimedia Foundation acted under'&lt;br /&gt;
					.. ' applicable law and took down and restricted the content'&lt;br /&gt;
					.. ' in question.'&lt;br /&gt;
				if args.notice then&lt;br /&gt;
					ret = ret .. ' A copy of the received notice can be found here: '&lt;br /&gt;
						.. args.notice .. '.'&lt;br /&gt;
				end&lt;br /&gt;
				ret = ret .. ' For more information, including websites discussing'&lt;br /&gt;
					.. ' how to file a counter-notice, please see'&lt;br /&gt;
					.. &amp;quot; [[Wikipedia:Office actions]] and the article's ${TALKPAGE}.&amp;quot;&lt;br /&gt;
					.. &amp;quot;'''Do not remove this template from the article until the&amp;quot;&lt;br /&gt;
					.. &amp;quot; restrictions are withdrawn'''.&amp;quot;&lt;br /&gt;
				return ret&lt;br /&gt;
			end,&lt;br /&gt;
			image = 'Office-protection-shackle.svg',&lt;br /&gt;
		},&lt;br /&gt;
		dispute = {&lt;br /&gt;
			description = 'For pages protected due to editing disputes',&lt;br /&gt;
			text = function (protectionObj, args)&lt;br /&gt;
				-- Find the value of &amp;quot;disputes&amp;quot;.&lt;br /&gt;
				local display = 'disputes'&lt;br /&gt;
				local disputes&lt;br /&gt;
				if args.section then&lt;br /&gt;
					disputes = string.format(&lt;br /&gt;
						'[[%s:%s#%s|%s]]',&lt;br /&gt;
						mw.site.namespaces[protectionObj.title.namespace].talk.name,&lt;br /&gt;
						protectionObj.title.text,&lt;br /&gt;
						args.section,&lt;br /&gt;
						display&lt;br /&gt;
					)&lt;br /&gt;
				else&lt;br /&gt;
					disputes = display&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				-- Make the blurb, depending on the expiry.&lt;br /&gt;
				local msg&lt;br /&gt;
				if type(protectionObj.expiry) == 'number' then&lt;br /&gt;
					msg = '${INTROFRAGMENT} or until editing %s have been resolved.'&lt;br /&gt;
				else&lt;br /&gt;
					msg = '${INTROFRAGMENT} until editing %s have been resolved.'&lt;br /&gt;
				end&lt;br /&gt;
				return string.format(msg, disputes)&lt;br /&gt;
			end,&lt;br /&gt;
			explanation = &amp;quot;This protection is '''not''' an endorsement of the&amp;quot;&lt;br /&gt;
				.. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}',&lt;br /&gt;
			tooltip = '${TOOLTIPFRAGMENT} due to editing disputes',&lt;br /&gt;
		},&lt;br /&gt;
		ecp = {&lt;br /&gt;
			description = 'For articles in topic areas authorized by'&lt;br /&gt;
				.. ' [[Wikipedia:Arbitration Committee|ArbCom]] or'&lt;br /&gt;
				.. ' meets the criteria for community use',&lt;br /&gt;
			tooltip = 'This ${PAGETYPE} is extended-confirmed protected',&lt;br /&gt;
			alt = 'Extended-protected ${PAGETYPE}',&lt;br /&gt;
		},&lt;br /&gt;
		mainpage = {&lt;br /&gt;
			description = 'For pages protected for being displayed on the [[Main Page]]',&lt;br /&gt;
			text = 'This file is currently'&lt;br /&gt;
				.. ' [[Wikipedia:This page is protected|protected]] from'&lt;br /&gt;
				.. ' editing because it is currently or will soon be displayed'&lt;br /&gt;
				.. ' on the [[Main Page]].',&lt;br /&gt;
			explanation = 'Images on the Main Page are protected due to their high'&lt;br /&gt;
				.. ' visibility. Please discuss any necessary changes on the ${TALKPAGE}.'&lt;br /&gt;
				.. '&amp;lt;br /&amp;gt;&amp;lt;span style=&amp;quot;font-size:90%;&amp;quot;&amp;gt;'&lt;br /&gt;
				.. &amp;quot;'''Administrators:''' Once this image is definitely off the Main Page,&amp;quot;&lt;br /&gt;
				.. ' please unprotect this file, or reduce to semi-protection,'&lt;br /&gt;
				.. ' as appropriate.&amp;lt;/span&amp;gt;',&lt;br /&gt;
		},&lt;br /&gt;
		office = {&lt;br /&gt;
			description = 'For pages protected by the Wikimedia Foundation',&lt;br /&gt;
			text = function (protectionObj, args)&lt;br /&gt;
				local ret = 'This ${PAGETYPE} is currently under the'&lt;br /&gt;
					.. ' scrutiny of the'&lt;br /&gt;
					.. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]'&lt;br /&gt;
					.. ' and is protected.'&lt;br /&gt;
				if protectionObj.protectionDate then&lt;br /&gt;
					ret = ret .. ' It has been protected since ${PROTECTIONDATE}.'&lt;br /&gt;
				end&lt;br /&gt;
				return ret&lt;br /&gt;
			end,&lt;br /&gt;
			explanation = &amp;quot;If you can edit this page, please discuss all changes and&amp;quot;&lt;br /&gt;
				.. &amp;quot; additions on the ${TALKPAGE} first. '''Do not remove protection from this&amp;quot;&lt;br /&gt;
				.. &amp;quot; page unless you are authorized by the Wikimedia Foundation to do&amp;quot;&lt;br /&gt;
				.. &amp;quot; so.'''&amp;quot;,&lt;br /&gt;
			image = 'Office-protection-shackle.svg',&lt;br /&gt;
		},&lt;br /&gt;
		reset = {&lt;br /&gt;
			description = 'For pages protected by the Wikimedia Foundation and'&lt;br /&gt;
				.. ' &amp;quot;reset&amp;quot; to a bare-bones version',&lt;br /&gt;
 			text = 'This ${PAGETYPE} is currently under the'&lt;br /&gt;
					.. ' scrutiny of the'&lt;br /&gt;
					.. ' [[Wikipedia:Office actions|Wikimedia Foundation Office]]'&lt;br /&gt;
					.. ' and is protected.',&lt;br /&gt;
			explanation = function (protectionObj, args)&lt;br /&gt;
				local ret = ''&lt;br /&gt;
				if protectionObj.protectionDate then&lt;br /&gt;
					ret = ret .. 'On ${PROTECTIONDATE} this ${PAGETYPE} was'&lt;br /&gt;
				else&lt;br /&gt;
					ret = ret .. 'This ${PAGETYPE} has been'&lt;br /&gt;
				end&lt;br /&gt;
				ret = ret .. ' reduced to a'&lt;br /&gt;
				.. ' simplified, &amp;quot;bare bones&amp;quot; version so that it may be completely'&lt;br /&gt;
				.. ' rewritten to ensure it meets the policies of'&lt;br /&gt;
				.. ' [[WP:NPOV|Neutral Point of View]] and [[WP:V|Verifiability]].'&lt;br /&gt;
				.. ' Standard Wikipedia policies will apply to its rewriting—which'&lt;br /&gt;
				.. ' will eventually be open to all editors—and will be strictly'&lt;br /&gt;
				.. ' enforced. The ${PAGETYPE} has been ${PROTECTIONLEVEL} while'&lt;br /&gt;
				.. ' it is being rebuilt.\n\n'&lt;br /&gt;
				.. 'Any insertion of material directly from'&lt;br /&gt;
				.. ' pre-protection revisions of the ${PAGETYPE} will be removed, as'&lt;br /&gt;
				.. ' will any material added to the ${PAGETYPE} that is not properly'&lt;br /&gt;
				.. ' sourced. The associated talk page(s) were also cleared on the'&lt;br /&gt;
				.. &amp;quot; same date.\n\n&amp;quot;&lt;br /&gt;
				.. &amp;quot;If you can edit this page, please discuss all changes and&amp;quot;&lt;br /&gt;
				.. &amp;quot; additions on the ${TALKPAGE} first. '''Do not override&amp;quot;&lt;br /&gt;
				.. &amp;quot; this action, and do not remove protection from this page,&amp;quot;&lt;br /&gt;
				.. &amp;quot; unless you are authorized by the Wikimedia Foundation&amp;quot;&lt;br /&gt;
				.. &amp;quot; to do so. No editor may remove this notice.'''&amp;quot;&lt;br /&gt;
&lt;br /&gt;
				return ret&lt;br /&gt;
			end,&lt;br /&gt;
			image = 'Office-protection-shackle.svg',&lt;br /&gt;
		},&lt;br /&gt;
		sock = {&lt;br /&gt;
			description = 'For pages protected due to'&lt;br /&gt;
				.. ' [[Wikipedia:Sock puppetry|sock puppetry]]',&lt;br /&gt;
			text = '${INTROFRAGMENT} to prevent [[Wikipedia:Sock puppetry|sock puppets]] of'&lt;br /&gt;
				.. ' [[Wikipedia:Blocking policy|blocked]] or'&lt;br /&gt;
				.. ' [[Wikipedia:Banning policy|banned users]]'&lt;br /&gt;
				.. ' from editing it.',&lt;br /&gt;
			tooltip = '${TOOLTIPFRAGMENT} to prevent sock puppets of blocked or banned users from'&lt;br /&gt;
				.. ' editing it',&lt;br /&gt;
		},&lt;br /&gt;
		template = {&lt;br /&gt;
			description = 'For [[Wikipedia:High-risk templates|high-risk]]'&lt;br /&gt;
				.. ' templates and Lua modules',&lt;br /&gt;
			text = 'This is a permanently [[Help:Protection|protected]] ${PAGETYPE},'&lt;br /&gt;
				.. ' as it is [[Wikipedia:High-risk templates|high-risk]].',&lt;br /&gt;
			explanation = 'Please discuss any changes on the ${TALKPAGE}; you may'&lt;br /&gt;
				.. ' ${EDITREQUEST} to ask an'&lt;br /&gt;
				.. ' [[Wikipedia:Administrators|administrator]] or'&lt;br /&gt;
				.. ' [[Wikipedia:Template editor|template editor]] to make an edit if'&lt;br /&gt;
				.. ' it is [[Help:Minor edit#When to mark an edit as a minor edit'&lt;br /&gt;
				.. '|uncontroversial]] or supported by'&lt;br /&gt;
				.. ' [[Wikipedia:Consensus|consensus]]. You can also'&lt;br /&gt;
				.. ' [[Wikipedia:Requests for page protection|request]] that the page be'&lt;br /&gt;
				.. ' unprotected.',&lt;br /&gt;
			tooltip = 'This high-risk ${PAGETYPE} is permanently ${PROTECTIONLEVEL}'&lt;br /&gt;
				.. ' to prevent vandalism',&lt;br /&gt;
			alt = 'Permanently protected ${PAGETYPE}',&lt;br /&gt;
		},&lt;br /&gt;
		usertalk = {&lt;br /&gt;
			description = 'For pages protected against disruptive edits by a'&lt;br /&gt;
				.. ' particular user',&lt;br /&gt;
			text = '${INTROFRAGMENT} to prevent ${VANDAL} from using it to make disruptive edits,'&lt;br /&gt;
				.. ' such as abusing the'&lt;br /&gt;
				.. ' &amp;amp;#123;&amp;amp;#123;[[Template:unblock|unblock]]&amp;amp;#125;&amp;amp;#125; template.',&lt;br /&gt;
			explanation = 'If you cannot edit this user talk page and you need to'&lt;br /&gt;
				.. ' make a change or leave a message, you can'&lt;br /&gt;
				.. ' [[Wikipedia:Requests for page protection'&lt;br /&gt;
				.. '#Current requests for edits to a protected page'&lt;br /&gt;
				.. '|request an edit]],'&lt;br /&gt;
				.. ' [[Wikipedia:Requests for page protection'&lt;br /&gt;
				.. '#Current requests for reduction in protection level'&lt;br /&gt;
				.. '|request unprotection]],'&lt;br /&gt;
				.. ' [[Special:Userlogin|log in]],'&lt;br /&gt;
				.. ' or [[Special:UserLogin/signup|create an account]].',&lt;br /&gt;
		},&lt;br /&gt;
		vandalism = {&lt;br /&gt;
			description = 'For pages protected against'&lt;br /&gt;
				.. ' [[Wikipedia:Vandalism|vandalism]]',&lt;br /&gt;
			text = '${INTROFRAGMENT} due to [[Wikipedia:Vandalism|vandalism]].',&lt;br /&gt;
			explanation = function (protectionObj, args)&lt;br /&gt;
				local ret = ''&lt;br /&gt;
				if protectionObj.level == 'sysop' then&lt;br /&gt;
					ret = ret .. &amp;quot;This protection is '''not''' an endorsement of the&amp;quot;&lt;br /&gt;
						.. ' ${CURRENTVERSION}. '&lt;br /&gt;
				end&lt;br /&gt;
				return ret .. '${EXPLANATIONBLURB}'&lt;br /&gt;
			end,&lt;br /&gt;
			tooltip = '${TOOLTIPFRAGMENT} due to vandalism',&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		dispute = {&lt;br /&gt;
			description = 'For pages protected against page moves due to'&lt;br /&gt;
				.. ' disputes over the page title',&lt;br /&gt;
			explanation = &amp;quot;This protection is '''not''' an endorsement of the&amp;quot;&lt;br /&gt;
				.. ' ${CURRENTVERSION}. ${EXPLANATIONBLURB}',&lt;br /&gt;
			image = 'Move-protection-shackle.svg'&lt;br /&gt;
		},&lt;br /&gt;
		vandalism = {&lt;br /&gt;
			description = 'For pages protected against'&lt;br /&gt;
				.. ' [[Wikipedia:Vandalism#Page-move vandalism'&lt;br /&gt;
				.. ' |page-move vandalism]]'&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {},&lt;br /&gt;
	upload = {}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--&lt;br /&gt;
--                            GENERAL DATA TABLES&lt;br /&gt;
--&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection blurbs&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table produces the protection blurbs available with the&lt;br /&gt;
-- ${PROTECTIONBLURB} parameter. It is sorted by protection action and&lt;br /&gt;
-- protection level, and is checked by the module in the following order:&lt;br /&gt;
-- 1. page's protection action, page's protection level&lt;br /&gt;
-- 2. page's protection action, default protection level&lt;br /&gt;
-- 3. &amp;quot;edit&amp;quot; protection action, default protection level&lt;br /&gt;
--&lt;br /&gt;
-- It is possible to use banner parameters inside this table.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
protectionBlurbs = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		default = 'This ${PAGETYPE} is currently [[Help:Protection|'&lt;br /&gt;
			.. 'protected]] from editing',&lt;br /&gt;
		autoconfirmed = 'Editing of this ${PAGETYPE} by [[Wikipedia:User access'&lt;br /&gt;
			.. ' levels#New users|new]] or [[Wikipedia:User access levels#Unregistered'&lt;br /&gt;
			.. ' users|unregistered]] users is currently [[Help:Protection|disabled]]',&lt;br /&gt;
		extendedconfirmed = 'This ${PAGETYPE} is currently under extended confirmed protection',&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = 'This ${PAGETYPE} is currently [[Help:Protection|protected]]'&lt;br /&gt;
			.. ' from [[Help:Moving a page|page moves]]'&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = 'All edits made to this ${PAGETYPE} by'&lt;br /&gt;
			.. ' [[Wikipedia:User access levels#New users|new]] or'&lt;br /&gt;
			.. ' [[Wikipedia:User access levels#Unregistered users|unregistered]]'&lt;br /&gt;
			.. ' users are currently'&lt;br /&gt;
			.. ' [[Wikipedia:Pending changes|subject to review]]'&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = 'Uploading new versions of this ${PAGETYPE} is currently disabled'&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Explanation blurbs&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table produces the explanation blurbs available with the&lt;br /&gt;
-- ${EXPLANATIONBLURB} parameter. It is sorted by protection action,&lt;br /&gt;
-- protection level, and whether the page is a talk page or not. If the page is&lt;br /&gt;
-- a talk page it will have a talk key of &amp;quot;talk&amp;quot;; otherwise it will have a talk&lt;br /&gt;
-- key of &amp;quot;subject&amp;quot;. The table is checked in the following order:&lt;br /&gt;
-- 1. page's protection action, page's protection level, page's talk key&lt;br /&gt;
-- 2. page's protection action, page's protection level, default talk key&lt;br /&gt;
-- 3. page's protection action, default protection level, page's talk key&lt;br /&gt;
-- 4. page's protection action, default protection level, default talk key&lt;br /&gt;
--&lt;br /&gt;
-- It is possible to use banner parameters inside this table.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
explanationBlurbs = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		autoconfirmed = {&lt;br /&gt;
			subject = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
				.. 'protection policy]] and ${PROTECTIONLOG} for more details. If you'&lt;br /&gt;
				.. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can'&lt;br /&gt;
				.. ' ${EDITREQUEST}, discuss changes on the ${TALKPAGE},'&lt;br /&gt;
				.. ' [[Wikipedia:Requests for page protection'&lt;br /&gt;
				.. '#Current requests for reduction in protection level'&lt;br /&gt;
				.. '|request unprotection]], [[Special:Userlogin|log in]], or'&lt;br /&gt;
				.. ' [[Special:UserLogin/signup|create an account]].',&lt;br /&gt;
			default = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
				.. 'protection policy]] and ${PROTECTIONLOG} for more details. If you'&lt;br /&gt;
				.. ' cannot edit this ${PAGETYPE} and you wish to make a change, you can'&lt;br /&gt;
				.. ' [[Wikipedia:Requests for page protection'&lt;br /&gt;
				.. '#Current requests for reduction in protection level'&lt;br /&gt;
				.. '|request unprotection]], [[Special:Userlogin|log in]], or'&lt;br /&gt;
				.. ' [[Special:UserLogin/signup|create an account]].',&lt;br /&gt;
		},&lt;br /&gt;
		extendedconfirmed = {&lt;br /&gt;
			default = 'Extended confirmed protection prevents edits from all unregistered editors'&lt;br /&gt;
				.. ' and registered users with fewer than 30 days tenure and 500 edits.'&lt;br /&gt;
				.. ' The [[Wikipedia:Protection policy#extended|policy on community use]]'&lt;br /&gt;
				.. ' specifies that extended confirmed protection can be applied to combat'&lt;br /&gt;
				.. ' disruption, if semi-protection has proven to be ineffective.'&lt;br /&gt;
				.. ' Extended confirmed protection may also be applied to enforce'&lt;br /&gt;
				.. ' [[Wikipedia:Arbitration Committee|arbitration sanctions]].'&lt;br /&gt;
				.. ' Please discuss any changes on the ${TALKPAGE}; you may'&lt;br /&gt;
				.. ' ${EDITREQUEST} to ask for uncontroversial changes supported by'&lt;br /&gt;
				.. ' [[Wikipedia:Consensus|consensus]].'&lt;br /&gt;
		},&lt;br /&gt;
		default = {&lt;br /&gt;
			subject = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
				.. 'protection policy]] and ${PROTECTIONLOG} for more details.'&lt;br /&gt;
				.. ' Please discuss any changes on the ${TALKPAGE}; you'&lt;br /&gt;
				.. ' may ${EDITREQUEST} to ask an'&lt;br /&gt;
				.. ' [[Wikipedia:Administrators|administrator]] to make an edit if it'&lt;br /&gt;
				.. ' is [[Help:Minor edit#When to mark an edit as a minor edit'&lt;br /&gt;
				.. '|uncontroversial]] or supported by [[Wikipedia:Consensus'&lt;br /&gt;
				.. '|consensus]]. You may also [[Wikipedia:Requests for'&lt;br /&gt;
				.. ' page protection#Current requests for reduction in protection level'&lt;br /&gt;
				.. '|request]] that this page be unprotected.',&lt;br /&gt;
			default = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
				.. 'protection policy]] and ${PROTECTIONLOG} for more details.'&lt;br /&gt;
				.. ' You may [[Wikipedia:Requests for page'&lt;br /&gt;
				.. ' protection#Current requests for edits to a protected page|request an'&lt;br /&gt;
				.. ' edit]] to this page, or [[Wikipedia:Requests for'&lt;br /&gt;
				.. ' page protection#Current requests for reduction in protection level'&lt;br /&gt;
				.. '|ask]] for it to be unprotected.'&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = {&lt;br /&gt;
			subject = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
				.. 'protection policy]] and ${PROTECTIONLOG} for more details.'&lt;br /&gt;
				.. ' The page may still be edited but cannot be moved'&lt;br /&gt;
				.. ' until unprotected. Please discuss any suggested moves on the'&lt;br /&gt;
				.. ' ${TALKPAGE} or at [[Wikipedia:Requested moves]]. You can also'&lt;br /&gt;
				.. ' [[Wikipedia:Requests for page protection|request]] that the page be'&lt;br /&gt;
				.. ' unprotected.',&lt;br /&gt;
			default = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
				.. 'protection policy]] and ${PROTECTIONLOG} for more details.'&lt;br /&gt;
				.. ' The page may still be edited but cannot be moved'&lt;br /&gt;
				.. ' until unprotected. Please discuss any suggested moves at'&lt;br /&gt;
				.. ' [[Wikipedia:Requested moves]]. You can also'&lt;br /&gt;
				.. ' [[Wikipedia:Requests for page protection|request]] that the page be'&lt;br /&gt;
				.. ' unprotected.'&lt;br /&gt;
		}&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = {&lt;br /&gt;
			default = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
				.. 'protection policy]] and ${PROTECTIONLOG} for more details.'&lt;br /&gt;
				.. ' Edits to this ${PAGETYPE} by new and unregistered users'&lt;br /&gt;
				.. ' will not be visible to readers until they are accepted by'&lt;br /&gt;
				.. ' a reviewer. To avoid the need for your edits to be'&lt;br /&gt;
				.. ' reviewed, you may'&lt;br /&gt;
				.. ' [[Wikipedia:Requests for page protection'&lt;br /&gt;
				.. '#Current requests for reduction in protection level'&lt;br /&gt;
				.. '|request unprotection]], [[Special:Userlogin|log in]], or'&lt;br /&gt;
				.. ' [[Special:UserLogin/signup|create an account]].'&lt;br /&gt;
		},&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = {&lt;br /&gt;
			default = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
				.. 'protection policy]] and ${PROTECTIONLOG} for more details.'&lt;br /&gt;
				.. ' The page may still be edited but new versions of the file'&lt;br /&gt;
				.. ' cannot be uploaded until it is unprotected. You can'&lt;br /&gt;
				.. ' request that a new version be uploaded by using a'&lt;br /&gt;
				.. ' [[Wikipedia:Edit requests|protected edit request]], or you'&lt;br /&gt;
				.. ' can  [[Wikipedia:Requests for page protection|request]]'&lt;br /&gt;
				.. ' that the file be unprotected.'&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection levels&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table provides the data for the ${PROTECTIONLEVEL} parameter, which&lt;br /&gt;
-- produces a short label for different protection levels. It is sorted by&lt;br /&gt;
-- protection action and protection level, and is checked in the following&lt;br /&gt;
-- order:&lt;br /&gt;
-- 1. page's protection action, page's protection level&lt;br /&gt;
-- 2. page's protection action, default protection level&lt;br /&gt;
-- 3. &amp;quot;edit&amp;quot; protection action, default protection level&lt;br /&gt;
--&lt;br /&gt;
-- It is possible to use banner parameters inside this table.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
protectionLevels = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		default = 'protected',&lt;br /&gt;
		templateeditor = 'template-protected',&lt;br /&gt;
		extendedconfirmed = 'extended-protected',&lt;br /&gt;
		autoconfirmed = 'semi-protected',&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = 'move-protected'&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = 'upload-protected'&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Images&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table lists different padlock images for each protection action and&lt;br /&gt;
-- protection level. It is used if an image is not specified in any of the&lt;br /&gt;
-- banner data tables, and if the page does not satisfy the conditions for using&lt;br /&gt;
-- the ['image-filename-indef'] image. It is checked in the following order:&lt;br /&gt;
-- 1. page's protection action, page's protection level&lt;br /&gt;
-- 2. page's protection action, default protection level&lt;br /&gt;
images = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		default = 'Full-protection-shackle.svg',&lt;br /&gt;
		templateeditor = 'Template-protection-shackle.svg',&lt;br /&gt;
		extendedconfirmed = 'Extended-protection-shackle.svg',&lt;br /&gt;
		autoconfirmed = 'Semi-protection-shackle.svg'&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = 'Move-protection-shackle.svg',&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = 'Pending-protection-shackle.svg'&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = 'Upload-protection-shackle.svg'&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
-- Pages with a reason specified in this table will show the special &amp;quot;indef&amp;quot;&lt;br /&gt;
-- padlock, defined in the 'image-filename-indef' message, if no expiry is set.&lt;br /&gt;
indefImageReasons = {&lt;br /&gt;
	template = true&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Image links&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table provides the data for the ${IMAGELINK} parameter, which gets&lt;br /&gt;
-- the image link for small padlock icons based on the page's protection action&lt;br /&gt;
-- and protection level. It is checked in the following order:&lt;br /&gt;
-- 1. page's protection action, page's protection level&lt;br /&gt;
-- 2. page's protection action, default protection level&lt;br /&gt;
-- 3. &amp;quot;edit&amp;quot; protection action, default protection level&lt;br /&gt;
--&lt;br /&gt;
-- It is possible to use banner parameters inside this table.&lt;br /&gt;
-- *required* - this table needs edit, move, autoreview and upload subtables.&lt;br /&gt;
imageLinks = {&lt;br /&gt;
	edit = {&lt;br /&gt;
		default = 'Wikipedia:Protection policy#full',&lt;br /&gt;
		templateeditor = 'Wikipedia:Protection policy#template',&lt;br /&gt;
		extendedconfirmed = 'Wikipedia:Protection policy#extended',&lt;br /&gt;
		autoconfirmed = 'Wikipedia:Protection policy#semi'&lt;br /&gt;
	},&lt;br /&gt;
	move = {&lt;br /&gt;
		default = 'Wikipedia:Protection policy#move'&lt;br /&gt;
	},&lt;br /&gt;
	autoreview = {&lt;br /&gt;
		default = 'Wikipedia:Protection policy#pending'&lt;br /&gt;
	},&lt;br /&gt;
	upload = {&lt;br /&gt;
		default = 'Wikipedia:Protection policy#upload'&lt;br /&gt;
	}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Padlock indicator names&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table provides the &amp;quot;name&amp;quot; attribute for the &amp;lt;indicator&amp;gt; extension tag&lt;br /&gt;
-- with which small padlock icons are generated. All indicator tags on a page&lt;br /&gt;
-- are displayed in alphabetical order based on this attribute, and with&lt;br /&gt;
-- indicator tags with duplicate names, the last tag on the page wins.&lt;br /&gt;
-- The attribute is chosen based on the protection action; table keys must be a&lt;br /&gt;
-- protection action name or the string &amp;quot;default&amp;quot;.&lt;br /&gt;
padlockIndicatorNames = {&lt;br /&gt;
	autoreview = 'pp-autoreview',&lt;br /&gt;
	default = 'pp-default'&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection categories&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
--[[&lt;br /&gt;
-- The protection categories are stored in the protectionCategories table.&lt;br /&gt;
-- Keys to this table are made up of the following strings:&lt;br /&gt;
--&lt;br /&gt;
-- 1. the expiry date&lt;br /&gt;
-- 2. the namespace&lt;br /&gt;
-- 3. the protection reason (e.g. &amp;quot;dispute&amp;quot; or &amp;quot;vandalism&amp;quot;)&lt;br /&gt;
-- 4. the protection level (e.g. &amp;quot;sysop&amp;quot; or &amp;quot;autoconfirmed&amp;quot;)&lt;br /&gt;
-- 5. the action (e.g. &amp;quot;edit&amp;quot; or &amp;quot;move&amp;quot;)&lt;br /&gt;
-- &lt;br /&gt;
-- When the module looks up a category in the table, first it will will check to&lt;br /&gt;
-- see a key exists that corresponds to all five parameters. For example, a&lt;br /&gt;
-- user page semi-protected from vandalism for two weeks would have the key&lt;br /&gt;
-- &amp;quot;temp-user-vandalism-autoconfirmed-edit&amp;quot;. If no match is found, the module&lt;br /&gt;
-- changes the first part of the key to &amp;quot;all&amp;quot; and checks the table again. It&lt;br /&gt;
-- keeps checking increasingly generic key combinations until it finds the&lt;br /&gt;
-- field, or until it reaches the key &amp;quot;all-all-all-all-all&amp;quot;.&lt;br /&gt;
--&lt;br /&gt;
-- The module uses a binary matrix to determine the order in which to search.&lt;br /&gt;
-- This is best demonstrated by a table. In this table, the &amp;quot;0&amp;quot; values&lt;br /&gt;
-- represent &amp;quot;all&amp;quot;, and the &amp;quot;1&amp;quot; values represent the original data (e.g.&lt;br /&gt;
-- &amp;quot;indef&amp;quot; or &amp;quot;file&amp;quot; or &amp;quot;vandalism&amp;quot;).&lt;br /&gt;
--&lt;br /&gt;
--        expiry    namespace reason   level     action&lt;br /&gt;
-- order&lt;br /&gt;
-- 1      1         1         1        1         1&lt;br /&gt;
-- 2      0         1         1        1         1&lt;br /&gt;
-- 3      1         0         1        1         1&lt;br /&gt;
-- 4      0         0         1        1         1&lt;br /&gt;
-- 5      1         1         0        1         1&lt;br /&gt;
-- 6      0         1         0        1         1&lt;br /&gt;
-- 7      1         0         0        1         1&lt;br /&gt;
-- 8      0         0         0        1         1&lt;br /&gt;
-- 9      1         1         1        0         1&lt;br /&gt;
-- 10     0         1         1        0         1&lt;br /&gt;
-- 11     1         0         1        0         1&lt;br /&gt;
-- 12     0         0         1        0         1&lt;br /&gt;
-- 13     1         1         0        0         1&lt;br /&gt;
-- 14     0         1         0        0         1&lt;br /&gt;
-- 15     1         0         0        0         1&lt;br /&gt;
-- 16     0         0         0        0         1&lt;br /&gt;
-- 17     1         1         1        1         0&lt;br /&gt;
-- 18     0         1         1        1         0&lt;br /&gt;
-- 19     1         0         1        1         0&lt;br /&gt;
-- 20     0         0         1        1         0&lt;br /&gt;
-- 21     1         1         0        1         0&lt;br /&gt;
-- 22     0         1         0        1         0&lt;br /&gt;
-- 23     1         0         0        1         0&lt;br /&gt;
-- 24     0         0         0        1         0&lt;br /&gt;
-- 25     1         1         1        0         0&lt;br /&gt;
-- 26     0         1         1        0         0&lt;br /&gt;
-- 27     1         0         1        0         0&lt;br /&gt;
-- 28     0         0         1        0         0&lt;br /&gt;
-- 29     1         1         0        0         0&lt;br /&gt;
-- 30     0         1         0        0         0&lt;br /&gt;
-- 31     1         0         0        0         0&lt;br /&gt;
-- 32     0         0         0        0         0&lt;br /&gt;
--&lt;br /&gt;
-- In this scheme the action has the highest priority, as it is the last&lt;br /&gt;
-- to change, and the expiry has the least priority, as it changes the most.&lt;br /&gt;
-- The priorities of the expiry, the protection level and the action are&lt;br /&gt;
-- fixed, but the priorities of the reason and the namespace can be swapped&lt;br /&gt;
-- through the use of the cfg.bannerDataNamespaceHasPriority table.&lt;br /&gt;
--]]&lt;br /&gt;
&lt;br /&gt;
-- If the reason specified to the template is listed in this table,&lt;br /&gt;
-- namespace data will take priority over reason data in the protectionCategories&lt;br /&gt;
-- table.&lt;br /&gt;
reasonsWithNamespacePriority = {&lt;br /&gt;
	vandalism = true,&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
-- The string to use as a namespace key for the protectionCategories table for each&lt;br /&gt;
-- namespace number.&lt;br /&gt;
categoryNamespaceKeys = {&lt;br /&gt;
	[  2] = 'user',&lt;br /&gt;
	[  3] = 'user',&lt;br /&gt;
	[  4] = 'project',&lt;br /&gt;
	[  6] = 'file',&lt;br /&gt;
	[  8] = 'mediawiki',&lt;br /&gt;
	[ 10] = 'template',&lt;br /&gt;
	[ 12] = 'project',&lt;br /&gt;
	[ 14] = 'category',&lt;br /&gt;
	[100] = 'portal',&lt;br /&gt;
	[828] = 'module',&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
protectionCategories = {&lt;br /&gt;
	['all|all|all|all|all']                     = 'Wikipedia fully protected pages',&lt;br /&gt;
	['all|all|office|all|all']                  = 'Wikipedia Office-protected pages',&lt;br /&gt;
	['all|all|reset|all|all']                   = 'Wikipedia Office-protected pages',&lt;br /&gt;
	['all|all|dmca|all|all']                    = 'Wikipedia Office-protected pages',&lt;br /&gt;
	['all|all|mainpage|all|all']                = 'Wikipedia fully-protected main page files',&lt;br /&gt;
	['all|all|all|extendedconfirmed|all']       = 'Wikipedia extended-confirmed-protected pages',&lt;br /&gt;
	['all|all|ecp|extendedconfirmed|all']       = 'Wikipedia extended-confirmed-protected pages',&lt;br /&gt;
	['all|template|all|all|edit']               = 'Wikipedia fully protected templates',&lt;br /&gt;
	['all|all|all|autoconfirmed|edit']          = 'Wikipedia semi-protected pages',&lt;br /&gt;
	['indef|all|all|autoconfirmed|edit']        = 'Wikipedia indefinitely semi-protected pages',&lt;br /&gt;
	['all|all|blp|autoconfirmed|edit']          = 'Wikipedia indefinitely semi-protected biographies of living people',&lt;br /&gt;
	['temp|all|blp|autoconfirmed|edit']         = 'Wikipedia temporarily semi-protected biographies of living people',&lt;br /&gt;
	['all|all|dispute|autoconfirmed|edit']      = 'Wikipedia pages semi-protected due to dispute',&lt;br /&gt;
	['all|all|sock|autoconfirmed|edit']         = 'Wikipedia pages semi-protected from banned users',&lt;br /&gt;
	['all|all|vandalism|autoconfirmed|edit']    = 'Wikipedia pages semi-protected against vandalism',&lt;br /&gt;
	['all|category|all|autoconfirmed|edit']     = 'Wikipedia semi-protected categories',&lt;br /&gt;
	['all|file|all|autoconfirmed|edit']         = 'Wikipedia semi-protected files',&lt;br /&gt;
	['all|portal|all|autoconfirmed|edit']       = 'Wikipedia semi-protected portals',&lt;br /&gt;
	['all|project|all|autoconfirmed|edit']      = 'Wikipedia semi-protected project pages',&lt;br /&gt;
	['all|talk|all|autoconfirmed|edit']         = 'Wikipedia semi-protected talk pages',&lt;br /&gt;
	['all|template|all|autoconfirmed|edit']     = 'Wikipedia semi-protected templates',&lt;br /&gt;
	['all|user|all|autoconfirmed|edit']         = 'Wikipedia semi-protected user and user talk pages',&lt;br /&gt;
	['all|all|all|templateeditor|edit']         = 'Wikipedia template-protected pages other than templates and modules',&lt;br /&gt;
	['all|template|all|templateeditor|edit']    = 'Wikipedia template-protected templates',&lt;br /&gt;
	['all|template|all|templateeditor|move']    = 'Wikipedia template-protected templates', -- move-protected templates&lt;br /&gt;
	['all|all|blp|sysop|edit']                  = 'Wikipedia indefinitely protected biographies of living people',&lt;br /&gt;
	['temp|all|blp|sysop|edit']                 = 'Wikipedia temporarily protected biographies of living people',&lt;br /&gt;
	['all|all|dispute|sysop|edit']              = 'Wikipedia pages protected due to dispute',&lt;br /&gt;
	['all|all|sock|sysop|edit']                 = 'Wikipedia pages protected from banned users',&lt;br /&gt;
	['all|all|vandalism|sysop|edit']            = 'Wikipedia pages protected against vandalism',&lt;br /&gt;
	['all|category|all|sysop|edit']             = 'Wikipedia fully protected categories',&lt;br /&gt;
	['all|file|all|sysop|edit']                 = 'Wikipedia fully-protected files',&lt;br /&gt;
	['all|project|all|sysop|edit']              = 'Wikipedia fully-protected project pages',&lt;br /&gt;
	['all|talk|all|sysop|edit']                 = 'Wikipedia fully-protected talk pages',&lt;br /&gt;
	['all|template|all|extendedconfirmed|edit'] = 'Wikipedia extended-confirmed-protected templates',&lt;br /&gt;
	['all|template|all|sysop|edit']             = 'Wikipedia fully protected templates',&lt;br /&gt;
	['all|user|all|sysop|edit']                 = 'Wikipedia fully protected user and user talk pages',&lt;br /&gt;
	['all|module|all|all|edit']                 = 'Wikipedia fully-protected modules',&lt;br /&gt;
	['all|module|all|templateeditor|edit']      = 'Wikipedia template-protected modules',&lt;br /&gt;
	['all|module|all|extendedconfirmed|edit']   = 'Wikipedia extended-confirmed-protected modules',&lt;br /&gt;
	['all|module|all|autoconfirmed|edit']       = 'Wikipedia semi-protected modules',&lt;br /&gt;
	['all|all|all|sysop|move']                  = 'Wikipedia move-protected pages',&lt;br /&gt;
	['indef|all|all|sysop|move']                = 'Wikipedia indefinitely move-protected pages',&lt;br /&gt;
	['all|all|dispute|sysop|move']              = 'Wikipedia pages move-protected due to dispute',&lt;br /&gt;
	['all|all|vandalism|sysop|move']            = 'Wikipedia pages move-protected due to vandalism',&lt;br /&gt;
	['all|portal|all|sysop|move']               = 'Wikipedia move-protected portals',&lt;br /&gt;
	['all|project|all|sysop|move']              = 'Wikipedia move-protected project pages',&lt;br /&gt;
	['all|talk|all|sysop|move']                 = 'Wikipedia move-protected talk pages',&lt;br /&gt;
	['all|template|all|sysop|move']             = 'Wikipedia move-protected templates',&lt;br /&gt;
	['all|user|all|sysop|move']                 = 'Wikipedia move-protected user and user talk pages',&lt;br /&gt;
	['all|all|all|autoconfirmed|autoreview']    = 'Wikipedia pending changes protected pages',&lt;br /&gt;
	['all|file|all|all|upload']                 = 'Wikipedia upload-protected files',&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Expiry category config&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table configures the expiry category behaviour for each protection&lt;br /&gt;
-- action.&lt;br /&gt;
-- * If set to true, setting that action will always categorise the page if&lt;br /&gt;
--   an expiry parameter is not set.&lt;br /&gt;
-- * If set to false, setting that action will never categorise the page.&lt;br /&gt;
-- * If set to nil, the module will categorise the page if:&lt;br /&gt;
--   1) an expiry parameter is not set, and&lt;br /&gt;
--   2) a reason is provided, and&lt;br /&gt;
--   3) the specified reason is not blacklisted in the reasonsWithoutExpiryCheck&lt;br /&gt;
--      table.&lt;br /&gt;
&lt;br /&gt;
expiryCheckActions = {&lt;br /&gt;
	edit = nil,&lt;br /&gt;
	move = false,&lt;br /&gt;
	autoreview = true,&lt;br /&gt;
	upload = false&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
reasonsWithoutExpiryCheck = {&lt;br /&gt;
	blp = true,&lt;br /&gt;
	template = true,&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Pagetypes&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table produces the page types available with the ${PAGETYPE} parameter.&lt;br /&gt;
-- Keys are namespace numbers, or the string &amp;quot;default&amp;quot; for the default value.&lt;br /&gt;
pagetypes = {&lt;br /&gt;
	[0] = 'article',&lt;br /&gt;
	[6] = 'file',&lt;br /&gt;
	[10] = 'template',&lt;br /&gt;
	[14] = 'category',&lt;br /&gt;
	[828] = 'module',&lt;br /&gt;
	default = 'page'&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Strings marking indefinite protection&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table contains values passed to the expiry parameter that mean the page&lt;br /&gt;
-- is protected indefinitely.&lt;br /&gt;
indefStrings = {&lt;br /&gt;
	['indef'] = true,&lt;br /&gt;
	['indefinite'] = true,&lt;br /&gt;
	['indefinitely'] = true,&lt;br /&gt;
	['infinite'] = true,&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Group hierarchy&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table maps each group to all groups that have a superset of the original&lt;br /&gt;
-- group's page editing permissions.&lt;br /&gt;
hierarchy = {&lt;br /&gt;
	sysop = {},&lt;br /&gt;
	reviewer = {'sysop'},&lt;br /&gt;
	filemover = {'sysop'},&lt;br /&gt;
	templateeditor = {'sysop'},&lt;br /&gt;
	extendedconfirmed = {'sysop'},&lt;br /&gt;
	autoconfirmed = {'reviewer', 'filemover', 'templateeditor', 'extendedconfirmed'},&lt;br /&gt;
	user = {'autoconfirmed'},&lt;br /&gt;
	['*'] = {'user'}&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Wrapper templates and their default arguments&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This table contains wrapper templates used with the module, and their&lt;br /&gt;
-- default arguments. Templates specified in this table should contain the&lt;br /&gt;
-- following invocation, and no other template content:&lt;br /&gt;
--&lt;br /&gt;
-- {{#invoke:Protection banner|main}}&lt;br /&gt;
--&lt;br /&gt;
-- If other content is desired, it can be added between&lt;br /&gt;
-- &amp;lt;noinclude&amp;gt;...&amp;lt;/noinclude&amp;gt; tags.&lt;br /&gt;
--&lt;br /&gt;
-- When a user calls one of these wrapper templates, they will use the&lt;br /&gt;
-- default arguments automatically. However, users can override any of the&lt;br /&gt;
-- arguments.&lt;br /&gt;
wrappers = {&lt;br /&gt;
	['Template:Pp']                         = {},&lt;br /&gt;
	['Template:Pp-extended']                = {'ecp'},&lt;br /&gt;
	['Template:Pp-blp']                     = {'blp'},&lt;br /&gt;
	-- we don't need Template:Pp-create&lt;br /&gt;
	['Template:Pp-dispute']                 = {'dispute'},&lt;br /&gt;
	['Template:Pp-main-page']               = {'mainpage'},&lt;br /&gt;
	['Template:Pp-move']                    = {action = 'move', catonly = 'yes'},&lt;br /&gt;
	['Template:Pp-move-dispute']            = {'dispute', action = 'move', catonly = 'yes'},&lt;br /&gt;
	-- we don't need Template:Pp-move-indef&lt;br /&gt;
	['Template:Pp-move-vandalism']          = {'vandalism', action = 'move', catonly = 'yes'},&lt;br /&gt;
	['Template:Pp-office']                  = {'office'},&lt;br /&gt;
	['Template:Pp-office-dmca']             = {'dmca'},&lt;br /&gt;
	['Template:Pp-pc']                      = {action = 'autoreview', small = true},&lt;br /&gt;
	['Template:Pp-pc1']                     = {action = 'autoreview', small = true},&lt;br /&gt;
	['Template:Pp-reset']                   = {'reset'},&lt;br /&gt;
	['Template:Pp-semi-indef']              = {small = true},&lt;br /&gt;
	['Template:Pp-sock']                    = {'sock'},&lt;br /&gt;
	['Template:Pp-template']                = {'template', small = true},&lt;br /&gt;
	['Template:Pp-upload']                  = {action = 'upload'},&lt;br /&gt;
	['Template:Pp-usertalk']                = {'usertalk'},&lt;br /&gt;
	['Template:Pp-vandalism']               = {'vandalism'},&lt;br /&gt;
},&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- &lt;br /&gt;
--                                 MESSAGES&lt;br /&gt;
-- &lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
msg = {&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Intro blurb and intro fragment&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages specify what is produced by the ${INTROBLURB} and&lt;br /&gt;
-- ${INTROFRAGMENT} parameters. If the protection is temporary they use the&lt;br /&gt;
-- intro-blurb-expiry or intro-fragment-expiry, and if not they use&lt;br /&gt;
-- intro-blurb-noexpiry or intro-fragment-noexpiry.&lt;br /&gt;
-- It is possible to use banner parameters in these messages.&lt;br /&gt;
['intro-blurb-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY}.',&lt;br /&gt;
['intro-blurb-noexpiry'] = '${PROTECTIONBLURB}.',&lt;br /&gt;
['intro-fragment-expiry'] = '${PROTECTIONBLURB} until ${EXPIRY},',&lt;br /&gt;
['intro-fragment-noexpiry'] = '${PROTECTIONBLURB}',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Tooltip blurb&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages specify what is produced by the ${TOOLTIPBLURB} parameter.&lt;br /&gt;
-- If the protection is temporary the tooltip-blurb-expiry message is used, and&lt;br /&gt;
-- if not the tooltip-blurb-noexpiry message is used.&lt;br /&gt;
-- It is possible to use banner parameters in these messages.&lt;br /&gt;
['tooltip-blurb-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY}.',&lt;br /&gt;
['tooltip-blurb-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}.',&lt;br /&gt;
['tooltip-fragment-expiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL} until ${EXPIRY},',&lt;br /&gt;
['tooltip-fragment-noexpiry'] = 'This ${PAGETYPE} is ${PROTECTIONLEVEL}',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Special explanation blurb&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- An explanation blurb for pages that cannot be unprotected, e.g. for pages&lt;br /&gt;
-- in the MediaWiki namespace.&lt;br /&gt;
-- It is possible to use banner parameters in this message.&lt;br /&gt;
['explanation-blurb-nounprotect'] = 'See the [[Wikipedia:Protection policy|'&lt;br /&gt;
	.. 'protection policy]] and ${PROTECTIONLOG} for more details.'&lt;br /&gt;
	.. ' Please discuss any changes on the ${TALKPAGE}; you'&lt;br /&gt;
	.. ' may ${EDITREQUEST} to ask an'&lt;br /&gt;
	.. ' [[Wikipedia:Administrators|administrator]] to make an edit if it'&lt;br /&gt;
	.. ' is [[Help:Minor edit#When to mark an edit as a minor edit'&lt;br /&gt;
	.. '|uncontroversial]] or supported by [[Wikipedia:Consensus'&lt;br /&gt;
	.. '|consensus]].',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection log display values&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages determine the display values for the protection log link&lt;br /&gt;
-- or the pending changes log link produced by the ${PROTECTIONLOG} parameter.&lt;br /&gt;
-- It is possible to use banner parameters in these messages.&lt;br /&gt;
['protection-log-display'] = 'protection log',&lt;br /&gt;
['pc-log-display'] = 'pending changes log',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Current version display values&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages determine the display values for the page history link&lt;br /&gt;
-- or the move log link produced by the ${CURRENTVERSION} parameter.&lt;br /&gt;
-- It is possible to use banner parameters in these messages.&lt;br /&gt;
['current-version-move-display'] = 'current title',&lt;br /&gt;
['current-version-edit-display'] = 'current version',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Talk page&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This message determines the display value of the talk page link produced&lt;br /&gt;
-- with the ${TALKPAGE} parameter.&lt;br /&gt;
-- It is possible to use banner parameters in this message.&lt;br /&gt;
['talk-page-link-display'] = 'talk page',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Edit requests&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This message determines the display value of the edit request link produced&lt;br /&gt;
-- with the ${EDITREQUEST} parameter.&lt;br /&gt;
-- It is possible to use banner parameters in this message.&lt;br /&gt;
['edit-request-display'] = 'submit an edit request',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Expiry date format&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- This is the format for the blurb expiry date. It should be valid input for&lt;br /&gt;
-- the first parameter of the #time parser function.&lt;br /&gt;
['expiry-date-format'] = 'F j, Y &amp;quot;at&amp;quot; H:i e',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Tracking categories&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These messages determine which tracking categories the module outputs.&lt;br /&gt;
['tracking-category-incorrect'] = 'Wikipedia pages with incorrect protection templates',&lt;br /&gt;
['tracking-category-template'] = 'Wikipedia template-protected pages other than templates and modules',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Images&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
-- These are images that are not defined by their protection action and protection level.&lt;br /&gt;
['image-filename-indef'] = 'Full-protection-shackle.svg',&lt;br /&gt;
['image-filename-default'] = 'Transparent.gif',&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- End messages&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- End configuration&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
}&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Effective_protection_expiry&amp;diff=218</id>
		<title>Module:Effective protection expiry</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Effective_protection_expiry&amp;diff=218"/>
		<updated>2022-03-29T15:37:15Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;local p = {}  -- Returns the expiry of a restriction of an action on a given title, or unknown if it cannot be known. -- If no title is specified, the title of the page being...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Returns the expiry of a restriction of an action on a given title, or unknown if it cannot be known.&lt;br /&gt;
-- If no title is specified, the title of the page being displayed is used.&lt;br /&gt;
function p._main(action, pagename)&lt;br /&gt;
	local title&lt;br /&gt;
	if type(pagename) == 'table' and pagename.prefixedText then&lt;br /&gt;
		title = pagename&lt;br /&gt;
	elseif pagename then&lt;br /&gt;
		title = mw.title.new(pagename)&lt;br /&gt;
	else&lt;br /&gt;
		title = mw.title.getCurrentTitle()&lt;br /&gt;
	end&lt;br /&gt;
	pagename = title.prefixedText&lt;br /&gt;
	if action == 'autoreview' then&lt;br /&gt;
		local stabilitySettings = mw.ext.FlaggedRevs.getStabilitySettings(title)&lt;br /&gt;
		return stabilitySettings and stabilitySettings.expiry or 'unknown'&lt;br /&gt;
	elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' then&lt;br /&gt;
		error( 'First parameter must be one of edit, move, create, upload, autoreview', 2 )&lt;br /&gt;
	end&lt;br /&gt;
	local rawExpiry = mw.getCurrentFrame():callParserFunction('PROTECTIONEXPIRY', action, pagename)&lt;br /&gt;
	if rawExpiry == 'infinity' then&lt;br /&gt;
		return 'infinity'&lt;br /&gt;
	elseif rawExpiry == '' then&lt;br /&gt;
		return 'unknown'&lt;br /&gt;
	else&lt;br /&gt;
		local year, month, day, hour, minute, second = rawExpiry:match(&lt;br /&gt;
			'^(%d%d%d%d)(%d%d)(%d%d)(%d%d)(%d%d)(%d%d)$'&lt;br /&gt;
		)&lt;br /&gt;
		if year then&lt;br /&gt;
			return string.format(&lt;br /&gt;
				'%s-%s-%sT%s:%s:%s',&lt;br /&gt;
				year, month, day, hour, minute, second&lt;br /&gt;
			)&lt;br /&gt;
		else&lt;br /&gt;
			error('internal error in Module:Effective protection expiry; malformed expiry timestamp')&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
setmetatable(p, { __index = function(t, k)&lt;br /&gt;
	return function(frame)&lt;br /&gt;
		return t._main(k, frame.args[1])&lt;br /&gt;
	end&lt;br /&gt;
end })&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Effective_protection_level&amp;diff=217</id>
		<title>Module:Effective protection level</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Effective_protection_level&amp;diff=217"/>
		<updated>2022-03-29T15:36:51Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;local p = {}  -- Returns the permission required to perform a given action on a given title. -- If no title is specified, the title of the page being displayed is used. functi...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local p = {}&lt;br /&gt;
&lt;br /&gt;
-- Returns the permission required to perform a given action on a given title.&lt;br /&gt;
-- If no title is specified, the title of the page being displayed is used.&lt;br /&gt;
function p._main(action, pagename)&lt;br /&gt;
	local title&lt;br /&gt;
	if type(pagename) == 'table' and pagename.prefixedText then&lt;br /&gt;
		title = pagename&lt;br /&gt;
	elseif pagename then&lt;br /&gt;
		title = mw.title.new(pagename)&lt;br /&gt;
	else&lt;br /&gt;
		title = mw.title.getCurrentTitle()&lt;br /&gt;
	end&lt;br /&gt;
	pagename = title.prefixedText&lt;br /&gt;
	if action == 'autoreview' then&lt;br /&gt;
		local level = mw.ext.FlaggedRevs.getStabilitySettings(title)&lt;br /&gt;
		level = level and level.autoreview&lt;br /&gt;
		if level == 'review' then&lt;br /&gt;
			return 'reviewer'&lt;br /&gt;
		elseif level ~= '' then&lt;br /&gt;
			return level&lt;br /&gt;
		else&lt;br /&gt;
			return nil -- not '*'. a page not being PC-protected is distinct from it being PC-protected with anyone able to review. also not '', as that would mean PC-protected but nobody can review&lt;br /&gt;
		end&lt;br /&gt;
	elseif action ~= 'edit' and action ~= 'move' and action ~= 'create' and action ~= 'upload' and action ~= 'undelete' then&lt;br /&gt;
		error( 'First parameter must be one of edit, move, create, upload, undelete, autoreview', 2 )&lt;br /&gt;
	end&lt;br /&gt;
	if title.namespace == 8 then -- MediaWiki namespace&lt;br /&gt;
		if title.text:sub(-3) == '.js' or title.text:sub(-4) == '.css' or title.contentModel == 'javascript' or title.contentModel == 'css' then -- site JS or CSS page&lt;br /&gt;
			return 'interfaceadmin'&lt;br /&gt;
		else -- any non-JS/CSS MediaWiki page&lt;br /&gt;
			return 'sysop'&lt;br /&gt;
		end&lt;br /&gt;
	elseif title.namespace == 2 and title.isSubpage then&lt;br /&gt;
		if title.contentModel == 'javascript' or title.contentModel == 'css' then -- user JS or CSS page&lt;br /&gt;
			return 'interfaceadmin'&lt;br /&gt;
		elseif title.contentModel == 'json' then -- user JSON page&lt;br /&gt;
			return 'sysop'&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if action == 'undelete' then&lt;br /&gt;
		return 'sysop'&lt;br /&gt;
	end&lt;br /&gt;
	local level = title.protectionLevels[action] and title.protectionLevels[action][1]&lt;br /&gt;
	if level == 'sysop' or level == 'editprotected' then&lt;br /&gt;
		return 'sysop'&lt;br /&gt;
	elseif title.cascadingProtection.restrictions[action] and title.cascadingProtection.restrictions[action][1] then -- used by a cascading-protected page&lt;br /&gt;
		return 'sysop'&lt;br /&gt;
	elseif level == 'templateeditor' then&lt;br /&gt;
		return 'templateeditor'&lt;br /&gt;
	elseif action == 'move' then&lt;br /&gt;
		local blacklistentry = mw.ext.TitleBlacklist.test('edit', pagename) -- Testing action edit is correct, since this is for the source page. The target page name gets tested with action move.&lt;br /&gt;
		if blacklistentry and not blacklistentry.params.autoconfirmed then&lt;br /&gt;
			return 'templateeditor'&lt;br /&gt;
		elseif title.namespace == 6 then&lt;br /&gt;
			return 'filemover'&lt;br /&gt;
		elseif level == 'extendedconfirmed' then&lt;br /&gt;
			return 'extendedconfirmed'&lt;br /&gt;
		else&lt;br /&gt;
			return 'autoconfirmed'&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	local blacklistentry = mw.ext.TitleBlacklist.test(action, pagename)&lt;br /&gt;
	if blacklistentry then&lt;br /&gt;
		if not blacklistentry.params.autoconfirmed then&lt;br /&gt;
			return 'templateeditor'&lt;br /&gt;
		elseif level == 'extendedconfirmed' then&lt;br /&gt;
			return 'extendedconfirmed'&lt;br /&gt;
		else&lt;br /&gt;
			return 'autoconfirmed'&lt;br /&gt;
		end&lt;br /&gt;
	elseif level == 'editsemiprotected' then -- create-semiprotected pages return this for some reason&lt;br /&gt;
		return 'autoconfirmed'&lt;br /&gt;
	elseif level then&lt;br /&gt;
		return level&lt;br /&gt;
	elseif action == 'upload' then&lt;br /&gt;
		return 'autoconfirmed'&lt;br /&gt;
	elseif action == 'create' and title.namespace % 2 == 0 and title.namespace ~= 118 then -- You need to be registered, but not autoconfirmed, to create non-talk pages other than drafts&lt;br /&gt;
		return 'user'&lt;br /&gt;
	else&lt;br /&gt;
		return '*'&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
setmetatable(p, { __index = function(t, k)&lt;br /&gt;
	return function(frame)&lt;br /&gt;
		return t._main(k, frame.args[1])&lt;br /&gt;
	end&lt;br /&gt;
end })&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Arguments&amp;diff=216</id>
		<title>Module:Arguments</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Arguments&amp;diff=216"/>
		<updated>2022-03-29T15:35:06Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- This module provides easy processing of arguments passed to Scribunto from -- #invoke. It is intended for use by other Lua modules, and should not be -- called from #invoke...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module provides easy processing of arguments passed to Scribunto from&lt;br /&gt;
-- #invoke. It is intended for use by other Lua modules, and should not be&lt;br /&gt;
-- called from #invoke directly.&lt;br /&gt;
&lt;br /&gt;
local libraryUtil = require('libraryUtil')&lt;br /&gt;
local checkType = libraryUtil.checkType&lt;br /&gt;
&lt;br /&gt;
local arguments = {}&lt;br /&gt;
&lt;br /&gt;
-- Generate four different tidyVal functions, so that we don't have to check the&lt;br /&gt;
-- options every time we call it.&lt;br /&gt;
&lt;br /&gt;
local function tidyValDefault(key, val)&lt;br /&gt;
	if type(val) == 'string' then&lt;br /&gt;
		val = val:match('^%s*(.-)%s*$')&lt;br /&gt;
		if val == '' then&lt;br /&gt;
			return nil&lt;br /&gt;
		else&lt;br /&gt;
			return val&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		return val&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tidyValTrimOnly(key, val)&lt;br /&gt;
	if type(val) == 'string' then&lt;br /&gt;
		return val:match('^%s*(.-)%s*$')&lt;br /&gt;
	else&lt;br /&gt;
		return val&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tidyValRemoveBlanksOnly(key, val)&lt;br /&gt;
	if type(val) == 'string' then&lt;br /&gt;
		if val:find('%S') then&lt;br /&gt;
			return val&lt;br /&gt;
		else&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		return val&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function tidyValNoChange(key, val)&lt;br /&gt;
	return val&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function matchesTitle(given, title)&lt;br /&gt;
	local tp = type( given )&lt;br /&gt;
	return (tp == 'string' or tp == 'number') and mw.title.new( given ).prefixedText == title&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local translate_mt = { __index = function(t, k) return k end }&lt;br /&gt;
&lt;br /&gt;
function arguments.getArgs(frame, options)&lt;br /&gt;
	checkType('getArgs', 1, frame, 'table', true)&lt;br /&gt;
	checkType('getArgs', 2, options, 'table', true)&lt;br /&gt;
	frame = frame or {}&lt;br /&gt;
	options = options or {}&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Set up argument translation.&lt;br /&gt;
	--]]&lt;br /&gt;
	options.translate = options.translate or {}&lt;br /&gt;
	if getmetatable(options.translate) == nil then&lt;br /&gt;
		setmetatable(options.translate, translate_mt)&lt;br /&gt;
	end&lt;br /&gt;
	if options.backtranslate == nil then&lt;br /&gt;
		options.backtranslate = {}&lt;br /&gt;
		for k,v in pairs(options.translate) do&lt;br /&gt;
			options.backtranslate[v] = k&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if options.backtranslate and getmetatable(options.backtranslate) == nil then&lt;br /&gt;
		setmetatable(options.backtranslate, {&lt;br /&gt;
			__index = function(t, k)&lt;br /&gt;
				if options.translate[k] ~= k then&lt;br /&gt;
					return nil&lt;br /&gt;
				else&lt;br /&gt;
					return k&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		})&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Get the argument tables. If we were passed a valid frame object, get the&lt;br /&gt;
	-- frame arguments (fargs) and the parent frame arguments (pargs), depending&lt;br /&gt;
	-- on the options set and on the parent frame's availability. If we weren't&lt;br /&gt;
	-- passed a valid frame object, we are being called from another Lua module&lt;br /&gt;
	-- or from the debug console, so assume that we were passed a table of args&lt;br /&gt;
	-- directly, and assign it to a new variable (luaArgs).&lt;br /&gt;
	--]]&lt;br /&gt;
	local fargs, pargs, luaArgs&lt;br /&gt;
	if type(frame.args) == 'table' and type(frame.getParent) == 'function' then&lt;br /&gt;
		if options.wrappers then&lt;br /&gt;
			--[[&lt;br /&gt;
			-- The wrappers option makes Module:Arguments look up arguments in&lt;br /&gt;
			-- either the frame argument table or the parent argument table, but&lt;br /&gt;
			-- not both. This means that users can use either the #invoke syntax&lt;br /&gt;
			-- or a wrapper template without the loss of performance associated&lt;br /&gt;
			-- with looking arguments up in both the frame and the parent frame.&lt;br /&gt;
			-- Module:Arguments will look up arguments in the parent frame&lt;br /&gt;
			-- if it finds the parent frame's title in options.wrapper;&lt;br /&gt;
			-- otherwise it will look up arguments in the frame object passed&lt;br /&gt;
			-- to getArgs.&lt;br /&gt;
			--]]&lt;br /&gt;
			local parent = frame:getParent()&lt;br /&gt;
			if not parent then&lt;br /&gt;
				fargs = frame.args&lt;br /&gt;
			else&lt;br /&gt;
				local title = parent:getTitle():gsub('/sandbox$', '')&lt;br /&gt;
				local found = false&lt;br /&gt;
				if matchesTitle(options.wrappers, title) then&lt;br /&gt;
					found = true&lt;br /&gt;
				elseif type(options.wrappers) == 'table' then&lt;br /&gt;
					for _,v in pairs(options.wrappers) do&lt;br /&gt;
						if matchesTitle(v, title) then&lt;br /&gt;
							found = true&lt;br /&gt;
							break&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
&lt;br /&gt;
				-- We test for false specifically here so that nil (the default) acts like true.&lt;br /&gt;
				if found or options.frameOnly == false then&lt;br /&gt;
					pargs = parent.args&lt;br /&gt;
				end&lt;br /&gt;
				if not found or options.parentOnly == false then&lt;br /&gt;
					fargs = frame.args&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		else&lt;br /&gt;
			-- options.wrapper isn't set, so check the other options.&lt;br /&gt;
			if not options.parentOnly then&lt;br /&gt;
				fargs = frame.args&lt;br /&gt;
			end&lt;br /&gt;
			if not options.frameOnly then&lt;br /&gt;
				local parent = frame:getParent()&lt;br /&gt;
				pargs = parent and parent.args or nil&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if options.parentFirst then&lt;br /&gt;
			fargs, pargs = pargs, fargs&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		luaArgs = frame&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set the order of precedence of the argument tables. If the variables are&lt;br /&gt;
	-- nil, nothing will be added to the table, which is how we avoid clashes&lt;br /&gt;
	-- between the frame/parent args and the Lua args.&lt;br /&gt;
	local argTables = {fargs}&lt;br /&gt;
	argTables[#argTables + 1] = pargs&lt;br /&gt;
	argTables[#argTables + 1] = luaArgs&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Generate the tidyVal function. If it has been specified by the user, we&lt;br /&gt;
	-- use that; if not, we choose one of four functions depending on the&lt;br /&gt;
	-- options chosen. This is so that we don't have to call the options table&lt;br /&gt;
	-- every time the function is called.&lt;br /&gt;
	--]]&lt;br /&gt;
	local tidyVal = options.valueFunc&lt;br /&gt;
	if tidyVal then&lt;br /&gt;
		if type(tidyVal) ~= 'function' then&lt;br /&gt;
			error(&lt;br /&gt;
				&amp;quot;bad value assigned to option 'valueFunc'&amp;quot;&lt;br /&gt;
					.. '(function expected, got '&lt;br /&gt;
					.. type(tidyVal)&lt;br /&gt;
					.. ')',&lt;br /&gt;
				2&lt;br /&gt;
			)&lt;br /&gt;
		end&lt;br /&gt;
	elseif options.trim ~= false then&lt;br /&gt;
		if options.removeBlanks ~= false then&lt;br /&gt;
			tidyVal = tidyValDefault&lt;br /&gt;
		else&lt;br /&gt;
			tidyVal = tidyValTrimOnly&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		if options.removeBlanks ~= false then&lt;br /&gt;
			tidyVal = tidyValRemoveBlanksOnly&lt;br /&gt;
		else&lt;br /&gt;
			tidyVal = tidyValNoChange&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Set up the args, metaArgs and nilArgs tables. args will be the one&lt;br /&gt;
	-- accessed from functions, and metaArgs will hold the actual arguments. Nil&lt;br /&gt;
	-- arguments are memoized in nilArgs, and the metatable connects all of them&lt;br /&gt;
	-- together.&lt;br /&gt;
	--]]&lt;br /&gt;
	local args, metaArgs, nilArgs, metatable = {}, {}, {}, {}&lt;br /&gt;
	setmetatable(args, metatable)&lt;br /&gt;
&lt;br /&gt;
	local function mergeArgs(tables)&lt;br /&gt;
		--[[&lt;br /&gt;
		-- Accepts multiple tables as input and merges their keys and values&lt;br /&gt;
		-- into one table. If a value is already present it is not overwritten;&lt;br /&gt;
		-- tables listed earlier have precedence. We are also memoizing nil&lt;br /&gt;
		-- values, which can be overwritten if they are 's' (soft).&lt;br /&gt;
		--]]&lt;br /&gt;
		for _, t in ipairs(tables) do&lt;br /&gt;
			for key, val in pairs(t) do&lt;br /&gt;
				if metaArgs[key] == nil and nilArgs[key] ~= 'h' then&lt;br /&gt;
					local tidiedVal = tidyVal(key, val)&lt;br /&gt;
					if tidiedVal == nil then&lt;br /&gt;
						nilArgs[key] = 's'&lt;br /&gt;
					else&lt;br /&gt;
						metaArgs[key] = tidiedVal&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	-- Define metatable behaviour. Arguments are memoized in the metaArgs table,&lt;br /&gt;
	-- and are only fetched from the argument tables once. Fetching arguments&lt;br /&gt;
	-- from the argument tables is the most resource-intensive step in this&lt;br /&gt;
	-- module, so we try and avoid it where possible. For this reason, nil&lt;br /&gt;
	-- arguments are also memoized, in the nilArgs table. Also, we keep a record&lt;br /&gt;
	-- in the metatable of when pairs and ipairs have been called, so we do not&lt;br /&gt;
	-- run pairs and ipairs on the argument tables more than once. We also do&lt;br /&gt;
	-- not run ipairs on fargs and pargs if pairs has already been run, as all&lt;br /&gt;
	-- the arguments will already have been copied over.&lt;br /&gt;
	--]]&lt;br /&gt;
&lt;br /&gt;
	metatable.__index = function (t, key)&lt;br /&gt;
		--[[&lt;br /&gt;
		-- Fetches an argument when the args table is indexed. First we check&lt;br /&gt;
		-- to see if the value is memoized, and if not we try and fetch it from&lt;br /&gt;
		-- the argument tables. When we check memoization, we need to check&lt;br /&gt;
		-- metaArgs before nilArgs, as both can be non-nil at the same time.&lt;br /&gt;
		-- If the argument is not present in metaArgs, we also check whether&lt;br /&gt;
		-- pairs has been run yet. If pairs has already been run, we return nil.&lt;br /&gt;
		-- This is because all the arguments will have already been copied into&lt;br /&gt;
		-- metaArgs by the mergeArgs function, meaning that any other arguments&lt;br /&gt;
		-- must be nil.&lt;br /&gt;
		--]]&lt;br /&gt;
		if type(key) == 'string' then&lt;br /&gt;
			key = options.translate[key]&lt;br /&gt;
		end&lt;br /&gt;
		local val = metaArgs[key]&lt;br /&gt;
		if val ~= nil then&lt;br /&gt;
			return val&lt;br /&gt;
		elseif metatable.donePairs or nilArgs[key] then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		for _, argTable in ipairs(argTables) do&lt;br /&gt;
			local argTableVal = tidyVal(key, argTable[key])&lt;br /&gt;
			if argTableVal ~= nil then&lt;br /&gt;
				metaArgs[key] = argTableVal&lt;br /&gt;
				return argTableVal&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		nilArgs[key] = 'h'&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	metatable.__newindex = function (t, key, val)&lt;br /&gt;
		-- This function is called when a module tries to add a new value to the&lt;br /&gt;
		-- args table, or tries to change an existing value.&lt;br /&gt;
		if type(key) == 'string' then&lt;br /&gt;
			key = options.translate[key]&lt;br /&gt;
		end&lt;br /&gt;
		if options.readOnly then&lt;br /&gt;
			error(&lt;br /&gt;
				'could not write to argument table key &amp;quot;'&lt;br /&gt;
					.. tostring(key)&lt;br /&gt;
					.. '&amp;quot;; the table is read-only',&lt;br /&gt;
				2&lt;br /&gt;
			)&lt;br /&gt;
		elseif options.noOverwrite and args[key] ~= nil then&lt;br /&gt;
			error(&lt;br /&gt;
				'could not write to argument table key &amp;quot;'&lt;br /&gt;
					.. tostring(key)&lt;br /&gt;
					.. '&amp;quot;; overwriting existing arguments is not permitted',&lt;br /&gt;
				2&lt;br /&gt;
			)&lt;br /&gt;
		elseif val == nil then&lt;br /&gt;
			--[[&lt;br /&gt;
			-- If the argument is to be overwritten with nil, we need to erase&lt;br /&gt;
			-- the value in metaArgs, so that __index, __pairs and __ipairs do&lt;br /&gt;
			-- not use a previous existing value, if present; and we also need&lt;br /&gt;
			-- to memoize the nil in nilArgs, so that the value isn't looked&lt;br /&gt;
			-- up in the argument tables if it is accessed again.&lt;br /&gt;
			--]]&lt;br /&gt;
			metaArgs[key] = nil&lt;br /&gt;
			nilArgs[key] = 'h'&lt;br /&gt;
		else&lt;br /&gt;
			metaArgs[key] = val&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local function translatenext(invariant)&lt;br /&gt;
		local k, v = next(invariant.t, invariant.k)&lt;br /&gt;
		invariant.k = k&lt;br /&gt;
		if k == nil then&lt;br /&gt;
			return nil&lt;br /&gt;
		elseif type(k) ~= 'string' or not options.backtranslate then&lt;br /&gt;
			return k, v&lt;br /&gt;
		else&lt;br /&gt;
			local backtranslate = options.backtranslate[k]&lt;br /&gt;
			if backtranslate == nil then&lt;br /&gt;
				-- Skip this one. This is a tail call, so this won't cause stack overflow&lt;br /&gt;
				return translatenext(invariant)&lt;br /&gt;
			else&lt;br /&gt;
				return backtranslate, v&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	metatable.__pairs = function ()&lt;br /&gt;
		-- Called when pairs is run on the args table.&lt;br /&gt;
		if not metatable.donePairs then&lt;br /&gt;
			mergeArgs(argTables)&lt;br /&gt;
			metatable.donePairs = true&lt;br /&gt;
		end&lt;br /&gt;
		return translatenext, { t = metaArgs }&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local function inext(t, i)&lt;br /&gt;
		-- This uses our __index metamethod&lt;br /&gt;
		local v = t[i + 1]&lt;br /&gt;
		if v ~= nil then&lt;br /&gt;
			return i + 1, v&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	metatable.__ipairs = function (t)&lt;br /&gt;
		-- Called when ipairs is run on the args table.&lt;br /&gt;
		return inext, t, 0&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return args&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return arguments&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Yesno&amp;diff=215</id>
		<title>Module:Yesno</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Yesno&amp;diff=215"/>
		<updated>2022-03-29T15:34:36Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- Function allowing for consistent treatment of boolean-like wikitext input. -- It works similarly to the template {{yesno}}.  return function (val, default) 	-- If your wiki...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- Function allowing for consistent treatment of boolean-like wikitext input.&lt;br /&gt;
-- It works similarly to the template {{yesno}}.&lt;br /&gt;
&lt;br /&gt;
return function (val, default)&lt;br /&gt;
	-- If your wiki uses non-ascii characters for any of &amp;quot;yes&amp;quot;, &amp;quot;no&amp;quot;, etc., you&lt;br /&gt;
	-- should replace &amp;quot;val:lower()&amp;quot; with &amp;quot;mw.ustring.lower(val)&amp;quot; in the&lt;br /&gt;
	-- following line.&lt;br /&gt;
	val = type(val) == 'string' and val:lower() or val&lt;br /&gt;
	if val == nil then&lt;br /&gt;
		return nil&lt;br /&gt;
	elseif val == true &lt;br /&gt;
		or val == 'yes'&lt;br /&gt;
		or val == 'y'&lt;br /&gt;
		or val == 'true'&lt;br /&gt;
		or val == 't'&lt;br /&gt;
		or val == 'on'&lt;br /&gt;
		or tonumber(val) == 1&lt;br /&gt;
	then&lt;br /&gt;
		return true&lt;br /&gt;
	elseif val == false&lt;br /&gt;
		or val == 'no'&lt;br /&gt;
		or val == 'n'&lt;br /&gt;
		or val == 'false'&lt;br /&gt;
		or val == 'f'&lt;br /&gt;
		or val == 'off'&lt;br /&gt;
		or tonumber(val) == 0&lt;br /&gt;
	then&lt;br /&gt;
		return false&lt;br /&gt;
	else&lt;br /&gt;
		return default&lt;br /&gt;
	end&lt;br /&gt;
end&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:File_link&amp;diff=214</id>
		<title>Module:File link</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:File_link&amp;diff=214"/>
		<updated>2022-03-29T15:33:58Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- This module provides a library for formatting file wikilinks.  local yesno = require('Module:Yesno') local checkType = require('libraryUtil').checkType  local p = {}  funct...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module provides a library for formatting file wikilinks.&lt;br /&gt;
&lt;br /&gt;
local yesno = require('Module:Yesno')&lt;br /&gt;
local checkType = require('libraryUtil').checkType&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p._main(args)&lt;br /&gt;
	checkType('_main', 1, args, 'table')&lt;br /&gt;
&lt;br /&gt;
	-- This is basically libraryUtil.checkTypeForNamedArg, but we are rolling our&lt;br /&gt;
	-- own function to get the right error level.&lt;br /&gt;
	local function checkArg(key, val, level)&lt;br /&gt;
		if type(val) ~= 'string' then&lt;br /&gt;
			error(string.format(&lt;br /&gt;
				&amp;quot;type error in '%s' parameter of '_main' (expected string, got %s)&amp;quot;,&lt;br /&gt;
				key, type(val)&lt;br /&gt;
			), level)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local ret = {}&lt;br /&gt;
&lt;br /&gt;
	-- Adds a positional parameter to the buffer.&lt;br /&gt;
	local function addPositional(key)&lt;br /&gt;
		local val = args[key]&lt;br /&gt;
		if not val then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		checkArg(key, val, 4)&lt;br /&gt;
		ret[#ret + 1] = val&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Adds a named parameter to the buffer. We assume that the parameter name&lt;br /&gt;
	-- is the same as the argument key.&lt;br /&gt;
	local function addNamed(key)&lt;br /&gt;
		local val = args[key]&lt;br /&gt;
		if not val then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		checkArg(key, val, 4)&lt;br /&gt;
		ret[#ret + 1] = key .. '=' .. val&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Filename&lt;br /&gt;
	checkArg('file', args.file, 3)&lt;br /&gt;
	ret[#ret + 1] = 'File:' .. args.file&lt;br /&gt;
&lt;br /&gt;
	-- Format&lt;br /&gt;
	if args.format then&lt;br /&gt;
		checkArg('format', args.format)&lt;br /&gt;
		if args.formatfile then&lt;br /&gt;
			checkArg('formatfile', args.formatfile)&lt;br /&gt;
			ret[#ret + 1] = args.format .. '=' .. args.formatfile&lt;br /&gt;
		else&lt;br /&gt;
			ret[#ret + 1] = args.format&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Border&lt;br /&gt;
	if yesno(args.border) then&lt;br /&gt;
		ret[#ret + 1] = 'border'&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	addPositional('location')&lt;br /&gt;
	addPositional('alignment')&lt;br /&gt;
	addPositional('size')&lt;br /&gt;
	addNamed('upright')&lt;br /&gt;
	addNamed('link')&lt;br /&gt;
	addNamed('alt')&lt;br /&gt;
	addNamed('page')&lt;br /&gt;
	addNamed('class')&lt;br /&gt;
	addNamed('lang')&lt;br /&gt;
	addNamed('start')&lt;br /&gt;
	addNamed('end')&lt;br /&gt;
	addNamed('thumbtime')&lt;br /&gt;
	addPositional('caption')&lt;br /&gt;
&lt;br /&gt;
	return string.format('[[%s]]', table.concat(ret, '|'))&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame)&lt;br /&gt;
	local origArgs = require('Module:Arguments').getArgs(frame, {&lt;br /&gt;
		wrappers = 'Template:File link'&lt;br /&gt;
	})&lt;br /&gt;
	if not origArgs.file then&lt;br /&gt;
		error(&amp;quot;'file' parameter missing from [[Template:File link]]&amp;quot;, 0)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Copy the arguments that were passed to a new table to avoid looking up&lt;br /&gt;
	-- every possible parameter in the frame object.&lt;br /&gt;
	local args = {}&lt;br /&gt;
	for k, v in pairs(origArgs) do&lt;br /&gt;
		-- Make _BLANK a special argument to add a blank parameter. For use in&lt;br /&gt;
		-- conditional templates etc. it is useful for blank arguments to be&lt;br /&gt;
		-- ignored, but we still need a way to specify them so that we can do&lt;br /&gt;
		-- things like [[File:Example.png|link=]].&lt;br /&gt;
		if v == '_BLANK' then&lt;br /&gt;
			v = ''&lt;br /&gt;
		end&lt;br /&gt;
		args[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	return p._main(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:No_globals&amp;diff=213</id>
		<title>Module:No globals</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:No_globals&amp;diff=213"/>
		<updated>2022-03-29T15:33:20Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;local mt = getmetatable(_G) or {} function mt.__index (t, k) 	if k ~= 'arg' then 		error('Tried to read nil global ' .. tostring(k), 2) 	end 	return nil end function mt.__newi...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;local mt = getmetatable(_G) or {}&lt;br /&gt;
function mt.__index (t, k)&lt;br /&gt;
	if k ~= 'arg' then&lt;br /&gt;
		error('Tried to read nil global ' .. tostring(k), 2)&lt;br /&gt;
	end&lt;br /&gt;
	return nil&lt;br /&gt;
end&lt;br /&gt;
function mt.__newindex(t, k, v)&lt;br /&gt;
	if k ~= 'arg' then&lt;br /&gt;
		error('Tried to write global ' .. tostring(k), 2)&lt;br /&gt;
	end&lt;br /&gt;
	rawset(t, k, v)&lt;br /&gt;
end&lt;br /&gt;
setmetatable(_G, mt)&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=Module:Protection_banner&amp;diff=212</id>
		<title>Module:Protection banner</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=Module:Protection_banner&amp;diff=212"/>
		<updated>2022-03-29T15:30:52Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Created page with &amp;quot;-- This module implements {{pp-meta}} and its daughter templates such as -- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.  -- Initialise necessary modules. require('Module...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;-- This module implements {{pp-meta}} and its daughter templates such as&lt;br /&gt;
-- {{pp-dispute}}, {{pp-vandalism}} and {{pp-sock}}.&lt;br /&gt;
&lt;br /&gt;
-- Initialise necessary modules.&lt;br /&gt;
require('Module:No globals')&lt;br /&gt;
local makeFileLink = require('Module:File link')._main&lt;br /&gt;
local effectiveProtectionLevel = require('Module:Effective protection level')._main&lt;br /&gt;
local effectiveProtectionExpiry = require('Module:Effective protection expiry')._main&lt;br /&gt;
local yesno = require('Module:Yesno')&lt;br /&gt;
&lt;br /&gt;
-- Lazily initialise modules and objects we don't always need.&lt;br /&gt;
local getArgs, makeMessageBox, lang&lt;br /&gt;
&lt;br /&gt;
-- Set constants.&lt;br /&gt;
local CONFIG_MODULE = 'Module:Protection banner/config'&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Helper functions&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local function makeCategoryLink(cat, sort)&lt;br /&gt;
	if cat then&lt;br /&gt;
		return string.format(&lt;br /&gt;
			'[[%s:%s|%s]]',&lt;br /&gt;
			mw.site.namespaces[14].name,&lt;br /&gt;
			cat,&lt;br /&gt;
			sort&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Validation function for the expiry and the protection date&lt;br /&gt;
local function validateDate(dateString, dateType)&lt;br /&gt;
	if not lang then&lt;br /&gt;
		lang = mw.language.getContentLanguage()&lt;br /&gt;
	end&lt;br /&gt;
	local success, result = pcall(lang.formatDate, lang, 'U', dateString)&lt;br /&gt;
	if success then&lt;br /&gt;
		result = tonumber(result)&lt;br /&gt;
		if result then&lt;br /&gt;
			return result&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	error(string.format(&lt;br /&gt;
		'invalid %s: %s',&lt;br /&gt;
		dateType,&lt;br /&gt;
		tostring(dateString)&lt;br /&gt;
	), 4)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeFullUrl(page, query, display)&lt;br /&gt;
	return string.format(&lt;br /&gt;
		'[%s %s]',&lt;br /&gt;
		tostring(mw.uri.fullUrl(page, query)),&lt;br /&gt;
		display&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Given a directed graph formatted as node -&amp;gt; table of direct successors,&lt;br /&gt;
-- get a table of all nodes reachable from a given node (though always&lt;br /&gt;
-- including the given node).&lt;br /&gt;
local function getReachableNodes(graph, start)&lt;br /&gt;
	local toWalk, retval = {[start] = true}, {}&lt;br /&gt;
	while true do&lt;br /&gt;
		-- Can't use pairs() since we're adding and removing things as we're iterating&lt;br /&gt;
		local k = next(toWalk) -- This always gets the &amp;quot;first&amp;quot; key&lt;br /&gt;
		if k == nil then&lt;br /&gt;
			return retval&lt;br /&gt;
		end&lt;br /&gt;
		toWalk[k] = nil&lt;br /&gt;
		retval[k] = true&lt;br /&gt;
		for _,v in ipairs(graph[k]) do&lt;br /&gt;
			if not retval[v] then&lt;br /&gt;
				toWalk[v] = true&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Protection class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Protection = {}&lt;br /&gt;
Protection.__index = Protection&lt;br /&gt;
&lt;br /&gt;
Protection.supportedActions = {&lt;br /&gt;
	edit = true,&lt;br /&gt;
	move = true,&lt;br /&gt;
	autoreview = true,&lt;br /&gt;
	upload = true&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
Protection.bannerConfigFields = {&lt;br /&gt;
	'text',&lt;br /&gt;
	'explanation',&lt;br /&gt;
	'tooltip',&lt;br /&gt;
	'alt',&lt;br /&gt;
	'link',&lt;br /&gt;
	'image'&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function Protection.new(args, cfg, title)&lt;br /&gt;
	local obj = {}&lt;br /&gt;
	obj._cfg = cfg&lt;br /&gt;
	obj.title = title or mw.title.getCurrentTitle()&lt;br /&gt;
&lt;br /&gt;
	-- Set action&lt;br /&gt;
	if not args.action then&lt;br /&gt;
		obj.action = 'edit'&lt;br /&gt;
	elseif Protection.supportedActions[args.action] then&lt;br /&gt;
		obj.action = args.action&lt;br /&gt;
	else&lt;br /&gt;
		error(string.format(&lt;br /&gt;
			'invalid action: %s',&lt;br /&gt;
			tostring(args.action)&lt;br /&gt;
		), 3)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set level&lt;br /&gt;
	obj.level = args.demolevel or effectiveProtectionLevel(obj.action, obj.title)&lt;br /&gt;
	if not obj.level or (obj.action == 'move' and obj.level == 'autoconfirmed') then&lt;br /&gt;
		-- Users need to be autoconfirmed to move pages anyway, so treat&lt;br /&gt;
		-- semi-move-protected pages as unprotected.&lt;br /&gt;
		obj.level = '*'&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set expiry&lt;br /&gt;
	local effectiveExpiry = effectiveProtectionExpiry(obj.action, obj.title)&lt;br /&gt;
	if effectiveExpiry == 'infinity' then&lt;br /&gt;
		obj.expiry = 'indef'&lt;br /&gt;
	elseif effectiveExpiry ~= 'unknown' then&lt;br /&gt;
		obj.expiry = validateDate(effectiveExpiry, 'expiry date')&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set reason&lt;br /&gt;
	if args[1] then&lt;br /&gt;
		obj.reason = mw.ustring.lower(args[1])&lt;br /&gt;
		if obj.reason:find('|') then&lt;br /&gt;
			error('reasons cannot contain the pipe character (&amp;quot;|&amp;quot;)', 3)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Set protection date&lt;br /&gt;
	if args.date then&lt;br /&gt;
		obj.protectionDate = validateDate(args.date, 'protection date')&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- Set banner config&lt;br /&gt;
	do&lt;br /&gt;
		obj.bannerConfig = {}&lt;br /&gt;
		local configTables = {}&lt;br /&gt;
		if cfg.banners[obj.action] then&lt;br /&gt;
			configTables[#configTables + 1] = cfg.banners[obj.action][obj.reason]&lt;br /&gt;
		end&lt;br /&gt;
		if cfg.defaultBanners[obj.action] then&lt;br /&gt;
			configTables[#configTables + 1] = cfg.defaultBanners[obj.action][obj.level]&lt;br /&gt;
			configTables[#configTables + 1] = cfg.defaultBanners[obj.action].default&lt;br /&gt;
		end&lt;br /&gt;
		configTables[#configTables + 1] = cfg.masterBanner&lt;br /&gt;
		for i, field in ipairs(Protection.bannerConfigFields) do&lt;br /&gt;
			for j, t in ipairs(configTables) do&lt;br /&gt;
				if t[field] then&lt;br /&gt;
					obj.bannerConfig[field] = t[field]&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return setmetatable(obj, Protection)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:isUserScript()&lt;br /&gt;
	-- Whether the page is a user JavaScript or CSS page.&lt;br /&gt;
	local title = self.title&lt;br /&gt;
	return title.namespace == 2 and (&lt;br /&gt;
		title.contentModel == 'javascript' or title.contentModel == 'css'&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:isProtected()&lt;br /&gt;
	return self.level ~= '*'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:shouldShowLock()&lt;br /&gt;
	-- Whether we should output a banner/padlock&lt;br /&gt;
	return self:isProtected() and not self:isUserScript()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Whether this page needs a protection category.&lt;br /&gt;
Protection.shouldHaveProtectionCategory = Protection.shouldShowLock&lt;br /&gt;
&lt;br /&gt;
function Protection:isTemporary()&lt;br /&gt;
	return type(self.expiry) == 'number'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:makeProtectionCategory()&lt;br /&gt;
	if not self:shouldHaveProtectionCategory() then&lt;br /&gt;
		return ''&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local cfg = self._cfg&lt;br /&gt;
	local title = self.title&lt;br /&gt;
	&lt;br /&gt;
	-- Get the expiry key fragment.&lt;br /&gt;
	local expiryFragment&lt;br /&gt;
	if self.expiry == 'indef' then&lt;br /&gt;
		expiryFragment = self.expiry&lt;br /&gt;
	elseif type(self.expiry) == 'number' then&lt;br /&gt;
		expiryFragment = 'temp'&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Get the namespace key fragment.&lt;br /&gt;
	local namespaceFragment = cfg.categoryNamespaceKeys[title.namespace]&lt;br /&gt;
	if not namespaceFragment and title.namespace % 2 == 1 then&lt;br /&gt;
			namespaceFragment = 'talk'&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Define the order that key fragments are tested in. This is done with an&lt;br /&gt;
	-- array of tables containing the value to be tested, along with its&lt;br /&gt;
	-- position in the cfg.protectionCategories table.&lt;br /&gt;
	local order = {&lt;br /&gt;
		{val = expiryFragment,    keypos = 1},&lt;br /&gt;
		{val = namespaceFragment, keypos = 2},&lt;br /&gt;
		{val = self.reason,       keypos = 3},&lt;br /&gt;
		{val = self.level,        keypos = 4},&lt;br /&gt;
		{val = self.action,       keypos = 5}&lt;br /&gt;
	}&lt;br /&gt;
&lt;br /&gt;
	--[[&lt;br /&gt;
	-- The old protection templates used an ad-hoc protection category system,&lt;br /&gt;
	-- with some templates prioritising namespaces in their categories, and&lt;br /&gt;
	-- others prioritising the protection reason. To emulate this in this module&lt;br /&gt;
	-- we use the config table cfg.reasonsWithNamespacePriority to set the&lt;br /&gt;
	-- reasons for which namespaces have priority over protection reason.&lt;br /&gt;
	-- If we are dealing with one of those reasons, move the namespace table to&lt;br /&gt;
	-- the end of the order table, i.e. give it highest priority. If not, the&lt;br /&gt;
	-- reason should have highest priority, so move that to the end of the table&lt;br /&gt;
	-- instead.&lt;br /&gt;
	--]]&lt;br /&gt;
	table.insert(order, table.remove(order, self.reason and cfg.reasonsWithNamespacePriority[self.reason] and 2 or 3))&lt;br /&gt;
 &lt;br /&gt;
	--[[&lt;br /&gt;
	-- Define the attempt order. Inactive subtables (subtables with nil &amp;quot;value&amp;quot;&lt;br /&gt;
	-- fields) are moved to the end, where they will later be given the key&lt;br /&gt;
	-- &amp;quot;all&amp;quot;. This is to cut down on the number of table lookups in&lt;br /&gt;
	-- cfg.protectionCategories, which grows exponentially with the number of&lt;br /&gt;
	-- non-nil keys. We keep track of the number of active subtables with the&lt;br /&gt;
	-- noActive parameter.&lt;br /&gt;
	--]]&lt;br /&gt;
	local noActive, attemptOrder&lt;br /&gt;
	do&lt;br /&gt;
		local active, inactive = {}, {}&lt;br /&gt;
		for i, t in ipairs(order) do&lt;br /&gt;
			if t.val then&lt;br /&gt;
				active[#active + 1] = t&lt;br /&gt;
			else&lt;br /&gt;
				inactive[#inactive + 1] = t&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		noActive = #active&lt;br /&gt;
		attemptOrder = active&lt;br /&gt;
		for i, t in ipairs(inactive) do&lt;br /&gt;
			attemptOrder[#attemptOrder + 1] = t&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
 &lt;br /&gt;
	--[[&lt;br /&gt;
	-- Check increasingly generic key combinations until we find a match. If a&lt;br /&gt;
	-- specific category exists for the combination of key fragments we are&lt;br /&gt;
	-- given, that match will be found first. If not, we keep trying different&lt;br /&gt;
	-- key fragment combinations until we match using the key&lt;br /&gt;
	-- &amp;quot;all-all-all-all-all&amp;quot;.&lt;br /&gt;
	--&lt;br /&gt;
	-- To generate the keys, we index the key subtables using a binary matrix&lt;br /&gt;
	-- with indexes i and j. j is only calculated up to the number of active&lt;br /&gt;
	-- subtables. For example, if there were three active subtables, the matrix&lt;br /&gt;
	-- would look like this, with 0 corresponding to the key fragment &amp;quot;all&amp;quot;, and&lt;br /&gt;
	-- 1 corresponding to other key fragments.&lt;br /&gt;
	-- &lt;br /&gt;
	--   j 1  2  3&lt;br /&gt;
	-- i  &lt;br /&gt;
	-- 1   1  1  1&lt;br /&gt;
	-- 2   0  1  1&lt;br /&gt;
	-- 3   1  0  1&lt;br /&gt;
	-- 4   0  0  1&lt;br /&gt;
	-- 5   1  1  0&lt;br /&gt;
	-- 6   0  1  0&lt;br /&gt;
	-- 7   1  0  0&lt;br /&gt;
	-- 8   0  0  0&lt;br /&gt;
	-- &lt;br /&gt;
	-- Values of j higher than the number of active subtables are set&lt;br /&gt;
	-- to the string &amp;quot;all&amp;quot;.&lt;br /&gt;
	--&lt;br /&gt;
	-- A key for cfg.protectionCategories is constructed for each value of i.&lt;br /&gt;
	-- The position of the value in the key is determined by the keypos field in&lt;br /&gt;
	-- each subtable.&lt;br /&gt;
	--]]&lt;br /&gt;
	local cats = cfg.protectionCategories&lt;br /&gt;
	for i = 1, 2^noActive do&lt;br /&gt;
		local key = {}&lt;br /&gt;
		for j, t in ipairs(attemptOrder) do&lt;br /&gt;
			if j &amp;gt; noActive then&lt;br /&gt;
				key[t.keypos] = 'all'&lt;br /&gt;
			else&lt;br /&gt;
				local quotient = i / 2 ^ (j - 1)&lt;br /&gt;
				quotient = math.ceil(quotient)&lt;br /&gt;
				if quotient % 2 == 1 then&lt;br /&gt;
					key[t.keypos] = t.val&lt;br /&gt;
				else&lt;br /&gt;
					key[t.keypos] = 'all'&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		key = table.concat(key, '|')&lt;br /&gt;
		local attempt = cats[key]&lt;br /&gt;
		if attempt then&lt;br /&gt;
			return makeCategoryLink(attempt, title.text)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return ''&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:isIncorrect()&lt;br /&gt;
	local expiry = self.expiry&lt;br /&gt;
	return not self:shouldHaveProtectionCategory()&lt;br /&gt;
		or type(expiry) == 'number' and expiry &amp;lt; os.time()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:isTemplateProtectedNonTemplate()&lt;br /&gt;
	local action, namespace = self.action, self.title.namespace&lt;br /&gt;
	return self.level == 'templateeditor'&lt;br /&gt;
		and (&lt;br /&gt;
			(action ~= 'edit' and action ~= 'move')&lt;br /&gt;
			or (namespace ~= 10 and namespace ~= 828)&lt;br /&gt;
		)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Protection:makeCategoryLinks()&lt;br /&gt;
	local msg = self._cfg.msg&lt;br /&gt;
	local ret = {self:makeProtectionCategory()}&lt;br /&gt;
	if self:isIncorrect() then&lt;br /&gt;
		ret[#ret + 1] = makeCategoryLink(&lt;br /&gt;
			msg['tracking-category-incorrect'],&lt;br /&gt;
			self.title.text&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
	if self:isTemplateProtectedNonTemplate() then&lt;br /&gt;
		ret[#ret + 1] = makeCategoryLink(&lt;br /&gt;
			msg['tracking-category-template'],&lt;br /&gt;
			self.title.text&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
	return table.concat(ret)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Blurb class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Blurb = {}&lt;br /&gt;
Blurb.__index = Blurb&lt;br /&gt;
&lt;br /&gt;
Blurb.bannerTextFields = {&lt;br /&gt;
	text = true,&lt;br /&gt;
	explanation = true,&lt;br /&gt;
	tooltip = true,&lt;br /&gt;
	alt = true,&lt;br /&gt;
	link = true&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
function Blurb.new(protectionObj, args, cfg)&lt;br /&gt;
	return setmetatable({&lt;br /&gt;
		_cfg = cfg,&lt;br /&gt;
		_protectionObj = protectionObj,&lt;br /&gt;
		_args = args&lt;br /&gt;
	}, Blurb)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Private methods --&lt;br /&gt;
&lt;br /&gt;
function Blurb:_formatDate(num)&lt;br /&gt;
	-- Formats a Unix timestamp into dd Month, YYYY format.&lt;br /&gt;
	lang = lang or mw.language.getContentLanguage()&lt;br /&gt;
	local success, date = pcall(&lt;br /&gt;
		lang.formatDate,&lt;br /&gt;
		lang,&lt;br /&gt;
		self._cfg.msg['expiry-date-format'] or 'j F Y',&lt;br /&gt;
		'@' .. tostring(num)&lt;br /&gt;
	)&lt;br /&gt;
	if success then&lt;br /&gt;
		return date&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_getExpandedMessage(msgKey)&lt;br /&gt;
	return self:_substituteParameters(self._cfg.msg[msgKey])&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_substituteParameters(msg)&lt;br /&gt;
	if not self._params then&lt;br /&gt;
		local parameterFuncs = {}&lt;br /&gt;
&lt;br /&gt;
		parameterFuncs.CURRENTVERSION     = self._makeCurrentVersionParameter&lt;br /&gt;
		parameterFuncs.EDITREQUEST        = self._makeEditRequestParameter&lt;br /&gt;
		parameterFuncs.EXPIRY             = self._makeExpiryParameter&lt;br /&gt;
		parameterFuncs.EXPLANATIONBLURB   = self._makeExplanationBlurbParameter&lt;br /&gt;
		parameterFuncs.IMAGELINK          = self._makeImageLinkParameter&lt;br /&gt;
		parameterFuncs.INTROBLURB         = self._makeIntroBlurbParameter&lt;br /&gt;
		parameterFuncs.INTROFRAGMENT      = self._makeIntroFragmentParameter&lt;br /&gt;
		parameterFuncs.PAGETYPE           = self._makePagetypeParameter&lt;br /&gt;
		parameterFuncs.PROTECTIONBLURB    = self._makeProtectionBlurbParameter&lt;br /&gt;
		parameterFuncs.PROTECTIONDATE     = self._makeProtectionDateParameter&lt;br /&gt;
		parameterFuncs.PROTECTIONLEVEL    = self._makeProtectionLevelParameter&lt;br /&gt;
		parameterFuncs.PROTECTIONLOG      = self._makeProtectionLogParameter&lt;br /&gt;
		parameterFuncs.TALKPAGE           = self._makeTalkPageParameter&lt;br /&gt;
		parameterFuncs.TOOLTIPBLURB       = self._makeTooltipBlurbParameter&lt;br /&gt;
		parameterFuncs.TOOLTIPFRAGMENT    = self._makeTooltipFragmentParameter&lt;br /&gt;
		parameterFuncs.VANDAL             = self._makeVandalTemplateParameter&lt;br /&gt;
		&lt;br /&gt;
		self._params = setmetatable({}, {&lt;br /&gt;
			__index = function (t, k)&lt;br /&gt;
				local param&lt;br /&gt;
				if parameterFuncs[k] then&lt;br /&gt;
					param = parameterFuncs[k](self)&lt;br /&gt;
				end&lt;br /&gt;
				param = param or ''&lt;br /&gt;
				t[k] = param&lt;br /&gt;
				return param&lt;br /&gt;
			end&lt;br /&gt;
		})&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	msg = msg:gsub('${(%u+)}', self._params)&lt;br /&gt;
	return msg&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeCurrentVersionParameter()&lt;br /&gt;
	-- A link to the page history or the move log, depending on the kind of&lt;br /&gt;
	-- protection.&lt;br /&gt;
	local pagename = self._protectionObj.title.prefixedText&lt;br /&gt;
	if self._protectionObj.action == 'move' then&lt;br /&gt;
		-- We need the move log link.&lt;br /&gt;
		return makeFullUrl(&lt;br /&gt;
			'Special:Log',&lt;br /&gt;
			{type = 'move', page = pagename},&lt;br /&gt;
			self:_getExpandedMessage('current-version-move-display')&lt;br /&gt;
		)&lt;br /&gt;
	else&lt;br /&gt;
		-- We need the history link.&lt;br /&gt;
		return makeFullUrl(&lt;br /&gt;
			pagename,&lt;br /&gt;
			{action = 'history'},&lt;br /&gt;
			self:_getExpandedMessage('current-version-edit-display')&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeEditRequestParameter()&lt;br /&gt;
	local mEditRequest = require('Module:Submit an edit request')&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	&lt;br /&gt;
	-- Get the edit request type.&lt;br /&gt;
	local requestType&lt;br /&gt;
	if action == 'edit' then&lt;br /&gt;
		if level == 'autoconfirmed' then&lt;br /&gt;
			requestType = 'semi'&lt;br /&gt;
		elseif level == 'extendedconfirmed' then&lt;br /&gt;
			requestType = 'extended'&lt;br /&gt;
		elseif level == 'templateeditor' then&lt;br /&gt;
			requestType = 'template'&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	requestType = requestType or 'full'&lt;br /&gt;
	&lt;br /&gt;
	-- Get the display value.&lt;br /&gt;
	local display = self:_getExpandedMessage('edit-request-display')&lt;br /&gt;
&lt;br /&gt;
	return mEditRequest._link{type = requestType, display = display}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeExpiryParameter()&lt;br /&gt;
	local expiry = self._protectionObj.expiry&lt;br /&gt;
	if type(expiry) == 'number' then&lt;br /&gt;
		return self:_formatDate(expiry)&lt;br /&gt;
	else&lt;br /&gt;
		return expiry&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeExplanationBlurbParameter()&lt;br /&gt;
	-- Cover special cases first.&lt;br /&gt;
	if self._protectionObj.title.namespace == 8 then&lt;br /&gt;
		-- MediaWiki namespace&lt;br /&gt;
		return self:_getExpandedMessage('explanation-blurb-nounprotect')&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Get explanation blurb table keys&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	local talkKey = self._protectionObj.title.isTalkPage and 'talk' or 'subject'&lt;br /&gt;
&lt;br /&gt;
	-- Find the message in the explanation blurb table and substitute any&lt;br /&gt;
	-- parameters.&lt;br /&gt;
	local explanations = self._cfg.explanationBlurbs&lt;br /&gt;
	local msg&lt;br /&gt;
	if explanations[action][level] and explanations[action][level][talkKey] then&lt;br /&gt;
		msg = explanations[action][level][talkKey]&lt;br /&gt;
	elseif explanations[action][level] and explanations[action][level].default then&lt;br /&gt;
		msg = explanations[action][level].default&lt;br /&gt;
	elseif explanations[action].default and explanations[action].default[talkKey] then&lt;br /&gt;
		msg = explanations[action].default[talkKey]&lt;br /&gt;
	elseif explanations[action].default and explanations[action].default.default then&lt;br /&gt;
		msg = explanations[action].default.default&lt;br /&gt;
	else&lt;br /&gt;
		error(string.format(&lt;br /&gt;
			'could not find explanation blurb for action &amp;quot;%s&amp;quot;, level &amp;quot;%s&amp;quot; and talk key &amp;quot;%s&amp;quot;',&lt;br /&gt;
			action,&lt;br /&gt;
			level,&lt;br /&gt;
			talkKey&lt;br /&gt;
		), 8)&lt;br /&gt;
	end&lt;br /&gt;
	return self:_substituteParameters(msg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeImageLinkParameter()&lt;br /&gt;
	local imageLinks = self._cfg.imageLinks&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	local msg&lt;br /&gt;
	if imageLinks[action][level] then&lt;br /&gt;
		msg = imageLinks[action][level]&lt;br /&gt;
	elseif imageLinks[action].default then&lt;br /&gt;
		msg = imageLinks[action].default&lt;br /&gt;
	else&lt;br /&gt;
		msg = imageLinks.edit.default&lt;br /&gt;
	end&lt;br /&gt;
	return self:_substituteParameters(msg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeIntroBlurbParameter()&lt;br /&gt;
	if self._protectionObj:isTemporary() then&lt;br /&gt;
		return self:_getExpandedMessage('intro-blurb-expiry')&lt;br /&gt;
	else&lt;br /&gt;
		return self:_getExpandedMessage('intro-blurb-noexpiry')&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeIntroFragmentParameter()&lt;br /&gt;
	if self._protectionObj:isTemporary() then&lt;br /&gt;
		return self:_getExpandedMessage('intro-fragment-expiry')&lt;br /&gt;
	else&lt;br /&gt;
		return self:_getExpandedMessage('intro-fragment-noexpiry')&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makePagetypeParameter()&lt;br /&gt;
	local pagetypes = self._cfg.pagetypes&lt;br /&gt;
	return pagetypes[self._protectionObj.title.namespace]&lt;br /&gt;
		or pagetypes.default&lt;br /&gt;
		or error('no default pagetype defined', 8)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeProtectionBlurbParameter()&lt;br /&gt;
	local protectionBlurbs = self._cfg.protectionBlurbs&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	local msg&lt;br /&gt;
	if protectionBlurbs[action][level] then&lt;br /&gt;
		msg = protectionBlurbs[action][level]&lt;br /&gt;
	elseif protectionBlurbs[action].default then&lt;br /&gt;
		msg = protectionBlurbs[action].default&lt;br /&gt;
	elseif protectionBlurbs.edit.default then&lt;br /&gt;
		msg = protectionBlurbs.edit.default&lt;br /&gt;
	else&lt;br /&gt;
		error('no protection blurb defined for protectionBlurbs.edit.default', 8)&lt;br /&gt;
	end&lt;br /&gt;
	return self:_substituteParameters(msg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeProtectionDateParameter()&lt;br /&gt;
	local protectionDate = self._protectionObj.protectionDate&lt;br /&gt;
	if type(protectionDate) == 'number' then&lt;br /&gt;
		return self:_formatDate(protectionDate)&lt;br /&gt;
	else&lt;br /&gt;
		return protectionDate&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeProtectionLevelParameter()&lt;br /&gt;
	local protectionLevels = self._cfg.protectionLevels&lt;br /&gt;
	local action = self._protectionObj.action&lt;br /&gt;
	local level = self._protectionObj.level&lt;br /&gt;
	local msg&lt;br /&gt;
	if protectionLevels[action][level] then&lt;br /&gt;
		msg = protectionLevels[action][level]&lt;br /&gt;
	elseif protectionLevels[action].default then&lt;br /&gt;
		msg = protectionLevels[action].default&lt;br /&gt;
	elseif protectionLevels.edit.default then&lt;br /&gt;
		msg = protectionLevels.edit.default&lt;br /&gt;
	else&lt;br /&gt;
		error('no protection level defined for protectionLevels.edit.default', 8)&lt;br /&gt;
	end&lt;br /&gt;
	return self:_substituteParameters(msg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeProtectionLogParameter()&lt;br /&gt;
	local pagename = self._protectionObj.title.prefixedText&lt;br /&gt;
	if self._protectionObj.action == 'autoreview' then&lt;br /&gt;
		-- We need the pending changes log.&lt;br /&gt;
		return makeFullUrl(&lt;br /&gt;
			'Special:Log',&lt;br /&gt;
			{type = 'stable', page = pagename},&lt;br /&gt;
			self:_getExpandedMessage('pc-log-display')&lt;br /&gt;
		)&lt;br /&gt;
	else&lt;br /&gt;
		-- We need the protection log.&lt;br /&gt;
		return makeFullUrl(&lt;br /&gt;
			'Special:Log',&lt;br /&gt;
			{type = 'protect', page = pagename},&lt;br /&gt;
			self:_getExpandedMessage('protection-log-display')&lt;br /&gt;
		)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeTalkPageParameter()&lt;br /&gt;
	return string.format(&lt;br /&gt;
		'[[%s:%s#%s|%s]]',&lt;br /&gt;
		mw.site.namespaces[self._protectionObj.title.namespace].talk.name,&lt;br /&gt;
		self._protectionObj.title.text,&lt;br /&gt;
		self._args.section or 'top',&lt;br /&gt;
		self:_getExpandedMessage('talk-page-link-display')&lt;br /&gt;
	)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeTooltipBlurbParameter()&lt;br /&gt;
	if self._protectionObj:isTemporary() then&lt;br /&gt;
		return self:_getExpandedMessage('tooltip-blurb-expiry')&lt;br /&gt;
	else&lt;br /&gt;
		return self:_getExpandedMessage('tooltip-blurb-noexpiry')&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeTooltipFragmentParameter()&lt;br /&gt;
	if self._protectionObj:isTemporary() then&lt;br /&gt;
		return self:_getExpandedMessage('tooltip-fragment-expiry')&lt;br /&gt;
	else&lt;br /&gt;
		return self:_getExpandedMessage('tooltip-fragment-noexpiry')&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Blurb:_makeVandalTemplateParameter()&lt;br /&gt;
	return mw.getCurrentFrame():expandTemplate{&lt;br /&gt;
		title=&amp;quot;vandal-m&amp;quot;,&lt;br /&gt;
		args={self._args.user or self._protectionObj.title.baseText}&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- Public methods --&lt;br /&gt;
&lt;br /&gt;
function Blurb:makeBannerText(key)&lt;br /&gt;
	-- Validate input.&lt;br /&gt;
	if not key or not Blurb.bannerTextFields[key] then&lt;br /&gt;
		error(string.format(&lt;br /&gt;
			'&amp;quot;%s&amp;quot; is not a valid banner config field',&lt;br /&gt;
			tostring(key)&lt;br /&gt;
		), 2)&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Generate the text.&lt;br /&gt;
	local msg = self._protectionObj.bannerConfig[key]&lt;br /&gt;
	if type(msg) == 'string' then&lt;br /&gt;
		return self:_substituteParameters(msg)&lt;br /&gt;
	elseif type(msg) == 'function' then&lt;br /&gt;
		msg = msg(self._protectionObj, self._args)&lt;br /&gt;
		if type(msg) ~= 'string' then&lt;br /&gt;
			error(string.format(&lt;br /&gt;
				'bad output from banner config function with key &amp;quot;%s&amp;quot;'&lt;br /&gt;
					.. ' (expected string, got %s)',&lt;br /&gt;
				tostring(key),&lt;br /&gt;
				type(msg)&lt;br /&gt;
			), 4)&lt;br /&gt;
		end&lt;br /&gt;
		return self:_substituteParameters(msg)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- BannerTemplate class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local BannerTemplate = {}&lt;br /&gt;
BannerTemplate.__index = BannerTemplate&lt;br /&gt;
&lt;br /&gt;
function BannerTemplate.new(protectionObj, cfg)&lt;br /&gt;
	local obj = {}&lt;br /&gt;
	obj._cfg = cfg&lt;br /&gt;
&lt;br /&gt;
	-- Set the image filename.&lt;br /&gt;
	local imageFilename = protectionObj.bannerConfig.image&lt;br /&gt;
	if imageFilename then&lt;br /&gt;
		obj._imageFilename = imageFilename&lt;br /&gt;
	else&lt;br /&gt;
		-- If an image filename isn't specified explicitly in the banner config,&lt;br /&gt;
		-- generate it from the protection status and the namespace.&lt;br /&gt;
		local action = protectionObj.action&lt;br /&gt;
		local level = protectionObj.level&lt;br /&gt;
		local namespace = protectionObj.title.namespace&lt;br /&gt;
		local reason = protectionObj.reason&lt;br /&gt;
		&lt;br /&gt;
		-- Deal with special cases first.&lt;br /&gt;
		if (&lt;br /&gt;
			namespace == 10&lt;br /&gt;
			or namespace == 828&lt;br /&gt;
			or reason and obj._cfg.indefImageReasons[reason]&lt;br /&gt;
			)&lt;br /&gt;
			and action == 'edit'&lt;br /&gt;
			and level == 'sysop'&lt;br /&gt;
			and not protectionObj:isTemporary()&lt;br /&gt;
		then&lt;br /&gt;
			-- Fully protected modules and templates get the special red &amp;quot;indef&amp;quot;&lt;br /&gt;
			-- padlock.&lt;br /&gt;
			obj._imageFilename = obj._cfg.msg['image-filename-indef']&lt;br /&gt;
		else&lt;br /&gt;
			-- Deal with regular protection types.&lt;br /&gt;
			local images = obj._cfg.images&lt;br /&gt;
			if images[action] then&lt;br /&gt;
				if images[action][level] then&lt;br /&gt;
					obj._imageFilename = images[action][level]&lt;br /&gt;
				elseif images[action].default then&lt;br /&gt;
					obj._imageFilename = images[action].default&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return setmetatable(obj, BannerTemplate)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function BannerTemplate:renderImage()&lt;br /&gt;
	local filename = self._imageFilename&lt;br /&gt;
		or self._cfg.msg['image-filename-default']&lt;br /&gt;
		or 'Transparent.gif'&lt;br /&gt;
	return makeFileLink{&lt;br /&gt;
		file = filename,&lt;br /&gt;
		size = (self.imageWidth or 20) .. 'px',&lt;br /&gt;
		alt = self._imageAlt,&lt;br /&gt;
		link = self._imageLink,&lt;br /&gt;
		caption = self.imageCaption&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Banner class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Banner = setmetatable({}, BannerTemplate)&lt;br /&gt;
Banner.__index = Banner&lt;br /&gt;
&lt;br /&gt;
function Banner.new(protectionObj, blurbObj, cfg)&lt;br /&gt;
	local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.&lt;br /&gt;
	obj.imageWidth = 40&lt;br /&gt;
	obj.imageCaption = blurbObj:makeBannerText('alt') -- Large banners use the alt text for the tooltip.&lt;br /&gt;
	obj._reasonText = blurbObj:makeBannerText('text')&lt;br /&gt;
	obj._explanationText = blurbObj:makeBannerText('explanation')&lt;br /&gt;
	obj._page = protectionObj.title.prefixedText -- Only makes a difference in testing.&lt;br /&gt;
	return setmetatable(obj, Banner)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Banner:__tostring()&lt;br /&gt;
	-- Renders the banner.&lt;br /&gt;
	makeMessageBox = makeMessageBox or require('Module:Message box').main&lt;br /&gt;
	local reasonText = self._reasonText or error('no reason text set', 2)&lt;br /&gt;
	local explanationText = self._explanationText&lt;br /&gt;
	local mbargs = {&lt;br /&gt;
		page = self._page,&lt;br /&gt;
		type = 'protection',&lt;br /&gt;
		image = self:renderImage(),&lt;br /&gt;
		text = string.format(&lt;br /&gt;
			&amp;quot;'''%s'''%s&amp;quot;,&lt;br /&gt;
			reasonText,&lt;br /&gt;
			explanationText and '&amp;lt;br /&amp;gt;' .. explanationText or ''&lt;br /&gt;
		)&lt;br /&gt;
	}&lt;br /&gt;
	return makeMessageBox('mbox', mbargs)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Padlock class&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local Padlock = setmetatable({}, BannerTemplate)&lt;br /&gt;
Padlock.__index = Padlock&lt;br /&gt;
&lt;br /&gt;
function Padlock.new(protectionObj, blurbObj, cfg)&lt;br /&gt;
	local obj = BannerTemplate.new(protectionObj, cfg) -- This doesn't need the blurb.&lt;br /&gt;
	obj.imageWidth = 20&lt;br /&gt;
	obj.imageCaption = blurbObj:makeBannerText('tooltip')&lt;br /&gt;
	obj._imageAlt = blurbObj:makeBannerText('alt')&lt;br /&gt;
	obj._imageLink = blurbObj:makeBannerText('link')&lt;br /&gt;
	obj._indicatorName = cfg.padlockIndicatorNames[protectionObj.action]&lt;br /&gt;
		or cfg.padlockIndicatorNames.default&lt;br /&gt;
		or 'pp-default'&lt;br /&gt;
	return setmetatable(obj, Padlock)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Padlock:__tostring()&lt;br /&gt;
	local frame = mw.getCurrentFrame()&lt;br /&gt;
	-- The nowiki tag helps prevent whitespace at the top of articles.&lt;br /&gt;
	return frame:extensionTag{name = 'nowiki'} .. frame:extensionTag{&lt;br /&gt;
		name = 'indicator',&lt;br /&gt;
		args = {name = self._indicatorName},&lt;br /&gt;
		content = self:renderImage()&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
-- Exports&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
function p._exportClasses()&lt;br /&gt;
	-- This is used for testing purposes.&lt;br /&gt;
	return {&lt;br /&gt;
		Protection = Protection,&lt;br /&gt;
		Blurb = Blurb,&lt;br /&gt;
		BannerTemplate = BannerTemplate,&lt;br /&gt;
		Banner = Banner,&lt;br /&gt;
		Padlock = Padlock,&lt;br /&gt;
	}&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p._main(args, cfg, title)&lt;br /&gt;
	args = args or {}&lt;br /&gt;
	cfg = cfg or require(CONFIG_MODULE)&lt;br /&gt;
&lt;br /&gt;
	local protectionObj = Protection.new(args, cfg, title)&lt;br /&gt;
&lt;br /&gt;
	local ret = {}&lt;br /&gt;
&lt;br /&gt;
	-- If a page's edit protection is equally or more restrictive than its&lt;br /&gt;
	-- protection from some other action, then don't bother displaying anything&lt;br /&gt;
	-- for the other action (except categories).&lt;br /&gt;
	if not yesno(args.catonly) and (protectionObj.action == 'edit' or&lt;br /&gt;
		args.demolevel or&lt;br /&gt;
		not getReachableNodes(&lt;br /&gt;
			cfg.hierarchy,&lt;br /&gt;
			protectionObj.level&lt;br /&gt;
		)[effectiveProtectionLevel('edit', protectionObj.title)])&lt;br /&gt;
	then&lt;br /&gt;
		-- Initialise the blurb object&lt;br /&gt;
		local blurbObj = Blurb.new(protectionObj, args, cfg)&lt;br /&gt;
	&lt;br /&gt;
		-- Render the banner&lt;br /&gt;
		if protectionObj:shouldShowLock() then&lt;br /&gt;
			ret[#ret + 1] = tostring(&lt;br /&gt;
				(yesno(args.small) and Padlock or Banner)&lt;br /&gt;
				.new(protectionObj, blurbObj, cfg)&lt;br /&gt;
			)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	-- Render the categories&lt;br /&gt;
	if yesno(args.category) ~= false then&lt;br /&gt;
		ret[#ret + 1] = protectionObj:makeCategoryLinks()&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return table.concat(ret)	&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.main(frame, cfg)&lt;br /&gt;
	cfg = cfg or require(CONFIG_MODULE)&lt;br /&gt;
&lt;br /&gt;
	-- Find default args, if any.&lt;br /&gt;
	local parent = frame.getParent and frame:getParent()&lt;br /&gt;
	local defaultArgs = parent and cfg.wrappers[parent:getTitle():gsub('/sandbox$', '')]&lt;br /&gt;
&lt;br /&gt;
	-- Find user args, and use the parent frame if we are being called from a&lt;br /&gt;
	-- wrapper template.&lt;br /&gt;
	getArgs = getArgs or require('Module:Arguments').getArgs&lt;br /&gt;
	local userArgs = getArgs(frame, {&lt;br /&gt;
		parentOnly = defaultArgs,&lt;br /&gt;
		frameOnly = not defaultArgs&lt;br /&gt;
	})&lt;br /&gt;
&lt;br /&gt;
	-- Build the args table. User-specified args overwrite default args.&lt;br /&gt;
	local args = {}&lt;br /&gt;
	for k, v in pairs(defaultArgs or {}) do&lt;br /&gt;
		args[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	for k, v in pairs(userArgs) do&lt;br /&gt;
		args[k] = v&lt;br /&gt;
	end&lt;br /&gt;
	return p._main(args, cfg)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=File:Check-saira-green.svg&amp;diff=209</id>
		<title>File:Check-saira-green.svg</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=File:Check-saira-green.svg&amp;diff=209"/>
		<updated>2022-03-29T14:49:14Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Zoran reverted File:Check-saira-green.svg to an old version&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
	<entry>
		<id>https://wiki.sanctions.net/index.php?title=File:Check-saira-green.svg&amp;diff=208</id>
		<title>File:Check-saira-green.svg</title>
		<link rel="alternate" type="text/html" href="https://wiki.sanctions.net/index.php?title=File:Check-saira-green.svg&amp;diff=208"/>
		<updated>2022-03-29T14:45:08Z</updated>

		<summary type="html">&lt;p&gt;Zoran: Zoran uploaded a new version of File:Check-saira-green.svg&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Zoran</name></author>
	</entry>
</feed>