-- Original module located at :en:Module:Wd and :en:Module:Wd/i18n.  local p = {} local arg = ... local i18n  local function loadI18n(aliasesP, frame) 	local title  	if frame then 		-- current module invoked by page/template, get its title from frame 		title = frame:getTitle() 	else 		-- current module included by other module, get its title from ... 		title = arg 	end  	if not i18n then 		i18n = require(title .. "/i18n").init(aliasesP) 	end end  p.claimCommands = { 	property   = "property", 	properties = "properties", 	qualifier  = "qualifier", 	qualifiers = "qualifiers", 	reference  = "reference", 	references = "references" }  p.generalCommands = { 	label       = "label", 	title       = "title", 	description = "description", 	alias       = "alias", 	aliases     = "aliases", 	badge       = "badge", 	badges      = "badges" }  p.flags = { 	linked        = "linked", 	short         = "short", 	raw           = "raw", 	multilanguage = "multilanguage", 	unit          = "unit", 	------------- 	preferred     = "preferred", 	normal        = "normal", 	deprecated    = "deprecated", 	best          = "best", 	future        = "future", 	current       = "current", 	former        = "former", 	edit          = "edit", 	editAtEnd     = "edit@end", 	mdy           = "mdy", 	single        = "single", 	sourced       = "sourced" }  p.args = { 	eid  = "eid", 	page = "page", 	date = "date" }  local aliasesP = { 	coord                   = "P625", 	----------------------- 	image                   = "P18", 	author                  = "P50", 	publisher               = "P123", 	importedFrom            = "P143", 	statedIn                = "P248", 	pages                   = "P304", 	language                = "P407", 	hasPart                 = "P527", 	publicationDate         = "P577", 	startTime               = "P580", 	endTime                 = "P582", 	chapter                 = "P792", 	retrieved               = "P813", 	referenceURL            = "P854", 	sectionVerseOrParagraph = "P958", 	archiveURL              = "P1065", 	title                   = "P1476", 	formatterURL            = "P1630", 	quote                   = "P1683", 	shortName               = "P1813", 	definingFormula         = "P2534", 	archiveDate             = "P2960", 	inferredFrom            = "P3452", 	typeOfReference         = "P3865", 	column                  = "P3903" }  local aliasesQ = { 	percentage              = "Q11229", 	prolepticJulianCalendar = "Q1985786", 	citeWeb                 = "Q5637226", 	citeQ                   = "Q22321052" }  local parameters = { 	property  = "%p", 	qualifier = "%q", 	reference = "%r", 	alias     = "%a", 	badge     = "%b", 	separator = "%s", 	general   = "%x" }  local formats = { 	property              = "%p[%s][%r]", 	qualifier             = "%q[%s][%r]", 	reference             = "%r", 	propertyWithQualifier = "%p[ (%q)][%s][%r]", 	alias                 = "%a[%s]", 	badge                 = "%b[%s]" }  local hookNames = {              -- {level_1, level_2} 	[parameters.property]         = {"getProperty"}, 	[parameters.reference]        = {"getReferences", "getReference"}, 	[parameters.qualifier]        = {"getAllQualifiers"}, 	[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"}, 	[parameters.alias]            = {"getAlias"}, 	[parameters.badge]            = {"getBadge"} }  -- default value objects, should NOT be mutated but instead copied local defaultSeparators = { 	["sep"]      = {" "}, 	["sep%s"]    = {","}, 	["sep%q"]    = {"; "}, 	["sep%q\\d"] = {", "}, 	["sep%r"]    = nil,  -- none 	["punc"]     = nil   -- none }  local rankTable = { 	["preferred"]  = 1, 	["normal"]     = 2, 	["deprecated"] = 3 }  local Config = {}  -- allows for recursive calls function Config:new() 	local cfg = {} 	setmetatable(cfg, self) 	self.__index = self  	cfg.separators = { 		-- single value objects wrapped in arrays so that we can pass by reference 		["sep"]   = {copyTable(defaultSeparators["sep"])}, 		["sep%s"] = {copyTable(defaultSeparators["sep%s"])}, 		["sep%q"] = {copyTable(defaultSeparators["sep%q"])}, 		["sep%r"] = {copyTable(defaultSeparators["sep%r"])}, 		["punc"]  = {copyTable(defaultSeparators["punc"])} 	}  	cfg.entity = nil 	cfg.entityID = nil 	cfg.propertyID = nil 	cfg.propertyValue = nil 	cfg.qualifierIDs = {} 	cfg.qualifierIDsAndValues = {}  	cfg.bestRank = true 	cfg.ranks = {true, true, false}  -- preferred = true, normal = true, deprecated = false 	cfg.foundRank = #cfg.ranks 	cfg.flagBest = false 	cfg.flagRank = false  	cfg.periods = {true, true, true}  -- future = true, current = true, former = true 	cfg.flagPeriod = false 	cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))}  -- today as {year, month, day}  	cfg.mdyDate = false 	cfg.singleClaim = false 	cfg.sourcedOnly = false 	cfg.editable = false 	cfg.editAtEnd = false  	cfg.inSitelinks = false  	cfg.langCode = mw.language.getContentLanguage().code 	cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode) 	cfg.langObj = mw.language.new(cfg.langCode)  	cfg.siteID = mw.wikibase.getGlobalSiteId()  	cfg.states = {} 	cfg.states.qualifiersCount = 0 	cfg.curState = nil  	cfg.prefetchedRefs = nil  	return cfg end  local State = {}  function State:new(cfg, type) 	local stt = {} 	setmetatable(stt, self) 	self.__index = self  	stt.conf = cfg 	stt.type = type  	stt.results = {}  	stt.parsedFormat = {} 	stt.separator = {} 	stt.movSeparator = {} 	stt.puncMark = {}  	stt.linked = false 	stt.rawValue = false 	stt.shortName = false 	stt.anyLanguage = false 	stt.unitOnly = false 	stt.singleValue = false  	return stt end  local function replaceAlias(id) 	if aliasesP[id] then 		id = aliasesP[id] 	end  	return id end  local function errorText(code, param) 	local text = i18n["errors"][code] 	if param then text = mw.ustring.gsub(text, "$1", param) end 	return text end  local function throwError(errorMessage, param) 	error(errorText(errorMessage, param)) end  local function replaceDecimalMark(num) 	return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1) end  local function padZeros(num, numDigits) 	local numZeros 	local negative = false  	if num < 0 then 		negative = true 		num = num * -1 	end  	num = tostring(num) 	numZeros = numDigits - num:len()  	for _ = 1, numZeros do 		num = "0"..num 	end  	if negative then 		num = "-"..num 	end  	return num end  local function replaceSpecialChar(chr) 	if chr == '_' then 		-- replace underscores with spaces 		return ' ' 	else 		return chr 	end end  local function replaceSpecialChars(str) 	local chr 	local esc = false 	local strOut = ""  	for i = 1, #str do 		chr = str:sub(i,i)  		if not esc then 			if chr == '\\' then 				esc = true 			else 				strOut = strOut .. replaceSpecialChar(chr) 			end 		else 			strOut = strOut .. chr 			esc = false 		end 	end  	return strOut end  local function buildWikilink(target, label) 	if not label or target == label then 		return "" .. target .. "" 	else 		return "" .. label .. "" 	end end  -- used to make frame.args mutable, to replace #frame.args (which is always 0) -- with the actual amount and to simply copy tables function copyTable(tIn) 	if not tIn then 		return nil 	end  	local tOut = {}  	for i, v in pairs(tIn) do 		tOut[i] = v 	end  	return tOut end  -- used to merge output arrays together; -- note that it currently mutates the first input array local function mergeArrays(a1, a2) 	for i = 1, #a2 do 		a1[#a1 + 1] = a2[i] 	end  	return a1 end  local function split(str, del) 	local out = {} 	local i, j = str:find(del)  	if i and j then 		out[1] = str:sub(1, i - 1) 		out[2] = str:sub(j + 1) 	else 		out[1] = str 	end  	return out end  local function parseWikidataURL(url) 	local id  	if url:match('^http[s]?://') then 		id = split(url, "Q")  		if id[2] then 			return "Q" .. id[2] 		end 	end  	return nil end  function parseDate(dateStr, precision) 	precision = precision or "d"  	local i, j, index, ptr 	local parts = {nil, nil, nil}  	if dateStr == nil then 		return parts[1], parts[2], parts[3]  -- year, month, day 	end  	-- 'T' for snak values, '/' for outputs with '/Julian' attached 	i, j = dateStr:find("[T/]")  	if i then 		dateStr = dateStr:sub(1, i-1) 	end  	local from = 1  	if dateStr:sub(1,1) == "-" then 		-- this is a negative number, look further ahead 		from = 2 	end  	index = 1 	ptr = 1  	i, j = dateStr:find("-", from)  	if i then 		-- year 		parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), "^\+(.+)$", "%1"), 10)  -- remove '+' sign (explicitly give base 10 to prevent error)  		if parts[index] == -0 then 			parts[index] = tonumber("0")  -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead 		end  		if precision == "y" then 			-- we're done 			return parts[1], parts[2], parts[3]  -- year, month, day 		end  		index = index + 1 		ptr = i + 1  		i, j = dateStr:find("-", ptr)  		if i then 			-- month 			parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)  			if precision == "m" then 				-- we're done 				return parts[1], parts[2], parts[3]  -- year, month, day 			end  			index = index + 1 			ptr = i + 1 		end 	end  	if dateStr:sub(ptr) ~= "" then 		-- day if we have month, month if we have year, or year 		parts[index] = tonumber(dateStr:sub(ptr), 10) 	end  	return parts[1], parts[2], parts[3]  -- year, month, day end  local function datePrecedesDate(aY, aM, aD, bY, bM, bD) 	if aY == nil or bY == nil then 		return nil 	end 	aM = aM or 1 	aD = aD or 1 	bM = bM or 1 	bD = bD or 1  	if aY < bY then 		return true 	end  	if aY > bY then 		return false 	end  	if aM < bM then 		return true 	end  	if aM > bM then 		return false 	end  	if aD < bD then 		return true 	end  	return false end  local function getHookName(param, index) 	if hookNames[param] then 		return hookNames[param][index] 	elseif param:len() > 2 then 		return hookNames[param:sub(1, 2).."\\d"][index] 	else 		return nil 	end end  local function alwaysTrue() 	return true end  -- The following function parses a format string. -- -- The example below shows how a parsed string is structured in memory. -- Variables other than 'str' and 'child' are left out for clarity's sake. -- -- Example: -- "A %p B [%s[%q1]] C [%r] D" -- -- Structure: -- [ --   { --     str = "A " --   }, --   { --     str = "%p" --   }, --   { --     str = " B ", --     child = --     [ --       { --         str = "%s", --         child = --         [ --           { --             str = "%q1" --           } --         ] --       } --     ] --   }, --   { --     str = " C ", --     child = --     [ --       { --         str = "%r" --       } --     ] --   }, --   { --     str = " D" --   } -- ] -- local function parseFormat(str) 	local chr, esc, param, root, cur, prev, new 	local params = {}  	local function newObject(array) 		local obj = {}  -- new object 		obj.str = ""  		array[#array + 1] = obj  -- array{object} 		obj.parent = array  		return obj 	end  	local function endParam() 		if param > 0 then 			if cur.str ~= "" then 				cur.str = "%"..cur.str 				cur.param = true 				params[cur.str] = true 				cur.parent.req[cur.str] = true 				prev = cur 				cur = newObject(cur.parent) 			end 			param = 0 		end 	end  	root = {}  -- array 	root.req = {} 	cur = newObject(root) 	prev = nil  	esc = false 	param = 0  	for i = 1, #str do 		chr = str:sub(i,i)  		if not esc then 			if chr == '\\' then 				endParam() 				esc = true 			elseif chr == '%' then 				endParam() 				if cur.str ~= "" then 					cur = newObject(cur.parent) 				end 				param = 2 			elseif chr == '[' then 				endParam() 				if prev and cur.str == "" then 					table.remove(cur.parent) 					cur = prev 				end 				cur.child = {}  -- new array 				cur.child.req = {} 				cur.child.parent = cur 				cur = newObject(cur.child) 			elseif chr == ']' then 				endParam() 				if cur.parent.parent then 					new = newObject(cur.parent.parent.parent) 					if cur.str == "" then 						table.remove(cur.parent) 					end 					cur = new 				end 			else 				if param > 1 then 					param = param - 1 				elseif param == 1 then 					if not chr:match('%d') then 						endParam() 					end 				end  				cur.str = cur.str .. replaceSpecialChar(chr) 			end 		else 			cur.str = cur.str .. chr 			esc = false 		end  		prev = nil 	end  	endParam()  	-- make sure that at least one required parameter has been defined 	if not next(root.req) then 		throwError("missing-required-parameter") 	end  	-- make sure that the separator parameter "%s" is not amongst the required parameters 	if root.req[parameters.separator] then 		throwError("extra-required-parameter", parameters.separator) 	end  	return root, params end  local function sortOnRank(claims) 	local rankPos 	local ranks = {{}, {}, {}, {}}  -- preferred, normal, deprecated, (default) 	local sorted = {}  	for _, v in ipairs(claims) do 		rankPos = rankTable[v.rank] or 4 		ranks[rankPos][#ranks[rankPos] + 1] = v 	end  	sorted = ranks[1] 	sorted = mergeArrays(sorted, ranks[2]) 	sorted = mergeArrays(sorted, ranks[3])  	return sorted end  -- if id == nil then item connected to current page is used function Config:getLabel(id, raw, link, short) 	local label = nil 	local title = nil 	local prefix= ""  	if not id then 		id = mw.wikibase.getEntityIdForCurrentPage()  		if not id then 			return "" 		end 	end  	id = id:upper()  -- just to be sure  	if raw then 		-- check if given id actually exists 		if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then 			label = id  			if id:sub(1,1) == "P" then 				prefix = "Property:" 			end 		end  		prefix = "d:" .. prefix  		title = label  -- may be nil 	else 		-- try short name first if requested 		if short then 			label = p._property{aliasesP.shortName, [p.args.eid] = id}  -- get short name  			if label == "" then 				label = nil 			end 		end  		-- get label 		if not label then 			label = mw.wikibase.getLabelByLang(id, self.langCode) 		end 	end  	if not label then 		label = "" 	elseif link then 		-- build a link if requested 		if not title then 			if id:sub(1,1) == "Q" then 				title = mw.wikibase.getSitelink(id) 			elseif id:sub(1,1) == "P" then 				-- properties have no sitelink, link to Wikidata instead 				title = id 				prefix = "d:Property:" 			end 		end  		if title then 			label = buildWikilink(prefix .. title, label) 		end 	end  	return label end  function Config:getEditIcon() 	local value = "" 	local prefix = "" 	local front = " " 	local back = ""  	if self.entityID:sub(1,1) == "P" then 		prefix = "Property:" 	end  	if self.editAtEnd then 		front = '' 		back = '' 	end  	value = "frameless|text-top|10px|alt=" .. i18n['info']['edit-on-wikidata'] .. "|link=https://www.wikidata.org/wiki/" .. prefix .. self.entityID .. "?uselang=" .. self.langCode  	if self.propertyID then 		value = value .. "#" .. self.propertyID 	elseif self.inSitelinks then 		value = value .. "#sitelinks-wikipedia" 	end  	value = value .. "|" .. i18n['info']['edit-on-wikidata'] .. ""  	return front .. value .. back end  -- used to create the final output string when it's all done, so that for references the -- function extensionTag("ref", ...) is only called when they really ended up in the final output function Config:concatValues(valuesArray) 	local outString = "" 	local j, skip  	for i = 1, #valuesArray do 		-- check if this is a reference 		if valuesArray[i].refHash then 			j = i - 1 			skip = false  			-- skip this reference if it is part of a continuous row of references that already contains the exact same reference 			while valuesArray[j] and valuesArray[j].refHash do 				if valuesArray[i].refHash == valuesArray[j].refHash then 					skip = true 					break 				end 				j = j - 1 			end  			if not skip then 				-- add <ref> tag with the reference's hash as its name (to deduplicate references) 				outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = valuesArray[i].refHash}) 			end 		else 			outString = outString .. valuesArray[i][1] 		end 	end  	return outString end  function Config:convertUnit(unit, raw, link, short, unitOnly) 	local space = " " 	local label = "" 	local itemID  	if unit == "" or unit == "1" then 		return nil 	end  	if unitOnly then 		space = "" 	end  	itemID = parseWikidataURL(unit)  	if itemID then 		if itemID == aliasesQ.percentage then 			return "%" 		else 			label = self:getLabel(itemID, raw, link, short)  			if label ~= "" then 				return space .. label 			end 		end 	end  	return "" end  function State:getValue(snak) 	return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2)) end  function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type) 	if snak.snaktype == 'value' then 		local datatype = snak.datavalue.type 		local subtype = snak.datatype 		local datavalue = snak.datavalue.value  		if datatype == 'string' then 			if subtype == 'url' and link then 				-- create link explicitly 				if raw then 					-- will render as a linked number like [1] 					return "[" .. datavalue .. "]" 				else 					return "[" .. datavalue .. " " .. datavalue .. "]" 				end 			elseif subtype == 'commonsMedia' then 				if link then 					return buildWikilink("c:File:" .. datavalue, datavalue) 				elseif not raw then 					return "File:" .. datavalue .. "" 				else 					return datavalue 				end 			elseif subtype == 'geo-shape' and link then 				return buildWikilink("c:" .. datavalue, datavalue) 			elseif subtype == 'math' and not raw then 				local attribute = nil  				if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then 					attribute = {qid = self.entityID} 				end  				return mw.getCurrentFrame():extensionTag("math", datavalue, attribute) 			elseif subtype == 'external-id' and link then 				local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property}  -- get formatter URL  				if url ~= "" then 					url = mw.ustring.gsub(url, "$1", datavalue) 					return "[" .. url .. " " .. datavalue .. "]" 				else 					return datavalue 				end 			else 				return datavalue 			end 		elseif datatype == 'monolingualtext' then 			if anyLang or datavalue['language'] == self.langCode then 				return datavalue['text'] 			else 				return nil 			end 		elseif datatype == 'quantity' then 			local value = "" 			local unit  			if not unitOnly then 				-- get value and strip + signs from front 				value = mw.ustring.gsub(datavalue['amount'], "^\+(.+)$", "%1")  				if raw then 					return value 				end  				-- replace decimal mark based on locale 				value = replaceDecimalMark(value)  				-- add delimiters for readability 				value = i18n.addDelimiters(value) 			end  			unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)  			if unit then 				value = value .. unit 			end  			return value 		elseif datatype == 'time' then 			local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr 			local yFactor = 1 			local sign = 1 			local prefix = "" 			local suffix = "" 			local mayAddCalendar = false 			local calendar = "" 			local precision = datavalue['precision']  			if precision == 11 then 				p = "d" 			elseif precision == 10 then 				p = "m" 			else 				p = "y" 				yFactor = 10^(9-precision) 			end  			y, m, d = parseDate(datavalue['time'], p)  			if y < 0 then 				sign = -1 				y = y * sign 			end  			-- if precision is tens/hundreds/thousands/millions/billions of years 			if precision <= 8 then 				yDiv = y / yFactor  				-- if precision is tens/hundreds/thousands of years 				if precision >= 6 then 					mayAddCalendar = true  					if precision <= 7 then 						-- round centuries/millenniums up (e.g. 20th century or 3rd millennium) 						yRound = math.ceil(yDiv)  						if not raw then 							if precision == 6 then 								suffix = i18n['datetime']['suffixes']['millennium'] 							else 								suffix = i18n['datetime']['suffixes']['century'] 							end  							suffix = i18n.getOrdinalSuffix(yRound) .. suffix 						else 							-- if not verbose, take the first year of the century/millennium 							-- (e.g. 1901 for 20th century or 2001 for 3rd millennium) 							yRound = (yRound - 1) * yFactor + 1 						end 					else 						-- precision == 8 						-- round decades down (e.g. 2010s) 						yRound = math.floor(yDiv) * yFactor  						if not raw then 							prefix = i18n['datetime']['prefixes']['decade-period'] 							suffix = i18n['datetime']['suffixes']['decade-period'] 						end 					end  					if raw and sign < 0 then 						-- if BCE then compensate for "counting backwards" 						-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE) 						yRound = yRound + yFactor - 1 					end 				else 					local yReFactor, yReDiv, yReRound  					-- round to nearest for tens of thousands of years or more 					yRound = math.floor(yDiv + 0.5)  					if yRound == 0 then 						if precision <= 2 and y ~= 0 then 							yReFactor = 1e6 							yReDiv = y / yReFactor 							yReRound = math.floor(yReDiv + 0.5)  							if yReDiv == yReRound then 								-- change precision to millions of years only if we have a whole number of them 								precision = 3 								yFactor = yReFactor 								yRound = yReRound 							end 						end  						if yRound == 0 then 							-- otherwise, take the unrounded (original) number of years 							precision = 5 							yFactor = 1 							yRound = y 							mayAddCalendar = true 						end 					end  					if precision >= 1 and y ~= 0 then 						yFull = yRound * yFactor  						yReFactor = 1e9 						yReDiv = yFull / yReFactor 						yReRound = math.floor(yReDiv + 0.5)  						if yReDiv == yReRound then 							-- change precision to billions of years if we're in that range 							precision = 0 							yFactor = yReFactor 							yRound = yReRound 						else 							yReFactor = 1e6 							yReDiv = yFull / yReFactor 							yReRound = math.floor(yReDiv + 0.5)  							if yReDiv == yReRound then 								-- change precision to millions of years if we're in that range 								precision = 3 								yFactor = yReFactor 								yRound = yReRound 							end 						end 					end  					if not raw then 						if precision == 3 then 							suffix = i18n['datetime']['suffixes']['million-years'] 						elseif precision == 0 then 							suffix = i18n['datetime']['suffixes']['billion-years'] 						else 							yRound = yRound * yFactor 							if yRound == 1 then 								suffix = i18n['datetime']['suffixes']['year'] 							else 								suffix = i18n['datetime']['suffixes']['years'] 							end 						end 					else 						yRound = yRound * yFactor 					end 				end 			else 				yRound = y 				mayAddCalendar = true 			end  			if mayAddCalendar then 				calendarID = parseWikidataURL(datavalue['calendarmodel'])  				if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then 					if not raw then 						if link then 							calendar = " ("..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..")" 						else 							calendar = " ("..i18n['datetime']['julian']..")" 						end 					else 						calendar = "/"..i18n['datetime']['julian'] 					end 				end 			end  			if not raw then 				local ce = nil  				if sign < 0 then 					ce = i18n['datetime']['BCE'] 				elseif precision <= 5 then 					ce = i18n['datetime']['CE'] 				end  				if ce then 					if link then 						ce = buildWikilink(i18n['datetime']['common-era'], ce) 					end 					suffix = suffix .. " " .. ce 				end  				value = tostring(yRound)  				if m then 					dateStr = self.langObj:formatDate("F", "1-"..m.."-1")  					if d then 						if self.mdyDate then 							dateStr = dateStr .. " " .. d .. "," 						else 							dateStr = d .. " " .. dateStr 						end 					end  					value = dateStr .. " " .. value 				end  				value = prefix .. value .. suffix .. calendar 			else 				value = padZeros(yRound * sign, 4)  				if m then 					value = value .. "-" .. padZeros(m, 2)  					if d then 						value = value .. "-" .. padZeros(d, 2) 					end 				end  				value = value .. calendar 			end  			return value 		elseif datatype == 'globecoordinate' then 			-- logic from https://github.com/DataValues/Geo (v4.0.1)  			local precision, unitsPerDegree, numDigits, strFormat, value, globe 			local latitude, latConv, latValue, latLink 			local longitude, lonConv, lonValue, lonLink 			local latDirection, latDirectionN, latDirectionS, latDirectionEN 			local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN 			local degSymbol, minSymbol, secSymbol, separator  			local latDegrees = nil 			local latMinutes = nil 			local latSeconds = nil 			local lonDegrees = nil 			local lonMinutes = nil 			local lonSeconds = nil  			local latDegSym = "" 			local latMinSym = "" 			local latSecSym = "" 			local lonDegSym = "" 			local lonMinSym = "" 			local lonSecSym = ""  			local latDirectionEN_N = "N" 			local latDirectionEN_S = "S" 			local lonDirectionEN_E = "E" 			local lonDirectionEN_W = "W"  			if not raw then 				latDirectionN = i18n['coord']['latitude-north'] 				latDirectionS = i18n['coord']['latitude-south'] 				lonDirectionE = i18n['coord']['longitude-east'] 				lonDirectionW = i18n['coord']['longitude-west']  				degSymbol = i18n['coord']['degrees'] 				minSymbol = i18n['coord']['minutes'] 				secSymbol = i18n['coord']['seconds'] 				separator = i18n['coord']['separator'] 			else 				latDirectionN = latDirectionEN_N 				latDirectionS = latDirectionEN_S 				lonDirectionE = lonDirectionEN_E 				lonDirectionW = lonDirectionEN_W  				degSymbol = "/" 				minSymbol = "/" 				secSymbol = "/" 				separator = "/" 			end  			latitude = datavalue['latitude'] 			longitude = datavalue['longitude']  			if latitude < 0 then 				latDirection = latDirectionS 				latDirectionEN = latDirectionEN_S 				latitude = math.abs(latitude) 			else 				latDirection = latDirectionN 				latDirectionEN = latDirectionEN_N 			end  			if longitude < 0 then 				lonDirection = lonDirectionW 				lonDirectionEN = lonDirectionEN_W 				longitude = math.abs(longitude) 			else 				lonDirection = lonDirectionE 				lonDirectionEN = lonDirectionEN_E 			end  			precision = datavalue['precision']  			if not precision or precision <= 0 then 				precision = 1 / 3600  -- precision not set (correctly), set to arcsecond 			end  			-- remove insignificant detail 			latitude = math.floor(latitude / precision + 0.5) * precision 			longitude = math.floor(longitude / precision + 0.5) * precision  			if precision >= 1 - (1 / 60) and precision < 1 then 				precision = 1 			elseif precision >= (1 / 60) - (1 / 3600) and precision < (1 / 60) then 				precision = 1 / 60 			end  			if precision >= 1 then 				unitsPerDegree = 1 			elseif precision >= (1 / 60)  then 				unitsPerDegree = 60 			else 				unitsPerDegree = 3600 			end  			numDigits = math.ceil(-math.log10(unitsPerDegree * precision))  			if numDigits <= 0 then 				numDigits = tonumber("0")  -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead 			end  			strFormat = "%." .. numDigits .. "f"  			if precision >= 1 then 				latDegrees = strFormat:format(latitude) 				lonDegrees = strFormat:format(longitude)  				if not raw then 					latDegSym = replaceDecimalMark(latDegrees) .. degSymbol 					lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol 				else 					latDegSym = latDegrees .. degSymbol 					lonDegSym = lonDegrees .. degSymbol 				end 			else 				latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits 				lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits  				if precision >= (1 / 60) then 					latMinutes = latConv 					lonMinutes = lonConv 				else 					latSeconds = latConv 					lonSeconds = lonConv  					latMinutes = math.floor(latSeconds / 60) 					lonMinutes = math.floor(lonSeconds / 60)  					latSeconds = strFormat:format(latSeconds - (latMinutes * 60)) 					lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))  					if not raw then 						latSecSym = replaceDecimalMark(latSeconds) .. secSymbol 						lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol 					else 						latSecSym = latSeconds .. secSymbol 						lonSecSym = lonSeconds .. secSymbol 					end 				end  				latDegrees = math.floor(latMinutes / 60) 				lonDegrees = math.floor(lonMinutes / 60)  				latDegSym = latDegrees .. degSymbol 				lonDegSym = lonDegrees .. degSymbol  				latMinutes = latMinutes - (latDegrees * 60) 				lonMinutes = lonMinutes - (lonDegrees * 60)  				if precision >= (1 / 60) then 					latMinutes = strFormat:format(latMinutes) 					lonMinutes = strFormat:format(lonMinutes)  					if not raw then 						latMinSym = replaceDecimalMark(latMinutes) .. minSymbol 						lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol 					else 						latMinSym = latMinutes .. minSymbol 						lonMinSym = lonMinutes .. minSymbol 					end 				else 					latMinSym = latMinutes .. minSymbol 					lonMinSym = lonMinutes .. minSymbol 				end 			end  			latValue = latDegSym .. latMinSym .. latSecSym .. latDirection 			lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection  			value = latValue .. separator .. lonValue  			if link then 				globe = parseWikidataURL(datavalue['globe'])  				if globe then 					globe = mw.wikibase.getLabelByLang(globe, "en"):lower() 				else 					globe = "earth" 				end  				latLink = table.concat({latDegrees, latMinutes, latSeconds}, "_") 				lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, "_")  				value = ""..self.langCode.."&params="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."" 			end  			return value 		elseif datatype == 'wikibase-entityid' then 			local label 			local itemID = datavalue['numeric-id']  			if subtype == 'wikibase-item' then 				itemID = "Q" .. itemID 			elseif subtype == 'wikibase-property' then 				itemID = "P" .. itemID 			else 				return '' .. errorText('unknown-data-type', subtype) .. '' 			end  			label = self:getLabel(itemID, raw, link, short)  			if label == "" then 				label = nil 			end  			return label 		else 			return '' .. errorText('unknown-data-type', datatype) .. '' 		end 	elseif snak.snaktype == 'somevalue' and not noSpecial then 		if raw then 			return " "  -- single space represents 'somevalue' 		else 			return i18n['values']['unknown'] 		end 	elseif snak.snaktype == 'novalue' and not noSpecial then 		if raw then 			return ""  -- empty string represents 'novalue' 		else 			return i18n['values']['none'] 		end 	else 		return nil 	end end  function Config:getSingleRawQualifier(claim, qualifierID) 	local qualifiers  	if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end  	if qualifiers and qualifiers[1] then 		return self:getValue(qualifiers[1], true)  -- raw = true 	else 		return nil 	end end  function Config:snakEqualsValue(snak, value) 	local snakValue = self:getValue(snak, true)  -- raw = true  	if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end  	return snakValue == value end  function Config:setRank(rank) 	local rankPos  	if rank == p.flags.best then 		self.bestRank = true 		self.flagBest = true  -- mark that 'best' flag was given 		return 	end  	if rank:sub(1,9) == p.flags.preferred then 		rankPos = 1 	elseif rank:sub(1,6) == p.flags.normal then 		rankPos = 2 	elseif rank:sub(1,10) == p.flags.deprecated then 		rankPos = 3 	else 		return 	end  	-- one of the rank flags was given, check if another one was given before 	if not self.flagRank then 		self.ranks = {false, false, false}  -- no other rank flag given before, so unset ranks 		self.bestRank = self.flagBest       -- unsets bestRank only if 'best' flag was not given before 		self.flagRank = true                -- mark that a rank flag was given 	end  	if rank:sub(-1) == "+" then 		for i = rankPos, 1, -1 do 			self.ranks[i] = true 		end 	elseif rank:sub(-1) == "-" then 		for i = rankPos, #self.ranks do 			self.ranks[i] = true 		end 	else 		self.ranks[rankPos] = true 	end end  function Config:setPeriod(period) 	local periodPos  	if period == p.flags.future then 		periodPos = 1 	elseif period == p.flags.current then 		periodPos = 2 	elseif period == p.flags.former then 		periodPos = 3 	else 		return 	end  	-- one of the period flags was given, check if another one was given before 	if not self.flagPeriod then 		self.periods = {false, false, false}  -- no other period flag given before, so unset periods 		self.flagPeriod = true                -- mark that a period flag was given 	end  	self.periods[periodPos] = true end  function Config:qualifierMatches(claim, id, value) 	local qualifiers  	if claim.qualifiers then qualifiers = claim.qualifiers[id] end 	if qualifiers then 		for _, v in pairs(qualifiers) do 			if self:snakEqualsValue(v, value) then 				return true 			end 		end 	elseif value == "" then 		-- if the qualifier is not present then treat it the same as the special value 'novalue' 		return true 	end  	return false end  function Config:rankMatches(rankPos) 	if self.bestRank then 		return (self.ranks[rankPos] and self.foundRank >= rankPos) 	else 		return self.ranks[rankPos] 	end end  function Config:timeMatches(claim) 	local startTime = nil 	local startTimeY = nil 	local startTimeM = nil 	local startTimeD = nil 	local endTime = nil 	local endTimeY = nil 	local endTimeM = nil 	local endTimeD = nil  	if self.periods[1] and self.periods[2] and self.periods[3] then 		-- any time 		return true 	end  	startTime = self:getSingleRawQualifier(claim, aliasesP.startTime) 	if startTime and startTime ~= "" and startTime ~= " " then 		startTimeY, startTimeM, startTimeD = parseDate(startTime) 	end  	endTime = self:getSingleRawQualifier(claim, aliasesP.endTime) 	if endTime and endTime ~= "" and endTime ~= " " then 		endTimeY, endTimeM, endTimeD = parseDate(endTime) 	end  	if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then 		-- invalidate end time if it precedes start time 		endTimeY = nil 		endTimeM = nil 		endTimeD = nil 	end  	if self.periods[1] then 		-- future 		if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then 			return true 		end 	end  	if self.periods[2] then 		-- current 		if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and 		   (endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then 			return true 		end 	end  	if self.periods[3] then 		-- former 		if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then 			return true 		end 	end  	return false end  function Config:processFlag(flag) 	if not flag then 		return false 	end  	if flag == p.flags.linked then 		self.curState.linked = true 		return true 	elseif flag == p.flags.raw then 		self.curState.rawValue = true  		if self.curState == self.states[parameters.reference] then 			-- raw reference values end with periods and require a separator (other than none) 			self.separators["sep%r"][1] = {" "} 		end  		return true 	elseif flag == p.flags.short then 		self.curState.shortName = true 		return true 	elseif flag == p.flags.multilanguage then 		self.curState.anyLanguage = true 		return true 	elseif flag == p.flags.unit then 		self.curState.unitOnly = true 		return true 	elseif flag == p.flags.mdy then 		self.mdyDate = true 		return true 	elseif flag == p.flags.single then 		self.singleClaim = true 		return true 	elseif flag == p.flags.sourced then 		self.sourcedOnly = true 		return true 	elseif flag == p.flags.edit then 		self.editable = true 		return true 	elseif flag == p.flags.editAtEnd then 		self.editable = true 		self.editAtEnd = true 		return true 	elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then 		self:setRank(flag) 		return true 	elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then 		self:setPeriod(flag) 		return true 	elseif flag == "" then 		-- ignore empty flags and carry on 		return true 	else 		return false 	end end  function Config:processFlagOrCommand(flag) 	local param = ""  	if not flag then 		return false 	end  	if flag == p.claimCommands.property or flag == p.claimCommands.properties then 		param = parameters.property 	elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then 		self.states.qualifiersCount = self.states.qualifiersCount + 1 		param = parameters.qualifier .. self.states.qualifiersCount 		self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])} 	elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then 		param = parameters.reference 	else 		return self:processFlag(flag) 	end  	if self.states[param] then 		return false 	end  	-- create a new state for each command 	self.states[param] = State:new(self, param)  	-- use "%x" as the general parameter name 	self.states[param].parsedFormat = parseFormat(parameters.general)  -- will be overwritten for param=="%p"  	-- set the separator 	self.states[param].separator = self.separators["sep"..param]  -- will be nil for param=="%p", which will be set separately  	if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then 		self.states[param].singleValue = true 	end  	self.curState = self.states[param]  	return true end  function Config:processSeparators(args) 	local sep  	for i, v in pairs(self.separators) do 		if args[i] then 			sep = replaceSpecialChars(args[i])  			if sep ~= "" then 				self.separators[i][1] = {sep} 			else 				self.separators[i][1] = nil 			end 		end 	end end  function Config:setFormatAndSeparators(state, parsedFormat) 	state.parsedFormat = parsedFormat 	state.separator = self.separators["sep"] 	state.movSeparator = self.separators["sep"..parameters.separator] 	state.puncMark = self.separators["punc"] end  -- determines if a claim has references by prefetching them from the claim using getReferences, -- which applies some filtering that determines if a reference is actually returned, -- and caches the references for later use function State:isSourced(claim) 	self.conf.prefetchedRefs = self:getReferences(claim) 	return (#self.conf.prefetchedRefs > 0) end  function State:resetCaches() 	-- any prefetched references of the previous claim must not be used 	self.conf.prefetchedRefs = nil end  function State:claimMatches(claim) 	local matches, rankPos  	-- first of all, reset any cached values used for the previous claim 	self:resetCaches()  	-- if a property value was given, check if it matches the claim's property value 	if self.conf.propertyValue then 		matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue) 	else 		matches = true 	end  	-- if any qualifier values were given, check if each matches one of the claim's qualifier values 	for i, v in pairs(self.conf.qualifierIDsAndValues) do 		matches = (matches and self.conf:qualifierMatches(claim, i, v)) 	end  	-- check if the claim's rank and time period match 	rankPos = rankTable[claim.rank] or 4 	matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))  	-- if only claims with references must be returned, check if this one has any 	if self.conf.sourcedOnly then 		matches = (matches and self:isSourced(claim))  -- prefetches and caches references 	end  	return matches, rankPos end  function State:out() 	local result  -- collection of arrays with value objects 	local valuesArray  -- array with value objects 	local sep = nil  -- value object 	local out = {}  -- array with value objects  	local function walk(formatTable, result) 		local valuesArray = {}  -- array with value objects  		for i, v in pairs(formatTable.req) do 			if not result[i] or not result[i][1] then 				-- we've got no result for a parameter that is required on this level, 				-- so skip this level (and its children) by returning an empty result 				return {} 			end 		end  		for _, v in ipairs(formatTable) do 			if v.param then 				valuesArray = mergeArrays(valuesArray, result[v.str]) 			elseif v.str ~= "" then 				valuesArray[#valuesArray + 1] = {v.str} 			end  			if v.child then 				valuesArray = mergeArrays(valuesArray, walk(v.child, result)) 			end 		end  		return valuesArray 	end  	-- iterate through the results from back to front, so that we know when to add separators 	for i = #self.results, 1, -1 do 		result = self.results[i]  		-- if there is already some output, then add the separators 		if #out > 0 then 			sep = self.separator[1]  -- fixed separator 			result[parameters.separator] = {self.movSeparator[1]}  -- movable separator 		else 			sep = nil 			result[parameters.separator] = {self.puncMark[1]}  -- optional punctuation mark 		end  		valuesArray = walk(self.parsedFormat, result)  		if #valuesArray > 0 then 			if sep then 				valuesArray[#valuesArray + 1] = sep 			end  			out = mergeArrays(valuesArray, out) 		end 	end  	-- reset state before next iteration 	self.results = {}  	return out end  -- level 1 hook function State:getProperty(claim) 	local value = {self:getValue(claim.mainsnak)}  -- create one value object  	if #value > 0 then 		return {value}  -- wrap the value object in an array and return it 	else 		return {}  -- return empty array if there was no value 	end end  -- level 1 hook function State:getQualifiers(claim, param) 	local qualifiers  	if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end 	if qualifiers then 		-- iterate through claim's qualifier statements to collect their values; 		-- return array with multiple value objects 		return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier.."\\d"][2], count = 1})  -- pass qualifier state with level 2 hook 	else 		return {}  -- return empty array 	end end  -- level 2 hook function State:getQualifier(snak) 	local value = {self:getValue(snak)}  -- create one value object  	if #value > 0 then 		return {value}  -- wrap the value object in an array and return it 	else 		return {}  -- return empty array if there was no value 	end end  -- level 1 hook function State:getAllQualifiers(claim, param, result, hooks) 	local out = {}  -- array with value objects 	local sep = self.conf.separators["sep"..parameters.qualifier][1]  -- value object  	-- iterate through the output of the separate "qualifier(s)" commands 	for i = 1, self.conf.states.qualifiersCount do  		-- if a hook has not been called yet, call it now 		if not result[parameters.qualifier..i] then 			self:callHook(parameters.qualifier..i, hooks, claim, result) 		end  		-- if there is output for this particular "qualifier(s)" command, then add it 		if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then  			-- if there is already some output, then add the separator 			if #out > 0 and sep then 				out[#out + 1] = sep 			end  			out = mergeArrays(out, result[parameters.qualifier..i]) 		end 	end  	return out end  -- level 1 hook function State:getReferences(claim) 	if self.conf.prefetchedRefs then 		-- return references that have been prefetched by isSourced 		return self.conf.prefetchedRefs 	end  	if claim.references then 		-- iterate through claim's reference statements to collect their values; 		-- return array with multiple value objects 		return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1})  -- pass reference state with level 2 hook 	else 		return {}  -- return empty array 	end end  -- level 2 hook function State:getReference(statement) 	local key, citeWeb, citeQ, label 	local params = {} 	local citeParams = {['web'] = {}, ['q'] = {}} 	local citeMismatch = {} 	local useCite = nil 	local useParams = nil 	local value = "" 	local ref = {}  	local version = 1  -- increment this each time the below logic is changed to avoid conflict errors  	if statement.snaks then 		-- don't include "imported from", which is added by a bot 		if statement.snaks[aliasesP.importedFrom] then 			statement.snaks[aliasesP.importedFrom] = nil 		end  		-- don't include "inferred from", which is added by a bot 		if statement.snaks[aliasesP.inferredFrom] then 			statement.snaks[aliasesP.inferredFrom] = nil 		end  		-- don't include "type of reference" 		if statement.snaks[aliasesP.typeOfReference] then 			statement.snaks[aliasesP.typeOfReference] = nil 		end  		-- don't include "image" to prevent littering 		if statement.snaks[aliasesP.image] then 			statement.snaks[aliasesP.image] = nil 		end  		-- don't include "language" if it is equal to the local one 		if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then 			statement.snaks[aliasesP.language] = nil 		end  		-- retrieve all the parameters 		for i in pairs(statement.snaks) do 			label = ""  			-- multiple authors may be given 			if i == aliasesP.author then 				params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true)  -- link = true/false, anyLang = true 			else 				params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)}  -- link = true/false, anyLang = true 			end  			if #params[i] == 0 then 				params[i] = nil 			else 				if statement.snaks[i][1].datatype == 'external-id' then 					key = "external-id" 					label = self.conf:getLabel(i)  					if label ~= "" then 						label = label .. " " 					end 				else 					key = i 				end  				-- add the parameter to each matching type of citation 				for j in pairs(citeParams) do 					-- do so if there was no mismatch with a previous parameter 					if not citeMismatch[j] then 						-- check if this parameter is not mismatching itself 						if i18n['cite'][j][key] then 							-- continue if an option is available in the corresponding cite template 							if i18n['cite'][j][key] ~= "" then 								citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]  								-- if there are multiple parameter values (authors), add those too 								for k=2, #params[i] do 									citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k] 								end 							end 						else 							citeMismatch[j] = true 						end 					end 				end 			end 		end  		-- get title of general template for citing web references 		citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or "", ":")[2]  -- split off namespace from front  		-- get title of template that expands stated-in references into citations 		citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or "", ":")[2]  -- split off namespace from front  		-- (1) use the general template for citing web references if there is a match and if at least both "reference URL" and "title" are present 		if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then 			useCite = citeWeb 			useParams = citeParams['web']  		-- (2) use the template that expands stated-in references into citations if there is a match and if at least "stated in" is present 		elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then 			-- we need the raw "stated in" Q-identifier for the this template 			citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true)  -- raw = true  			useCite = citeQ 			useParams = citeParams['q'] 		end  		if useCite and useParams then 			-- if this module is being substituted then build a regular template call, otherwise expand the template 			if mw.isSubsting() then 				for i, v in pairs(useParams) do 					value = value .. "|" .. i .. "=" .. v 				end  				value = "" 			else 				value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams} 			end  		-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present 		elseif params[aliasesP.statedIn] or params[aliasesP.referenceURL] or params[aliasesP.title] then 			citeParams['default'] = {}  			-- start by adding authors up front 			if params[aliasesP.author] and #params[aliasesP.author] > 0 then 				citeParams['default'][#citeParams['default'] + 1] = table.concat(params[aliasesP.author], " & ") 			end  			-- combine "reference URL" and "title" into one link if both are present 			if params[aliasesP.referenceURL] and params[aliasesP.title] then 				citeParams['default'][#citeParams['default'] + 1] = '[' .. params[aliasesP.referenceURL][1] .. ' "' .. params[aliasesP.title][1] .. '"]' 			elseif params[aliasesP.referenceURL] then 				citeParams['default'][#citeParams['default'] + 1] = params[aliasesP.referenceURL][1] 			elseif params[aliasesP.title] then 				citeParams['default'][#citeParams['default'] + 1] = '"' .. params[aliasesP.title][1] .. '"' 			end  			-- then add "stated in" 			if params[aliasesP.statedIn] then 				citeParams['default'][#citeParams['default'] + 1] = "" .. params[aliasesP.statedIn][1] .. "" 			end  			-- remove previously added parameters so that they won't be added a second time 			params[aliasesP.author] = nil 			params[aliasesP.referenceURL] = nil 			params[aliasesP.title] = nil 			params[aliasesP.statedIn] = nil  			-- add the rest of the parameters 			for i, v in pairs(params) do 				i = self.conf:getLabel(i)  				if i ~= "" then 					citeParams['default'][#citeParams['default'] + 1] = i .. ": " .. v[1] 				end 			end  			value = table.concat(citeParams['default'], "; ")  			if value ~= "" then 				value = value .. "." 			end 		end  		if value ~= "" then 			value = {value}  -- create one value object  			if not self.rawValue then 				-- this should become a <ref> tag, so save the reference's hash for later 				value.refHash = "wikidata-" .. statement.hash .. "-v" .. (tonumber(i18n['cite']['version']) + version) 			end  			ref = {value}  -- wrap the value object in an array 		end 	end  	return ref end  -- gets a detail of one particular type for a reference function State:getReferenceDetail(snaks, dType, raw, link, anyLang) 	local switchLang = anyLang 	local value = nil  	if not snaks[dType] then 		return nil 	end  	-- if anyLang, first try the local language and otherwise any language 	repeat 		for _, v in ipairs(snaks[dType]) do 			value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true)  -- noSpecial = true  			if value then 				break 			end 		end  		if value or not anyLang then 			break 		end  		switchLang = not switchLang 	until anyLang and switchLang  	return value end  -- gets the details of one particular type for a reference function State:getReferenceDetails(snaks, dType, raw, link, anyLang) 	local values = {}  	if not snaks[dType] then 		return {} 	end  	for _, v in ipairs(snaks[dType]) do 		-- if nil is returned then it will not be added to the table 		values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true)  -- noSpecial = true 	end  	return values end  -- level 1 hook function State:getAlias(object) 	local value = object.value 	local title = nil  	if value and self.linked then 		if self.conf.entityID:sub(1,1) == "Q" then 			title = mw.wikibase.getSitelink(self.conf.entityID) 		elseif self.conf.entityID:sub(1,1) == "P" then 			title = "d:Property:" .. self.conf.entityID 		end  		if title then 			value = buildWikilink(title, value) 		end 	end  	value = {value}  -- create one value object  	if #value > 0 then 		return {value}  -- wrap the value object in an array and return it 	else 		return {}  -- return empty array if there was no value 	end end  -- level 1 hook function State:getBadge(value) 	value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)  	if value == "" then 		value = nil 	end  	value = {value}  -- create one value object  	if #value > 0 then 		return {value}  -- wrap the value object in an array and return it 	else 		return {}  -- return empty array if there was no value 	end end  function State:callHook(param, hooks, statement, result) 	local valuesArray, refHash  	-- call a parameter's hook if it has been defined and if it has not been called before 	if not result[param] and hooks[param] then 		valuesArray = self[hooks[param]](self, statement, param, result, hooks)  -- array with value objects  		-- add to the result 		if #valuesArray > 0 then 			result[param] = valuesArray 			result.count = result.count + 1 		else 			result[param] = {}  -- an empty array to indicate that we've tried this hook already 			return true  -- miss == true 		end 	end  	return false end  -- iterate through claims, claim's qualifiers or claim's references to collect values function State:iterate(statements, hooks, matchHook) 	matchHook = matchHook or alwaysTrue  	local matches = false 	local rankPos = nil 	local result, gotRequired  	for _, v in ipairs(statements) do 		-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.) 		matches, rankPos = matchHook(self, v)  		if matches then 			result = {count = 0}  -- collection of arrays with value objects  			local function walk(formatTable) 				local miss  				for i2, v2 in pairs(formatTable.req) do 					-- call a hook, adding its return value to the result 					miss = self:callHook(i2, hooks, v, result)  					if miss then 						-- we miss a required value for this level, so return false 						return false 					end  					if result.count == hooks.count then 						-- we're done if all hooks have been called; 						-- returning at this point breaks the loop 						return true 					end 				end  				for _, v2 in ipairs(formatTable) do 					if result.count == hooks.count then 						-- we're done if all hooks have been called; 						-- returning at this point prevents further childs from being processed 						return true 					end  					if v2.child then 						walk(v2.child) 					end 				end  				return true 			end 			gotRequired = walk(self.parsedFormat)  			-- only append the result if we got values for all required parameters on the root level 			if gotRequired then 				-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank 				if rankPos and self.conf.foundRank > rankPos then 					self.conf.foundRank = rankPos 				end  				-- append the result 				self.results[#self.results + 1] = result  				-- break if we only need a single value 				if self.singleValue then 					break 				end 			end 		end 	end  	return self:out() end  local function getEntityId(arg, eid, page, allowOmitPropPrefix) 	local id = nil 	local prop = nil  	if arg then 		if arg:sub(1,1) == ":" then 			page = arg 			eid = nil 		elseif arg:sub(1,1):upper() == "Q" or arg:sub(1,9):lower() == "property:" or allowOmitPropPrefix then 			eid = arg 			page = nil 		else 			prop = arg 		end 	end  	if eid then 		if eid:sub(1,9):lower() == "property:" then 			id = replaceAlias(mw.text.trim(eid:sub(10)))  			if id:sub(1,1):upper() ~= "P" then 				id = "" 			end 		else 			id = replaceAlias(eid) 		end 	elseif page then 		if page:sub(1,1) == ":" then 			page = mw.text.trim(page:sub(2)) 		end  		id = mw.wikibase.getEntityIdForTitle(page) or "" 	end  	if not id then 		id = mw.wikibase.getEntityIdForCurrentPage() or "" 	end  	id = id:upper()  	if not mw.wikibase.isValidEntityId(id) then 		id = "" 	end  	return id, prop end  local function nextArg(args) 	local arg = args[args.pointer]  	if arg then 		args.pointer = args.pointer + 1 		return mw.text.trim(arg) 	else 		return nil 	end end  local function claimCommand(args, funcName) 	local cfg = Config:new() 	cfg:processFlagOrCommand(funcName)  -- process first command (== function name)  	local lastArg, parsedFormat, formatParams, claims, value 	local hooks = {count = 0}  	-- set the date if given; 	-- must come BEFORE processing the flags 	if args[p.args.date] then 		cfg.atDate = {parseDate(args[p.args.date])} 		cfg.periods = {false, true, false}  -- change default time constraint to 'current' 	end  	-- process flags and commands 	repeat 		lastArg = nextArg(args) 	until not cfg:processFlagOrCommand(lastArg)  	-- get the entity ID from either the positional argument, the eid argument or the page argument 	cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])  	if cfg.entityID == "" then 		return ""  -- we cannot continue without a valid entity ID 	end  	cfg.entity = mw.wikibase.getEntity(cfg.entityID)  	if not cfg.propertyID then 		cfg.propertyID = nextArg(args) 	end  	cfg.propertyID = replaceAlias(cfg.propertyID)  	if not cfg.entity or not cfg.propertyID then 		return ""  -- we cannot continue without an entity or a property ID 	end  	cfg.propertyID = cfg.propertyID:upper()  	if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then 		return ""  -- there is no use to continue without any claims 	end  	claims = cfg.entity.claims[cfg.propertyID]  	if cfg.states.qualifiersCount > 0 then 		-- do further processing if "qualifier(s)" command was given  		if #args - args.pointer + 1 > cfg.states.qualifiersCount then 			-- claim ID or literal value has been given  			cfg.propertyValue = nextArg(args) 		end  		for i = 1, cfg.states.qualifiersCount do 			-- check if given qualifier ID is an alias and add it 			cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or ""):upper() 		end 	elseif cfg.states[parameters.reference] then 		-- do further processing if "reference(s)" command was given  		cfg.propertyValue = nextArg(args) 	end  	-- check for special property value 'somevalue' or 'novalue' 	if cfg.propertyValue then 		cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)  		if cfg.propertyValue ~= "" and mw.text.trim(cfg.propertyValue) == "" then 			cfg.propertyValue = " "  -- single space represents 'somevalue', whereas empty string represents 'novalue' 		else 			cfg.propertyValue = mw.text.trim(cfg.propertyValue) 		end 	end  	-- parse the desired format, or choose an appropriate format 	if args["format"] then 		parsedFormat, formatParams = parseFormat(args["format"]) 	elseif cfg.states.qualifiersCount > 0 then  -- "qualifier(s)" command given 		if cfg.states[parameters.property] then  -- "propert(y|ies)" command given 			parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier) 		else 			parsedFormat, formatParams = parseFormat(formats.qualifier) 		end 	elseif cfg.states[parameters.property] then  -- "propert(y|ies)" command given 		parsedFormat, formatParams = parseFormat(formats.property) 	else  -- "reference(s)" command given 		parsedFormat, formatParams = parseFormat(formats.reference) 	end  	-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolon 	if cfg.states.qualifiersCount > 0 and not cfg.states[parameters.property] then 		cfg.separators["sep"..parameters.separator][1] = {";"} 	end  	-- if only "reference(s)" has been given, set the default separator to none (except when raw) 	if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0 	   and not cfg.states[parameters.reference].rawValue then 		cfg.separators["sep"][1] = nil 	end  	-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent 	if cfg.states.qualifiersCount == 1 then 		cfg.separators["sep"..parameters.qualifier] = cfg.separators["sep"..parameters.qualifier.."1"] 	end  	-- process overridden separator values; 	-- must come AFTER tweaking the default separators 	cfg:processSeparators(args)  	-- define the hooks that should be called (getProperty, getQualifiers, getReferences); 	-- only define a hook if both its command ("propert(y|ies)", "reference(s)", "qualifier(s)") and its parameter ("%p", "%r", "%q1", "%q2", "%q3") have been given 	for i, v in pairs(cfg.states) do 		-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q" 		if formatParams[i] or formatParams[i:sub(1, 2)] then 			hooks[i] = getHookName(i, 1) 			hooks.count = hooks.count + 1 		end 	end  	-- the "%q" parameter is not attached to a state, but is a collection of the results of multiple states (attached to "%q1", "%q2", "%q3", ...); 	-- so if this parameter is given then this hook must be defined separately, but only if at least one "qualifier(s)" command has been given 	if formatParams[parameters.qualifier] and cfg.states.qualifiersCount > 0 then 		hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1) 		hooks.count = hooks.count + 1 	end  	-- create a state for "properties" if it doesn't exist yet, which will be used as a base configuration for each claim iteration; 	-- must come AFTER defining the hooks 	if not cfg.states[parameters.property] then 		cfg.states[parameters.property] = State:new(cfg, parameters.property)  		-- if the "single" flag has been given then this state should be equivalent to "property" (singular) 		if cfg.singleClaim then 			cfg.states[parameters.property].singleValue = true 		end 	end  	-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values, 	-- which must exist in order to be able to determine if a claim has any references; 	-- must come AFTER defining the hooks 	if cfg.sourcedOnly and not cfg.states[parameters.reference] then 		cfg:processFlagOrCommand(p.claimCommands.reference)  -- use singular "reference" to minimize overhead 	end  	-- set the parsed format and the separators (and optional punctuation mark); 	-- must come AFTER creating the additonal states 	cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)  	-- process qualifier matching values, analogous to cfg.propertyValue 	for i, v in pairs(args) do 		i = tostring(i)  		if i:match('^[Pp]%d+$') or aliasesP[i] then 			v = replaceSpecialChars(v)  			-- check for special qualifier value 'somevalue' 			if v ~= "" and mw.text.trim(v) == "" then 				v = " "  -- single space represents 'somevalue' 			end  			cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v 		end 	end  	-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated) 	claims = sortOnRank(claims)  	-- then iterate through the claims to collect values 	value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches))  -- pass property state with level 1 hooks and matchHook  	-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata 	if cfg.editable and value ~= "" then 		value = value .. cfg:getEditIcon() 	end  	return value end  local function generalCommand(args, funcName) 	local cfg = Config:new() 	cfg.curState = State:new(cfg)  	local lastArg 	local value = nil  	repeat 		lastArg = nextArg(args) 	until not cfg:processFlag(lastArg)  	-- get the entity ID from either the positional argument, the eid argument or the page argument 	cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)  	if cfg.entityID == "" or not mw.wikibase.entityExists(cfg.entityID) then 		return ""  -- we cannot continue without an entity 	end  	-- serve according to the given command 	if funcName == p.generalCommands.label then 		value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName) 	elseif funcName == p.generalCommands.title then 		cfg.inSitelinks = true  		if cfg.entityID:sub(1,1) == "Q" then 			value = mw.wikibase.getSitelink(cfg.entityID) 		end  		if cfg.curState.linked and value then 			value = buildWikilink(value) 		end 	elseif funcName == p.generalCommands.description then 		value = mw.wikibase.getDescription(cfg.entityID) 	else 		local parsedFormat, formatParams 		local hooks = {count = 0}  		cfg.entity = mw.wikibase.getEntity(cfg.entityID)  		if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then 			cfg.curState.singleValue = true 		end  		if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then 			if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then 				return ""  -- there is no use to continue without any aliasses 			end  			local aliases = cfg.entity.aliases[cfg.langCode]  			-- parse the desired format, or parse the default aliases format 			if args["format"] then 				parsedFormat, formatParams = parseFormat(args["format"]) 			else 				parsedFormat, formatParams = parseFormat(formats.alias) 			end  			-- process overridden separator values; 			-- must come AFTER tweaking the default separators 			cfg:processSeparators(args)  			-- define the hook that should be called (getAlias); 			-- only define the hook if the parameter ("%a") has been given 			if formatParams[parameters.alias] then 				hooks[parameters.alias] = getHookName(parameters.alias, 1) 				hooks.count = hooks.count + 1 			end  			-- set the parsed format and the separators (and optional punctuation mark) 			cfg:setFormatAndSeparators(cfg.curState, parsedFormat)  			-- iterate to collect values 			value = cfg:concatValues(cfg.curState:iterate(aliases, hooks)) 		elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then 			if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then 				return ""  -- there is no use to continue without any badges 			end  			local badges = cfg.entity.sitelinks[cfg.siteID].badges  			cfg.inSitelinks = true  			-- parse the desired format, or parse the default aliases format 			if args["format"] then 				parsedFormat, formatParams = parseFormat(args["format"]) 			else 				parsedFormat, formatParams = parseFormat(formats.badge) 			end  			-- process overridden separator values; 			-- must come AFTER tweaking the default separators 			cfg:processSeparators(args)  			-- define the hook that should be called (getBadge); 			-- only define the hook if the parameter ("%b") has been given 			if formatParams[parameters.badge] then 				hooks[parameters.badge] = getHookName(parameters.badge, 1) 				hooks.count = hooks.count + 1 			end  			-- set the parsed format and the separators (and optional punctuation mark) 			cfg:setFormatAndSeparators(cfg.curState, parsedFormat)  			-- iterate to collect values 			value = cfg:concatValues(cfg.curState:iterate(badges, hooks)) 		end 	end  	value = value or ""  	if cfg.editable and value ~= "" then 		-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata 		value = value .. cfg:getEditIcon() 	end  	return value end  -- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args) local function establishCommands(commandList, commandFunc) 	for _, commandName in pairs(commandList) do 		local function wikitextWrapper(frame) 			local args = copyTable(frame.args) 			args.pointer = 1 			loadI18n(aliasesP, frame) 			return commandFunc(args, commandName) 		end 		p[commandName] = wikitextWrapper  		local function luaWrapper(args) 			args = copyTable(args) 			args.pointer = 1 			loadI18n(aliasesP) 			return commandFunc(args, commandName) 		end 		p["_" .. commandName] = luaWrapper 	end end  establishCommands(p.claimCommands, claimCommand) establishCommands(p.generalCommands, generalCommand)  -- main function that is supposed to be used by wrapper templates function p.main(frame) 	if not mw.wikibase then return nil end  	local f, args  	loadI18n(aliasesP, frame)  	-- get the parent frame to take the arguments that were passed to the wrapper template 	frame = frame:getParent() or frame  	if not frame.args[1] then 		throwError("no-function-specified") 	end  	f = mw.text.trim(frame.args[1])  	if f == "main" then 		throwError("main-called-twice") 	end  	assert(p["_"..f], errorText('no-such-function', f))  	-- copy arguments from immutable to mutable table 	args = copyTable(frame.args)  	-- remove the function name from the list 	table.remove(args, 1)  	return p["_"..f](args) end  return p