Module:Wd: Difference between revisions

10,191 bytes added ,  2 years ago
m
1 revision imported
m (1 revision imported)
m (1 revision imported)
 
(One intermediate revision by one other user not shown)
Line 1:
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/i18naliasesP]].
 
local p = {}
Line 6:
 
function loadSubmodules(frame)
local title
-- internationalization
if not i18n then
if frame then
-- current module invoked by page/template, get its title from frame
i18ntitle = require(frame:getTitle().."/i18n")
else
-- current module included by other module, get its title from ...
i18ntitle = require(arg.."/i18n")
end
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
 
local aliasesPp.claimCommands = {
coord property = "P625property",
properties = "properties",
---------------
author qualifier = "P50qualifier",
publisher qualifiers = "P123qualifiers",
importedFrom reference = "P143reference",
references = "references"
statedIn = "P248",
}
publicationDate = "P577",
 
startTime = "P580",
p.generalCommands = {
endTime = "P582",
retrieved label = "P813label",
referenceURLtitle = "P854title",
archiveURLalias = "P1065alias",
title aliases = "P1476aliases",
quote badge = "P1683badge",
shortName badges = "P1813badges",
}
language = "P2439",
 
archiveDate = "P2960"
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"
}
 
local aliasesQ = {
percentage = "Q11229",
prolepticJulianCalendar = "Q1985786",
citeWeb = "Q5637226",
citeQ = "Q22321052"
}
 
Line 47 ⟶ 74:
qualifier = "%q",
reference = "%r",
alias = "%a",
badge = "%b",
separator = "%s",
general = "%x"
Line 55 ⟶ 84:
qualifier = "%q[%s][%r]",
reference = "%r",
propertyWithQualifier = "%p[ <span style=\"font-size:smaller85%\">(%q)</span>][%s][%r]",
alias = "%a[%s]",
badge = "%b[%s]"
}
 
Line 62 ⟶ 93:
[parameters.reference] = {"getReferences", "getReference"},
[parameters.qualifier] = {"getAllQualifiers"},
[parameters.qualifier.."\\d"] = {"getQualifiers", "getQualifier"},
[parameters.alias] = {"getAlias"},
[parameters.badge] = {"getBadge"}
}
 
Line 73 ⟶ 106:
["sep%r"] = nil, -- none
["punc"] = nil -- none
}
 
local rankTable = {
["preferred"] = 1,
["normal"] = 2,
["deprecated"] = 3
}
 
Line 93 ⟶ 132:
cfg.entity = nil
cfg.entityID = nil
cfg.propertyID = nil
cfg.propertyValue = nil
cfg.qualifierIDs = {}
cfg.qualifierIDsAndValues = {}
cfg.bestRank = true
Line 107 ⟶ 148:
cfg.mdyDate = false
cfg.singleClaim = false
cfg.sourcedOnly = false
cfg.editable = false
cfg.editAtEnd = false
cfg.pageTitleinSitelinks = false
cfg.langCode = mw.language.getContentLanguage().code
cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
cfg.langObj = mw.language.new(cfg.langCode)
-- somewhat reliable way of determining global site ID in the absence of a library function, targeting the Wikipedia project (i.e. appending "wiki")
cfg.siteID = (function() for i,v in pairs(mw.site.interwikiMap("local")) do if v.isCurrentWiki and i~="w" then return mw.ustring.gsub(i,"-","_").."wiki" end end end)()
cfg.states = {}
cfg.states.qualifiersCount = 0
cfg.curState = nil
cfg.prefetchedRefs = nil
return cfg
Line 140 ⟶ 190:
stt.rawValue = false
stt.shortName = false
stt.anyLanguage = false
stt.unitOnly = false
stt.singleValue = false
Line 145 ⟶ 197:
end
 
function applyStringParamsreplaceAlias(str, ...id)
if p.aliasesP[id] then
for i, v in ipairs(arg) do
id = p.aliasesP[id]
str = mw.ustring.gsub(str, "$"..i, v)
end
return strid
end
 
