Module:Cite Q
-- Version: 2021-10-19
 
 local p = {}
 
 require('strict')
 local wdib = require('Module:WikidataIB')
 local getValue = wdib._getValue
 local getPropOfProp = wdib._getPropOfProp
 local followQid = wdib._followQid
 local getPropertyIDs = wdib._getPropertyIDs
 
 local i18n = {
 	[&quot;unknown-author&quot;] = mw.wikibase.getLabel(&quot;Q4233718&quot;):gsub(&quot;^%l&quot;, mw.ustring.upper),
 	[&quot;unknown-author-trackingcat&quot;] = &quot;[[Category:Cite Q - author unknown]]&quot;,
 	[&quot;ordinal&quot;] = {
 		[1] = &quot;st&quot;,
 		[2] = &quot;nd&quot;,
 		[3] = &quot;rd&quot;,
 		[&quot;default&quot;] = &quot;th&quot;
 	},
 	[&quot;months&quot;] = {
 		&quot;January&quot;, &quot;February&quot;, &quot;March&quot;, &quot;April&quot;, &quot;May&quot;, &quot;June&quot;,
 		&quot;July&quot;, &quot;August&quot;, &quot;September&quot;, &quot;October&quot;, &quot;November&quot;, &quot;December&quot;
 	},
 }
 
 -------------------------------------------------------------------------------
 -- makeOrdinal needs to be internationalised along with the above i18n
 -- takes cardinal number as a numeric and returns the ordinal as a string
 -- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc.
 -------------------------------------------------------------------------------
 p.makeOrdinal = function(cardinal)
 	local card = tonumber(cardinal)
 	if not card then return cardinal end
 	local ordsuffix = i18n.ordinal.default
 	if card % 10 == 1 then
 		ordsuffix = i18n.ordinal[1]
 	elseif card % 10 == 2 then
 		ordsuffix = i18n.ordinal[2]
 	elseif card % 10 == 3 then
 		ordsuffix = i18n.ordinal[3]
 	end
 	-- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th'
 	-- similarly for 12 and 13, etc.
 	if (card % 100 == 11) or (card % 100 == 12) or (card % 100 == 13) then
 		ordsuffix = i18n.ordinal.default
 	end
 	return card .. ordsuffix
 end
 
 -- Table of simple properties that can be fetched in roughly the same way:
 -- id = PXXX
 -- maxvals = maximum number of multiple values (0 for all)
 -- linked = &quot;no&quot; suppresses linking
 -- populate_from_journal = true/false determines whether to look in a journal where the source is published
 -- rank = &quot;best&quot;, &quot;preferred&quot;, normal, etc. determines how Wikidata ranks are treated
 -- others = true - the value for the property goes to &quot;others&quot; section
 local simple_properties = {
 	publisher = {id = &quot;P123&quot;, maxvals = 1},
 	oclc = {id = &quot;P243&quot;, maxvals = 1},
 	['publication-place'] = {id = &quot;P291&quot;, maxvals = 0, linked = 'no'}, -- publication place (don't put into |place=; is treated specially in {{citation}} if both are given)
 	doi = {id = &quot;P356&quot;, maxvals = 1}, -- take care of |doi-broken-date= (WD &quot;reason for deprecation&quot;/&quot;stated as&quot;) and |doi-access= (WD &quot;access status&quot;)?
 	issue = {id = &quot;P433&quot;, maxvals = 0, populate_from_journal = true}, -- distinguish from |number= (&quot;P1545&quot;?) if both are given (still blocked by {{citation}}, but will be supported in the future)
 	pmid = {id = &quot;P698&quot;, maxvals = 1},
 --	gbooks = {id = &quot;P675&quot;, maxvals = 1}, -- to be added to {{citation}}
 --	ia = {id = &quot;P724&quot;, maxvals = 1}, -- to be added to {{citation}}
 	arxiv = {id = &quot;P818&quot;, maxvals = 1},
 	bibcode = {id = &quot;P819&quot;, maxvals = 1}, -- take care of |bibcode-access=?
 	jstor = {id = &quot;P888&quot;, maxvals = 1}, -- take care of |jstor-access=?
 	mr = {id = &quot;P889&quot;, maxvals = 1},
 	rfc = {id = &quot;P892&quot;, maxvals = 1},
 	zbl = {id = &quot;P894&quot;, maxvals = 1},
 	ssrn = {id = &quot;P893&quot;, maxvals = 1},
 	place = {id = &quot;P1071&quot;, maxvals = 0, linked = 'no'}, -- written-at place
 --	['total-pages'] = {id = &quot;P1104&quot;, maxvals = 0, linked = 'no'}, -- to be added to {{citation}} / COinS &amp;rft.tpages=
 --	coden = {id = &quot;P1159&quot;, maxvals = 1}, -- to be added to {{citation}} / COinS &amp;rft.coden=
 	s2cid = {id = &quot;P8299&quot;, maxvals = 1}, -- take care of |s2cid-access=?
 	pmc = {id = &quot;P932&quot;, maxvals = 1}, -- take care of |pmc-embargo-date= (WD &quot;reason for deprecation&quot;)?
 	lccn = {id = &quot;P1144&quot;, maxvals = 1},
 	hdl = {id = &quot;P1184&quot;, maxvals = 1}, -- take care of |hdl-access=?
 	ismn = {id = &quot;P1208&quot;, maxvals = 1},
 	journal = {id = &quot;P1433&quot;, maxvals = 1},
 	citeseerx = {id = &quot;P3784&quot;, maxvals = 1},
 	osti = {id = &quot;P3894&quot;, maxvals = 1}, -- take care of |osti-access=?
 	biorxiv = {id = &quot;P3951&quot;, maxvals = 1},
 	asin = {id = &quot;P5749&quot;, maxvals = 1}, -- What about |asin-tld=? (WD examples resolve to .com at present, but may change)
 --	['catalog-number'] = {id = &quot;P528&quot;, maxvals = 0}, -- to be added to {{citation}} / COinS &amp;rft.artnum=
 	isbn = {id = &quot;P212&quot;, maxvals = 1, populate_from_journal = true}, -- ISBN 13
 	issn = {id = &quot;P236&quot;, maxvals = 1, populate_from_journal = true}, -- distinguish from |eissn= for electronic issues?
 --	jfm = {id = &quot;P?&quot;, maxvals = 1}, -- Jahrbuch über die Fortschritte der Mathematik (not Zbl)
 --	sbn = {id = &quot;P?&quot;, maxvals = 1}, -- Standard Book Number (predecessor of ISBN, not ICCU)
 --	message-id = {id = &quot;P?&quot;, maxvals = 1}, -- Usenet message ID
 	chapter = {id = &quot;P792&quot;, maxvals = 1},
 	['publication-date'] = {id = &quot;P577&quot;, maxvals = 1, populate_from_journal = true}, -- publication date (don't use |date=; is treated specially in {{citation}} if both are given.)
 	series = {id = &quot;P179&quot;, maxvals = 1, populate_from_journal = true},
 	version = {id = &quot;P348&quot;, maxvals = 0},
 	edition = {id = &quot;P393&quot;, maxvals = 0},
 	volume = {id = &quot;P478&quot;, maxvals = 0, populate_from_journal = true},
 --	part = {id = &quot;P1545&quot;?, maxvals = 0}, --  to be added to {{citation}} / COinS &amp;rft.part=
 	title = {id = &quot;P1476&quot;, rank=&quot;p n&quot;},
 --	url = {id = &quot;P953&quot;, maxvals = 1}, -- deal with this along with archive-url
 	pages = {id = &quot;P304&quot;, maxvals = 0, populate_from_journal = true},
 	at = {id = &quot;P958&quot;, maxvals = 0, populate_from_journal = true}, -- also incorporate lines (P7421) and columns (P3903) into this (cite map also supports |section=)
 --	sheets = {id = &quot;P7416&quot;, maxvals = 0, populate_from_journal = true},
 --	interviewer = {id = &quot;P?&quot;, maxvals = 0}, -- does **not** go to &quot;others&quot; section! Multiple interviewers should be n-enumerated
 	illustrator = {id = &quot;P110&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 -- foreword and afterword, when contributions to another author's work, are contributions so belong in |contribution=;
 -- the writer's name goes in |contributor=; requires |title= and |author=
 -- However, this might need to add support for multiple contributors and their roles to {{citation}}, see Help_talk:Citation_Style_1#Others
 --	foreword = {id = &quot;P2679&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 --	afterword = {id = &quot;P2680&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 	composer = {id = &quot;P86&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 	animator = {id = &quot;P6942&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 	director = {id = &quot;P57&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 	screenwriter = {id = &quot;P58&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 	signatory = {id = &quot;P1891&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 	presenter = {id = &quot;P371&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 	performer = {id = &quot;P175&quot;, maxvals = 10, others = true}, -- goes to &quot;others&quot; section
 }
 
 --[[--------------------------&lt; I S _ S E T &gt;--------------------------------------------------------------
 Returns true if argument is set; false otherwise. Argument is 'set' when it exists (not nil) or when it is not an empty string.
 ]]
 local function is_set( var )
 	return not (var == nil or var == '')
 end
 
 --[[--------------------------&lt; I N _ A R R A Y &gt;--------------------------------------------------------------
 Whether needle is in haystack (taken from Module:Citation/CS1/Utilities)
 ]]
 local function in_array( needle, haystack )
 	if needle == nil then
 		return false
 	end
 	for n, v in ipairs( haystack ) do
 		if v == needle then
 			return n
 		end
 	end
 	return false
 end
 
 
 --[[--------------------------&lt; A C C E P T _ V A L U E &gt;-------------------------------------------------------
 Accept WD value by framing in ((...)) if param_val is equal to keyword; else pass-through WD value as is.
 ]]
 local function accept_value( param_val, wd_val )
 	local val = param_val
 
 	if val then
 		if in_array (val, {'accept', '))((', ':d:'}) then
 			val = '((' .. wd_val .. '))'
 		elseif '((accept))' == val then
 			val = 'accept'
 		elseif '(())(())' == val then
 			val = '))(('
 		elseif '((:d:))' == val then
 			val = ':d:'
 		else
 			val = wd_val
 		end
 	end
 
 	return val
 end
 
 -- function to fetch a value to display
 local function makelink(v, out, link, maxpos, wdl)
 	local label
 	if v.mainsnak.snaktype == &quot;value&quot; then
 		if v.mainsnak.datatype == &quot;wikibase-item&quot; then
 			local qnumber = v.mainsnak.datavalue.value.id
 			local sitelink = mw.wikibase.getSitelink(qnumber)
 			if qnumber == &quot;Q2818964&quot; then sitelink = nil end -- suppress link to &quot;Various authors&quot;
 			if v.qualifiers and v.qualifiers.P1932 then
 				label = v.qualifiers.P1932[1].datavalue.value
 			else
 				label = mw.wikibase.getLabel(qnumber)
 				if label then
 					label = mw.text.nowiki(label)
 				else
 					label = qnumber -- should add tracking category
 				end
 			end
 			local position = maxpos + 1 -- Default to 'next' author.
 			-- use P1545 (series ordinal) instead of default position.
 			if v[&quot;qualifiers&quot;] and v.qualifiers[&quot;P1545&quot;] and v.qualifiers[&quot;P1545&quot;][1] then
 				position = tonumber(v.qualifiers[&quot;P1545&quot;][1].datavalue.value)
 			end
 			maxpos = math.max(maxpos, position)
 			if sitelink then
 				-- just the plain name,
 				-- but keep a record of the links, using the same index
 				out[position] = label
 				link[position] = sitelink
 			else
 				if wdl then
 					-- show that there's a Wikidata entry available
 					out[position] = &quot;[[:d:Q&quot; .. v.mainsnak.datavalue.value[&quot;numeric-id&quot;] .. &quot;|&quot; .. label .. &quot;]]&amp;nbsp;&lt;span title='&quot; .. i18n[&quot;errors&quot;][&quot;local-article-not-found&quot;] .. &quot;'&gt;[[File:Wikidata-logo.svg|16px|alt=|link=]]&lt;/span&gt;&quot;
 				else
 					-- no Wikidata links wanted, so just give the plain label
 					out[position] = label
 				end
 			end
 		elseif v.mainsnak.datatype == &quot;string&quot; then
 			local position = maxpos + 1 -- Default to 'next' author.
 			-- use P1545 (series ordinal) instead of default position.
 			if v[&quot;qualifiers&quot;] and v.qualifiers[&quot;P1545&quot;] and v.qualifiers[&quot;P1545&quot;][1] then
 				position = tonumber(v.qualifiers[&quot;P1545&quot;][1].datavalue.value)
 			end
 			maxpos = math.max(maxpos, position)
 			out[position] = v.mainsnak.datavalue.value
 		else
 			-- not a wikibase-item or a string!
 		end
 	else
 		-- code here if we want to return something when author is &quot;unknown&quot;
 		if v.qualifiers and v.qualifiers.P1932 then
 			label = v.qualifiers.P1932[1].datavalue.value
 		else
 			label = i18n[&quot;unknown-author&quot;] .. (i18n[&quot;unknown-author-trackingcat&quot;] or &quot;&quot;)
 		end
 		maxpos = maxpos + 1
 		out[maxpos] = label
 	end
 	return maxpos
 end
 
 --[=[-------------------------&lt; G E T _ N A M E _ L I S T &gt;----------------------------------------------------
 get_name_list -- adapted from getAuthors code taken from Module:RexxS
 arguments:
 	nl_type - type of name list to fetch: nl_type = 'author' for authors; 'editor' for editors; 'translator' for translators
 	args - pointer to the parameter arguments table from the template call
 	qid - value from |qid= parameter; the Q-id of the source (book, etc.) in qid
 	wdl - value from the |wdl= parameter; a Boolean passed to enable links to Wikidata when no article exists
 returns nothing; modifies the args table
 ]=]
 
 local function get_name_list (nl_type, args, qid, wdl)
 	local propertyID = &quot;P50&quot;
 	local fallbackID = &quot;P2093&quot; -- author name string
 
 	if nl_type ==&quot;author&quot; then
 		propertyID = 'P50'		-- for authors
 		fallbackID = 'P2093'	-- author-string
 	elseif nl_type ==&quot;editor&quot; then
 		propertyID = 'P5769'	-- &quot;editor-in-chief&quot;
 		fallbackID = 'P98'		-- for editors - So-called &quot;fallbacks&quot; are actually a second set of properties processed
 		-- TBD. Take book series editors into account as well (if they have a separate P code as well)?
 	elseif nl_type == &quot;translator&quot; then
 		propertyID = 'P655'		-- for translators
 		fallbackID = nil
 --	elseif 'contributor' == nl_type then
 --		f.e. author of forewords (P2679) and afterwords (P2680); requires |contribution=, |title= and |author=
 --		propertyID = 'P'		-- for contributors
 --		fallbackID = nil
 	else
 		return					-- not specified so return
 	end
 
 	-- wdl is a Boolean passed to enable links to Wikidata when no article exists
 	-- if &quot;false&quot; or &quot;no&quot; or &quot;0&quot; is passed set it false
 	-- if nothing or an empty string is passed set it false
 	if wdl and (#wdl &gt; 0) then
 		wdl = wdl:lower()
 		wdl = in_array (wdl, {&quot;false&quot;, &quot;no&quot;, &quot;0&quot;})
 	else
 		-- wdl is empty, so
 		wdl = false
 	end
 
 	local props = nil
 	local fallback = nil
 	if mw.wikibase.entityExists(qid) then
 		props = mw.wikibase.getAllStatements(qid, propertyID)
 		if props and fallbackID then
 			fallback = mw.wikibase.getAllStatements(qid, fallbackID)
 		end
 	end
 
 	-- Make sure it actually has at least one of the properties requested
 	if not (props and props[1]) and not (fallback and fallback[1]) then
 		return nil
 	end
 
 	-- So now we have something to return:
 	-- table 'out' is going to store the names(s):
 	-- and table 'link' will store any links to the name's article
 	local out = {}
 	local link = {}
 	local maxpos = 0
 	if props and props[1] then
 		for k, v in pairs(props) do
 			maxpos = makelink(v, out, link, maxpos, wdl)
 		end
 	end
 	if fallback and fallback[1] then
 		-- second properties
 		for k, v in pairs(fallback) do
 			maxpos = makelink(v, out, link, maxpos, wdl)
 		end
 	end
 
 	-- if there's anything to return, then insert the additions in the template arguments table
 	-- in the form |author1=firstname secondname |author2= ...
 	-- Renumber, in case we have inconsistent numbering
 	local keys = {}
 	for k, v in pairs(out) do
 		keys[#keys + 1] = k
 	end
 	table.sort(keys) -- as they might be out of order
 	for i, k in ipairs(keys) do
 		out[k] = out[k]:gsub ('&amp;#39;', '\'');									-- prevent cs1|2 multiple names categorization; replace html entity with the actual character
 		mw.log(i .. &quot; &quot; .. k .. &quot; &quot; .. (out[k]))
 		if args[nl_type .. i] then -- name gets overwritten
 			-- pull corresponding -link only if overwritten name is same as WD name
 			if link[k] and (args[nl_type .. i] == out[k]) then
 				args[nl_type .. '-link' .. i] = args[nl_type .. '-link' .. i] or link[k] -- author-linkn or editor-linkn
 			end
 		else -- name does not get overwritten, so pull name from WD
 			args[nl_type .. i] = out[k]
 			if link[k] then
 				args[nl_type .. '-link' .. i] = args[nl_type .. '-link' .. i] or link[k] -- author-linkn or editor-linkn
 			end
 		end
 	end
 end
 
 -- gets language codes used for a monolingual text property as a table
 function p._getLangOfProp(qid, pid)
 	if not pid then return {} end
 	local out = {}
 	local props = mw.wikibase.getAllStatements(qid, pid)
 	for i, v in ipairs(props) do
 		if v.mainsnak.datatype == &quot;monolingualtext&quot; and v.mainsnak.datavalue then
 			out[#out + 1] = v.mainsnak.datavalue.value.language
 		end
 	end
 	return out
 end
 function p.getLangOfProp(frame)
 	local pid = frame.args.pid or mw.text.trim(frame.args[1] or &quot;&quot;)
 	if pid == &quot;&quot; then return end
 	local qid = frame.args.qid
 	if qid == &quot;&quot; then qid = nil end
 	return table.concat(p._getLangOfProp(qid, pid), &quot;, &quot;)
 end
 
 -- gets the language codes of a Wikidata entry as a table
 local function _lang_code(qid)
 	local lc = getPropOfProp( {qid = qid, prop1 = &quot;P407&quot;, prop2 = &quot;P424&quot;, ps = 1} )
 	if lc then return mw.text.split( lc, &quot;[, ]+&quot; ) end
 	lc = getPropOfProp( {qid = qid, prop1 = &quot;P407&quot;, prop2 = &quot;P218&quot;, ps = 1} )
 	if lc then return mw.text.split( lc, &quot;[, ]+&quot; ) end
 	return p._getLangOfProp(qid, &quot;P1476&quot;)
 end
 function p.lang_code(frame)
 	return table.concat(_lang_code(frame.args.qid or mw.text.trim(frame.args[1] or &quot;&quot;)), &quot;, &quot;)
 end
 
 -- export for debug
 function p.getPropOfProp(frame)
 	return getPropOfProp(frame.args)
 end
 
 -- wraps a string in nowiki unless disable flag is set
 local function wrap_nowiki(str, disable)
 	if disable then return str or '' end
 	return mw.text.nowiki(str or '')
 end
 
 -- sort sequence table whose values are key-value pairs by key
 local function comp_key(a, b)
 	return a[1] &lt; b[1]
 end
 
 -- sort sequence table whose values are key-value pairs by value
 local function comp_val(a, b)
 	return a[2] &lt; b[2]
 end
 
 --[[-------------------------&lt; C I T E _ Q &gt;------------------------------------------------------------------
 Takes standard CS1|2 template parameters and passes all to {{citation}}.  If neither of |author= and |author1=
 are set, calls get_authors() to try to get an author name-list from Wikidata.  The result is passed to
 {{citation}} for rendering.
 --]]
 function p._cite_q (citeq_args)
 	local frame = mw.getCurrentFrame()
 
 	-- parameters that don't get passed to Citation
 	local expand = citeq_args.expand -- when set to anything, causes {{cite q}} to render &lt;code&gt;&lt;nowiki&gt;{{citation|...}}&lt;/nowiki&gt;&lt;/code&gt;
 	local qid = citeq_args.qid or citeq_args[1]
 	local wdl = citeq_args.wdl
 	local template = citeq_args.template
 	citeq_args.expand = nil
 	citeq_args[1] = nil
 	citeq_args.qid = nil
 	citeq_args.wdl = nil
 	citeq_args.template = nil
 
 	-- if title supplied, flag to not read html title
 	local titleforced = (citeq_args.title ~= nil)
 
 	local oth = {}
 
 	-- put the language codes into a sequential table langcodes[]
 	local langcodes = {}
 	if citeq_args.language then
 		-- check these are a supported language codes
 		for lc in mw.text.gsplit( citeq_args.language, &quot;[, ]+&quot;, false ) do
 			langcodes[#langcodes+1] = mw.language.isSupportedLanguage(citeq_args.language) and citeq_args.language
 		end
 	end
 	if not langcodes[1] then
 		-- try to find language of work
 		langcodes = _lang_code(qid)
 	end
 	if not langcodes[1] then
 		-- try fallback to journal's language
 		local journal_qid = followQid({qid = qid, props = &quot;P1433&quot;})
 		langcodes = journal_qid and _lang_code(journal_qid)
 	end
 	citeq_args.language = citeq_args.language or table.concat(langcodes, &quot;, &quot;)
 
 	-- loop through list of simple properties and get their values in citeq_args
 	for name, data in pairs(simple_properties) do
 		citeq_args[name] = getValue( {data.id, fwd = &quot;ALL&quot;, osd = &quot;no&quot;, noicon = &quot;true&quot;, qid = qid, maxvals = data.maxvals, linked = data.linked, rank = data.rank or &quot;best&quot;, citeq_args[name] } )
 		if data.populate_from_journal then
 			local publishedin = getValue( {&quot;P1433&quot;, ps = 1, qid = qid, maxvals = 0, citeq_args[name], qual = data.id, qualsonly = 'yes'} )
 			citeq_args[name] = publishedin or getPropOfProp({qid = qid, prop1 = &quot;P1433&quot;, prop2 = data.id, maxvals = data.maxvals, ps = 1})
 		end
 		if citeq_args[name] and citeq_args[name]:find('[[Category:Articles with missing Wikidata information]]', 1, true) then
 			-- try fallback to work's native language
 			citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals = data.maxvals, linked = &quot;no&quot;, lang = langcodes[1] } )
 			if citeq_args[name]:find('^Q%d+$') then -- qid was returned
 				-- try fallback to qid's native language
 				local qid_languages = _lang_code(citeq_args[name])
 				citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals = data.maxvals, linked = &quot;no&quot;, lang = qid_languages[1] } )
 				if citeq_args[name]:find('^Q%d+$') then -- qid was returned again
 					citeq_args[name] = nil
 				else
 					-- record the language found if no lang specified
 					citeq_args.language = citeq_args.language or qid_languages[1]
 				end
 			end
 		end
 		if data.others then
 			oth[#oth + 1] = citeq_args[name] and (name:gsub(&quot;^%l&quot;, string.upper) .. &quot;: &quot; .. citeq_args[name])
 			citeq_args[name] = nil
 		end
 	end
 
 	citeq_args.others = citeq_args.others or table.concat(oth, &quot;. &quot;)
 	if citeq_args.others == &quot;&quot; then
 		citeq_args.others = nil
 	end
 
 	citeq_args.journal = citeq_args.journal and citeq_args.journal:gsub(&quot;^''&quot;, &quot;&quot;):gsub(&quot;''$&quot;, &quot;&quot;):gsub(&quot;|''&quot;, &quot;|&quot;):gsub(&quot;'']]&quot;, &quot;]]&quot;)
 
 	citeq_args.ol = (getValue( {&quot;P648&quot;, ps = 1, qid = qid, maxvals = 1, citeq_args.ol } ) or ''):gsub(&quot;^OL(.+)$&quot;, &quot;%1&quot;)
 	if citeq_args.ol == &quot;&quot; then
 		citeq_args.ol = nil
 	end
 	-- TBD. Take care of |ol-access=?
 
 	citeq_args.biorxiv = citeq_args.biorxiv and (&quot;10.1101/&quot; .. citeq_args.biorxiv)
 
 	citeq_args.isbn = getValue( {&quot;P957&quot;, ps = 1, qid = qid, maxvals = 1, rank=&quot;best&quot;, citeq_args.isbn } ) -- try ISBN 10 (only one value accepted)
 
 	-- if url then see if there's an archive: citeq_args.url
 	local url
 	if not citeq_args.url then
 		for i, pr in ipairs( {&quot;P953&quot;, &quot;P856&quot;, &quot;P2699&quot;} ) do
 			url = getValue( {pr, ps = 1, qid = qid, maxvals = 1, qual=&quot;P1065&quot; } )
 			if url then
 				citeq_args.url = mw.text.split( url, &quot; (&quot;, true )[1]
 				local arcurl = mw.ustring.match( url, &quot; %((.*)%)&quot; )				-- when there is an archive url, &lt;url&gt; holds: url&lt;space&gt;(archive url); here extract the archive url if present
 				if arcurl then
 					local arcy, arcm, arcd = arcurl:match(&quot;(20%d%d)%p?(%d%d)%p?(%d%d)&quot;)
 					if arcy and arcm and arcd then
 						citeq_args[&quot;archive-url&quot;] = arcurl
 						citeq_args[&quot;archive-date&quot;] = tonumber(arcd) .. &quot; &quot; .. i18n.months[tonumber(arcm)] .. &quot; &quot; .. arcy
 					end
 				end
 				break
 			end
 		end
 	end
 
 	if citeq_args.publisher == &quot;Unknown&quot; then -- look for &quot;stated as&quot; (P1932)
 		local stated_as = getValue( {&quot;P123&quot;, ps = 1, qid = qid, maxvals = 1, qual=&quot;P1932&quot;, qo=&quot;y&quot;} )
 		if stated_as then citeq_args.publisher = stated_as end
 	end
 
 	if not titleforced then
 		-- Handle subtitle.
 		if citeq_args.title then
 			local subtitle = mw.wikibase.getBestStatements (qid, 'P1680');
 			if 0 ~= #subtitle then
 				subtitle = subtitle[1].mainsnak.datavalue.value.text;
 				citeq_args.title = citeq_args.title .. &quot;: &quot; .. subtitle
 			end
 		end
 		
 		local htmltitle = getValue( {&quot;P1476&quot;, qual = &quot;P6833&quot;, ps = 1, qid = qid, maxvals = 1, qo = &quot;y&quot;} )
 		if htmltitle then
 			citeq_args.title = htmltitle:gsub(&quot;&lt;/?i&gt;&quot;, &quot;''&quot;)
 		else
 			local title_display = citeq_args.title
 				or mw.wikibase.getLabel(qid)
 				or (langcodes[1] and mw.wikibase.getLabelByLang(qid, langcodes[1]))
 				or (&quot;No label or title -- debug: &quot; .. qid)
 			if citeq_args.url then
 				citeq_args.title = wrap_nowiki(title_display)
 			else
 				local slink = mw.wikibase.getSitelink(qid)
 				local slink_flag = false
 				local wrap_title = ''
 				local wslink = false
 				if not slink then
 					-- See if we have wikisource
 					if not citeq_args.url then
 						local wikisource_sitelink = mw.wikibase.getSitelink(qid, &quot;enwikisource&quot;) or nil
 						if wikisource_sitelink then
 							slink = ':s:'..wikisource_sitelink
 							wslink = true
 						end
 					end
 				end
 				if citeq_args.title then
 					if slink then
 						wrap_title = wrap_nowiki(citeq_args.title)
 						slink_flag = true
 					else
 						citeq_args.title = wrap_nowiki(citeq_args.title)
 					end
 				else
 					if slink and not wslink then
 						if slink:lower() == title_display:lower() then
 							citeq_args.title = '[[' .. slink .. ']]'
 						else
 							wrap_title = wrap_nowiki(slink:gsub(&quot;%s%(.+%)$&quot;, &quot;&quot;):gsub(&quot;,.+$&quot;, &quot;&quot;))
 							slink_flag = true
 						end
 					elseif wslink then
 						wrap_title = wrap_nowiki(title_display)
 						slink_flag = true
 					else
 						citeq_args.title = wrap_nowiki(title_display)
 					end
 				end
 				if slink_flag then
 					if slink == wrap_title and not wslink then -- direct link
 						citeq_args.title = '[[' .. slink .. ']]'
 					else -- piped link
 						citeq_args.title = '[[' .. slink .. '|' .. wrap_title .. ']]'
 					end
 				end
 			end
 		end
 	end
 
 	-- TBD: incorporate |at, |sheets= and |sheet= here as well
 	-- Sort out what should happen if several of them are given at the same time
 	if citeq_args.page or citeq_args.p then -- let single take precedence over multiple
 		citeq_args.pages = nil
 		citeq_args.pp = nil
 	end
 	if citeq_args.pages then
 		local _, count = string.gsub(citeq_args.pages, &quot;[,;%s]%d+&quot;, &quot;&quot;)
 		if count == 1 then
 			citeq_args.page = citeq_args.pages
 			citeq_args.pages = nil
 		end
 	end
 
 	if is_set (qid) then
 		if not is_set (citeq_args.author) and not is_set (citeq_args.author1)
 			and not is_set (citeq_args.subject) and not is_set (citeq_args.subject1)
 			and not is_set (citeq_args.host) and not is_set (citeq_args.host1)
 			and not is_set (citeq_args.last) and not is_set (citeq_args.last1)
 			and not is_set (citeq_args.surname) and not is_set (citeq_args.surname1)
 			and not is_set (citeq_args['author-last']) and not is_set (citeq_args['author-last1']) and not is_set (citeq_args['author1-last'])
 			and not is_set (citeq_args['author-surname']) and not is_set (citeq_args['author-surname1']) and not is_set (citeq_args['author1-surname1']) then	-- if neither are set, try to get authors from Wikidata
 			get_name_list ('author', citeq_args, qid, wdl)				-- modify citeq_args table with authors from Wikidata
 		end
 
 		if not is_set (citeq_args.editor) and not is_set (citeq_args.editor1)
 			and not is_set (citeq_args['editor-last']) and not is_set (citeq_args['editor-last1']) and not is_set (citeq_args['editor1-last'])
 			and not is_set (citeq_args['editor-surname']) and not is_set (citeq_args['editor-surname1']) and not is_set (citeq_args['editor1-surname']) then	-- if neither are set, try to get editors from Wikidata
 			get_name_list ('editor', citeq_args, qid, wdl)				-- modify citeq_args table with editors from Wikidata
 		end
 
 		if not is_set (citeq_args.translator) and not is_set (citeq_args.translator1)
 			and not is_set (citeq_args['translator-last']) and not is_set (citeq_args['translator-last1']) and not is_set (citeq_args['translator1-last'])
 			and not is_set (citeq_args['translator-surname']) and not is_set (citeq_args['translator-surname1']) and not is_set (citeq_args['translator1-surname']) then	-- if neither are set, try to get translators from Wikidata
 			get_name_list ('translator', citeq_args, qid, wdl)			-- modify citeq_args table with translators from Wikidata
 		end
 	end
 
 	for k, v in pairs(citeq_args) do
 		if in_array (v, {'(())', 'unset', 'ignore'}) or 'string' ~= type(k) then -- empty accept-as-is-written (()) markup to indicate an empty/unused parameter value, other ((...)) markups are deliberately passed down to {{citation}}
 			citeq_args[k] = nil
 		elseif in_array (v, {'((unset))', '((ignore))'}) then -- strip off markup for free-text values clashing with local keywords
 			citeq_args[k] = 'unset'
 		end
 	end
 
 	local author_count = 0
 	for k, v in pairs(citeq_args) do
 		if k:find(&quot;^author%d+$&quot;) then
 			author_count = author_count + 1
 		end
 	end
 	if author_count &gt; 8 then -- convention in astronomy journals, optional mode for this?
 		if 'all' == citeq_args['display-authors'] then
 			citeq_args['display-authors'] = nil;								-- unset because no longer needed
 		else
 			citeq_args['display-authors'] = citeq_args['display-authors'] or 3	-- limit to three displayed names
 		end
 	end
 
 	local editor_count = 0
 	for k, v in pairs(citeq_args) do
 		if k:find(&quot;^editor%d+$&quot;) then
 			editor_count = editor_count + 1
 		end
 	end
 	if editor_count &gt; 8 then -- convention in astronomy journals, optional mode for this?
 		if 'all' == citeq_args['display-editors'] then
 			citeq_args['display-editors'] = nil;								-- unset because no longer needed
 		else
 			citeq_args['display-editors'] = citeq_args['display-editors'] or 3	-- limit to three displayed names
 		end
 	end
 
 	-- change edition to ordinal if it's set and numeric
 	citeq_args.edition = citeq_args.edition and p.makeOrdinal(citeq_args.edition)
 
 	-- code to make a guess what template to use from the supplied parameters
 	-- (first draft for proof-of-concept)
 	if citeq_args.isbn then
 		template = template or &quot;book&quot;
 		citeq_args.asin = nil -- suppress ASIN if ISBN exists
 	elseif citeq_args.journal then
 		template = template or &quot;journal&quot;
 	elseif citeq_args.website then
 		template = template or &quot;web&quot;
 	end
 
 	-- template is CS1 designator: journal, web, news, etc.
 	if template then
 --		citeq_args.mode = citeq_args.mode or &quot;cs1&quot;								-- a cs1 template already knows that it is cs1 so this line is superfluous
 		template = &quot;Cite &quot; .. template
 	else
 --		citeq_args.mode = citeq_args.mode or &quot;cs2&quot;								-- a cs2 template already knows that it is cs2 so this line is superfluous
 		template = &quot;Citation&quot;
 	end
 
 	-- |id= could hold more than one identifier pulled from Wikidata not supported by {{citation}}, right now only add our qid to the list
 	local list_sep = '. '
 	if citeq_args.mode ~= 'cs1' then
 		list_sep = ', '
 	end
 	local id = '[[WDQ (identifier)|விக்கித்தரவு]]&amp;nbsp;[[:d:' .. qid .. '|' .. qid .. ']]' -- go through &quot;WDQ (identifier)&quot; redirect to reduce clutter in &quot;What links here&quot; and improve reverse lookup. Keep in sync with {{QID}}.
 	local old_id = citeq_args.id
 	if wdl then -- show WD logo
 		id = id .. '[[File:Wikidata-logo.svg|16px|alt=|link=]]' -- possibly replace by WD edit icon?
 	end
 	if is_set (old_id) then
 		citeq_args.id = old_id .. list_sep .. id -- append to user-specified contents
 	else
 		citeq_args.id = id
 	end
 
 	-- clean up any blank parameters
 	for k, v in pairs(citeq_args) do
 		if v == &quot;&quot; then citeq_args[k] = nil end
 	end
 
 	-- if |expand=&lt;anything&gt;, write a nowiki'd version to see what the {{citation}} template call looks like
 	if expand then
 		local expand_args = { &quot;{{&quot; .. template }		-- init with citation template
 		if expand == &quot;self&quot; then
 			citeq_args.id = old_id -- restore original |id= parameter
 			expand_args = { &quot;{{cite Q|&quot; .. qid } -- expand to itself
 		end
 		-- make a sortable table and sort it by param name
 		local sorttable = {}
 		for param, val in pairs (citeq_args) do
 			table.insert(sorttable, {param, val})
 		end
 		table.sort(sorttable, comp_key)
 		-- add contents to expand_args
 		for idx, val in ipairs(sorttable) do
 			table.insert(expand_args, val[1] .. '=' .. val[2])
 		end
 		-- make the nowiki'd string and done
 		return &quot;&lt;code&gt;&quot; .. table.concat (expand_args, ' |') .. &quot;}}&lt;/code&gt;&quot;
 	end
 
 	local erratumid = getPropertyIDs( { &quot;P2507&quot;, qid = qid, fwd = &quot;ALL&quot;, osd = &quot;no&quot;, rank = &quot;best&quot;, maxvals = 1 } )
 	if erratumid then
 		erratumid = &quot; [[d:&quot; .. erratumid .. &quot;|(erratum)]]&quot; .. &quot;[[Category:Cite Q - cites a work with an erratum]]&quot;
 	else
 		erratumid = &quot;&quot;
 	end
 
 	local opt_cat = ''
 	if getValue( {&quot;P5824&quot;, ps = 1, qid = qid} ) then
 		opt_cat = '[[Category:Cite Q - cites a retracted work]]&lt;!-- retracted --&gt;'
 	end
 	if getValue( {&quot;P1366&quot;, ps = 1, qid = qid} ) then
 		opt_cat = opt_cat .. '[[Category:Cite Q - cites a replaced work]]&lt;!-- replaced --&gt;'
 	end
 	return frame:expandTemplate{title = template, args = citeq_args} .. erratumid .. opt_cat -- render the template
 end
 
 function p.cite_q (frame)
 	local args = {}
 	for k, v in pairs(frame:getParent().args) do
 		if v ~= &quot;&quot; then args[k] = v end
 	end
 	for k, v in pairs(frame.args) do
 		if v ~= &quot;&quot; then args[k] = v end
 	end
 	args.qid = args.qid or args[1] or &quot;&quot;
 	if args.qid == &quot;&quot; then return nil end
 	args[1] = nil
 
 	local citesep = (args.citesep or &quot;&quot;)
 	if citesep == &quot;&quot; then citesep = &quot;, &quot; end
 	citesep = citesep:gsub('&quot;', '') -- strip double quotes after setting default to allow |citesep=&quot;&quot; as a blank separator
 	args.citesep = nil
 
 	local tag = args.tag or &quot;&quot;
 	if tag == &quot;&quot; then tag = nil end
 	args.tag = nil
 
 	local list = args.list or &quot;&quot;
 	if list == &quot;&quot; then list = nil end
 	args.list = nil
 
 	args.language = args.language or args.lang
 	args.lang = nil
 
 	local cites = {}
 	for q in args.qid:gmatch(&quot;Q%d+&quot;) do
 		-- make a new copy of the arguments
 		local newargs = {}
 		for k, v in pairs(args) do
 			if k ~= &quot;qid&quot; then
 				newargs[k] = v
 			end
 		end
 		newargs.qid = q
 		if tag == &quot;ref&quot; then
 			cites[#cites + 1] = frame:callParserFunction{ name = &quot;#tag:ref&quot;, args = { p._cite_q(newargs), name = q } }
 			-- expand like this: args = { p._cite_q(newargs), name = 'foo', group = 'bar' }
 		else
 			cites[#cites + 1] = p._cite_q(newargs)
 		end
 	end
 
 	if list then
 		return frame:expandTemplate{ title = list, args = cites }
 	else
 		return table.concat(cites, citesep)
 	end
 end