وحدة:Citation/CS1/sandbox

--require("strict")
local languagescode = require("وحدة:Citation/CS1/lang")
local fetchName = require("وحدة:لغات").getname
local check_date_n = require("وحدة:Citation/CS1/dates").check_date

local validation

local utilities
local z = {}

local identifiers
local metadata
local cfg = {}
local whitelist = {}

local added_deprecated_cat
local added_vanc_errs
local added_generic_name_errs
local Frame
local is_preview_mode
local is_sandbox

local function first_set(list, count)
	local i = 1
	while i <= count do
		if utilities.is_set(list[i]) then
			return list[i]
		end
		i = i + 1
	end
end

local function add_vanc_error(source, position)
	if added_vanc_errs then
		return
	end

	added_vanc_errs = true
	utilities.set_message("err_vancouver", {source, position})
end

local function is_scheme(scheme)
	return scheme and scheme:match("^%a[%a%d%+%.%-]*:")
end

local function is_domain_name(domain)
	if not domain then
		return false
	end

	domain = domain:gsub("^//", "")

	if not domain:match("^[%w]") then
		return false
	end

	if domain:match("^%a+:") then
		return false
	end

	local patterns = {
		"%f[%w][%w][%w%-]+[%w]%.%a%a+$",
		"%f[%w][%w][%w%-]+[%w]%.xn%-%-[%w]+$",
		"%f[%a][qxz]%.com$",
		"%f[%a][iq]%.net$",
		"%f[%w][%w]%.%a%a$",
		"%f[%w][%w][%w]%.%a%a+$",
		"^%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?"
	}

	for _, pattern in ipairs(patterns) do
		if domain:match(pattern) then
			return true
		end
	end

	for _, d in ipairs({"cash", "company", "today", "org"}) do
		if domain:match("%f[%w][%w]%." .. d) then
			return true
		end
	end
	return false
end

local function is_url(scheme, domain)
	if utilities.is_set(scheme) then
		return is_scheme(scheme) and is_domain_name(domain)
	else
		return is_domain_name(domain)
	end
end

local function split_url(url_str)
	local scheme, authority, domain

	url_str = url_str:gsub("([%a%d])%.?[/%?#].*$", "%1")

	if url_str:match("^//%S*") then
		domain = url_str:match("^//(%S*)")
	elseif url_str:match("%S-:/*%S+") then
		scheme, authority, domain = url_str:match("(%S-:)(/*)(%S+)")
		if utilities.is_set(authority) then
			authority = authority:gsub("//", "", 1)
			if utilities.is_set(authority) then
				return scheme
			end
		else
			if not scheme:match("^news:") then
				return scheme
			end
		end
		domain = domain:gsub("(%a):%d+", "%1")
	end

	return scheme, domain
end

local function link_param_ok(value)
	local scheme, domain
	if value:find("[<>%[%]|{}]") then
		return false
	end

	scheme, domain = split_url(value)
	return not is_url(scheme, domain)
end

local function link_title_ok(link, lorig, title, torig)
	local orig
	if utilities.is_set(link) then
		if not link_param_ok(link) then
			orig = lorig
		elseif title:find("%[%[") then
			orig = torig
		elseif link:match("^%a+:") then
			local prefix = link:match("^(%a+):"):lower()

			if cfg.inter_wiki_map[prefix] then
				orig = lorig
			end
		end
	end

	if utilities.is_set(orig) then
		link = ""
		utilities.set_message("err_bad_paramlink", orig)
	end

	return link
end

local function check_url(url_str)
	if nil == url_str:match("^%S+$") then
		return false
	end
	local scheme, domain

	scheme, domain = split_url(url_str)

	if "news:" == scheme then
		return domain:match("^[%a%d%+%-_]+%.[%a%d%+%-_%.]*[%a%d%+%-_]$")
	end

	return is_url(scheme, domain)
end

local function is_parameter_ext_wikilink(value)
	local scheme, domain

	if value:match("%f[%[]%[%a%S*:%S+.*%]") then
		scheme, domain = split_url(value:match("%f[%[]%[(%a%S*:%S+).*%]"))
	elseif value:match("%f[%[]%[//%S+.*%]") then
		scheme, domain = split_url(value:match("%f[%[]%[(//%S+).*%]"))
	elseif value:match("%a%S*:%S+") then
		scheme, domain = split_url(value:match("(%a%S*:%S+)"))
	elseif value:match("//%S+") then
		scheme, domain = split_url(value:match("(//%S+)"))
	else
		return false
	end

	return is_url(scheme, domain)
end

local function check_for_url(parameter_list, error_list)
	for k, v in pairs(parameter_list) do
		if is_parameter_ext_wikilink(v) then
			table.insert(error_list, utilities.wrap_style("parameter", k))
		end
	end
end

local function safe_for_url(str)
	if str:match("%[%[.-%]%]") ~= nil then
		utilities.set_message("err_wikilink_in_url", {})
	end

	return str:gsub(
		"[%[%]\n]",
		{
			["["] = "&#91;",
			["]"] = "&#93;",
			["\n"] = " "
		}
	)
end

local function external_link(URL, label, source, access)
	local err_msg = ""
	local domain
	local path
	local base_url

	if not utilities.is_set(label) then
		label = URL
		if utilities.is_set(source) then
			utilities.set_message("err_bare_url_missing_title", {utilities.wrap_style("parameter", source)})
		else
			error(cfg.messages["bare_url_no_origin"])
		end
	end
	if not check_url(URL) then
		utilities.set_message("err_bad_url", {utilities.wrap_style("parameter", source)})
	end

	domain, path = URL:match("^([/%.%-%+:%a%d]+)([/%?#].*)$")
	if path then
		path = path:gsub("[%[%]]", {["["] = "%5b", ["]"] = "%5d"})
		URL = table.concat({domain, path})
	end

	base_url = table.concat({"[", URL, " ", safe_for_url(label), "]"})

	if utilities.is_set(access) then
		base_url =
			utilities.substitute(
			cfg.presentation["ext-link-access-signal"],
			{cfg.presentation[access].class, cfg.presentation[access].title, base_url}
		)
	end

	return base_url
end

local function deprecated_parameter(name)
	if not added_deprecated_cat then
		added_deprecated_cat = true
		utilities.set_message("err_deprecated_params", {name})
	end
end

local function kern_quotes(str)
	local cap = ""
	local wl_type, label, link

	wl_type, label, link = utilities.is_wikilink(str)

	if 1 == wl_type then
		if mw.ustring.match(str, '%[%[["“”\'‘’].+["“”\'‘’]%]%]') then
			str = utilities.substitute(cfg.presentation["kern-left"], str)
			str = utilities.substitute(cfg.presentation["kern-right"], str)
		elseif mw.ustring.match(str, '%[%[["“”\'‘’].+%]%]') then
			str = utilities.substitute(cfg.presentation["kern-left"], str)
		elseif mw.ustring.match(str, '%[%[.+["“”\'‘’]%]%]') then
			str = utilities.substitute(cfg.presentation["kern-right"], str)
		end
	else
		label = mw.ustring.gsub(label, "[“”]", '"')
		label = mw.ustring.gsub(label, "[‘’]", "'")

		cap = mw.ustring.match(label, '^(["\'][^\'].+)')
		if utilities.is_set(cap) then
			label = utilities.substitute(cfg.presentation["kern-left"], cap)
		end

		cap = mw.ustring.match(label, '^(.+[^\']["\'])$')
		if utilities.is_set(cap) then
			label = utilities.substitute(cfg.presentation["kern-right"], cap)
		end

		if 2 == wl_type then
			str = utilities.make_wikilink(link, label)
		else
			str = label
		end
	end
	return str
end

local function format_script_value(script_value, script_param)
	local lang = ""
	local name
	if script_value:match("^%l%l%l?%s*:") then
		lang = script_value:match("^(%l%l%l?)%s*:%s*%S.*")
		if not utilities.is_set(lang) then
			utilities.set_message("err_script_parameter", {script_param, "missing title part"})
			return ""
		end

		name = fetchName(lang, "al")
		if name == lang then
			 name = cfg.lang_code_remap[lang] or mw.language.fetchLanguageName(lang, cfg.this_wiki_code)
		end
		
		if utilities.is_set(name) then
			script_value = script_value:gsub("^%l+%s*:%s*", "")

			if utilities.in_array(lang, cfg.script_lang_codes) then
				utilities.add_prop_cat("script", {name, lang})
			else
				utilities.set_message("err_script_parameter", {script_param, "unknown language code"})
			end
			lang = ' lang="' .. lang .. '" '
		else
			utilities.set_message("err_script_parameter", {script_param, "invalid language code"})
			lang = ""
		end
	else
		utilities.set_message("err_script_parameter", {script_param, "missing prefix"})
	end
	script_value = utilities.substitute(cfg.presentation["bdi"], {lang, script_value})

	return script_value
end

local function script_concatenate(title, script, script_param)
	if utilities.is_set(script) then
		script = format_script_value(script, script_param)
		if utilities.is_set(script) then
			title = title .. " " .. script
		end
	end
	return title
end

local function wrap_msg(key, str, lower)
	if not utilities.is_set(str) then
		return ""
	end
	if true == lower then
		local msg
		msg = cfg.messages[key]:lower()
		return utilities.substitute(msg, str)
	else
		return utilities.substitute(cfg.messages[key], str)
	end
end

local function wikisource_url_make(str)
	local wl_type, D, L
	local ws_url, ws_label
	local wikisource_prefix = table.concat({"https://", cfg.this_wiki_code, ".wikisource.org/wiki/"})

	wl_type, D, L = utilities.is_wikilink(str)

	if 0 == wl_type then
		str = D:match("^[Ww]ikisource:(.+)") or D:match("^[Ss]:(.+)")
		if utilities.is_set(str) then
			ws_url =
				table.concat(
				{
					wikisource_prefix,
					str
				}
			)
			ws_label = str
		end
	elseif 1 == wl_type then
		str = D:match("^[Ww]ikisource:(.+)") or D:match("^[Ss]:(.+)")
		if utilities.is_set(str) then
			ws_url =
				table.concat(
				{
					wikisource_prefix,
					str
				}
			)
			ws_label = str
		end
	elseif 2 == wl_type then
		str = L:match("^[Ww]ikisource:(.+)") or L:match("^[Ss]:(.+)")
		if utilities.is_set(str) then
			ws_label = D
			ws_url =
				table.concat(
				{
					wikisource_prefix,
					str
				}
			)
		end
	end

	if ws_url then
		ws_url = mw.uri.encode(ws_url, "WIKI")
		ws_url = ws_url:gsub("%%23", "#")
	end

	return ws_url, ws_label, L or D
end

local function format_periodical(script_periodical, script_periodical_source, periodical, trans_periodical)
	if not utilities.is_set(periodical) then
		periodical = ""
	else
		periodical = utilities.wrap_style("italic-title", periodical)
	end

	periodical = script_concatenate(periodical, script_periodical, script_periodical_source)

	if utilities.is_set(trans_periodical) then
		trans_periodical = utilities.wrap_style("trans-italic-title", trans_periodical)
		if utilities.is_set(periodical) then
			periodical = periodical .. " " .. trans_periodical
		else
			periodical = trans_periodical
			utilities.set_message("err_trans_missing_title", {"periodical"})
		end
	end

	return periodical
end

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)
	local ws_url, ws_label, L = wikisource_url_make(chapter)
	if ws_url then
		ws_label = ws_label:gsub("_", " ")
		chapter = ws_label
	end

	if not utilities.is_set(chapter) then
		chapter = ""
	else
		if false == no_quotes then
			chapter = kern_quotes(chapter)
			chapter = utilities.wrap_style("quoted-title", chapter)
		end
	end

	chapter = script_concatenate(chapter, script_chapter, script_chapter_source)

	if utilities.is_set(chapter_url) then
		chapter = external_link(chapter_url, chapter, chapter_url_source, access)
	elseif ws_url then
		chapter = external_link(ws_url, chapter .. "&nbsp;", "ws link in chapter")
		chapter =
			utilities.substitute(cfg.presentation["interwiki-icon"], {cfg.presentation["class-wikisource"], L, chapter})
	end

	if utilities.is_set(trans_chapter) then
		trans_chapter = utilities.wrap_style("trans-quoted-title", trans_chapter)
		if utilities.is_set(chapter) then
			chapter = chapter .. " " .. trans_chapter
		else
			chapter = trans_chapter
			chapter_source = trans_chapter_source:match("trans%-?(.+)")
			utilities.set_message("err_trans_missing_title", {chapter_source})
		end
	end

	return chapter
end

local function has_invisible_chars(param, v)
	local position = ""
	local capture
	local stripmarker

	capture = string.match(v, "[%w%p ]*")
	if capture == v then
		return
	end

	for _, invisible_char in ipairs(cfg.invisible_chars) do
		local char_name = invisible_char[1]
		local pattern = invisible_char[2]
		position, _, capture = mw.ustring.find(v, pattern)

		if position and (cfg.invisible_defs.zwj == capture) then
			if mw.ustring.find(v, cfg.indic_script) then
				position = nil
			elseif cfg.emoji[mw.ustring.codepoint(v, position + 1)] then
				position = nil
			end
		end

		if position then
			if
				"nowiki" == capture or "math" == capture or
					("templatestyles" == capture and utilities.in_array(param, {"id", "quote"}))
			 then
				stripmarker = true
			elseif true == stripmarker and cfg.invisible_defs.del == capture then
				position = nil
			else
				local err_msg
				if capture and not (cfg.invisible_defs.del == capture or cfg.invisible_defs.zwj == capture) then
					err_msg = capture .. " " .. char_name
				else
					err_msg = char_name .. " " .. "character"
				end

				utilities.set_message(
					"err_invisible_char",
					{err_msg, utilities.wrap_style("parameter", param), position}
				)
				return
			end
		end
	end
end

local function argument_wrapper(args)
	local origin = {}

	return setmetatable(
		{
			ORIGIN = function(self, k)
				local dummy = self[k]
				return origin[k]
			end
		},
		{
			__index = function(tbl, k)
				if origin[k] ~= nil then
					return nil
				end

				local args, list, v = args, cfg.aliases[k]

				if type(list) == "table" then
					v, origin[k] = utilities.select_one(args, list, "err_redundant_parameters")
					if origin[k] == nil then
						origin[k] = ""
					end
				elseif list ~= nil then
					v, origin[k] = args[list], list
				else
					error(cfg.messages["unknown_argument_map"] .. ": " .. k)
				end

				if v == nil then
					v = ""
					origin[k] = ""
				end

				tbl = rawset(tbl, k, v)
				return v
			end
		}
	)