function unknownDataTypeErrorerrorText(dataTypecode, param)
returnlocal text = applyStringParams(i18n['"errors'"]['unknown-data-type'code], dataType)
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
 
function throwError(errorMessage, param)
function missingRequiredParameterError()
error(errorText(errorMessage, param))
return i18n['errors']['missing-required-parameter']
end
 
function extraRequiredParameterErrorreplaceDecimalMark(paramnum)
return applyStringParamsmw.ustring.gsub(num, "[.]", i18n['errorsnumeric']['extra-requireddecimal-parametermark'], param1)
end
 
function getOrdinalSuffixpadZeros(num, numDigits)
local numZeros
return i18n.getOrdinalSuffix(num)
local negative = false
if num < 0 then
negative = true
num = num * -1
end
num = tostring(num)
numZeros = numDigits - num:len()
for i = 1, numZeros do
num = "0"..num
end
if negative then
num = "-"..num
end
return num
end
 
function addDelimitersreplaceSpecialChar(numchr)
if chr == '_' then
return i18n.addDelimiters(num)
-- replace underscores with spaces
return ' '
else
return chr
end
end
 
function replaceDecimalMarkreplaceSpecialChars(numstr)
local chr
return mw.ustring.gsub(num, "[.]", i18n['numeric']['decimal-mark'], 1)
local esc = false
end
local strOut = ""
 
-- used for cleaner output when subst:ituting this module
for i = 1, #str do
function replaceHTMLSpaces(str)
chr = str:sub(i,i)
return mw.ustring.gsub(str, "&#32;", " ")
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
 
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
Line 216 ⟶ 308:
end
 
function split(str, del)
-- used to create the final output string when it's all done, so that for references the
local out = {}
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
local i, j = str:find(del)
function concatValues(valuesArray)
local outString = ""
local j, skip
forif i =and 1,j #valuesArray dothen
out[1] = str:sub(1, i - 1)
-- check if this is a reference
out[2] = str:sub(j + 1)
if valuesArray[i].refHash then
else
j = i - 1
skipout[1] = falsestr
-- 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 = "wikidata-" .. valuesArray[i].refHash})
end
else
outString = outString .. valuesArray[i][1]
end
end
 
return outStringout
end
 
function parseWikidataURL(url)
local i, jid
if url:match('^http[s]?://') then
i, jid = url:findsplit(url, "Q")
if iid[2] then
return url:sub(i)"Q" .. id[2]
end
end
Line 265 ⟶ 338:
function parseDate(dateStr, precision)
precision = precision or "d"
local i, j, index, ptr
local parts = {nil, nil, nil}
Line 366 ⟶ 440:
if hookNames[param] then
return hookNames[param][index]
elseif string.param:len(param) > 2 then
return hookNames[string.param:sub(param, 1, 2).."\\d"][index]
else
return nil
Line 494 ⟶ 568:
param = param - 1
elseif param == 1 then
if not string.chr:match(chr, '%d') then
endParam()
end
end
cur.str = cur.str .. replaceSpecialChar(chr)
end
else
Line 511 ⟶ 585:
endParam()
-- make sure that at least one required parameter has been defined
return root, params
if not next(root.req) then
end
throwError("missing-required-parameter")
 
function convertRank(rank)
if rank == "preferred" then
return 1
elseif rank == "normal" then
return 2
elseif rank == "deprecated" then
return 3
else
return 4 -- default (in its literal sense)
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
 
