# 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 = {  
["unknown-author"] = mw.wikibase.getLabel("Q4233718"):gsub("^%l", mw.ustring.upper),  
["unknown-author-trackingcat"] = "",  
["ordinal"] = {  
[1] = "st",  
[2] = "nd",  
[3] = "rd",  
["default"] = "th"  
},  
["months"] = {  
"January", "February", "March", "April", "May", "June",  
"July", "August", "September", "October", "November", "December"  
},  
}  

---
-- 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 = "no" suppresses linking  
-- populate_from_journal = true/false determines whether to look in a journal where the source is published  
-- rank = "best", "preferred", normal, etc. determines how Wikidata ranks are treated  
-- others = true - the value for the property goes to "others" section  
local simple_properties = {  
publisher = {id = "P123", maxvals = 1},  
oclc = {id = "P243", maxvals = 1},  
['publication-place'] = {id = "P291", maxvals = 0, linked = 'no'}, -- publication place (don't put into |place=; is treated specially in if both are given)  
doi = {id = "P356", maxvals = 1}, -- take care of |doi-broken-date= (WD "reason for deprecation"/"stated as") and |doi-access= (WD "access status")?  
issue = {id = "P433", maxvals = 0, populate_from_journal = true}, -- distinguish from |number= ("P1545"?) if both are given (still blocked by , but will be supported in the future)  
pmid = {id = "P698", maxvals = 1},  
-- gbooks = {id = "P675", maxvals = 1}, -- to be added to  
-- ia = {id = "P724", maxvals = 1}, -- to be added to  
arxiv = {id = "P818", maxvals = 1},  
bibcode = {id = "P819", maxvals = 1}, -- take care of |bibcode-access=?  
jstor = {id = "P888", maxvals = 1}, -- take care of |jstor-access=?  
mr = {id = "P889", maxvals = 1},  
rfc = {id = "P892", maxvals = 1},  
zbl = {id = "P894", maxvals = 1},  
ssrn = {id = "P893", maxvals = 1},  
place = {id = "P1071", maxvals = 0, linked = 'no'}, -- written-at place  
-- ['total-pages'] = {id = "P1104", maxvals = 0, linked = 'no'}, -- to be added to / COinS &rft.tpages=  
-- coden = {id = "P1159", maxvals = 1}, -- to be added to / COinS &rft.coden=  
s2cid = {id = "P8299", maxvals = 1}, -- take care of |s2cid-access=?  
pmc = {id = "P932", maxvals = 1}, -- take care of |pmc-embargo-date= (WD "reason for deprecation")?  
lccn = {id = "P1144", maxvals = 1},  
hdl = {id = "P1184", maxvals = 1}, -- take care of |hdl-access=?  
ismn = {id = "P1208", maxvals = 1},  
journal = {id = "P1433", maxvals = 1},  
citeseerx = {id = "P3784", maxvals = 1},  
osti = {id = "P3894", maxvals = 1}, -- take care of |osti-access=?  
biorxiv = {id = "P3951", maxvals = 1},  
asin = {id = "P5749", maxvals = 1}, -- What about |asin-tld=? (WD examples resolve to .com at present, but may change)  
-- ['catalog-number'] = {id = "P528", maxvals = 0}, -- to be added to / COinS &rft.artnum=  
isbn = {id = "P212", maxvals = 1, populate_from_journal = true}, -- ISBN 13  
issn = {id = "P236", maxvals = 1, populate_from_journal = true}, -- distinguish from |eissn= for electronic issues?  
-- jfm = {id = "P?", maxvals = 1}, -- Jahrbuch über die Fortschritte der Mathematik (not Zbl)  
-- sbn = {id = "P?", maxvals = 1}, -- Standard Book Number (predecessor of ISBN, not ICCU)  
-- message-id = {id = "P?", maxvals = 1}, -- Usenet message ID  
chapter = {id = "P792", maxvals = 1},  
['publication-date'] = {id = "P577", maxvals = 1, populate_from_journal = true}, -- publication date (don't use |date=; is treated specially in if both are given.)  
series = {id = "P179", maxvals = 1, populate_from_journal = true},  
version = {id = "P348", maxvals = 0},  
edition = {id = "P393", maxvals = 0},  
volume = {id = "P478", maxvals = 0, populate_from_journal = true},  
-- part = {id = "P1545"?, maxvals = 0}, -- to be added to / COinS &rft.part=  
title = {id = "P1476", rank="p n"},  
-- url = {id = "P953", maxvals = 1}, -- deal with this along with archive-url  
pages = {id = "P304", maxvals = 0, populate_from_journal = true},  
at = {id = "P958", maxvals = 0, populate_from_journal = true}, -- also incorporate lines (P7421) and columns (P3903) into this (cite map also supports |section=)  
-- sheets = {id = "P7416", maxvals = 0, populate_from_journal = true},  
-- interviewer = {id = "P?", maxvals = 0}, -- does **not** go to "others" section! Multiple interviewers should be n-enumerated  
illustrator = {id = "P110", maxvals = 10, others = true}, -- goes to "others" 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 , see Help_talk:Citation_Style_1#Others  
-- foreword = {id = "P2679", maxvals = 10, others = true}, -- goes to "others" section  
-- afterword = {id = "P2680", maxvals = 10, others = true}, -- goes to "others" section  
composer = {id = "P86", maxvals = 10, others = true}, -- goes to "others" section  
animator = {id = "P6942", maxvals = 10, others = true}, -- goes to "others" section  
director = {id = "P57", maxvals = 10, others = true}, -- goes to "others" section  
screenwriter = {id = "P58", maxvals = 10, others = true}, -- goes to "others" section  
signatory = {id = "P1891", maxvals = 10, others = true}, -- goes to "others" section  
presenter = {id = "P371", maxvals = 10, others = true}, -- goes to "others" section  
performer = {id = "P175", maxvals = 10, others = true}, -- goes to "others" section  
}  

----------------------------< I S _ S E T >--------------------------------------------------------------  
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  

----------------------------< I N _ A R R A Y >--------------------------------------------------------------  
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  

----------------------------< A C C E P T _ V A L U E >-------------------------------------------------------  
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 == "value" then  
if v.mainsnak.datatype == "wikibase-item" then  
local qnumber = v.mainsnak.datavalue.value.id  
local sitelink = mw.wikibase.getSitelink(qnumber)  
if qnumber == "Q2818964" then sitelink = nil end -- suppress link to "Various authors"  
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["qualifiers"] and v.qualifiers["P1545"] and v.qualifiers["P1545"][1] then  
position = tonumber(v.qualifiers["P1545"][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] = "[[:d:Q" .. v.mainsnak.datavalue.value["numeric-id"] .. "|" .. label .. "]]&nbsp;<span title='" .. i18n["errors"]["local-article-not-found"] .. "'></span>"  
else  
-- no Wikidata links wanted, so just give the plain label  
out[position] = label  
end  
end  
elseif v.mainsnak.datatype == "string" then  
local position = maxpos + 1 -- Default to 'next' author.  
-- use P1545 (series ordinal) instead of default position.  
if v["qualifiers"] and v.qualifiers["P1545"] and v.qualifiers["P1545"][1] then  
position = tonumber(v.qualifiers["P1545"][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 "unknown"  
if v.qualifiers and v.qualifiers.P1932 then  
label = v.qualifiers.P1932[1].datavalue.value  
else  
label = i18n["unknown-author"] .. (i18n["unknown-author-trackingcat"] or "")  
end  
maxpos = maxpos + 1  
out[maxpos] = label  
end  
return maxpos  
end  

--[=[-------------------------< G E T _ N A M E _ L I S T >----------------------------------------------------  
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 = "P50"  
local fallbackID = "P2093" -- author name string  

if nl_type =="author" then  
propertyID = 'P50' -- for authors  
fallbackID = 'P2093' -- author-string  
elseif nl_type =="editor" then  
propertyID = 'P5769' -- "editor-in-chief"  
fallbackID = 'P98' -- for editors - So-called "fallbacks" 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 == "translator" 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 "false" or "no" or "0" is passed set it false  
-- if nothing or an empty string is passed set it false  
if wdl and (#wdl > 0) then  
wdl = wdl:lower  
wdl = in_array (wdl, {"false", "no", "0"})  
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 ('&#39;', '\''); -- prevent cs1|2 multiple names categorization; replace html entity with the actual character  
mw.log(i .. " " .. k .. " " .. (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 == "monolingualtext" and v.mainsnak.datavalue then  
out[#out + 1] = v.mainsnak.datavalue.value.language  
end  
end  
return out  
end  
function p.getLangOfProp  
local pid = .args.pid or mw.text.trim(.args[1] or "")  
if pid == "" then return end  
local qid = .args.qid  
if qid == "" then qid = nil end  
return table.concat(p._getLangOfProp(qid, pid), ", ")  
end  

-- gets the language codes of a Wikidata entry as a table  
local function _lang_code(qid)  
local lc = getPropOfProp( {qid = qid, prop1 = "P407", prop2 = "P424", ps = 1} )  
if lc then return mw.text.split( lc, "[, ]+" ) end  
lc = getPropOfProp( {qid = qid, prop1 = "P407", prop2 = "P218", ps = 1} )  
if lc then return mw.text.split( lc, "[, ]+" ) end  
return p._getLangOfProp(qid, "P1476")  
end  
function p.lang_code  
return table.concat(_lang_code(.args.qid or mw.text.trim(.args[1] or "")), ", ")  
end  

-- export for debug  
function p.getPropOfProp  
return getPropOfProp(.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] < b[1]  
end  

-- sort sequence table whose values are key-value pairs by value  
local function comp_val(a, b)  
return a[2] < b[2]  
end  

--2 template parameters and passes all to . 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  
for rendering.  
---
function p._cite_q (citeq_args)  
local = mw.getCurrentFrame  

-- parameters that don't get passed to Citation  
local expand = citeq_args.expand -- when set to anything, causes to render <code></code>  
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, "[, ]+", 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 = "P1433"})  
langcodes = journal_qid and _lang_code(journal_qid)  
end  
citeq_args.language = citeq_args.language or table.concat(langcodes, ", ")  

-- 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 = "ALL", osd = "no", noicon = "true", qid = qid, maxvals = data.maxvals, linked = data.linked, rank = data.rank or "best", citeq_args[name] } )  
if data.populate_from_journal then  
local publishedin = getValue( {"P1433", ps = 1, qid = qid, maxvals = 0, citeq_args[name], qual = data.id, qualsonly = 'yes'} )  
citeq_args[name] = publishedin or getPropOfProp({qid = qid, prop1 = "P1433", prop2 = data.id, maxvals = data.maxvals, ps = 1})  
end  
if citeq_args[name] and citeq_args[name]:find('', 1, true) then  
-- try fallback to work's native language  
citeq_args[name] = getValue( {data.id, ps = 1, qid = qid, maxvals = data.maxvals, linked = "no", 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 = "no", 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("^%l", string.upper) .. ": " .. citeq_args[name])  
citeq_args[name] = nil  
end  
end  

citeq_args.others = citeq_args.others or table.concat(oth, ". ")  
if citeq_args.others == "" then  
citeq_args.others = nil  
end  

citeq_args.journal = citeq_args.journal and citeq_args.journal:gsub("^*", ""):gsub("*$", ""):gsub("|*", "|"):gsub("*]]", "]]")  

citeq_args.ol = (getValue( {"P648", ps = 1, qid = qid, maxvals = 1, citeq_args.ol } ) or ''):gsub("^OL(.+)$", "%1")  
if citeq_args.ol == "" then  
citeq_args.ol = nil  
end  
-- TBD. Take care of |ol-access=?  

citeq_args.biorxiv = citeq_args.biorxiv and ("10.1101/" .. citeq_args.biorxiv)  

citeq_args.isbn = getValue( {"P957", ps = 1, qid = qid, maxvals = 1, rank="best", 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( {"P953", "P856", "P2699"} ) do  
url = getValue( {pr, ps = 1, qid = qid, maxvals = 1, qual="P1065" } )  
if url then  
citeq_args.url = mw.text.split( url, " (", true )[1]  
local arcurl = mw.ustring.match( url, " %((.*)%)" ) -- when there is an archive url, <url> holds: url<space>(archive url); here extract the archive url if present  
if arcurl then  
local arcy, arcm, arcd = arcurl:match("(20%d%d)%p?(%d%d)%p?(%d%d)")  
if arcy and arcm and arcd then  
citeq_args["archive-url"] = arcurl  
citeq_args["archive-date"] = tonumber(arcd) .. " " .. i18n.months[tonumber(arcm)] .. " " .. arcy  
end  
end  
break  
end  
end  
end  

if citeq_args.publisher == "Unknown" then -- look for "stated as" (P1932)  
local stated_as = getValue( {"P123", ps = 1, qid = qid, maxvals = 1, qual="P1932", qo="y"} )  
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 .. ": " .. subtitle  
end  
end  

local htmltitle = getValue( {"P1476", qual = "P6833", ps = 1, qid = qid, maxvals = 1, qo = "y"} )  
if htmltitle then  
citeq_args.title = htmltitle:gsub("</?i>", "''")  
else  
local title_display = citeq_args.title  
or mw.wikibase.getLabel(qid)  
or (langcodes[1] and mw.wikibase.getLabelByLang(qid, langcodes[1]))  
or ("No label or title -- debug: " .. 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, "enwikisource") 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("%s%(.+%)$", ""):gsub(",.+$", ""))  
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 = * .. 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, "[,;%s]%d+", "")  
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  
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("^author%d+$") then  
author_count = author_count + 1  
end  
end  
if author_count > 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("^editor%d+$") then  
editor_count = editor_count + 1  
end  
end  
if editor_count > 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 "book"  
citeq_args.asin = nil -- suppress ASIN if ISBN exists  
elseif citeq_args.journal then  
template = template or "journal"  
elseif citeq_args.website then  
template = template or "web"  
end  

-- template is CS1 designator: journal, web, news, etc.  
if template then  
-- citeq_args.mode = citeq_args.mode or "cs1" -- a cs1 template already knows that it is cs1 so this line is superfluous  
template = "Cite " .. template  
else  
-- citeq_args.mode = citeq_args.mode or "cs2" -- a cs2 template already knows that it is cs2 so this line is superfluous  
template = "Citation"  
end  

-- |id= could hold more than one identifier pulled from Wikidata not supported by , now only add our qid to the list  
local list_sep = '. '  
if citeq_args.mode ~= 'cs1' then  
list_sep = ', '  
end  
local id = 'விக்கித்தரவு&nbsp;' .. qid .. '' -- go through "WDQ (identifier)" redirect to reduce clutter in "What links here" and improve reverse lookup. Keep in sync with .  
local old_id = citeq_args.id  
if wdl then -- show WD logo  
id = id .. '' -- 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 == "" then citeq_args[k] = nil end  
end  

-- if |expand=<anything>, write a nowiki'd version to see what the template call looks like  
if expand then  
local expand_args = { "  
local erratumid = getPropertyIDs( { "P2507", qid = qid, fwd = "ALL", osd = "no", rank = "best", maxvals = 1 } )  
if erratumid then  
erratumid = " (erratum)" .. ""  
else  
erratumid = ""  
end  

local opt_cat = ''  
if getValue( {"P5824", ps = 1, qid = qid} ) then  
opt_cat = ''  
end  
if getValue( {"P1366", ps = 1, qid = qid} ) then  
opt_cat = opt_cat .. ''  
end  
return :expandTemplate{title = template, args = citeq_args} .. erratumid .. opt_cat -- render the template  
end  

function p.cite_q  
local args = {}  
for k, v in pairs(:getParent.args) do  
if v ~= "" then args[k] = v end  
end  
for k, v in pairs(.args) do  
if v ~= "" then args[k] = v end  
end  
args.qid = args.qid or args[1] or ""  
if args.qid == "" then return nil end  
args[1] = nil  

local citesep = (args.citesep or "")  
if citesep == "" then citesep = ", " end  
citesep = citesep:gsub('"', '') -- strip double quotes after setting default to allow |citesep="" as a blank separator  
args.citesep = nil  

local tag = args.tag or ""  
if tag == "" then tag = nil end  
args.tag = nil  

local list = args.list or ""  
if list == "" 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("Q%d+") do  
-- make a new copy of the arguments  
local newargs = {}  
for k, v in pairs(args) do  
if k ~= "qid" then  
newargs[k] = v  
end  
end  
newargs.qid = q  
if tag == "ref" then  
cites[#cites + 1] = :callParserFunction{ name = "#tag:ref", 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 :expandTemplate{ title = list, args = cites }  
else  
return table.concat(cites, citesep)  
end  
end  

return p