end

local function nowrap_date(date)
	local cap = ""
	local cap2 = ""

	if date:match("^%d%d%d%d%-%d%d%-%d%d$") then
		date = utilities.substitute(cfg.presentation["nowrap1"], date)
	elseif date:match("^%a+%s*%d%d?,%s+%d%d%d%d$") or date:match("^%d%d?%s*%a+%s+%d%d%d%d$") then
		cap, cap2 = string.match(date, "^(.*)%s+(%d%d%d%d)$")
		date = utilities.substitute(cfg.presentation["nowrap2"], {cap, cap2})
	end

	return date
end

local function set_titletype(cite_class, title_type)
	if utilities.is_set(title_type) then
		if "none" == cfg.keywords_xlate[title_type] then
			title_type = ""
		end
		return title_type
	end

	return cfg.title_types[cite_class] or ""
end

local function safe_join(tbl, duplicate_char)
	local f = {}
	if 1 == #duplicate_char then
		f.gsub = string.gsub
		f.match = string.match
		f.sub = string.sub
	else
		f.gsub = mw.ustring.gsub
		f.match = mw.ustring.match
		f.sub = mw.ustring.sub
	end

	local str = ""
	local comp = ""
	local end_chr = ""
	local trim
	for _, value in ipairs(tbl) do
		if value == nil then
			value = ""
		end

		if str == "" then
			str = value
		elseif value ~= "" then
			if value:sub(1, 1) == "<" then
				comp = value:gsub("%b<>", "")
			else
				comp = value
			end

			if f.sub(comp, 1, 1) == duplicate_char then
				trim = false
				end_chr = f.sub(str, -1, -1)

				if end_chr == duplicate_char then
					str = f.sub(str, 1, -2)
				elseif end_chr == "'" then
					if f.sub(str, -3, -1) == duplicate_char .. "''" then
						str = f.sub(str, 1, -4) .. "''"
					elseif f.sub(str, -5, -1) == duplicate_char .. "]]''" then
						trim = true
					elseif f.sub(str, -4, -1) == duplicate_char .. "]''" then
						trim = true
					end
				elseif end_chr == "]" then
					if f.sub(str, -3, -1) == duplicate_char .. "]]" then
						trim = true
					elseif f.sub(str, -3, -1) == duplicate_char .. '"]' then
						trim = true
					elseif f.sub(str, -2, -1) == duplicate_char .. "]" then
						trim = true
					elseif f.sub(str, -4, -1) == duplicate_char .. "'']" then
						trim = true
					end
				elseif end_chr == " " then
					if f.sub(str, -2, -1) == duplicate_char .. " " then
						str = f.sub(str, 1, -3)
					end
				end

				if trim then
					if value ~= comp then
						local dup2 = duplicate_char
						if f.match(dup2, "%A") then
							dup2 = "%" .. dup2
						end

						value = f.gsub(value, "(%b<>)" .. dup2, "%1", 1)
					else
						value = f.sub(value, 2, -1)
					end
				end
			end
			str = str .. value
		end
	end
	return str
end

local function is_suffix(suffix)
	if utilities.in_array(suffix, {"Jr", "Sr", "Jnr", "Snr", "1st", "2nd", "3rd"}) or suffix:match("^%dth$") then
		return true
	end
	return false
end

local function is_good_vanc_name(last, first, suffix, position)
	if not suffix then
		if first:find("[,%s]") then
			first = first:match("(.-)[,%s]+")
			suffix = first:match("[,%s]+(.+)$")
		end
	end
	if utilities.is_set(suffix) then
		if not is_suffix(suffix) then
			add_vanc_error(cfg.err_msg_supl.suffix, position)
			return false
		end
	end
	if
		nil ==
			mw.ustring.find(
				last,
				"^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%']*$"
			) or
			nil ==
				mw.ustring.find(
					first,
					"^[A-Za-z\195\128-\195\150\195\152-\195\182\195\184-\198\191\199\132-\201\143%-%s%'%.]*$"
				)
	 then
		add_vanc_error(cfg.err_msg_supl["non-Latin char"], position)
		return false
	end
	return true
end

local function reduce_to_initials(first, position)
	local name, suffix = mw.ustring.match(first, "^(%u+) ([%dJS][%drndth]+)$")

	if not name then
		name = mw.ustring.match(first, "^(%u+)$")
	end

	if name then
		if 3 > mw.ustring.len(name) then
			if suffix then
				if is_suffix(suffix) then
					return first
				else
					add_vanc_error(cfg.err_msg_supl.suffix, position)
					return first
				end
			else
				return first
			end
		end
	end

	local initials, names = {}, {}
	local i = 1

	names = mw.text.split(first, "[%s,]+")

	while names[i] do
		if 1 < i and names[i]:match("[%dJS][%drndth]+%.?$") then
			names[i] = names[i]:gsub("%.", "")
			if is_suffix(names[i]) then
				table.insert(initials, " " .. names[i])
				break
			end
		end
		if 3 > i then
			table.insert(initials, mw.ustring.sub(names[i], 1, 1))
		end
		i = i + 1
	end

	return table.concat(initials)
end