Line 532 ⟶ 604:
for i, v in ipairs(claims) do
rankPos = convertRank(rankTable[v.rank)] or 4
ranks[rankPos][#ranks[rankPos] + 1] = v
end
Line 543 ⟶ 615:
end
 
-- if id == nil then item connected to current page is used
function getShortName(itemID)
function Config:getLabel(id, raw, link, short)
if itemID then
local label = nil
return p._property({itemID, aliasesP.shortName}) -- "property" is single
local title = nil
local prefix= ""
local lang
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.getEntity(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
return p._property({aliasesP.shortName}) -- "property" is single
if short then
label = p._property({p.aliasesP.shortName, [p.args.eid] = id}) -- get short name
if label == "" then
label = nil
end
end
-- get label
if not label then
label, lang = mw.wikibase.getLabelWithLang(id)
-- don't allow language fallback
if lang ~= self.langCode then
label = nil
end
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.sitelink(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 getLabelConfig:getEditIcon(ID)
local value = ""
if ID then
local prefix = ""
return p._label({ID})
local front = " "
else
local back = ""
return p._label({})
if self.entityID:sub(1,1) == "P" then
prefix = "Property:"
end
if self.editAtEnd then
front = '<span style="float:'
if self.langObj:isRTL() then
front = front .. 'left'
else
front = front .. 'right'
end
front = front .. '">'
back = '</span>'
end
value = "[[File:Blue pencil.svg|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 Config:convertUnit(unit, link)
-- function extensionTag("ref", ...) is only called when they really ended up in the final output
link = link or false
function Config:concatValues(valuesArray)
local itemID, label, lang, title
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 = "wikidata-" .. valuesArray[i].refHash .. "-v" .. i18n['cite']['version']})
end
else
outString = outString .. valuesArray[i][1]
end
end
return outString
end
 
function Config:convertUnit(unit, raw, link, short, unitOnly)
local space = " "
local label = ""
if unit == "" or unit == "1" then
return nil
end
if unitOnly then
space = ""
end
Line 573 ⟶ 775:
return "%"
else
label, lang = mw.wikibase.getLabelWithLangself:getLabel(itemID, raw, link, short)
if label ~= "" then
-- don't allow language fallback
return space .. label
if lang ~= self.langCode then
label = nil
end
title = nil
if link or label == nil then
title = mw.wikibase.sitelink(itemID)
end
if link then
if title then
return " " .. buildWikilink(title, (label or title))
end
if not label then
return " " .. buildWikilink("d:" .. itemID, itemID)
end
end
return " " .. (label or title or itemID)
end
end
return " " .. unit
end
 
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
raw = raw or false
link = link or false
short = short or false
anyLang = anyLang or false
if snak.snaktype == 'value' then
iflocal datatype = snak.datavalue.type == 'string' then
iflocal subtype = snak.datatype == 'url' and link then
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 "[" .. snak.datavalue.value .. "]"
else
return "[" .. snak.datavalue.value .. " " .. snak.datavalue.value .. "]"
end
elseif snak.datatypesubtype == 'commonsMedia' then
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
if raw then
-- will render as a linked number like [1]
return "[https://commons.wikimedia.org/wiki/File:" .. mw.ustring.gsub(snak.datavalue.value, " ", "_") .. "]"
else
return "[https://commons.wikimedia.org/wiki/File:" .. mw.ustring.gsub(snak.datavalue.value, " ", "_") .. " " .. snak.datavalue.value .. "]"
end
elseif not raw then
return "[[File:" .. snak.datavalue.value .. "]]"
else
return snak.datavalue.value
end
elseif snak.datatypesubtype == 'geo-shape' and link then
return buildWikilink("c:" .. datavalue, datavalue)
if raw then
elseif subtype == 'math' and not raw then
-- will render as a linked number like [1]
return mw.getCurrentFrame():extensionTag("math", datavalue)
return "[https://commons.wikimedia.org/wiki/" .. mw.ustring.gsub(snak.datavalue.value, " ", "_") .. "]"
elseif subtype == 'external-id' and link then
local url = p._property({p.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
return "[https://commons.wikimedia.org/wiki/" .. mw.ustring.gsub(snak.datavalue.value, " ", "_") .. " " .. snak.datavalue.value .. "]"
end
elseif snak.datatype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", snak.datavalue.value)
else
return snak.datavalue.value
end
elseif snak.datavalue.typedatatype == 'monolingualtext' then
if anyLang then
return snak.datavalue.value['text'], snak.datavalue.value['language']
elseif snak.datavalue.value['language'] == self.langCode then
return snak.datavalue.value['text']
else
return nil
end
elseif snak.datavalue.typedatatype == 'quantity' then
local value = ""
-- strip + signs from front
local unit
local value = mw.ustring.gsub(snak.datavalue.value['amount'], "^\+(.+)$", "%1")
if not rawunitOnly 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
local unit = self:convertUnit(snak.datavalue.value['unit'], link)
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
if unit then
value = value .. unit
value = value .. unit
end
end
return value
elseif snak.datavalue.typedatatype == 'time' then
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
Line 679 ⟶ 867:
local mayAddCalendar = false
local calendar = ""
local precision = snak.datavalue.value['precision']
if precision == 11 then
Line 690 ⟶ 878:
end
y, m, d = parseDate(snak.datavalue.value['time'], p)
if y < 0 then
Line 716 ⟶ 904:
end
suffix = i18n.getOrdinalSuffix(yRound) .. suffix
else
-- if not verbose, take the first year of the century/millennium
Line 816 ⟶ 1,004:
if mayAddCalendar then
calendarID = parseWikidataURL(snak.datavalue.value['calendarmodel'])
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
Line 865 ⟶ 1,053:
value = prefix .. value .. suffix .. calendar
else
value = tostringpadZeros(yRound * sign, 4)
if m then
value = value .. "-" .. padZeros(m, 2)
if d then
value = value .. "-" .. padZeros(d, 2)
end
end
Line 879 ⟶ 1,067:
return value
elseif snak.datavalue.typedatatype == 'globecoordinate' then
-- logic from https://github.com/DataValues/Geo
Line 915 ⟶ 1,103:
end
latitude = snak.datavalue.value['latitude']
longitude = snak.datavalue.value['longitude']
if latitude < 0 then
Line 936 ⟶ 1,124:
end
precision = snak.datavalue.value['precision']
if not precision or precision == 0 then
precision = 1 / 3600 -- precision unspecified, set to arcsecond
end
latitude = math.floor(latitude / precision + 0.5) * precision
Line 950 ⟶ 1,142:
-- use string.format() to strip decimal point followed by a zero (.0) for whole numbers
latSeconds = tonumber(string.strFormat:format(strFormat, math.floor(latitude * 3600 * 10^numDigits + 0.5) / 10^numDigits))
lonSeconds = tonumber(string.strFormat:format(strFormat, math.floor(longitude * 3600 * 10^numDigits + 0.5) / 10^numDigits))
latMinutes = math.floor(latSeconds / 60)
Line 974 ⟶ 1,166:
if precision < (1 / 60) then
latSeconds = string.strFormat:format(strFormat, latSeconds)
lonSeconds = string.strFormat:format(strFormat, lonSeconds)
if not raw then
Line 993 ⟶ 1,185:
if link then
globe = parseWikidataURL(snak.datavalue.value['globe'])
if globe then
Line 1,005 ⟶ 1,197:
return value
elseif snak.datavalue.typedatatype == 'wikibase-entityid' then
local langlabel
local valueitemID = ""datavalue['numeric-id']
local title = nil
local itemID = "Q" .. snak.datavalue.value['numeric-id']
if rawsubtype == 'wikibase-item' then
ifitemID link= then"Q" .. itemID
elseif subtype == 'wikibase-property' then
return buildWikilink("d:" .. itemID, itemID)
itemID = "P" .. itemID
else
else
return itemID
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
end
label = self:getLabel(itemID, raw, link, short)
if short then
value = getShortName(itemID)
end
if valuelabel == "" then
label = nil
value, lang = mw.wikibase.getLabelWithLang(itemID)
-- don't allow language fallback
if lang ~= self.langCode then
value = nil
end
end
return label
if link or value == nil then
title = mw.wikibase.sitelink(itemID)
end
if link then
if title then
value = buildWikilink(title, (value or title))
elseif not value then
value = buildWikilink("d:" .. itemID, itemID)
end
elseif not value then
value = (title or itemID)
end
return value
else
return '<strong class="error">' .. unknownDataTypeErrorerrorText(snak.datavalue.'unknown-data-type', datatype) .. '</strong>'
end
elseif snak.snaktype == 'somevalue' and not noSpecial then
if raw then
return " " -- single space represents 'somevalue'
Line 1,056 ⟶ 1,225:
return i18n['values']['unknown']
end
elseif snak.snaktype == 'novalue' and not noSpecial then
if raw then
return "" -- empty string represents 'novalue'
Line 1,090 ⟶ 1,259:
local rankPos
if rank == "p.flags.best" then
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
Line 1,096 ⟶ 1,265:
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
Line 1,129 ⟶ 1,298:
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
Line 1,148 ⟶ 1,317:
end
 
function Config:processFlagqualifierMatches(flagclaim, id, value)
local qualifiers
if not flag then
return false
else
flag = mw.text.trim(flag)
end
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if flag == "linked" then
if qualifiers then
self.curState.linked = true
for i, v in pairs(qualifiers) do
return true
if self:snakEqualsValue(v, value) then
elseif flag == "raw" then
self.curState.rawValue = return true
end
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
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
elseif flag == "short" then
self.curState.shortName = true
return true
elseif flag == "mdy" then
self.mdyDate = true
return true
elseif flag == "best" or flag:match('^preferred[+-]?$') or flag:match('^normal[+-]?$') or flag:match('^deprecated[+-]?$') then
self:setRank(flag)
return true
elseif flag == "future" or flag == "current" or flag == "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 = ""
return false
if not flag then
return false
else
flag = mw.text.trim(flag)
end
if flag == "property" or flag == "properties" then
param = parameters.property
elseif flag:match('^qualifier[s]?$') 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:match('^reference[s]?$') 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)
-- 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 string.sub(flag, -1) ~= 's' then
self.states[param].singleValue = true
end
self.curState = self.states[param]
return true
end
 
Line 1,255 ⟶ 1,360:
local now = os.date('!*t')
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime)
if startTime and startTime ~= "" and startTime ~= " " then
startTimeY, startTimeM, startTimeD = parseDate(startTime)
end
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime)
if endTime and endTime ~= "" and endTime ~= " " then
endTimeY, endTimeM, endTimeD = parseDate(endTime)
Line 1,301 ⟶ 1,406:
return false
end
 
function Config:processFlag(flag)
if not flag then
return false
else
flag = mw.text.trim(flag)
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
else
flag = mw.text.trim(flag)
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)
-- 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:sub(-1) ~= 's' 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
Line 1,311 ⟶ 1,556:
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 = convertRank(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
Line 1,384 ⟶ 1,639:
-- level 1 hook
function State:getProperty(claim)
local value = {self.conf:getValue(claim.mainsnak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
Line 1,401 ⟶ 1,656:
-- 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 Statestate with level 2 hook
else
return {} -- return empty array
Line 1,409 ⟶ 1,664:
-- level 2 hook
function State:getQualifier(snak)
local value = {self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly)} -- create one value object
if #value > 0 then
Line 1,448 ⟶ 1,703:
-- 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 Statestate with level 2 hook
else
return {} -- return empty array
Line 1,458 ⟶ 1,718:
 
-- level 2 hook
-- logic determined based on https://www.wikidata.org/wiki/Help:Sources
function State:getReference(statement)
local snakValuekey, langciteWeb, propertyciteQ, url, titlelabel
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
local snaks = {}
local params = {}
local leadParams = {}
if statement.snaks then
-- don't include "imported from", which is added by a bot
for i, v in pairs(statement.snaks) do
if vstatement.snaks[1p.aliasesP.importedFrom] then
statement.snaks[ip.aliasesP.importedFrom] = v[1]nil
end
end
-- don't include "imported fromlanguage" thatif it hasis beenequal addedto bythe alocal botone
if self:getReferenceDetail(statement.snaks[, p.aliasesP.importedFrom]language) == self.conf.langName then
statement.snaks[p.aliasesP.importedFromlanguage] = nil
end
-- retrieve all the parameters
-- use the general template for citing web references if both URL and title are present
for i in pairs(statement.snaks) do
if snaks[aliasesP.referenceURL] and snaks[aliasesP.title] and i18n['cite']['cite-web'] and i18n['cite']['cite-web'] ~= "" then
label = ""
params[i18n['cite']['url']] = self.conf:getValue(snaks[aliasesP.referenceURL])
params[i18n['cite']['title']] = self.conf:getValue(snaks[aliasesP.title], false, false, false, true) -- anyLang = true
-- multiple authors may be given
if snaks[aliasesP.publicationDate] then params[i18n['cite']['date']] = self.conf:getValue(snaks[aliasesP.publicationDate]) end
if i == p.aliasesP.author then
if snaks[aliasesP.retrieved] then params[i18n['cite']['access-date']] = self.conf:getValue(snaks[aliasesP.retrieved]) end
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
if snaks[aliasesP.archiveURL] then params[i18n['cite']['archive-url']] = self.conf:getValue(snaks[aliasesP.archiveURL]) end
else
if snaks[aliasesP.archiveDate] then params[i18n['cite']['archive-date']] = self.conf:getValue(snaks[aliasesP.archiveDate]) end
if snaks[aliasesP.author] then params[i18n['cite']['author']i] = {self.conf:getValuegetReferenceDetail(statement.snaks[aliasesP.author], i, false, (self.linked) or (i == p.aliasesP.statedIn)) and (statement.snaks[i][1].datatype end~= 'url'), true)} -- link = true/false, anyLang = true
end
if snaks[aliasesP.publisher] then params[i18n['cite']['publisher']] = self.conf:getValue(snaks[aliasesP.publisher], false, self.linked) end -- link = true/false
if snaks[aliasesP.quote] then params[i18n['cite']['quote']] = self.conf:getValue(snaks[aliasesP.quote], false, false, false, true) end -- anyLang = true
if snaks#params[aliasesP.languagei] == 0 then
params[i] = nil
snakValue = self.conf:getValue(snaks[aliasesP.language], false, self.linked) -- link = true/false
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
if self.conf.langName ~= snakValue then
for j in pairs(citeParams) do
params[i18n['cite']['language']] = snakValue
-- 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.sitelink(aliasesQ.citeWeb) or "", ":")[2] -- split off namespace from front
-- get title of template that expands stated-in references into citations
citeQ = split(mw.wikibase.sitelink(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'][p.aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][p.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'][p.aliasesP.statedIn]] then
-- we need the raw "stated in" Q-identifier for the this template
citeParams['q'][i18n['cite']['q'][p.aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, p.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(paramsuseParams) do
value = value .. "|" .. i .. "=" .. v
end
value = "{{" .. i18n['cite']['cite-web']useCite .. value .. "}}"
else
value = mw.getCurrentFrame():expandTemplate{title=i18n['cite']['cite-web']useCite, args=paramsuseParams}
end
else
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
-- if no general template for citing web references was defined but URL and title are present, add these together
ifelseif snaksparams[p.aliasesP.statedIn] or params[p.aliasesP.referenceURL] andor snaksparams[p.aliasesP.title] then
citeParams['default'] = {}
url = self.conf:getValue(snaks[aliasesP.referenceURL])
title = self.conf:getValue(snaks[aliasesP.title], false, false, false, true) -- anyLang = true
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
leadParams[#leadParams + 1] = "[" .. url .. " " .. title .. "]"
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
-- set to nil so that they won't be added a second time
snaks[aliasesP.referenceURL] = nil
snaks[aliasesP.title] = nil
end
-- combine "reference URL" and "title" into one link if both are present
for i, v in pairs(snaks) do
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
property = getLabel(i)
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
if property ~= "" then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
snakValue, lang = self.conf:getValue(v, false, self.linked or (i == aliasesP.statedIn) or (i == aliasesP.referenceURL), false, true) -- link = true/false, anyLang = true
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
if lang and lang ~= self.conf.langCode then
snakValue = "''" .. snakValue .. "'' (" .. mw.language.fetchLanguageName(lang, self.conf.langCode) .. ")"
end
if i == aliasesP.referenceURL or i == aliasesP.statedIn then
leadParams[#leadParams + 1] = snakValue
elseif i ~= aliasesP.language or self.conf.langName ~= snakValue then
params[#params + 1] = property .. ": " .. snakValue
end
end
end
-- then add "stated in"
value = table.concat(leadParams, "; ")
if params = table[p.concat(params, ";aliasesP.statedIn] ")then
citeParams['default'][#citeParams['default'] + 1] = "''" .. params[p.aliasesP.statedIn][1] .. "''"
end
-- remove previously added parameters so that they won't be added a second time
if params ~= "" then
params[p.aliasesP.author] = nil
if value ~= "" then
params[p.aliasesP.referenceURL] = nil
value = value .. "; "
params[p.aliasesP.title] = nil
params[p.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
value = value .. params
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
Line 1,557 ⟶ 1,865:
if value ~= "" then
refvalue = {value} -- create one value object
if not self.rawValue then
-- this should become a <ref> tag, so safe the reference's hash for later
refvalue.refHash = statement.hash
end
ref = {refvalue} -- 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 i, 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 i, 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.sitelink(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
 
Line 1,662 ⟶ 2,058:
end
 
function extractEntityFromInput(id, allowOmitPropPrefix)
function p.property(frame)
if id:sub(1,1):upper() == "Q" then
loadSubmodules(frame)
return id:upper() -- entity ID of an item was given
return p._property(copyTable(frame.args))
elseif id:sub(1,9):lower() == "property:" then
return replaceAlias(mw.text.trim(id:sub(10))):upper() -- entity ID of a property was given
elseif allowOmitPropPrefix and id ~= "" then
return replaceAlias(id):upper() -- could be an entity ID of a property without "Property:" prefix
else
return nil
end
end
 
function p._propertyextractEntityFromArgs(args, nextIndex, allowOmitPropPrefix)
local id, eidArg
loadSubmodules()
return execCommand(args, "property")
if args[nextIndex] then
args[nextIndex] = mw.text.trim(args[nextIndex])
else
args[nextIndex] = ""
end
id = extractEntityFromInput(args[nextIndex], allowOmitPropPrefix)
eidArg = args[p.args.eid]
if id then
return id, nextIndex + 1
elseif eidArg then
return extractEntityFromInput(eidArg, true), nextIndex -- if no positional id was found but eid was given, use eid without a default
else
return mw.wikibase.getEntityIdForCurrentPage(), nextIndex -- by default, use item-entity connected to current page
end
end
 
function p.propertiesclaimCommand(frameargs, funcName)
local _ = Config.new()
loadSubmodules(frame)
return p._properties(copyTable(frame.args))
end
 
function p._properties(args)
loadSubmodules()
return execCommand(args, "properties")
end
 
function p.qualifier(frame)
loadSubmodules(frame)
return p._qualifier(copyTable(frame.args))
end
 
function p._qualifier(args)
loadSubmodules()
return execCommand(args, "qualifier")
end
 
function p.qualifiers(frame)
loadSubmodules(frame)
return p._qualifiers(copyTable(frame.args))
end
 
function p._qualifiers(args)
loadSubmodules()
return execCommand(args, "qualifiers")
end
 
function p.reference(frame)
loadSubmodules(frame)
return p._reference(copyTable(frame.args))
end
 
function p._reference(args)
loadSubmodules()
return execCommand(args, "reference")
end
 
function p.references(frame)
loadSubmodules(frame)
return p._references(copyTable(frame.args))
end
 
function p._references(args)
loadSubmodules()
return execCommand(args, "references")
end
 
function execCommand(args, funcName)
_ = Config.new()
_:processFlagOrCommand(funcName) -- process first command (== function name)
local parsedFormat, formatParams, claims, sepvalue
local hooks = {count = 0}
local nextArgnextIndex = args[1]
local nextIndex = 2
-- process flags and commands
while _:processFlagOrCommand(nextArgargs[nextIndex]) do
nextArg = args[nextIndex]
nextIndex = nextIndex + 1
end
_.entityID, nextIndex = extractEntityFromArgs(args, nextIndex, false)
if nextArg then
nextArg = mw.text.trim(nextArg)
else
nextArg = ""
end
-- if eid was explicitly set to empty, then this returns an empty string
-- check for optional entity ID of either item or property
if nextArg:sub(1,1):upper()_.entityID == "Q"nil then
return ""
_.entity = mw.wikibase.getEntity(nextArg) -- entity ID of an item given
_.propertyID = mw.text.trim(args[nextIndex] or "") -- property ID
nextIndex = nextIndex + 1
elseif nextArg:sub(1,9):lower() == "property:" then
nextArg = mw.text.trim(nextArg:sub(10))
if aliasesP[nextArg] then
nextArg = aliasesP[nextArg]
end
_.entity = mw.wikibase.getEntity(nextArg) -- entity ID of a property given
_.propertyID = mw.text.trim(args[nextIndex] or "") -- property ID
nextIndex = nextIndex + 1
else
_.entity = mw.wikibase.getEntity() -- no entity ID given, use item connected to current page
_.propertyID = nextArg -- property ID
end
_.entity = mw.wikibase.getEntity(_.entityID)
-- check if given property ID is an alias
if aliasesP[_.propertyID] then= replaceAlias(args[nextIndex]):upper()
nextIndex = nextIndex + 1
_.propertyID = aliasesP[_.propertyID]
end
_.propertyID = _.propertyID:upper()
if _.states.qualifiersCount > 0 then
Line 1,777 ⟶ 2,122:
-- claim ID or literal value has been given
_.propertyValue = mw.text.trim(args[nextIndex])
nextArg = args[nextIndex] -- don't trim because might be single space representing 'somevalue'
nextIndex = nextIndex + 1
_.propertyValue = nextArg
end
for i = 1, _.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
nextArg = mw.text.trim(args[nextIndex] or "") -- is a qualifierID
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(mw.text.trim(args[nextIndex] or "")):upper()
nextIndex = nextIndex + 1
-- check if given qualifier ID is an alias
if aliasesP[nextArg] then
nextArg = aliasesP[nextArg]
end
_.qualifierIDs[parameters.qualifier..i] = nextArg:upper()
end
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
nextArg =if args[nextIndex] then
nextIndex _.propertyValue = mw.text.trim(args[nextIndex + 1])
end
-- not incrementing nextIndex because it is never used after this
_.propertyValue = nextArg -- claim ID or literal value (possibly nil)
end
-- check for special property value 'somevalue' or 'novalue'
if _.propertyValue then
_.propertyValue = replaceSpecialChars(_.propertyValue)
if _.propertyValue ~= "" and mw.text.trim(_.propertyValue) == "" then
_.propertyValue = " " -- single space represents 'somevalue', whereas empty string represents 'novalue'
Line 1,814 ⟶ 2,153:
-- parse the desired format, or choose an appropriate format
if args["format"] then
parsedFormat, formatParams = parseFormat(replaceHTMLSpaces(mw.text.trim(args["format"])))
elseif _.states.qualifiersCount > 0 then -- "qualifier(s)" command given
if _.states[parameters.property] then -- "propert(y|ies)" command given
Line 1,838 ⟶ 2,177:
end
-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalent;
-- must come BEFORE overriding the separator values
if _.states.qualifiersCount == 1 then
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"]
Line 1,845 ⟶ 2,183:
-- process overridden separator values;
-- must come AFTER parsingtweaking the formatsdefault separators
_:processSeparators(args)
for i, v in pairs(_.separators) do
if args[i] then
sep = replaceHTMLSpaces(mw.text.trim(args[i]))
if sep ~= "" then
_.separators[i][1] = {sep}
else
_.separators[i][1] = nil
end
end
end
-- make sure that at least one required parameter has been defined
if not next(parsedFormat.req) then
error(missingRequiredParameterError())
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if parsedFormat.req[parameters.separator] then
error(extraRequiredParameterError(parameters.separator))
end
-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
Line 1,872 ⟶ 2,190:
for i, v in pairs(_.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[string.i:sub(i, 1, 2)] then
hooks[i] = getHookName(i, 1)
hooks.count = hooks.count + 1
Line 1,889 ⟶ 2,207:
if not _.states[parameters.property] then
_.states[parameters.property] = State.new(_)
-- if the "single" flag has been given then this state should be equivalent to "property" (singular)
if _.singleClaim then
_.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,
-- set the parsed format and the separators (and optional punctuation mark)
-- which must exist in order to be able to determine if a claim has any references;
_.states[parameters.property].parsedFormat = parsedFormat
-- must come AFTER defining the hooks
_.states[parameters.property].separator = _.separators["sep"]
if _.sourcedOnly and not _.states[parameters.reference] then
_.states[parameters.property].movSeparator = _.separators["sep"..parameters.separator]
_:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
_.states[parameters.property].puncMark = _.separators["punc"]
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
_:setFormatAndSeparators(_.states[parameters.property], parsedFormat)
-- process qualifier matching values, analogous to _.propertyValue
for i, v in pairs(args) do
i = tostring(i)
if i:match('^[Pp]%d+$') or p.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
_.qualifierIDsAndValues[replaceAlias(i):upper()] = v
end
end
if _.entity and _.entity.claims then claims = _.entity.claims[_.propertyID] end
Line 1,903 ⟶ 2,247:
-- then iterate through the claims to collect values
returnvalue = _:concatValues(_.states[parameters.property]:iterate(claims, hooks, State.claimMatches)) -- pass property Statestate with level 1 hooks and matchHook
-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
if _.editable and value ~= "" then
value = value .. _:getEditIcon()
end
return value
else
return ""
Line 1,909 ⟶ 2,260:
end
 
function p.labelgeneralCommand(frameargs, funcName)
local _ = Config.new()
loadSubmodules(frame)
return p._label(copyTable(frame.args))
end
 
function p._label(args, _)
_ = _ or Config.new()
_.curState = State.new(_)
local value = nil
loadSubmodules()
local nextIndex = 1
while _:processFlag(args[nextIndex]) do
local ID = nil
nextIndex = nextIndex + 1
local label = ""
end
local title = nil
local lang
_.entityID, nextIndex = extractEntityFromArgs(args, nextIndex, true)
local nextArg = args[1]
local nextIndex = 2
-- if eid was explicitly set to empty, then this returns an empty string
while _:processFlag(nextArg) do
if _.entityID == nil then
nextArg = args[nextIndex]
return ""
nextIndex = nextIndex + 1
end
-- serve according to the given command
if nextArg then
if funcName == p.generalCommands.label then
ID = mw.text.trim(nextArg)
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName)
elseif funcName == p.generalCommands.title then
_.inSitelinks = true
if ID_.entityID:sub(1,1) == "Q" then
value = mw.wikibase.sitelink(_.entityID)
ID = nil
elseif ID:sub(1,9):lower() == "property:" then
ID = mw.text.trim(ID:sub(10))
end
end
if _.curState.linked and value then
value = buildWikilink(value)
if ID then
if aliasesP[ID] then
ID = aliasesP[ID]
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
ID = ID:upper()
_.curState.singleValue = true
-- check if this is a valid ID, and if the number is not larger than max int (to prevent error)
if not string.match(ID, '^[QP]%d+$') or tonumber(string.match(ID, '%d+')) > 2147483647 then
return ""
end
_.entity = mw.wikibase.getEntity(_.entityID)
if _.curState.rawValue and not _.pageTitle then
if mw.wikibase.getEntity(ID) or mw.wikibase.resolvePropertyId(ID) then
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
if _.curState.linked then
local aliases
if ID:sub(1,1) == "P" then
label = buildWikilink("d:Property:" .. ID, ID)
-- parse the desired format, or parse the default aliases format
else
if args["format"] then
label = buildWikilink("d:" .. ID, ID)
parsedFormat, formatParams = parseFormat(args["format"])
end
else
parsedFormat, formatParams = parseFormat(formats.alias)
label = ID
end
end
-- process overridden separator values;
return label
-- must come AFTER tweaking the default separators
end
_:processSeparators(args)
else
if _.curState.rawValue and not _.pageTitle then
label = mw.wikibase.getEntityIdForCurrentPage() or ""
-- define the hook that should be called (getAlias);
if _.curState.linked and label ~= "" then
-- only define the hook if the parameter ("%a") has been given
label = buildWikilink("d:" .. label, label)
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)
return label
_:setFormatAndSeparators(_.curState, parsedFormat)
end
end
if ID and ID:sub(1,1) == "P" then
if not _.pageTitle then
label = mw.wikibase.label(ID) or ""
if _.entity and _.entity.aliases then aliases = _.entity.aliases[_.langCode] end
if _.curState.linked and label ~= "" then
if aliases then
label = buildWikilink("d:Property:" .. ID, label)
value = _:concatValues(_.curState:iterate(aliases, hooks))
end
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
end
_.inSitelinks = true
else
if not _.pageTitle then
local badges
if _.curState.shortName then
label = getShortName(ID)
-- 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;
-- at this point, 'label' will be a string and not nil
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getBadge);
if label == "" then
-- only define the hook if the parameter ("%b") has been given
label, lang = mw.wikibase.getLabelWithLang(ID)
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
-- don't allow language fallback
ifhooks.count lang ~= _hooks.langCodecount + then1
label = nil
end
end
else
-- set the parsed format and the separators (and optional punctuation mark)
-- set 'label' to nil so 'title' will always prevail
_:setFormatAndSeparators(_.curState, parsedFormat)
label = nil
end
if _.entity and _.entity.sitelinks and _.entity.sitelinks[_.siteID] then badges = _.entity.sitelinks[_.siteID].badges end
if badges then
-- at this point, 'label' will be nil or a non-empty string
value = _:concatValues(_.curState:iterate(badges, hooks))
if _.curState.linked or label == nil then
if ID then
title = mw.wikibase.sitelink(ID)
else
title = mw.title.getCurrentTitle().prefixedText
end
end
if _.curState.linked and title then
label = buildWikilink(title, (label or title))
else
label = label or title or ""
end
end
value = value or ""
return label
if _.editable and value ~= "" then
-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
value = value .. _:getEditIcon()
end
return value
end
 
-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
function p.title(frame)
function establishCommands(commandList, commandFunc)
loadSubmodules(frame)
for commandIndex, commandName in pairs(commandList) do
return p._title(copyTable(frame.args))
local function wikitextWrapper(frame)
loadSubmodules(frame)
return commandFunc(copyTable(frame.args), commandName)
end
p[commandName] = wikitextWrapper
local function luaWrapper(args)
loadSubmodules()
return commandFunc(args, commandName)
end
p["_" .. commandName] = luaWrapper
end
end
 
establishCommands(p.claimCommands, claimCommand)
function p._title(args, _)
establishCommands(p.generalCommands, generalCommand)
_ = _ or Config.new()
_.pageTitle = true
-- loadSubmodules() will already be called by _label()
return p._label(args, _)
end
 
-- main function that is supposed to be used by wrapper templates
Line 2,053 ⟶ 2,402:
if not frame.args[1] then
errorthrowError(i18n["errors"]["no-function-specified"])
end
Line 2,059 ⟶ 2,408:
if f == "main" then
errorthrowError(i18n["errors"]["main-called-twice"])
end
assert(p["_"..f], applyStringParamserrorText(i18n['errors']['no-such-function'], f))
-- copy arguments from immutable to mutable table