local function list_people(control, people, etal)
	local sep
	local namesep
	local format = control.format
	local maximum = control.maximum
	local name_list = {}

	if "vanc" == format then
		sep = cfg.presentation["sep_nl_vanc"]
		namesep = cfg.presentation["sep_name_vanc"]
	else
		sep = cfg.presentation["sep_nl"]
		namesep = cfg.presentation["sep_name"]
	end

	if sep:sub(-1, -1) ~= " " then
		sep = sep .. " "
	end
	if utilities.is_set(maximum) and maximum < 1 then
		return "", 0
	end

	for i, person in ipairs(people) do
		if utilities.is_set(person.last) then
			local mask = person.mask
			local one
			local sep_one = sep

			if utilities.is_set(maximum) and i > maximum then
				etal = true
				break
			end

			if mask then
				local n = tonumber(mask)
				if n then
					one = 0 ~= n and string.rep("&mdash;", n) or nil
					person.link = nil
				else
					one = mask
					sep_one = " "
				end
			else
				one = person.last
				local first = person.first
				if utilities.is_set(first) then
					if ("vanc" == format) then
						one = one:gsub("%.", "")
						if not person.corporate and is_good_vanc_name(one, first, nil, i) then
							first = reduce_to_initials(first, i)
						end
					end
					one = one .. namesep .. first
				end
			end
			if utilities.is_set(person.link) then
				one = utilities.make_wikilink(person.link, one)
			end
			if one then
				table.insert(name_list, one)
				table.insert(name_list, sep_one)
			end
		end
	end

	local count = #name_list / 2
	if 0 < count then
		if 1 < count and not etal then
			if "amp" == format then
				name_list[#name_list - 2] = " & "
			elseif "and" == format then
				if 2 == count then
					name_list[#name_list - 2] = cfg.presentation.sep_nl_and
				else
					name_list[#name_list - 2] = cfg.presentation.sep_nl_end
				end
			end
		end
		name_list[#name_list] = nil
	end

	local result = table.concat(name_list)
	if etal and utilities.is_set(result) then
		result = result .. sep .. " " .. cfg.messages["et al"]
	end

	return result, count
end

local function make_citeref_id(namelist, year)
	local names = {}
	for i, v in ipairs(namelist) do
		names[i] = v.last
		if i == 4 then
			break
		end
	end
	table.insert(names, year)
	local id = table.concat(names)
	if utilities.is_set(id) then
		return "CITEREF" .. id
	else
		return ""
	end
end

local function cite_class_attribute_make(cite_class, mode)
	local class_t = {}
	table.insert(class_t, "citation")
	if "citation" ~= cite_class then
		table.insert(class_t, cite_class)
		table.insert(class_t, utilities.is_set(mode) and mode or "cs1")
	else
		table.insert(class_t, utilities.is_set(mode) and mode or "cs2")
	end
	for _, prop_key in ipairs(z.prop_keys_t) do
		table.insert(class_t, prop_key)
	end

	return table.concat(class_t, " ")
end

local function name_has_etal(name, etal, nocat, param)
	if utilities.is_set(name) then
		local patterns = cfg.et_al_patterns

		for _, pattern in ipairs(patterns) do
			if name:match(pattern) then
				name = name:gsub(pattern, "")
				etal = true
				if not nocat then
					utilities.set_message("err_etal", {param})
				end
			end
		end
	end

	return name, etal
end

local function name_is_numeric(name, list_name)
	if utilities.is_set(name) then
		if mw.ustring.match(name, "^[%A]+$") then
			utilities.set_message("maint_numeric_names", cfg.special_case_translation[list_name])
		end
	end
end

local function name_has_mult_names(name, list_name)
	local _, commas, semicolons, nbsps
	if utilities.is_set(name) then
		_, commas = name:gsub(",", "")
		_, semicolons = name:gsub(";", "")

		_, nbsps = name:gsub("&nbsp;", "")

		if 1 < commas or 0 < (semicolons - nbsps) then
			utilities.set_message("maint_mult_names", cfg.special_case_translation[list_name])
		end
	end
end

local function is_generic(item, value)
	local test_val

	for _, generic_value in ipairs(cfg.special_case_translation[item]) do
		test_val = generic_value["en"][2] and value:lower() or value

		if test_val:find(generic_value["en"][1], 1, generic_value["en"][2]) then
			return true
		elseif generic_value["local"] then
			test_val = generic_value["local"][2] and mw.ustring.lower(value) or value

			if mw.ustring.find(test_val, generic_value["local"][1], 1, generic_value["local"][2]) then
				return true
			end
		end
	end
end

local function name_is_generic(name, name_alias)
	if not added_generic_name_errs and is_generic("generic_names", name) then
		utilities.set_message("err_generic_name", name_alias)
		added_generic_name_errs = true
	end
end

local function name_checks(last, first, list_name, last_alias, first_alias)
	local accept_name

	if utilities.is_set(last) then
		last, accept_name = utilities.has_accept_as_written(last)

		if not accept_name then
			name_has_mult_names(last, list_name)
			name_is_numeric(last, list_name)
			name_is_generic(last, last_alias)
		end
	end

	if utilities.is_set(first) then
		first, accept_name = utilities.has_accept_as_written(first)

		if not accept_name then
			name_is_numeric(first, list_name)
			name_is_generic(first, first_alias)
		end
		local wl_type, D = utilities.is_wikilink(first)
		if 0 ~= wl_type then
			first = D
			utilities.set_message("err_bad_paramlink", first_alias)
		end
	end

	return last, first
end

local function extract_names(args, list_name)
	local names = {}
	local last
	local first
	local link
	local mask
	local i = 1
	local n = 1
	local count = 0
	local etal = false

	local last_alias, first_alias, link_alias
	while true do
		last, last_alias = utilities.select_one(args, cfg.aliases[list_name .. "-Last"], "err_redundant_parameters", i)
		first, first_alias =
			utilities.select_one(args, cfg.aliases[list_name .. "-First"], "err_redundant_parameters", i)
		link, link_alias = utilities.select_one(args, cfg.aliases[list_name .. "-Link"], "err_redundant_parameters", i)
		mask = utilities.select_one(args, cfg.aliases[list_name .. "-Mask"], "err_redundant_parameters", i)

		last, etal = name_has_etal(last, etal, false, last_alias)
		first, etal = name_has_etal(first, etal, false, first_alias)
		last, first = name_checks(last, first, list_name, last_alias, first_alias)

		if first and not last then
			local alias = first_alias:find("given", 1, true) and "given" or "first"
			utilities.set_message(
				"err_first_missing_last",
				{
					first_alias,
					first_alias:gsub(alias, {["first"] = "last", ["given"] = "surname"})
				}
			)
		elseif not first and not last then
			count = count + 1
			if 2 <= count then
				break
			end
		else
			local result
			link = link_title_ok(link, link_alias, last, last_alias)

			if first then
				link = link_title_ok(link, link_alias, first, first_alias)
			end

			names[n] = {last = last, first = first, link = link, mask = mask, corporate = false}
			n = n + 1
			if 1 == count then
				utilities.set_message("err_missing_name", {list_name:match("(%w+)List"):lower(), i - 1})
			end
			count = 0
		end
		i = i + 1
	end

	return names, etal
end

local function name_tag_get(lang_param)
	local lang_param_lc = mw.ustring.lower(lang_param)
	local name
	local tag

	--name = cfg.lang_code_remap[lang_param_lc]
	name = fetchName(lang_param_lc, "al")
	if name ~= lang_param_lc then
		return name, lang_param
	end

	tag = lang_param_lc:match("^(%a%a%a?)%-.*")
	--name = cfg.lang_code_remap[tag]
	if name then
		return name, tag
	end

	if cfg.lang_name_remap[lang_param_lc] then
		return cfg.lang_name_remap[lang_param_lc][1], cfg.lang_name_remap[lang_param_lc][2]
	end

	tag = cfg.mw_languages_by_name_t[lang_param_lc]

	if tag then
		return cfg.mw_languages_by_tag_t[tag], tag
	end

	name = cfg.mw_languages_by_tag_t[lang_param_lc]

	if name then
		return name, lang_param
	end

	tag = lang_param_lc:match("^(%a%a%a?)%-.*")

	if tag then
		name = cfg.mw_languages_by_tag_t[tag]
		if name then
			return name, tag
		end
	end
end

local function language_parameter(lang)
	local tag
	local lang_subtag
	local name
	local language_list = {}
	local names_t = {}

	local this_wiki_name = mw.language.fetchLanguageName(cfg.this_wiki_code, cfg.this_wiki_code)

	names_t = mw.text.split(lang, "%s*,%s*")

	for _, lang in ipairs(names_t) do
		name, tag = name_tag_get(lang)

		if utilities.is_set(tag) then
			lang_subtag = tag:lower():gsub("^(%a%a%a?)%-.*", "%1")
			lang_subtag = languagescode[lang_subtag:lower()] or lang_subtag

			if cfg.this_wiki_code ~= lang_subtag then
				if 2 == lang_subtag:len() then
					utilities.add_prop_cat("foreign-lang-source", {name, lang_subtag}, lang_subtag)
				else
					utilities.add_prop_cat("foreign-lang-source-2", {lang_subtag}, lang_subtag)
				end
			elseif cfg.local_lang_cat_enable then
				utilities.add_prop_cat("local-lang-source", {name, lang_subtag})
			end
		else
			name = lang
			utilities.set_message("maint_unknown_lang")
		end

		table.insert(language_list, name)
		name = ""
	end

	name = utilities.make_sep_list(#language_list, language_list)
	if (1 == #language_list) and (lang_subtag == cfg.this_wiki_code) then
		return ""
	end
	return (" " .. wrap_msg("language", name))
end

local function set_cs_style(postscript, mode)
	if utilities.is_set(postscript) then
		if mode == "cs1" and postscript == cfg.presentation["ps_" .. mode] then
			utilities.set_message("maint_postscript")
		end
	else
		postscript = cfg.presentation["ps_" .. mode]
	end
	return cfg.presentation["sep_" .. mode], postscript
end

local function set_style(mode, postscript, cite_class)
	local sep
	if "cs2" == mode then
		sep, postscript = set_cs_style(postscript, "cs2")
	elseif "cs1" == mode then
		sep, postscript = set_cs_style(postscript, "cs1")
	elseif "citation" == cite_class then
		sep, postscript = set_cs_style(postscript, "cs2")
	else
		sep, postscript = set_cs_style(postscript, "cs1")
	end

	if cfg.keywords_xlate[postscript:lower()] == "none" then
		if "cs2" == mode or "citation" == cite_class then
			utilities.set_message("maint_postscript")
		end
		postscript = ""
	end

	return sep, postscript
end

local function is_pdf(url)
	return url:match("%.pdf$") or url:match("%.PDF$") or url:match("%.pdf[%?#]") or url:match("%.PDF[%?#]") or
		url:match("%.PDF&#035") or
		url:match("%.pdf&#035")
end

local function style_format(format, url, fmt_param, url_param)
	if utilities.is_set(format) then
		format = utilities.wrap_style("format", format)
		if not utilities.is_set(url) then
			utilities.set_message("err_format_missing_url", {fmt_param, url_param})
		end
	elseif is_pdf(url) then
		format = utilities.wrap_style("format", "PDF")
	else
		format = ""
	end
	return format
end

local function get_display_names(max, count, list_name, etal, param)
	if utilities.is_set(max) then
		if "etal" == max:lower():gsub("[ '%.]", "") then
			max = count + 1
			etal = true
		elseif max:match("^%d+$") then
			max = tonumber(max)
			if max >= count then
				utilities.set_message("err_disp_name", {param, max})
				max = nil
			end
		else
			utilities.set_message("err_disp_name", {param, max})
			max = nil
		end
	end

	return max, etal
end

local function extra_text_in_page_check(val, name)
	if not val:match(cfg.vol_iss_pg_patterns.good_ppattern) then
		for _, pattern in ipairs(cfg.vol_iss_pg_patterns.bad_ppatterns) do
			if val:match(pattern) then
				utilities.set_message("err_extra_text_pages", name)
				return
			end
		end
	end
end

local function extra_text_in_vol_iss_check(val, name, selector)
	if not utilities.is_set(val) then
		return
	end

	local patterns = "v" == selector and cfg.vol_iss_pg_patterns.vpatterns or cfg.vol_iss_pg_patterns.ipatterns

	local handler = "v" == selector and "err_extra_text_volume" or "err_extra_text_issue"
	val = val:lower()
	for _, pattern in ipairs(patterns) do
		if val:match(pattern) then
			utilities.set_message(handler, name)
			return
		end
	end
end

local function get_v_name_table(vparam, output_table, output_link_table)
	local name_table = mw.text.split(vparam or "", "%s*,%s*")
	local wl_type, label, link

	local i = 1

	while name_table[i] do
		if name_table[i]:match("^%(%(.*[^%)][^%)]$") then
			local name = name_table[i]
			i = i + 1
			while name_table[i] do
				name = name .. ", " .. name_table[i]
				if name_table[i]:match("^.*%)%)$") then
					break
				end
				i = i + 1
			end
			table.insert(output_table, name)
			table.insert(output_link_table, "")
		else
			wl_type, label, link = utilities.is_wikilink(name_table[i])
			table.insert(output_table, label)
			if 1 == wl_type then
				table.insert(output_link_table, label)
			else
				table.insert(output_link_table, link)
			end
		end
		i = i + 1
	end
	return output_table
end

local function parse_vauthors_veditors(args, vparam, list_name)
	local names = {}
	local v_name_table = {}
	local v_link_table = {}
	local etal = false
	local last, first, link, mask, suffix
	local corporate = false

	vparam, etal = name_has_etal(vparam, etal, true)
	v_name_table = get_v_name_table(vparam, v_name_table, v_link_table)

	for i, v_name in ipairs(v_name_table) do
		first = ""
		local accept_name
		v_name, accept_name = utilities.has_accept_as_written(v_name)

		if accept_name then
			last = v_name
			corporate = true
		elseif string.find(v_name, "%s") then
			if v_name:find("[;%.]") then
				add_vanc_error(cfg.err_msg_supl.punctuation, i)
			end
			local lastfirstTable = {}
			lastfirstTable = mw.text.split(v_name, "%s+")
			first = table.remove(lastfirstTable)

			if not mw.ustring.match(first, "^%u+$") then
				suffix = first
				first = table.remove(lastfirstTable)
			end
			last = table.concat(lastfirstTable, " ")
			if not utilities.is_set(last) then
				first = ""
				last = v_name
				add_vanc_error(cfg.err_msg_supl.name, i)
			end
			if mw.ustring.match(last, "%a+%s+%u+%s+%a+") then
				add_vanc_error(cfg.err_msg_supl["missing comma"], i)
			end
			if mw.ustring.match(v_name, " %u %u$") then
				add_vanc_error(cfg.err_msg_supl.initials, i)
			end
		else
			last = v_name
		end

		if utilities.is_set(first) then
			if not mw.ustring.match(first, "^%u?%u$") then
				add_vanc_error(cfg.err_msg_supl.initials, i)
			end
			is_good_vanc_name(last, first, suffix, i)
			if utilities.is_set(suffix) then
				first = first .. " " .. suffix
				suffix = ""
			end
		else
			if not corporate then
				is_good_vanc_name(last, "", nil, i)
			end
		end

		link =
			utilities.select_one(args, cfg.aliases[list_name .. "-Link"], "err_redundant_parameters", i) or
			v_link_table[i]
		mask = utilities.select_one(args, cfg.aliases[list_name .. "-Mask"], "err_redundant_parameters", i)
		names[i] = {last = last, first = first, link = link, mask = mask, corporate = corporate}
	end
	return names, etal
end

local function select_author_editor_source(vxxxxors, xxxxors, args, list_name)
	local lastfirst = false
	if
		utilities.select_one(args, cfg.aliases[list_name .. "-Last"], "none", 1) or
			utilities.select_one(args, cfg.aliases[list_name .. "-First"], "none", 1) or
			utilities.select_one(args, cfg.aliases[list_name .. "-Last"], "none", 2) or
			utilities.select_one(args, cfg.aliases[list_name .. "-First"], "none", 2)
	 then
		lastfirst = true
	end

	if
		(utilities.is_set(vxxxxors) and true == lastfirst) or (utilities.is_set(vxxxxors) and utilities.is_set(xxxxors)) or
			(true == lastfirst and utilities.is_set(xxxxors))
	 then
		local err_name
		if "AuthorList" == list_name then
			err_name = "author"
		else
			err_name = "editor"
		end
		utilities.set_message("err_redundant_parameters", err_name .. "-name-list parameters")
	end

	if true == lastfirst then
		return 1
	end
	if utilities.is_set(vxxxxors) then
		return 2
	end
	if utilities.is_set(xxxxors) then
		return 3
	end
	return 1
end

local function is_valid_parameter_value(value, name, possible, ret_val, invert)
	if not utilities.is_set(value) then
		return ret_val
	end

	if (not invert and utilities.in_array(value, possible)) then
		return cfg.keywords_xlate[value]
	elseif invert and not utilities.in_array(value, possible) then
		return value
	else
		utilities.set_message("err_invalid_param_val", {name, value})
		return ret_val
	end
end

local function terminate_name_list(name_list, sepc)
	if (string.sub(name_list, -3, -1) == sepc .. ". ") then
		return name_list
	elseif (string.sub(name_list, -1, -1) == sepc) or (string.sub(name_list, -3, -1) == sepc .. "]]") then
		return name_list .. " "
	else
		return name_list .. sepc .. " "
	end
end

local function format_volume_issue(volume, issue, cite_class, origin, sepc, lower)
	if not utilities.is_set(volume) and not utilities.is_set(issue) then
		return ""
	end

	local is_journal =
		"journal" == cite_class or
		(utilities.in_array(cite_class, {"citation", "map", "interview"}) and "journal" == origin)

	local is_numeric_vol = volume and (volume:match("^[MDCLXVI]+$") or volume:match("^%d+$"))
	local is_long_vol = volume and (4 < mw.ustring.len(volume))

	if volume and (not is_numeric_vol and is_long_vol) then
		utilities.add_prop_cat("long-vol")
	end

	if is_journal then
		local vol = ""

		if utilities.is_set(volume) then
			if is_numeric_vol then
				vol = utilities.substitute(cfg.presentation["vol-bold"], {sepc, volume})
			elseif is_long_vol then
				vol = utilities.substitute(cfg.messages["j-vol"], {sepc, utilities.hyphen_to_dash(volume)})
			else
				vol = utilities.substitute(cfg.presentation["vol-bold"], {sepc, utilities.hyphen_to_dash(volume)})
			end
		end
		if utilities.is_set(issue) then
			return vol .. utilities.substitute(cfg.messages["j-issue"], issue)
		end
		return vol
	end

	if "podcast" == cite_class and utilities.is_set(issue) then
		return wrap_msg("issue", {sepc, issue}, lower)
	end

	if utilities.is_set(volume) and utilities.is_set(issue) then
		return wrap_msg("vol-no", {sepc, utilities.hyphen_to_dash(volume), issue}, lower)
	elseif utilities.is_set(volume) then
		return wrap_msg("vol", {sepc, utilities.hyphen_to_dash(volume)}, lower)
	else
		return wrap_msg("issue", {sepc, issue}, lower)
	end
end

local function format_pages_sheets(page, pages, sheet, sheets, cite_class, origin, sepc, nopp, lower)
	if "map" == cite_class then
		if utilities.is_set(sheet) then
			if "journal" == origin then
				return "", "", wrap_msg("j-sheet", sheet, lower), ""
			else
				return "", "", wrap_msg("sheet", {sepc, sheet}, lower), ""
			end
		elseif utilities.is_set(sheets) then
			if "journal" == origin then
				return "", "", "", wrap_msg("j-sheets", sheets, lower)
			else
				return "", "", "", wrap_msg("sheets", {sepc, sheets}, lower)
			end
		end
	end

	local is_journal =
		"journal" == cite_class or
		(utilities.in_array(cite_class, {"citation", "map", "interview"}) and "journal" == origin)

	if utilities.is_set(page) then
		if is_journal then
			return utilities.substitute(cfg.messages["j-page(s)"], page), "", "", ""
		elseif not nopp then
			return utilities.substitute(cfg.messages["p-prefix"], {sepc, page}), "", "", ""
		else
			return utilities.substitute(cfg.messages["nopp"], {sepc, page}), "", "", ""
		end
	elseif utilities.is_set(pages) then
		if is_journal then
			return utilities.substitute(cfg.messages["j-page(s)"], pages), "", "", ""
		elseif tonumber(pages) ~= nil and not nopp then
			return "", utilities.substitute(cfg.messages["p-prefix"], {sepc, pages}), "", ""
		elseif not nopp then
			return "", utilities.substitute(cfg.messages["pp-prefix"], {sepc, pages}), "", ""
		else
			return "", utilities.substitute(cfg.messages["nopp"], {sepc, pages}), "", ""
		end
	end

	return "", "", "", ""
end

local function insource_loc_get(page, page_orig, pages, pages_orig, at)
	local ws_url, ws_label, coins_pages, L

	if utilities.is_set(page) then
		if utilities.is_set(pages) or utilities.is_set(at) then
			pages = ""
			at = ""
		end
		extra_text_in_page_check(page, page_orig)

		ws_url, ws_label, L = wikisource_url_make(page)
		if ws_url then
			page = external_link(ws_url, ws_label .. "&nbsp;", "ws link in page")
			page =
				utilities.substitute(
				cfg.presentation["interwiki-icon"],
				{cfg.presentation["class-wikisource"], L, page}
			)
			coins_pages = ws_label
		end
	elseif utilities.is_set(pages) then
		if utilities.is_set(at) then
			at = ""
		end
		extra_text_in_page_check(pages, pages_orig)

		ws_url, ws_label, L = wikisource_url_make(pages)
		if ws_url then
			pages = external_link(ws_url, ws_label .. "&nbsp;", "ws link in pages")
			pages =
				utilities.substitute(
				cfg.presentation["interwiki-icon"],
				{cfg.presentation["class-wikisource"], L, pages}
			)
			coins_pages = ws_label
		end
	elseif utilities.is_set(at) then
		ws_url, ws_label, L = wikisource_url_make(at)
		if ws_url then
			at = external_link(ws_url, ws_label .. "&nbsp;", "ws link in at")
			at = utilities.substitute(cfg.presentation["interwiki-icon"], {cfg.presentation["class-wikisource"], L, at})
			coins_pages = ws_label
		end
	end

	return page, pages, at, coins_pages
end

local function is_unique_archive_url(archive, url, c_url, source, date)
	if utilities.is_set(archive) then
		if archive == url or archive == c_url then
			utilities.set_message("err_bad_url", {utilities.wrap_style("parameter", source)})
			return "", ""
		end
	end

	return archive, date
end

local function archive_url_check(url, date)
	local err_msg = ""
	local path, timestamp, flag

	if (not url:match("//web%.archive%.org/")) and (not url:match("//liveweb%.archive%.org/")) then
		return url, date
	end

	if url:match("//web%.archive%.org/save/") then
		err_msg = cfg.err_msg_supl.save
		url = url:gsub("(//web%.archive%.org)/save/", "%1/*/", 1)
	elseif url:match("//liveweb%.archive%.org/") then
		err_msg = cfg.err_msg_supl.liveweb
	else
		path, timestamp, flag = url:match("//web%.archive%.org/([^%d]*)(%d+)([^/]*)/")
		if not path then
			err_msg = cfg.err_msg_supl.timestamp
		elseif 14 ~= timestamp:len() then
			err_msg = cfg.err_msg_supl.timestamp
			if "*" ~= flag then
				local replacement = timestamp:match("^%d%d%d%d%d%d") or timestamp:match("^%d%d%d%d")
				if replacement then
					replacement = replacement .. string.rep("0", 14 - replacement:len())
					url = url:gsub("(//web%.archive%.org/[^%d]*)%d[^/]*", "%1" .. replacement .. "*", 1)
				end
			end
		elseif utilities.is_set(path) and "web/" ~= path then
			err_msg = cfg.err_msg_supl.path
		elseif utilities.is_set(flag) and not utilities.is_set(path) then
			err_msg = cfg.err_msg_supl.flag
		elseif utilities.is_set(flag) and not flag:match("%a%a_") then
			err_msg = cfg.err_msg_supl.flag
		else
			return url, date
		end
	end

	utilities.set_message("err_archive_url", {err_msg})

	if is_preview_mode then
		return url, date
	else
		return "", ""
	end
end

local function place_check(param_val)
	if not utilities.is_set(param_val) then
		return param_val
	end

	if mw.ustring.find(param_val, "%d") then
		utilities.set_message("maint_location")
	end

	return param_val
end

local function is_archived_copy(title)
	title = mw.ustring.lower(title)
	if title:find(cfg.special_case_translation.archived_copy.en) then
		return true
	elseif cfg.special_case_translation.archived_copy["local"] then
		if mw.ustring.find(title, cfg.special_case_translation.archived_copy["local"]) then
			return true
		end
	end
end

local function citation0(CitationClass, args)
	local A = argument_wrapper(args)
	local i

	local author_etal
	local a = {}
	local Authors
	local NameListStyle =
		is_valid_parameter_value(
		A["NameListStyle"],
		A:ORIGIN("NameListStyle"),
		cfg.keywords_lists["name-list-style"],
		""
	)
	local Collaboration = A["Collaboration"]

	do
		local selected = select_author_editor_source(A["Vauthors"], A["Authors"], args, "AuthorList")
		if 1 == selected then
			a, author_etal = extract_names(args, "AuthorList")
		elseif 2 == selected then
			NameListStyle = "vanc"
			a, author_etal = parse_vauthors_veditors(args, args.vauthors, "AuthorList")
		elseif 3 == selected then
			Authors = A["Authors"]
			if "authors" == A:ORIGIN("Authors") then
				utilities.set_message("maint_authors")
			end
		end
		if utilities.is_set(Collaboration) then
			author_etal = true
		end
	end

	local editor_etal
	local e = {}

	do
		local selected = select_author_editor_source(A["Veditors"], nil, args, "EditorList")
		if 1 == selected then
			e, editor_etal = extract_names(args, "EditorList")
		elseif 2 == selected then
			NameListStyle = "vanc"
			e, editor_etal = parse_vauthors_veditors(args, args.veditors, "EditorList")
		end
	end

	local Chapter = A["Chapter"]
	local Chapter_origin = A:ORIGIN("Chapter")
	local Contribution
	if "contribution" == Chapter_origin then
		Contribution = Chapter
	end
	local c = {}

	if utilities.in_array(CitationClass, {"book", "citation"}) and not utilities.is_set(A["Periodical"]) then
		c = extract_names(args, "ContributorList")

		if 0 < #c then
			if not utilities.is_set(Contribution) then
				utilities.set_message("err_contributor_missing_required_param", "contribution")
				c = {}
			end
			if 0 == #a then
				utilities.set_message("err_contributor_missing_required_param", "author")
				c = {}
			end
		end
	else
		if utilities.select_one(args, cfg.aliases["ContributorList-Last"], "err_redundant_parameters", 1) then
			utilities.set_message("err_contributor_ignored")
		end
		Contribution = nil
	end

	local Title = A["Title"]
	local TitleLink = A["TitleLink"]

	local auto_select = ""
	local accept_link
	TitleLink, accept_link = utilities.has_accept_as_written(TitleLink, true)
	if (not accept_link) and utilities.in_array(TitleLink, {"none", "pmc", "doi"}) then
		auto_select = TitleLink
		TitleLink = ""
	end

	TitleLink = link_title_ok(TitleLink, A:ORIGIN("TitleLink"), Title, "title")

	local Section = ""
	if "map" == CitationClass and "section" == Chapter_origin then
		Section = A["Chapter"]
		Chapter = ""
	end

	local Periodical = A["Periodical"]
	local Periodical_origin = ""
	if utilities.is_set(Periodical) then
		Periodical_origin = A:ORIGIN("Periodical")
		local i
		Periodical, i = utilities.strip_apostrophe_markup(Periodical)
		if i then
			utilities.set_message("err_apostrophe_markup", {Periodical_origin})
		end
	end

	if "mailinglist" == CitationClass then
		if utilities.is_set(Periodical) and utilities.is_set(A["MailingList"]) then
			utilities.set_message(
				"err_redundant_parameters",
				{
					utilities.wrap_style("parameter", Periodical_origin) ..
						" و " .. utilities.wrap_style("parameter", "mailinglist")
				}
			)
		end

		Periodical = A["MailingList"]
		Periodical_origin = A:ORIGIN("MailingList")
	end

	local ScriptPeriodical = A["ScriptPeriodical"]

	if not (utilities.is_set(Periodical) or utilities.is_set(ScriptPeriodical)) then
		local p = {["journal"] = "journal", ["magazine"] = "magazine"}
		if p[CitationClass] then
			utilities.set_message("err_missing_periodical", {CitationClass, p[CitationClass]})
		end
	end

	local Volume
	local ScriptPeriodical_origin = A:ORIGIN("ScriptPeriodical")
	if "citation" == CitationClass then
		if utilities.is_set(Periodical) then
			if not utilities.in_array(Periodical_origin, {"website", "mailinglist"}) then
				Volume = A["Volume"]
			end
		elseif utilities.is_set(ScriptPeriodical) then
			if "script-website" ~= ScriptPeriodical_origin then
				Volume = A["Volume"]
			end
		else
			Volume = A["Volume"]
		end
	elseif utilities.in_array(CitationClass, cfg.templates_using_volume) then
		Volume = A["Volume"]
	end
	extra_text_in_vol_iss_check(Volume, A:ORIGIN("Volume"), "v")

	local Issue
	if "citation" == CitationClass then
		if
			utilities.is_set(Periodical) and
				utilities.in_array(Periodical_origin, {"journal", "magazine", "newspaper", "periodical", "work"}) or
				utilities.is_set(ScriptPeriodical) and
					utilities.in_array(
						ScriptPeriodical_origin,
						{"script-journal", "script-magazine", "script-newspaper", "script-periodical", "script-work"}
					)
		 then
			Issue = utilities.hyphen_to_dash(A["Issue"])
		end
	elseif utilities.in_array(CitationClass, cfg.templates_using_issue) then
		if
			not (utilities.in_array(CitationClass, {"conference", "map", "citation"}) and
				not (utilities.is_set(Periodical) or utilities.is_set(ScriptPeriodical)))
		 then
			Issue = utilities.hyphen_to_dash(A["Issue"])
		end
	end
	extra_text_in_vol_iss_check(Issue, A:ORIGIN("Issue"), "i")

	local Page
	local Pages
	local At
	if not utilities.in_array(CitationClass, cfg.templates_not_using_page) then
		Page = A["Page"]
		Pages = utilities.hyphen_to_dash(A["Pages"])
		At = A["At"]
	end

	local Edition = A["Edition"]
	local PublicationPlace = place_check(A["PublicationPlace"], A:ORIGIN("PublicationPlace"))
	local Place = place_check(A["Place"], A:ORIGIN("Place"))

	local PublisherName = A["PublisherName"]
	local PublisherName_origin = A:ORIGIN("PublisherName")
	if utilities.is_set(PublisherName) then
		local i = 0
		PublisherName, i = utilities.strip_apostrophe_markup(PublisherName)
		if i then
			utilities.set_message("err_apostrophe_markup", {PublisherName_origin})
		end
	end

	local Newsgroup = A["Newsgroup"]
	local Newsgroup_origin = A:ORIGIN("Newsgroup")

	if "newsgroup" == CitationClass then
		if utilities.is_set(PublisherName) then
			utilities.set_message("err_parameter_ignored", {PublisherName_origin})
		end

		PublisherName = nil
	end

	local URL = A["URL"]
	local UrlAccess =
		is_valid_parameter_value(A["UrlAccess"], A:ORIGIN("UrlAccess"), cfg.keywords_lists["url-access"], nil)

	if not utilities.is_set(URL) and utilities.is_set(UrlAccess) then
		UrlAccess = nil
		utilities.set_message("err_param_access_requires_param", "url")
	end

	local ChapterURL = A["ChapterURL"]
	local ChapterUrlAccess =
		is_valid_parameter_value(
		A["ChapterUrlAccess"],
		A:ORIGIN("ChapterUrlAccess"),
		cfg.keywords_lists["url-access"],
		nil
	)
	if not utilities.is_set(ChapterURL) and utilities.is_set(ChapterUrlAccess) then
		ChapterUrlAccess = nil
		utilities.set_message("err_param_access_requires_param", {A:ORIGIN("ChapterUrlAccess"):gsub("%-access", "")})
	end

	local MapUrlAccess =
		is_valid_parameter_value(A["MapUrlAccess"], A:ORIGIN("MapUrlAccess"), cfg.keywords_lists["url-access"], nil)
	if not utilities.is_set(A["MapURL"]) and utilities.is_set(MapUrlAccess) then
		MapUrlAccess = nil
		utilities.set_message("err_param_access_requires_param", {"map-url"})
	end

	local this_page = mw.title.getCurrentTitle()
	local no_tracking_cats =
		is_valid_parameter_value(A["NoTracking"], A:ORIGIN("NoTracking"), cfg.keywords_lists["yes_true_y"], nil)

	if not utilities.is_set(no_tracking_cats) then
		if utilities.in_array(this_page.nsText, cfg.uncategorized_namespaces) then
			no_tracking_cats = "true"
		end
		for _, v in ipairs(cfg.uncategorized_subpages) do
			if this_page.text:match(v) then
				no_tracking_cats = "true"
				break
			end
		end
	end

	utilities.select_one(args, {"page", "p", "pp", "pages", "at", "sheet", "sheets"}, "err_redundant_parameters")

	local coins_pages

	Page, Pages, At, coins_pages = insource_loc_get(Page, A:ORIGIN("Page"), Pages, A:ORIGIN("Pages"), At)

	local NoPP = is_valid_parameter_value(A["NoPP"], A:ORIGIN("NoPP"), cfg.keywords_lists["yes_true_y"], nil)

	if utilities.is_set(PublicationPlace) and utilities.is_set(Place) then
		utilities.add_prop_cat("location-test")
		if PublicationPlace == Place then
			Place = ""
		end
	elseif not utilities.is_set(PublicationPlace) and utilities.is_set(Place) then
		PublicationPlace = Place
	end

	if PublicationPlace == Place then
		Place = ""
	end

	local URL_origin = A:ORIGIN("URL")
	local ChapterURL_origin = A:ORIGIN("ChapterURL")
	local ScriptChapter = A["ScriptChapter"]
	local ScriptChapter_origin = A:ORIGIN("ScriptChapter")
	local Format = A["Format"]
	local ChapterFormat = A["ChapterFormat"]
	local TransChapter = A["TransChapter"]
	local TransChapter_origin = A:ORIGIN("TransChapter")
	local TransTitle = A["TransTitle"]
	local ScriptTitle = A["ScriptTitle"]

	local Encyclopedia = A["Encyclopedia"]

	if utilities.is_set(Encyclopedia) then
		if "encyclopaedia" ~= CitationClass and "citation" ~= CitationClass then
			utilities.set_message("err_parameter_ignored", {A:ORIGIN("Encyclopedia")})
			Encyclopedia = nil
		end
	end

	if
		("encyclopaedia" == CitationClass) or
			("citation" == CitationClass and utilities.is_set(Encyclopedia))
	 then
		if utilities.is_set(Periodical) and utilities.is_set(Encyclopedia) then
			utilities.set_message(
				"err_redundant_parameters",
				{
					utilities.wrap_style("parameter", A:ORIGIN("Encyclopedia")) ..
						" و " .. utilities.wrap_style("parameter", Periodical_origin)
				}
			)
		end

		if utilities.is_set(Encyclopedia) then
			Periodical = Encyclopedia
			Periodical_origin = A:ORIGIN("Encyclopedia")
		end

		if utilities.is_set(Periodical) then
			if utilities.is_set(Title) or utilities.is_set(ScriptTitle) then
				if not utilities.is_set(Chapter) then
					Chapter = Title
					ScriptChapter = ScriptTitle
					ScriptChapter_origin = A:ORIGIN("ScriptTitle")
					TransChapter = TransTitle
					ChapterURL = URL
					ChapterURL_origin = URL_origin

					ChapterUrlAccess = UrlAccess

					if not utilities.is_set(ChapterURL) and utilities.is_set(TitleLink) then
						Chapter = utilities.make_wikilink(TitleLink, Chapter)
					end
					Title = Periodical
					ChapterFormat = Format
					Periodical = ""
					TransTitle = ""
					URL = ""
					Format = ""
					TitleLink = ""
					ScriptTitle = ""
				end
			elseif utilities.is_set(Chapter) or utilities.is_set(ScriptChapter) then
				Title = Periodical
				Periodical = ""
			end
		end
	end

	local ID = A["ID"]
	if (CitationClass == "techreport") then
		if utilities.is_set(A["Number"]) then
			if not utilities.is_set(ID) then
				ID = A["Number"]
			else
				utilities.set_message(
					"err_redundant_parameters",
					{utilities.wrap_style("parameter", "id") .. " و " .. utilities.wrap_style("parameter", "number")}
				)
			end
		end
	end

	local ChapterLink
	local Conference = A["Conference"]
	local BookTitle = A["BookTitle"]
	local TransTitle_origin = A:ORIGIN("TransTitle")
	if "conference" == CitationClass then
		if utilities.is_set(BookTitle) then
			Chapter = Title
			Chapter_origin = "title"

			ChapterURL = URL
			ChapterUrlAccess = UrlAccess
			ChapterURL_origin = URL_origin
			URL_origin = ""
			ChapterFormat = Format
			TransChapter = TransTitle
			TransChapter_origin = TransTitle_origin
			Title = BookTitle
			Format = ""

			TransTitle = ""
			URL = ""
		end
	elseif "speech" ~= CitationClass then
		Conference = ""
	end

	local Mode = is_valid_parameter_value(A["Mode"], A:ORIGIN("Mode"), cfg.keywords_lists["mode"], "")

	local sepc, PostScript = set_style(Mode:lower(), A["PostScript"], CitationClass)

	local use_lowercase = (sepc == ",")

	local Cartography = ""
	local Scale = ""
	local Sheet = A["Sheet"] or ""
	local Sheets = A["Sheets"] or ""
	if CitationClass == "map" then
		if utilities.is_set(Chapter) then
			utilities.set_message(
				"err_redundant_parameters",
				{
					utilities.wrap_style("parameter", "map") ..
						" و " .. utilities.wrap_style("parameter", Chapter_origin)
				}
			)
		end
		Chapter = A["Map"]
		Chapter_origin = A:ORIGIN("Map")
		ChapterURL = A["MapURL"]
		ChapterURL_origin = A:ORIGIN("MapURL")
		TransChapter = A["TransMap"]
		ScriptChapter = A["ScriptMap"]
		ScriptChapter_origin = A:ORIGIN("ScriptMap")

		ChapterUrlAccess = MapUrlAccess
		ChapterFormat = A["MapFormat"]

		Cartography = A["Cartography"]
		if utilities.is_set(Cartography) then
			Cartography = sepc .. " " .. wrap_msg("cartography", Cartography, use_lowercase)
		end
		Scale = A["Scale"]
		if utilities.is_set(Scale) then
			Scale = sepc .. " " .. Scale
		end
	end

	local Series = A["Series"]
	if "episode" == CitationClass or "serial" == CitationClass then
		local SeriesLink = A["SeriesLink"]

		SeriesLink = link_title_ok(SeriesLink, A:ORIGIN("SeriesLink"), Series, "series")

		local Network = A["Network"]
		local Station = A["Station"]
		local s, n = {}, {}

		if utilities.is_set(Network) then
			table.insert(n, Network)
		end
		if utilities.is_set(Station) then
			table.insert(n, Station)
		end
		ID = table.concat(n, sepc .. " ")

		if "episode" == CitationClass then
			local Season = A["Season"]
			local SeriesNumber = A["SeriesNumber"]

			if utilities.is_set(Season) and utilities.is_set(SeriesNumber) then
				utilities.set_message(
					"err_redundant_parameters",
					{
						utilities.wrap_style("parameter", "season") ..
							" و " .. utilities.wrap_style("parameter", "seriesno")
					}
				)
				SeriesNumber = ""
			end

			if utilities.is_set(Season) then
				table.insert(s, wrap_msg("season", Season, use_lowercase))
			end
			if utilities.is_set(SeriesNumber) then
				table.insert(s, wrap_msg("seriesnum", SeriesNumber, use_lowercase))
			end
			if utilities.is_set(Issue) then
				table.insert(s, wrap_msg("episode", Issue, use_lowercase))
			end
			Issue = ""

			Chapter = Title
			ScriptChapter = ScriptTitle
			ScriptChapter_origin = A:ORIGIN("ScriptTitle")
			ChapterLink = TitleLink
			TransChapter = TransTitle
			ChapterURL = URL
			ChapterUrlAccess = UrlAccess
			ChapterURL_origin = URL_origin

			Title = Series
			TitleLink = SeriesLink
			Series = table.concat(s, sepc .. " ")

			if utilities.is_set(ChapterLink) and not utilities.is_set(ChapterURL) then
				Chapter = utilities.make_wikilink(ChapterLink, Chapter)
			elseif utilities.is_set(ChapterLink) and utilities.is_set(ChapterURL) then
				Series = utilities.make_wikilink(ChapterLink, Series)
			end
			URL = ""
			TransTitle = ""
			ScriptTitle = ""
		else
			Issue = ""
			Chapter = A["Episode"]
			if utilities.is_set(Series) and utilities.is_set(SeriesLink) then
				Series = utilities.make_wikilink(SeriesLink, Series)
			end
			Series = utilities.wrap_style("italic-title", Series)
		end
	end

	local TitleType = A["TitleType"]
	local Degree = A["Degree"]
	if
		utilities.in_array(
			CitationClass,
			{
				"AV-media-notes",
				"interview",
				"mailinglist",
				"map",
				"podcast",
				"pressrelease",
				"report",
				"speech",
				"techreport",
				"thesis"
			}
		)
	 then
		TitleType = set_titletype(CitationClass, TitleType)
		if utilities.is_set(Degree) and "Thesis" == TitleType then
			TitleType = Degree .. " " .. cfg.title_types["thesis"]:lower()
		end
	end

	if utilities.is_set(TitleType) then
		TitleType = utilities.substitute(cfg.messages["type"], TitleType)
	end

	local Date = A["Date"]
	local Date_origin
	local PublicationDate = A["PublicationDate"]
	local Year = A["Year"]

	if not utilities.is_set(Date) then
		Date = Year
		Year = nil
		if not utilities.is_set(Date) and utilities.is_set(PublicationDate) then
			Date = PublicationDate
			PublicationDate = ""
			Date_origin = A:ORIGIN("PublicationDate")
		else
			Date_origin = A:ORIGIN("Year")
		end
	else
		Date_origin = A:ORIGIN("Date")
	end

	if PublicationDate == Date then
		PublicationDate = ""
	end

	local DF = is_valid_parameter_value(A["DF"], A:ORIGIN("DF"), cfg.keywords_lists["df"], "")
	if not utilities.is_set(DF) then
		DF = cfg.global_df
	end

	local ArchiveURL
	local ArchiveDate
	local ArchiveFormat = A["ArchiveFormat"]

	ArchiveURL, ArchiveDate = archive_url_check(A["ArchiveURL"], A["ArchiveDate"])
	ArchiveFormat = style_format(ArchiveFormat, ArchiveURL, "archive-format", "archive-url")

	ArchiveURL, ArchiveDate = is_unique_archive_url(ArchiveURL, URL, ChapterURL, A:ORIGIN("ArchiveURL"), ArchiveDate)

	local AccessDate = A["AccessDate"]
	local LayDate = A["LayDate"]
	local COinS_date = {}
	local DoiBroken = A["DoiBroken"]
	local Embargo = A["Embargo"]
	local anchor_year
	do
		local error_message = ""
	
		if utilities.is_set(AccessDate) then AccessDate = check_date_n(AccessDate) end
		if utilities.is_set(ArchiveDate) then ArchiveDate = check_date_n(ArchiveDate) end
		if utilities.is_set(Date) then Date = check_date_n(Date) end
		if utilities.is_set(LayDate) then LayDate = check_date_n(LayDate) end
		if utilities.is_set(PublicationDate) then PublicationDate = check_date_n(PublicationDate) end

		local date_parameters_list = {
			["access-date"] = {val = AccessDate, name = A:ORIGIN("AccessDate")},
			["archive-date"] = {val = ArchiveDate, name = A:ORIGIN("ArchiveDate")},
			["date"] = {val = Date, name = Date_origin},
			["doi-broken-date"] = {val = DoiBroken, name = A:ORIGIN("DoiBroken")},
			["pmc-embargo-date"] = {val = Embargo, name = A:ORIGIN("Embargo")},
			["lay-date"] = {val = LayDate, name = A:ORIGIN("LayDate")},
			["publication-date"] = {val = PublicationDate, name = A:ORIGIN("PublicationDate")},
			["year"] = {val = Year, name = A:ORIGIN("Year")}
		}
		--mw.log("AccessDate: " .. AccessDate)
		local error_list = {}
		anchor_year, Embargo = validation.dates(date_parameters_list, COinS_date, error_list)

		if COinS_date.inter_cal_cat then
			utilities.add_prop_cat("jul-greg-uncertainty")
		end

		if utilities.is_set(Year) and utilities.is_set(Date) then
			validation.year_date_check(Year, A:ORIGIN("Year"), Date, A:ORIGIN("Date"), error_list)
		end

		if 0 == #error_list then
			mw.log(" 0 == #error_list" )
			local modified = false

			if utilities.is_set(DF) then
				mw.log(" start reformat_dates" )
				modified = validation.reformat_dates(date_parameters_list, DF)
			end

			if true == validation.date_hyphen_to_dash(date_parameters_list) then
				modified = true
				utilities.set_message("maint_date_format")
			end

			if
				cfg.date_name_auto_xlate_enable and
					validation.date_name_xlate(date_parameters_list, cfg.date_digit_auto_xlate_enable)
			 then
				utilities.set_message("maint_date_auto_xlated")
				modified = true
			end

			if modified then
				AccessDate = date_parameters_list["access-date"].val
				ArchiveDate = date_parameters_list["archive-date"].val
				Date = date_parameters_list["date"].val
				DoiBroken = date_parameters_list["doi-broken-date"].val
				LayDate = date_parameters_list["lay-date"].val
				PublicationDate = date_parameters_list["publication-date"].val
			end
		else
			utilities.set_message("err_bad_date", {utilities.make_sep_list(#error_list, error_list)})
		end
	end

	local ID_list = {}
	local ID_list_coins = {}
	local Class = A["Class"]

	local ID_support = {
		{A["ASINTLD"], "ASIN", "err_asintld_missing_asin", A:ORIGIN("ASINTLD")},
		{DoiBroken, "DOI", "err_doibroken_missing_doi", A:ORIGIN("DoiBroken")},
		{Embargo, "PMC", "err_embargo_missing_pmc", A:ORIGIN("Embargo")}
	}

	ID_list, ID_list_coins =
		identifiers.identifier_lists_get(
		args,
		{DoiBroken = DoiBroken, ASINTLD = A["ASINTLD"], Embargo = Embargo, Class = Class},
		ID_support
	)

	if utilities.in_array(CitationClass, whitelist.preprint_template_list) then
		if not utilities.is_set(ID_list_coins[CitationClass:upper()]) then
			utilities.set_message("err_" .. CitationClass .. "_missing")
		end

		Periodical =
			({
			["arxiv"] = "arXiv",
			["biorxiv"] = "bioRxiv",
			["citeseerx"] = "CiteSeerX",
			["ssrn"] = "Social Science Research Network"
		})[CitationClass]
	end

	if
		CitationClass == "journal" and not utilities.is_set(URL) and not utilities.is_set(TitleLink) and
			not utilities.in_array(cfg.keywords_xlate[Title], {"off", "none"})
	 then
		if "none" ~= cfg.keywords_xlate[auto_select] then
			if identifiers.auto_link_urls[auto_select] then
				URL = identifiers.auto_link_urls[auto_select]
				URL_origin = cfg.id_handlers[auto_select:upper()].parameters[1]
			elseif identifiers.auto_link_urls["pmc"] then
				URL = identifiers.auto_link_urls["pmc"]
				URL_origin = cfg.id_handlers["PMC"].parameters[1]
			elseif identifiers.auto_link_urls["doi"] then
				URL = identifiers.auto_link_urls["doi"]
				URL_origin = cfg.id_handlers["DOI"].parameters[1]
			end
		end

		if utilities.is_set(URL) and utilities.is_set(AccessDate) then
			utilities.set_message("err_accessdate_missing_url")
			AccessDate = ""
		end
	end

	if not utilities.is_set(Title) and not utilities.is_set(TransTitle) and not utilities.is_set(ScriptTitle) then
		utilities.set_message("err_citation_missing_title", {"episode" == CitationClass and "series" or "title"})
	end

	if
		utilities.in_array(cfg.keywords_xlate[Title], {"off", "none"}) and
			utilities.in_array(CitationClass, {"journal", "citation"}) and
			(utilities.is_set(Periodical) or utilities.is_set(ScriptPeriodical)) and
			("journal" == Periodical_origin or "script-journal" == ScriptPeriodical_origin)
	 then
		Title = ""
		utilities.set_message("maint_untitled")
	end

	local coins_chapter = Chapter
	local coins_title = Title
	if
		"encyclopaedia" == CitationClass or
			("citation" == CitationClass and utilities.is_set(Encyclopedia))
	 then
		if utilities.is_set(Chapter) and utilities.is_set(Title) and utilities.is_set(Periodical) then
			coins_chapter = Title
			coins_title = Periodical
		end
	end
	local coins_author = a
	if 0 < #c then
		coins_author = c
	end

	local QuotePage = A["QuotePage"]
	local QuotePages = utilities.hyphen_to_dash(A["QuotePages"])

	local OCinSoutput =
		metadata.COinS(
		{
			["Periodical"] = utilities.strip_apostrophe_markup(Periodical),
			["Encyclopedia"] = Encyclopedia,
			["Chapter"] = metadata.make_coins_title(coins_chapter, ScriptChapter),
			["Degree"] = Degree,
			["Title"] = metadata.make_coins_title(coins_title, ScriptTitle),
			["PublicationPlace"] = PublicationPlace,
			["Date"] = COinS_date.rftdate,
			["Season"] = COinS_date.rftssn,
			["Quarter"] = COinS_date.rftquarter,
			["Chron"] = COinS_date.rftchron or (not COinS_date.rftdate and Date) or "",
			["Series"] = Series,
			["Volume"] = Volume,
			["Issue"] = Issue,
			["Pages"] = coins_pages or
				metadata.get_coins_pages(first_set({Sheet, Sheets, Page, Pages, At, QuotePage, QuotePages}, 7)),
			["Edition"] = Edition,
			["PublisherName"] = PublisherName or Newsgroup,
			["URL"] = first_set({ChapterURL, URL}, 2),
			["Authors"] = coins_author,
			["ID_list"] = ID_list_coins,
			["RawPage"] = this_page.prefixedText
		},
		CitationClass
	)

	if utilities.in_array(CitationClass, whitelist.preprint_template_list) then
		Periodical = ""
	end

	if "newsgroup" == CitationClass and utilities.is_set(Newsgroup) then
		PublisherName =
			utilities.substitute(
			cfg.messages["newsgroup"],
			external_link("news:" .. Newsgroup, Newsgroup, Newsgroup_origin, nil)
		)
	end

	local Editors
	local EditorCount
	local Contributors
	local contributor_etal
	local Translators
	local translator_etal
	local t = {}
	t = extract_names(args, "TranslatorList")
	local Interviewers
	local interviewers_list = {}
	interviewers_list = extract_names(args, "InterviewerList")
	local interviewer_etal

	do
		local last_first_list
		local control = {
			format = NameListStyle,
			maximum = nil,
			mode = Mode
		}

		do
			control.maximum, editor_etal =
				get_display_names(A["DisplayEditors"], #e, "editors", editor_etal, A:ORIGIN("DisplayEditors"))
			Editors, EditorCount = list_people(control, e, editor_etal)

			if 1 == EditorCount and (true == editor_etal or 1 < #e) then
				EditorCount = 2
			end
		end
		do
			control.maximum, interviewer_etal =
				get_display_names(
				A["DisplayInterviewers"],
				#interviewers_list,
				"interviewers",
				interviewer_etal,
				A:ORIGIN("DisplayInterviewers")
			)
			Interviewers = list_people(control, interviewers_list, interviewer_etal)
		end
		do
			control.maximum, translator_etal =
				get_display_names(
				A["DisplayTranslators"],
				#t,
				"translators",
				translator_etal,
				A:ORIGIN("DisplayTranslators")
			)
			Translators = list_people(control, t, translator_etal)
		end
		do
			control.maximum, contributor_etal =
				get_display_names(
				A["DisplayContributors"],
				#c,
				"contributors",
				contributor_etal,
				A:ORIGIN("DisplayContributors")
			)
			Contributors = list_people(control, c, contributor_etal)
		end
		do
			control.maximum, author_etal =
				get_display_names(A["DisplayAuthors"], #a, "authors", author_etal, A:ORIGIN("DisplayAuthors"))

			last_first_list = list_people(control, a, author_etal)

			if utilities.is_set(Authors) then
				Authors, author_etal = name_has_etal(Authors, author_etal, false, "authors")
				if author_etal then
					Authors = Authors .. " " .. cfg.messages["et al"]
				end
			else
				Authors = last_first_list
			end
		end

		if utilities.is_set(Authors) and utilities.is_set(Collaboration) then
			Authors = Authors .. " (" .. Collaboration .. ")"
		end
	end

	local ConferenceFormat = A["ConferenceFormat"]
	local ConferenceURL = A["ConferenceURL"]
	ConferenceFormat = style_format(ConferenceFormat, ConferenceURL, "conference-format", "conference-url")
	Format = style_format(Format, URL, "format", "url")

	if
		not (utilities.in_array(
			CitationClass,
			{
				"web",
				"news",
				"journal",
				"magazine",
				"pressrelease",
				"podcast",
				"newsgroup",
				"arxiv",
				"biorxiv",
				"citeseerx",
				"ssrn"
			}
		) or
			("citation" == CitationClass and (utilities.is_set(Periodical) or utilities.is_set(ScriptPeriodical)) and
				not utilities.is_set(Encyclopedia)))
	 then
		ChapterFormat = style_format(ChapterFormat, ChapterURL, "chapter-format", "chapter-url")
	end

	if not utilities.is_set(URL) then
		if
			utilities.in_array(CitationClass, {"web", "podcast", "mailinglist"}) or
				("citation" == CitationClass and
					("website" == Periodical_origin or "script-website" == ScriptPeriodical_origin))
		 then
			utilities.set_message("err_cite_web_url")
		end

		if utilities.is_set(AccessDate) and not utilities.is_set(ChapterURL) then
			utilities.set_message("err_accessdate_missing_url")
			AccessDate = ""
		end
	end

	local UrlStatus =
		is_valid_parameter_value(A["UrlStatus"], A:ORIGIN("UrlStatus"), cfg.keywords_lists["url-status"], "")
	local OriginalURL
	local OriginalURL_origin
	local OriginalFormat
	local OriginalAccess
	UrlStatus = UrlStatus:lower()
	if utilities.is_set(ArchiveURL) then
		if utilities.is_set(ChapterURL) then
			OriginalURL = ChapterURL
			OriginalURL_origin = ChapterURL_origin
			OriginalFormat = ChapterFormat

			if "live" ~= UrlStatus then
				ChapterURL = ArchiveURL
				ChapterURL_origin = A:ORIGIN("ArchiveURL")
				ChapterFormat = ArchiveFormat or ""
				ChapterUrlAccess = nil
			end
		elseif utilities.is_set(URL) then
			OriginalURL = URL
			OriginalURL_origin = URL_origin
			OriginalFormat = Format
			OriginalAccess = UrlAccess

			if "live" ~= UrlStatus then
				URL = ArchiveURL
				URL_origin = A:ORIGIN("ArchiveURL")
				Format = ArchiveFormat or ""
				UrlAccess = nil
			end
		end
	elseif utilities.is_set(UrlStatus) then
		utilities.set_message("maint_url_status")
	end

	if
		utilities.in_array(
			CitationClass,
			{
				"web",
				"news",
				"journal",
				"magazine",
				"pressrelease",
				"podcast",
				"newsgroup",
				"arxiv",
				"biorxiv",
				"citeseerx",
				"ssrn"
			}
		) or
			("citation" == CitationClass and (utilities.is_set(Periodical) or utilities.is_set(ScriptPeriodical)) and
				not utilities.is_set(Encyclopedia))
	 then
		local chap_param
		if utilities.is_set(Chapter) then
			chap_param = A:ORIGIN("Chapter")
		elseif utilities.is_set(TransChapter) then
			chap_param = A:ORIGIN("TransChapter")
		elseif utilities.is_set(ChapterURL) then
			chap_param = A:ORIGIN("ChapterURL")
		elseif utilities.is_set(ScriptChapter) then
			chap_param = ScriptChapter_origin
		else
			utilities.is_set(ChapterFormat)
			chap_param = A:ORIGIN("ChapterFormat")
		end

		if utilities.is_set(chap_param) then
			utilities.set_message("err_chapter_ignored", {chap_param})
			Chapter = ""
			TransChapter = ""
			ChapterURL = ""
			ScriptChapter = ""
			ChapterFormat = ""
		end
	else
		local no_quotes = false
		if utilities.is_set(Contribution) and 0 < #c then
			if utilities.in_array(Contribution:lower(), cfg.keywords_lists.contribution) then
				no_quotes = true
			end
		end

		Chapter =
			format_chapter_title(
			ScriptChapter,
			ScriptChapter_origin,
			Chapter,
			Chapter_origin,
			TransChapter,
			TransChapter_origin,
			ChapterURL,
			ChapterURL_origin,
			no_quotes,
			ChapterUrlAccess
		)
		if utilities.is_set(Chapter) then
			Chapter = Chapter .. ChapterFormat
			if "map" == CitationClass and utilities.is_set(TitleType) then
				Chapter = Chapter .. " " .. TitleType
			end
			Chapter = Chapter .. sepc .. " "
		elseif utilities.is_set(ChapterFormat) then
			Chapter = ChapterFormat .. sepc .. " "
		end
	end

	local plain_title = false
	local accept_title
	Title, accept_title = utilities.has_accept_as_written(Title, true)
	if accept_title and ("" == Title) then
		Title = cfg.messages["notitle"]

		ScriptTitle = ""
		TransTitle = ""
		plain_title = true
		utilities.set_message("maint_untitled")
	end

	if not accept_title then
		if "..." == Title:sub(-3) then
			Title = Title:gsub("(%.%.%.)%.+$", "%1")
		elseif not mw.ustring.find(Title, "%.%s*%a%.$") and not mw.ustring.find(Title, "%s+%a%.$") then
			Title = mw.ustring.gsub(Title, "%" .. sepc .. "$", "")
		end

		if utilities.is_set(ArchiveURL) and is_archived_copy(Title) then
			utilities.set_message("maint_archived_copy")
		end

		if is_generic("generic_titles", Title) then
			utilities.set_message("err_generic_title")
		end
	end

	if
		(not plain_title) and
			(utilities.in_array(
				CitationClass,
				{
					"web",
					"news",
					"journal",
					"magazine",
					"pressrelease",
					"podcast",
					"newsgroup",
					"mailinglist",
					"interview",
					"arxiv",
					"biorxiv",
					"citeseerx",
					"ssrn"
				}
			) or
				("citation" == CitationClass and
					(utilities.is_set(Periodical) or utilities.is_set(ScriptPeriodical)) and
					not utilities.is_set(Encyclopedia)) or
				("map" == CitationClass and (utilities.is_set(Periodical) or utilities.is_set(ScriptPeriodical))))
	 then
		Title = kern_quotes(Title)
		Title = utilities.wrap_style("quoted-title", Title)
		Title = script_concatenate(Title, ScriptTitle, "script-title")
		TransTitle = utilities.wrap_style("trans-quoted-title", TransTitle)
	elseif plain_title or ("report" == CitationClass) then
		Title = script_concatenate(Title, ScriptTitle, "script-title")
		TransTitle = utilities.wrap_style("trans-quoted-title", TransTitle)
	else
		Title = utilities.wrap_style("italic-title", Title)
		Title = script_concatenate(Title, ScriptTitle, "script-title")
		TransTitle = utilities.wrap_style("trans-italic-title", TransTitle)
	end

	if utilities.is_set(TransTitle) then
		if utilities.is_set(Title) then
			TransTitle = " " .. TransTitle
		else
			utilities.set_message("err_trans_missing_title", {"title"})
		end
	end

	if utilities.is_set(Title) then
		if utilities.is_set(TitleLink) and utilities.is_set(URL) then
			utilities.set_message("err_wikilink_in_url")
			TitleLink = ""
		end

		if not utilities.is_set(TitleLink) and utilities.is_set(URL) then
			Title = external_link(URL, Title, URL_origin, UrlAccess) .. TransTitle .. Format
			URL = ""
			Format = ""
		elseif utilities.is_set(TitleLink) and not utilities.is_set(URL) then
			local ws_url
			ws_url = wikisource_url_make(TitleLink)
			if ws_url then
				Title = external_link(ws_url, Title .. "&nbsp;", "ws link in title-link")
				Title =
					utilities.substitute(
					cfg.presentation["interwiki-icon"],
					{cfg.presentation["class-wikisource"], TitleLink, Title}
				)
				Title = Title .. TransTitle
			else
				Title = utilities.make_wikilink(TitleLink, Title) .. TransTitle
			end
		else
			local ws_url, ws_label, L
			ws_url, ws_label, L = wikisource_url_make(Title:gsub('^[\'"]*(.-)[\'"]*$', "%1"))
			if ws_url then
				Title = Title:gsub("%b[]", ws_label)
				Title = external_link(ws_url, Title .. "&nbsp;", "ws link in title")
				Title =
					utilities.substitute(
					cfg.presentation["interwiki-icon"],
					{cfg.presentation["class-wikisource"], L, Title}
				)
				Title = Title .. TransTitle
			else
				Title = Title .. TransTitle
			end
		end
	else
		Title = TransTitle
	end

	if utilities.is_set(Place) then
		Place = " " .. wrap_msg("written", Place, use_lowercase) .. sepc .. " "
	end

	local ConferenceURL_origin = A:ORIGIN("ConferenceURL")
	if utilities.is_set(Conference) then
		if utilities.is_set(ConferenceURL) then
			Conference = external_link(ConferenceURL, Conference, ConferenceURL_origin, nil)
		end
		Conference = sepc .. " " .. Conference .. ConferenceFormat
	elseif utilities.is_set(ConferenceURL) then
		Conference = sepc .. " " .. external_link(ConferenceURL, nil, ConferenceURL_origin, nil)
	end

	local Position = ""
	if not utilities.is_set(Position) then
		local Minutes = A["Minutes"]
		local Time = A["Time"]

		if utilities.is_set(Minutes) then
			if utilities.is_set(Time) then
				utilities.set_message(
					"err_redundant_parameters",
					{
						utilities.wrap_style("parameter", "minutes") ..
							" و " .. utilities.wrap_style("parameter", "time")
					}
				)
			end
			Position = " " .. Minutes .. " " .. cfg.messages["minutes"]
		else
			if utilities.is_set(Time) then
				local TimeCaption = A["TimeCaption"]
				if not utilities.is_set(TimeCaption) then
					TimeCaption = cfg.messages["event"]
					--if sepc ~= "." then TimeCaption = TimeCaption:lower() end
				end
				Position = " " .. TimeCaption .. " " .. Time
			end
		end
	else
		Position = " " .. Position
		At = ""
	end

	Page, Pages, Sheet, Sheets =
		format_pages_sheets(
		Page,
		Pages,
		Sheet,
		Sheets,
		CitationClass,
		Periodical_origin,
		sepc,
		NoPP,
		use_lowercase
	)

	At = utilities.is_set(At) and (sepc .. " " .. At) or ""
	Position = utilities.is_set(Position) and (sepc .. " " .. Position) or ""
	if CitationClass == "map" then
		local Sections = A["Sections"]
		local Inset = A["Inset"]

		if utilities.is_set(Inset) then
			Inset = sepc .. " " .. wrap_msg("inset", Inset, use_lowercase)
		end

		if utilities.is_set(Sections) then
			Section = sepc .. " " .. wrap_msg("sections", Sections, use_lowercase)
		elseif utilities.is_set(Section) then
			Section = sepc .. " " .. wrap_msg("section", Section, use_lowercase)
		end
		At = At .. Inset .. Section
	end

	local Others = A["Others"]
	if utilities.is_set(Others) and 0 == #a and 0 == #e then
		if CitationClass == "AV-media-notes" or CitationClass == "audio-visual" then
			utilities.set_message("maint_others_avm")
		else
			utilities.set_message("maint_others")
		end
	end
	Others = utilities.is_set(Others) and (sepc .. " " .. Others) or ""

	if utilities.is_set(Translators) then
		Others = safe_join({sepc .. " ", wrap_msg("translated", Translators, use_lowercase), Others}, sepc)
	end
	if utilities.is_set(Interviewers) then
		Others = safe_join({sepc .. " ", wrap_msg("interview", Interviewers, use_lowercase), Others}, sepc)
	end

	local TitleNote = A["TitleNote"]
	TitleNote = utilities.is_set(TitleNote) and (sepc .. " " .. TitleNote) or ""
	if utilities.is_set(Edition) then
		if Edition:match("%f[%a][Ee]d%n?%.?$") or Edition:match("%f[%a][Ee]dition$") then
			utilities.set_message("err_extra_text_edition")
		end
		Edition = " " .. wrap_msg("edition", Edition)
	else
		Edition = ""
	end

	Series = utilities.is_set(Series) and wrap_msg("series", {sepc, Series}) or ""
	local Agency = A["Agency"]
	Agency = utilities.is_set(Agency) and wrap_msg("agency", {sepc, Agency}) or ""
	Volume = format_volume_issue(Volume, Issue, CitationClass, Periodical_origin, sepc, use_lowercase)

	if utilities.is_set(AccessDate) then
		local retrv_text = " " .. cfg.messages["retrieved"]
		--AccessDate = check_date_n(AccessDate)
		AccessDate = nowrap_date(AccessDate)
		--if (sepc ~= ".") then retrv_text = retrv_text:lower() end
		AccessDate = utilities.substitute(retrv_text, AccessDate)

		AccessDate = utilities.substitute(cfg.presentation["accessdate"], {sepc, AccessDate})
	end

	if utilities.is_set(ID) then
		ID = sepc .. " " .. ID
	end

	local Docket = A["Docket"]
	if "thesis" == CitationClass and utilities.is_set(Docket) then
		ID = sepc .. " Docket " .. Docket .. ID
	end
	if "report" == CitationClass and utilities.is_set(Docket) then
		ID = sepc .. " " .. Docket
	end

	if utilities.is_set(URL) then
		URL = " " .. external_link(URL, nil, URL_origin, UrlAccess)
	end

	local Quote = A["Quote"]
	local TransQuote = A["TransQuote"]
	local ScriptQuote = A["ScriptQuote"]
	if utilities.is_set(Quote) or utilities.is_set(TransQuote) or utilities.is_set(ScriptQuote) then
		if utilities.is_set(Quote) then
			if Quote:sub(1, 1) == '"' and Quote:sub(-1, -1) == '"' then
				Quote = Quote:sub(2, -2)
			end
		end

		Quote = utilities.wrap_style("quoted-text", Quote)

		if utilities.is_set(ScriptQuote) then
			Quote = script_concatenate(Quote, ScriptQuote, "script-quote")
		end

		if utilities.is_set(TransQuote) then
			if TransQuote:sub(1, 1) == '"' and TransQuote:sub(-1, -1) == '"' then
				TransQuote = TransQuote:sub(2, -2)
			end
			Quote = Quote .. " " .. utilities.wrap_style("trans-quoted-title", TransQuote)
		end

		if utilities.is_set(QuotePage) or utilities.is_set(QuotePages) then
			local quote_prefix = ""
			if utilities.is_set(QuotePage) then
				extra_text_in_page_check(QuotePage, "quote-page")
				if not NoPP then
					quote_prefix = utilities.substitute(cfg.messages["p-prefix"], {sepc, QuotePage}), "", "", ""
				else
					quote_prefix = utilities.substitute(cfg.messages["nopp"], {sepc, QuotePage}), "", "", ""
				end
			elseif utilities.is_set(QuotePages) then
				extra_text_in_page_check(QuotePages, "quote-pages")
				if tonumber(QuotePages) ~= nil and not NoPP then
					quote_prefix = utilities.substitute(cfg.messages["p-prefix"], {sepc, QuotePages}), "", ""
				elseif not NoPP then
					quote_prefix = utilities.substitute(cfg.messages["pp-prefix"], {sepc, QuotePages}), "", ""
				else
					quote_prefix = utilities.substitute(cfg.messages["nopp"], {sepc, QuotePages}), "", ""
				end
			end

			Quote = quote_prefix .. ": " .. Quote
		else
			Quote = sepc .. " " .. Quote
		end

		PostScript = ""
	end

	if utilities.is_set(PostScript) and mw.ustring.len(PostScript) > 1 then
		utilities.set_message("maint_postscript")
	end

	local Archived
	if utilities.is_set(ArchiveURL) then
		local arch_text
		if not utilities.is_set(ArchiveDate) then
			utilities.set_message("err_archive_missing_date")
			ArchiveDate = ""
		--else
			--ArchiveDate = check_date_n(ArchiveDate)
		end
		if "live" == UrlStatus then
			arch_text = cfg.messages["archived"]
			--if sepc ~= "." then arch_text = arch_text:lower() end
			if utilities.is_set(ArchiveDate) then
				Archived =
					sepc ..
					" " ..
						utilities.substitute(
							cfg.messages["archived-live"],
							{
								external_link(ArchiveURL, arch_text, A:ORIGIN("ArchiveURL"), nil) .. ArchiveFormat,
								ArchiveDate
							}
						)
			else
				Archived = ""
			end
			if not utilities.is_set(OriginalURL) then
				utilities.set_message("err_archive_missing_url")
				Archived = ""
			end
		elseif utilities.is_set(OriginalURL) then
			if utilities.in_array(UrlStatus, {"unfit", "usurped", "bot: unknown"}) then
				arch_text = cfg.messages["archived-unfit"]
				--if sepc ~= "." then arch_text = arch_text:lower() end
				Archived = sepc .. " " .. arch_text .. ArchiveDate
				if "bot: unknown" == UrlStatus then
					utilities.set_message("maint_bot_unknown")
				else
					utilities.set_message("maint_unfit")
				end
			else
				arch_text = cfg.messages["archived-dead"]
				--if sepc ~= "." then arch_text = arch_text:lower() end
				if utilities.is_set(ArchiveDate) then
					Archived =
						sepc ..
						" " ..
							utilities.substitute(
								arch_text,
								{
									external_link(
										OriginalURL,
										cfg.messages["original"],
										OriginalURL_origin,
										OriginalAccess
									) .. OriginalFormat,
									ArchiveDate
								}
							)
				else
					Archived = ""
				end
			end
		else
			arch_text = cfg.messages["archived-missing"]
			--if sepc ~= "." then arch_text = arch_text:lower() end
			utilities.set_message("err_archive_missing_url")
			Archived = ""
		end
	elseif utilities.is_set(ArchiveFormat) then
		Archived = ArchiveFormat
	else
		Archived = ""
	end

	local Lay = ""
	local LaySource = A["LaySource"]
	local LayURL = A["LayURL"]
	local LayFormat = A["LayFormat"]
	LayFormat = style_format(LayFormat, LayURL, "lay-format", "lay-url")
	if utilities.is_set(LayURL) then
		if utilities.is_set(LayDate) then
			LayDate = " (" .. LayDate .. ")"
		end
		if utilities.is_set(LaySource) then
			LaySource = " &ndash; ''" .. utilities.safe_for_italics(LaySource) .. "''"
		else
			LaySource = ""
		end
		if sepc == "." then
			Lay =
				sepc ..
				" " ..
					external_link(LayURL, cfg.messages["lay summary"], A:ORIGIN("LayURL"), nil) ..
						LayFormat .. LaySource .. LayDate
		else
			Lay =
				sepc ..
				" " ..
					external_link(LayURL, cfg.messages["lay summary"]:lower(), A:ORIGIN("LayURL"), nil) ..
						LayFormat .. LaySource .. LayDate
		end
	elseif utilities.is_set(LayFormat) then
		Lay = sepc .. LayFormat
	end

	local TranscriptURL = A["TranscriptURL"]
	local TranscriptFormat = A["TranscriptFormat"]
	TranscriptFormat = style_format(TranscriptFormat, TranscriptURL, "transcript-format", "transcripturl")
	local Transcript = A["Transcript"]
	local TranscriptURL_origin = A:ORIGIN("TranscriptURL")
	if utilities.is_set(Transcript) then
		if utilities.is_set(TranscriptURL) then
			Transcript = external_link(TranscriptURL, Transcript, TranscriptURL_origin, nil)
		end
		Transcript = sepc .. " " .. Transcript .. TranscriptFormat
	elseif utilities.is_set(TranscriptURL) then
		Transcript = external_link(TranscriptURL, nil, TranscriptURL_origin, nil)
	end

	local Publisher
	if utilities.is_set(PublicationDate) then
		-- PublicationDate = wrap_msg("published", PublicationDate)
		PublicationDate = "تاريخ النشر: "..  PublicationDate
	end
	if utilities.is_set(PublisherName) then
		if utilities.is_set(PublicationPlace) then
			Publisher = sepc .. " " .. PublicationPlace .. ": " .. PublisherName .. "، " .. PublicationDate
		else
			Publisher = sepc .. " " .. PublisherName .. "، " .. PublicationDate
		end
	elseif utilities.is_set(PublicationPlace) then
		Publisher = sepc .. " " .. PublicationPlace .. "، " .. PublicationDate
	else
		Publisher = PublicationDate
	end

	local TransPeriodical = A["TransPeriodical"]
	local TransPeriodical_origin = A:ORIGIN("TransPeriodical")

	if (utilities.is_set(Periodical) or utilities.is_set(ScriptPeriodical) or utilities.is_set(TransPeriodical)) then
		if utilities.is_set(Title) or utilities.is_set(TitleNote) then
			Periodical =
				sepc ..
				" " ..
					format_periodical(
						ScriptPeriodical,
						ScriptPeriodical_origin,
						Periodical,
						TransPeriodical,
						TransPeriodical_origin
					)
		else
			Periodical =
				format_periodical(
				ScriptPeriodical,
				ScriptPeriodical_origin,
				Periodical,
				TransPeriodical,
				TransPeriodical_origin
			)
		end
	end

	local Language = A["Language"]
	if utilities.is_set(Language) then
		Language = language_parameter(Language)
	else
		Language = ""
	end

	if "speech" == CitationClass then
		TitleNote = TitleType
		TitleType = ""

		if utilities.is_set(Periodical) then
			if utilities.is_set(Conference) then
				Conference = Conference .. sepc .. " "
			end
		end
	end

	local tcommon
	local tcommon2

	if utilities.in_array(CitationClass, {"journal", "citation"}) and utilities.is_set(Periodical) then
		if utilities.is_set(Others) then
			Others = safe_join({Others, sepc .. " "}, sepc)
		end
		tcommon =
			safe_join(
			{
				Others,
				Title,
				TitleNote,
				Conference,
				Periodical,
				Format,
				TitleType,
				Series,
				Language,
				Edition,
				Publisher,
				Agency,
				Volume
			},
			sepc
		)
	elseif utilities.in_array(CitationClass, {"book", "citation"}) and not utilities.is_set(Periodical) then
		if utilities.is_set(Contributors) then
			tcommon = safe_join({Title, TitleNote}, sepc)
			tcommon2 =
				safe_join(
				{
					Conference,
					Periodical,
					Format,
					TitleType,
					Series,
					Language,
					Others,
					Edition,
					Publisher,
					Agency,
					Volume
				},
				sepc
			)
		else
			tcommon =
				safe_join(
				{
					Title,
					TitleNote,
					Conference,
					Periodical,
					Format,
					TitleType,
					Series,
					Language,
					Others,
					Edition,
					Publisher,
					Agency,
					Volume
				},
				sepc
			)
		end
	elseif "map" == CitationClass then
		if utilities.is_set(Chapter) then
			tcommon =
				safe_join(
				{Title, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher, Volume},
				sepc
			)
		elseif utilities.is_set(Periodical) then
			tcommon =
				safe_join(
				{Title, TitleType, Format, Periodical, Scale, Series, Language, Cartography, Others, Publisher, Volume},
				sepc
			)
		else
			tcommon =
				safe_join(
				{Title, TitleType, Format, Edition, Scale, Series, Language, Cartography, Others, Publisher},
				sepc
			)
		end
	elseif "episode" == CitationClass then
		tcommon = safe_join({Title, TitleNote, TitleType, Series, Language, Edition, Publisher}, sepc)
	else
		tcommon =
			safe_join(
			{
				Title,
				TitleNote,
				Conference,
				Periodical,
				Format,
				TitleType,
				Series,
				Language,
				Volume,
				Others,
				Edition,
				Publisher,
				Agency
			},
			sepc
		)
	end

	if #ID_list > 0 then
		ID_list = safe_join({sepc .. " ", table.concat(ID_list, sepc .. " "), ID}, sepc)
	else
		ID_list = ID
	end

	local Via = A["Via"]
	Via = utilities.is_set(Via) and wrap_msg("via", Via) or ""
	local idcommon
	if "audio-visual" == CitationClass or "episode" == CitationClass then
		idcommon = safe_join({ID_list, URL, Archived, Transcript, AccessDate, Via, Lay, Quote}, sepc)
	else
		idcommon = safe_join({ID_list, URL, Archived, AccessDate, Via, Lay, Quote}, sepc)
	end

	local text
	local pgtext = Position .. Sheet .. Sheets .. Page .. Pages .. At

	local OrigDate = A["OrigDate"]
	OrigDate = utilities.is_set(OrigDate) and wrap_msg("origdate", OrigDate) or ""
	if utilities.is_set(Date) then
		if utilities.is_set(Authors) or utilities.is_set(Editors) then
			Date = " (" .. Date .. ")" .. OrigDate .. sepc .. " "
		else
			if (string.sub(tcommon, -1, -1) == sepc) then
				Date = " " .. Date .. OrigDate
			else
				Date = sepc .. " " .. Date .. OrigDate
			end
		end
	end
	if utilities.is_set(Authors) then
		if (not utilities.is_set(Date)) then
			Authors = terminate_name_list(Authors, sepc)
		end
		if utilities.is_set(Editors) then
			local in_text = " "
			local post_text = ""
			if utilities.is_set(Chapter) and 0 == #c then
				in_text = in_text .. cfg.messages["in"] .. " "
				--if (sepc ~= ".") then in_text = in_text:lower() end
			end
			if EditorCount <= 1 then
				post_text = " (" .. cfg.messages["editor"] .. ")"
			else
				post_text = " (" .. cfg.messages["editors"] .. ")"
			end
			Editors = terminate_name_list(in_text .. Editors .. post_text, sepc)
		end
		if utilities.is_set(Contributors) then
			local by_text = sepc .. " " .. cfg.messages["by"] .. " "
			--if (sepc ~= ".") then by_text = by_text:lower() end
			Authors = by_text .. Authors
			if utilities.is_set(Editors) and utilities.is_set(Date) then
				Authors = terminate_name_list(Authors, sepc)
			end
			if (not utilities.is_set(Date)) then
				Contributors = terminate_name_list(Contributors, sepc)
			end
			text =
				safe_join(
				{Contributors, Date, Chapter, tcommon, Authors, Place, Editors, tcommon2, pgtext, idcommon},
				sepc
			)
		else
			text = safe_join({Authors, Date, Chapter, Place, Editors, tcommon, pgtext, idcommon}, sepc)
		end
	elseif utilities.is_set(Editors) then
		if utilities.is_set(Date) then
			if EditorCount <= 1 then
				Editors = Editors .. ", " .. cfg.messages["editor"]
			else
				Editors = Editors .. ", " .. cfg.messages["editors"]
			end
		else
			if EditorCount <= 1 then
				Editors = Editors .. " (" .. cfg.messages["editor"] .. ")" .. sepc .. " "
			else
				Editors = Editors .. " (" .. cfg.messages["editors"] .. ")" .. sepc .. " "
			end
		end
		text = safe_join({Editors, Date, Chapter, Place, tcommon, pgtext, idcommon}, sepc)
	else
		if utilities.in_array(CitationClass, {"journal", "citation"}) and utilities.is_set(Periodical) then
			text = safe_join({Chapter, Place, tcommon, pgtext, Date, idcommon}, sepc)
		else
			text = safe_join({Chapter, Place, tcommon, Date, pgtext, idcommon}, sepc)
		end
	end

	if utilities.is_set(PostScript) and PostScript ~= sepc then
		text = safe_join({text, sepc}, sepc)
		text = text:sub(1, -sepc:len() - 1)
	end

	text = safe_join({text, PostScript}, sepc)

	local options_t = {}
	options_t.class = cite_class_attribute_make(CitationClass, Mode)

	local Ref = is_valid_parameter_value(A["Ref"], A:ORIGIN("Ref"), cfg.keywords_lists["ref"], nil, true)

	if "none" ~= cfg.keywords_xlate[(Ref and Ref:lower()) or ""] then
		local namelist_t = {}
		local year = first_set({Year, anchor_year}, 2)

		if #c > 0 then
			namelist_t = c
		elseif #a > 0 then
			namelist_t = a
		elseif #e > 0 then
			namelist_t = e
		end
		local citeref_id
		if #namelist_t > 0 then
			citeref_id = make_citeref_id(namelist_t, year)
			if mw.uri.anchorEncode(citeref_id) == ((Ref and mw.uri.anchorEncode(Ref)) or "") then
				utilities.set_message("maint_ref_duplicates_default")
			end
		else
			citeref_id = ""
		end
		options_t.id = Ref or citeref_id
	end

	if string.len(text:gsub("%b<>", "")) <= 2 then
		z.error_cats_t = {}
		z.error_msgs_t = {}
		OCinSoutput = nil
		text = ""
		utilities.set_message("err_empty_citation")
	end

	local render_t = {}

	if utilities.is_set(options_t.id) then
		table.insert(
			render_t,
			utilities.substitute(
				cfg.presentation["cite-id"],
				{mw.uri.anchorEncode(options_t.id), mw.text.nowiki(options_t.class), text}
			)
		)
	else
		table.insert(render_t, utilities.substitute(cfg.presentation["cite"], {mw.text.nowiki(options_t.class), text}))
	end

	if OCinSoutput then
		table.insert(render_t, utilities.substitute(cfg.presentation["ocins"], OCinSoutput))
	end
	
	local template_name =
		("citation" == CitationClass) and "citation" or
		"cite " .. (cfg.citation_class_map_t[CitationClass] or CitationClass)
		
	template_name = cfg.ar_temps_names[template_name:lower()] or template_name
	
	local template_link = "[[Template:" .. template_name .. "|" .. template_name .. "]]"
	local msg_prefix = '<code class="cs1-code">{{' .. template_link .. "}}</code>: "

	if 0 ~= #z.error_msgs_t then
		mw.addWarning(utilities.substitute(cfg.messages.warning_msg_e, template_link))

		table.insert(render_t, " ")
		table.sort(z.error_msgs_t)

		local hidden = true
		for _, v in ipairs(z.error_msgs_t) do
			if v:find("cs1-visible-error", 1, true) then
				hidden = false
				break
			end
		end

		z.error_msgs_t[1] = table.concat({utilities.error_comment(msg_prefix, hidden), z.error_msgs_t[1]})
		table.insert(render_t, table.concat(z.error_msgs_t, "، "))
	end

	if 0 ~= #z.maint_cats_t then
		mw.addWarning(utilities.substitute(cfg.messages.warning_msg_m, template_link))

		table.sort(z.maint_cats_t)

		local maint_msgs_t = {}

		if 0 == #z.error_msgs_t then
			table.insert(maint_msgs_t, msg_prefix)
		end

		for _, v in ipairs(z.maint_cats_t) do
			table.insert(
				maint_msgs_t,
				table.concat({v, " (", utilities.substitute(cfg.messages[":cat wikilink"], v), ")"})
			)
		end
		table.insert(render_t, utilities.substitute(cfg.presentation["hidden-maint"], table.concat(maint_msgs_t, " ")))
	end

	if not no_tracking_cats then
		for _, v in ipairs(z.error_cats_t) do
			table.insert(render_t, utilities.substitute(cfg.messages["cat wikilink"], v))
		end
		for _, v in ipairs(z.maint_cats_t) do
			table.insert(render_t, utilities.substitute(cfg.messages["cat wikilink"], v))
		end
		for _, v in ipairs(z.prop_cats_t) do
			table.insert(render_t, utilities.substitute(cfg.messages["cat wikilink"], v))
		end
	end

	return table.concat(render_t)
end

local function validate(name, cite_class, empty)
	local name = tostring(name)
	local enum_name
	local state
	local function state_test(state, name)
		if true == state then
			return true
		end
		if false == state then
			if empty then
				return nil
			end
			deprecated_parameter(name)
			return true
		end
		if "tracked" == state then
			local base_name = name:gsub("%d", "")
			utilities.add_prop_cat("tracked-param", {base_name}, base_name)
			return true
		end
		return nil
	end

	if name:find("#") then
		return nil
	end

	if utilities.in_array(cite_class, whitelist.preprint_template_list) then
		state = whitelist.limited_basic_arguments[name]
		if true == state_test(state, name) then
			return true
		end

		state = whitelist.preprint_arguments[cite_class][name]
		if true == state_test(state, name) then
			return true
		end

		enum_name = name:gsub("%d+", "#")
		state = whitelist.limited_numbered_arguments[enum_name]
		if true == state_test(state, name) then
			return true
		end

		return false
	end

	if utilities.in_array(cite_class, whitelist.unique_param_template_list) then
		state = whitelist.unique_arguments[cite_class][name]
		if true == state_test(state, name) then
			return true
		end
	end

	state = whitelist.basic_arguments[name]
	if true == state_test(state, name) then
		return true
	end

	enum_name = name:gsub("%d+", "#")
	state = whitelist.numbered_arguments[enum_name]
	if true == state_test(state, name) then
		return true
	end
	--mw.log('validate(' .. name .. ', cite_class, empty) == false')
	return false
end

local function inter_wiki_check(parameter, value)
	local prefix = value:match("%[%[(%a+):")
	local _

	if prefix and cfg.inter_wiki_map[prefix:lower()] then
		utilities.set_message("err_bad_paramlink", parameter)
		_, value, _ = utilities.is_wikilink(value)
	end
	return value
end

local function missing_pipe_check(parameter, value)
	local capture
	value = value:gsub("%b<>", "")

	capture = value:match("%s+(%a[%w%-]+)%s*=") or value:match("^(%a[%w%-]+)%s*=")
	if capture and validate(capture) then
		utilities.set_message("err_missing_pipe", parameter)
	end
end

local function has_extraneous_punc(param, value)
	if "number" == type(param) then
		return
	end

	param = param:gsub("%d+", "#")
	if cfg.punct_skip[param] then
		return
	end

	if value:match("[,;:]$") then
		utilities.set_message("maint_extra_punct")
	end
	if value:match("^=") then
		utilities.set_message("maint_extra_punct")
	end
end

local function has_extraneous_url(url_param_t)
	local url_error_t = {}

	check_for_url(url_param_t, url_error_t)
	if 0 ~= #url_error_t then
		table.sort(url_error_t)
		utilities.set_message("err_param_has_ext_link", {utilities.make_sep_list(#url_error_t, url_error_t)})
	end
end

local function citation(frame)
	Frame = frame;																-- save a copy in case we need to display an error message in preview mode
	is_sandbox = nil ~= string.find (frame:getTitle(), 'sandbox', 1, true);
	local pframe = frame:getParent()
	local styles;
	
	if is_sandbox then															-- did the {{#invoke:}} use sandbox version?
		--mw.log('sandbox')
		cfg = mw.loadData ('Module:Citation/CS1/Configuration/sandbox');		-- load sandbox versions of support modules
		whitelist = mw.loadData ('Module:Citation/CS1/Whitelist/sandbox');
		utilities = require ('Module:Citation/CS1/Utilities/sandbox');
		validation = require ('Module:Citation/CS1/Date_validation/sandbox');
		identifiers = require ('Module:Citation/CS1/Identifiers/sandbox');
		metadata = require ('Module:Citation/CS1/COinS/sandbox');
		styles = 'Module:Citation/CS1/sandbox/styles.css';
		
	else																		-- otherwise
		cfg = mw.loadData ('Module:Citation/CS1/Configuration');				-- load live versions of support modules
		whitelist = mw.loadData ('Module:Citation/CS1/Whitelist');
		utilities = require ('Module:Citation/CS1/Utilities');
		validation = require ('Module:Citation/CS1/Date_validation');
		identifiers = require ('Module:Citation/CS1/Identifiers');
		metadata = require ('Module:Citation/CS1/COinS');
		styles = 'Module:Citation/CS1/styles.css';
	end

	utilities.set_selected_modules (cfg);										-- so that functions in Utilities can see the selected cfg tables
	identifiers.set_selected_modules (cfg, utilities);							-- so that functions in Identifiers can see the selected cfg tables and selected Utilities module
	validation.set_selected_modules (cfg, utilities);							-- so that functions in Date validataion can see selected cfg tables and the selected Utilities module
	metadata.set_selected_modules (cfg, utilities);								-- so that functions in COinS can see the selected cfg tables and selected Utilities module

	z = utilities.z;															-- table of error and category tables in Module:Citation/CS1/Utilities

	is_preview_mode = not utilities.is_set (frame:preprocess ('{{REVISIONID}}'));

	local allargs = {};															-- table where we store all of the template's arguments
	local args = {};															-- table where we store all of the template's arguments
	local suggestions = {};														-- table where we store suggestions if we need to loadData them
	local error_text;															-- used as a flag

	if is_sandbox then										-- did the {{#invoke:}} use sandbox version?
		suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions/sandbox' );	-- use the sandbox version
	else
		suggestions = mw.loadData( 'Module:Citation/CS1/Suggestions' );			-- use the live version
	end
	local config = {};															-- table to store parameters from the module {{#invoke:}}
	for k, v in pairs( frame.args ) do											-- get parameters from the {{#invoke}} frame
		if k == "CitationClass" 
		then 
			config[k] = v; 
		else
			--k = suggestions.suggestions[k] or k
			allargs[k] = v;
		end
	end																-- table to store parameters from the module {{#invoke:}}
	for yk, vy in pairs( pframe.args ) do
		if yk ~= "وصلة مكسورة" then 
			--yk = suggestions.suggestions[yk] or yk
			args[yk] = vy;
			allargs[yk] = vy;
		end
	end	
	local CitationClass = config.CitationClass
	local capture;																-- the single supported capture when matching unknown parameters using patterns
	local empty_unknowns = {};													-- sequence table to hold empty unknown params for error message listing
	for k, v in pairs( allargs ) do											    -- get parameters from the parent (template) frame
		v = mw.ustring.gsub (v, '^%s*(.-)%s*$', '%1');							-- trim leading/trailing whitespace; when v is only whitespace, becomes empty string
		if v ~= '' then
			if ('string' == type (k)) then
				k = mw.ustring.gsub (k, '%d', cfg.date_names.local_digits);		-- for enumerated parameters, translate 'local' digits to Western 0-9
			end
			if not validate( k, CitationClass ) then			
				--mw.log('not validate( k, CitationClass )' .. k )
				if type (k) ~= 'string' then									-- exclude empty numbered parameters
					if v:match("%S+") ~= nil then
						error_text = utilities.set_message ('err_text_ignored', {v});
					end
				elseif validate(k:lower(), CitationClass) then 
					--error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, k:lower()});	-- suggest the lowercase version of the parameter
				else
					for pattern, param in pairs (suggestions.patterns) do		-- loop through the patterns to see if we can suggest a proper parameter
						capture = k:match(pattern);							-- the whole match if no capture in pattern else the capture if a match
						if capture then											-- if the pattern matches 
							param = utilities.substitute (param, capture);		-- add the capture to the suggested parameter (typically the enumerator)
							if validate(param, CitationClass) then		        -- validate the suggestion to make sure that the suggestion is supported by this template (necessary for limited parameter lists)
								--error_text = utilities.set_message ('err_parameter_ignored_suggest', {k, param});	-- set the suggestion error message
							else
								error_text = utilities.set_message ('err_parameter_ignored', {k});	-- suggested param not supported by this template
								v = '';											-- unset
							end
						end
					end
					if not utilities.is_set (error_text) then					-- couldn't match with a pattern, is there an explicit suggestion?						
						if (suggestions.suggestions[ k:lower() ] ~= nil) and validate(suggestions.suggestions[ k:lower() ], CitationClass) 
						then
							--utilities.set_message ('err_parameter_ignored_suggest', {k, suggestions.suggestions[ k:lower() ]});
						else
							utilities.set_message ('err_parameter_ignored', {k});
							v = '';												-- unset value assigned to unrecognized parameters (this for the limited parameter lists)
						end
					end
				end				  
			end
			args[k] = v;														-- save this parameter and its value

		elseif not utilities.is_set (v) then									-- for empty parameters
			if not validate(k, CitationClass, true) then						-- is this empty parameter a valid parameter
				local valid = false
				for pattern, param in pairs (suggestions.patterns) do			-- loop through the patterns to see if we can suggest a proper parameter
					capture = k:match(pattern);									-- the whole match if no capture in pattern else the capture if a match
					if capture then												-- if the pattern matches 
						param = utilities.substitute (param, capture);			-- add the capture to the suggested parameter (typically the enumerator)
						if validate(param, CitationClass) then		    	  -- validate the suggestion to make sure that the suggestion is supported by this template (necessary for limited parameter lists)
							valid = true
							break
						end
					end
				end
				if valid == false then											-- user:Mr. Ibrahem
					if (suggestions.suggestions[ k:lower() ] ~= nil) and validate(suggestions.suggestions[ k:lower() ], CitationClass) 
					then
						
					else
						k = ('' == k) and '(empty string)' or k;				-- when k is empty string (or was space(s) trimmed to empty string), replace with descriptive text
						table.insert (empty_unknowns, k);
					end
				end
			end
		end
	end	

	if 0 ~= #empty_unknowns then												-- create empty unknown error message
		utilities.set_message ('err_param_unknown_empty', 
			empty_unknowns
			--{ 1 == #empty_unknowns and '' or 's',utilities.make_sep_list (#empty_unknowns, empty_unknowns)}
		);
	end
	local url_param_t = {};

	for k, v in pairs( args ) do
		if 'string' == type (k) then											-- don't evaluate positional parameters
			has_invisible_chars (k, v);											-- look for invisible characters
		end
		has_extraneous_punc (k, v);												-- look for extraneous terminal punctuation in parameter values
		missing_pipe_check (k, v);												-- do we think that there is a parameter that is missing a pipe?
		args[k] = inter_wiki_check (k, v);										-- when language interwiki-linked parameter missing leading colon replace with wiki-link label

		if 'string' == type (k) and not cfg.url_skip[k] then					-- when parameter k is not positional and not in url skip table
			url_param_t[k] = v;													-- make a parameter/value list for extraneous url check
		end
	end

	has_extraneous_url (url_param_t);											-- look for url in parameter values where a url does not belong

	return table.concat ({
		frame:extensionTag ('templatestyles', '', {src=styles}),
		citation0( CitationClass, args)
	});
end

return {citation = citation}