Module:Wd: Difference between revisions
m
1 revision imported
(No longer allowing language fallback for item labels) |
Vivaporius (talk | contribs) m (1 revision imported) |
||
(2 intermediate revisions by 2 users not shown) | |||
Line 1:
-- Original module located at [[:en:Module:Wd]], [[:en:Module:Wd/i18n]] and [[:en:Module:Wd/
local p = {}
Line 6:
function loadSubmodules(frame)
local title
end
i18n = i18n or require(title .. "/i18n")
p.aliasesP = p.aliasesP or mw.loadData(title .. "/aliasesP")
end
properties = "properties",
references = "references"
}
p.generalCommands = {
}
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:
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.
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
if p.aliasesP[id] then
id = p.aliasesP[id]
end
return
end
function
if param then text = mw.ustring.gsub(text, "$1", param) end
return text
end
function throwError(errorMessage, param)
error(errorText(errorMessage, param))
end
function
return
end
function
local numZeros
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
if chr == '_' then
-- replace underscores with spaces
return ' '
else
return chr
end
end
function
local chr
local esc = false
local strOut = ""
for i = 1, #str do
chr = str:sub(i,i)
if not esc then
if chr == '\\' then
esc = true
else
strOut = strOut .. replaceSpecialChar(chr)
end
else
strOut = strOut .. chr
esc = false
end
end
return strOut
end
function buildWikilink(target, label)
if not label or target == label then
return "[[" .. target .. "]]"
else
Line 216 ⟶ 308:
end
function split(str, del)
local out = {}
local i, j = str:find(del)
out[1] = str:sub(1, i - 1)
out[2] = str:sub(j + 1)
else
end
return
end
function parseWikidataURL(url)
local
if url:match('^http[s]?://') then
if
return
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
return hookNames[
else
return nil
Line 494 ⟶ 568:
param = param - 1
elseif param == 1 then
if not
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
if not next(root.req) then
throwError("missing-required-parameter")
end
-- make sure that the separator parameter "%s" is not amongst the required parameters
if root.req[parameters.separator] then
throwError("extra-required-parameter", parameters.separator)
end
return root, params
end
Line 532 ⟶ 604:
for i, v in ipairs(claims) do
rankPos =
ranks[rankPos][#ranks[rankPos] + 1] = v
end
Line 543 ⟶ 615:
end
-- if id == nil then item connected to current page is used
function Config:getLabel(id, raw, link, short)
local label = nil
local title = nil
local prefix= ""
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
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
local value = ""
local prefix = ""
local front = " "
local back = ""
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 extensionTag("ref", ...) is only called when they really ended up in the final output
function Config:concatValues(valuesArray)
local outString = ""
local j, skip
for i = 1, #valuesArray do
-- check if this is a reference
if valuesArray[i].refHash then
j = i - 1
skip = false
-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
while valuesArray[j] and valuesArray[j].refHash do
if valuesArray[i].refHash == valuesArray[j].refHash then
skip = true
break
end
j = j - 1
end
if not skip then
-- add <ref> tag with the reference's hash as its name (to deduplicate references)
outString = outString .. mw.getCurrentFrame():extensionTag("ref", valuesArray[i][1], {name = "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
if label ~= "" then
return space .. label
end
end
end
return "
end
function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial)
if snak.snaktype == 'value' 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 "[" ..
else
return "[" ..
end
elseif
if link then
return buildWikilink("c:File:" .. datavalue, datavalue)
elseif not raw then
return "[[File:" ..
else
return
end
elseif
return buildWikilink("c:" .. datavalue, datavalue)
elseif subtype == 'math' and not raw then
return mw.getCurrentFrame():extensionTag("math", datavalue)
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
end
else
return
end
elseif
if anyLang then
return
elseif
return
else
return nil
end
elseif
local value = ""
local unit
if not
-- 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)
unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
if unit then
value = value .. unit
end
return value
elseif
local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
local yFactor = 1
Line 679 ⟶ 867:
local mayAddCalendar = false
local calendar = ""
local precision =
if precision == 11 then
Line 690 ⟶ 878:
end
y, m, d = parseDate(
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(
if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
Line 865 ⟶ 1,053:
value = prefix .. value .. suffix .. calendar
else
value =
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
-- logic from https://github.com/DataValues/Geo
Line 915 ⟶ 1,103:
end
latitude =
longitude =
if latitude < 0 then
Line 936 ⟶ 1,124:
end
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(
lonSeconds = tonumber(
latMinutes = math.floor(latSeconds / 60)
Line 974 ⟶ 1,166:
if precision < (1 / 60) then
latSeconds =
lonSeconds =
if not raw then
Line 993 ⟶ 1,185:
if link then
globe = parseWikidataURL(
if globe then
Line 1,005 ⟶ 1,197:
return value
elseif
local
local
if
elseif subtype == 'wikibase-property' then
itemID = "P" .. itemID
else
return '<strong class="error">' .. errorText('unknown-data-type', subtype) .. '</strong>'
end
label = self:getLabel(itemID, raw, link, short)
if
label = nil
end
return label
else
return '<strong class="error">' ..
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 ==
self.bestRank = true
self.flagBest = true -- mark that 'best' flag was given
Line 1,096 ⟶ 1,265:
end
if rank:sub(1,9) ==
rankPos = 1
elseif rank:sub(1,6) ==
rankPos = 2
elseif rank:sub(1,10) ==
rankPos = 3
else
Line 1,129 ⟶ 1,298:
local periodPos
if period ==
periodPos = 1
elseif period ==
periodPos = 2
elseif period ==
periodPos = 3
else
Line 1,148 ⟶ 1,317:
end
function Config:
local qualifiers
if claim.qualifiers then qualifiers = claim.qualifiers[id] end
if qualifiers then
for i, v in pairs(qualifiers) do
if self:snakEqualsValue(v, value) then
end
end
elseif value == "" then
-- if the qualifier is not present then treat it the same as the special value 'novalue'
return true
end
return false
end
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 =
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
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
else
return {} -- return empty array
Line 1,458 ⟶ 1,718:
-- level 2 hook
function State:getReference(statement)
local
local params = {}
local citeParams = {['web'] = {}, ['q'] = {}}
local citeMismatch = {}
local useCite = nil
local useParams = nil
local value = ""
local ref = {}
if statement.snaks then
-- don't include "imported from", which is added by a bot
end
-- don't include "
if self:getReferenceDetail(statement.snaks
statement.snaks[p.aliasesP.
end
-- retrieve all the parameters
for i in pairs(statement.snaks) do
label = ""
-- multiple authors may be given
if i == p.aliasesP.author then
params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true) -- link = true/false, anyLang = true
else
end
if
params[i] = nil
else
if statement.snaks[i][1].datatype == 'external-id' then
key = "external-id"
label = self.conf:getLabel(i)
if label ~= "" then
label = label .. " "
end
else
key = i
end
-- add the parameter to each matching type of citation
for j in pairs(citeParams) do
-- do so if there was no mismatch with a previous parameter
if not citeMismatch[j] then
-- check if this parameter is not mismatching itself
if i18n['cite'][j][key] then
-- continue if an option is available in the corresponding cite template
if i18n['cite'][j][key] ~= "" then
citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
-- if there are multiple parameter values (authors), add those too
for k=2, #params[i] do
citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
end
end
else
citeMismatch[j] = true
end
end
end
end
end
-- get title of general template for citing web references
citeWeb = split(mw.wikibase.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(
value = value .. "|" .. i .. "=" .. v
end
value = "{{" ..
else
value = mw.getCurrentFrame():expandTemplate{title=
end
-- (3) else, do some default rendering of name-value pairs, but only if at least "stated in", "reference URL" or "title" is present
citeParams['default'] = {}
-- start by adding authors up front
if params[p.aliasesP.author] and #params[p.aliasesP.author] > 0 then
citeParams['default'][#citeParams['default'] + 1] = table.concat(params[p.aliasesP.author], " & ")
end
-- combine "reference URL" and "title" into one link if both are present
if params[p.aliasesP.referenceURL] and params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '[' .. params[p.aliasesP.referenceURL][1] .. ' "' .. params[p.aliasesP.title][1] .. '"]'
elseif params[p.aliasesP.referenceURL] then
citeParams['default'][#citeParams['default'] + 1] = params[p.aliasesP.referenceURL][1]
elseif params[p.aliasesP.title] then
citeParams['default'][#citeParams['default'] + 1] = '"' .. params[p.aliasesP.title][1] .. '"'
end
-- then add "stated in"
if params
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
params[p.aliasesP.author] = nil
params[p.aliasesP.referenceURL] = nil
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
end
value = table.concat(citeParams['default'], "; ")
if value ~= "" then
Line 1,557 ⟶ 1,865:
if value ~= "" then
if not self.rawValue then
-- this should become a <ref> tag, so safe the reference's hash for later
end
ref = {
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)
if id:sub(1,1):upper() == "Q" then
return id:upper() -- entity ID of an item was given
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
local id, eidArg
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
local _ = Config.new()
_:processFlagOrCommand(funcName) -- process first command (== function name)
local parsedFormat, formatParams, claims,
local hooks = {count = 0}
local
-- process flags and commands
while _:processFlagOrCommand(
nextIndex = nextIndex + 1
end
_.entityID, nextIndex = extractEntityFromArgs(args, nextIndex, false)
-- if eid was explicitly set to empty, then this returns an empty string
if
return ""
end
_.entity = mw.wikibase.getEntity(_.entityID)
nextIndex = nextIndex + 1
if _.states.qualifiersCount > 0 then
Line 1,777 ⟶ 2,122:
-- claim ID or literal value has been given
_.propertyValue = mw.text.trim(args[nextIndex])
nextIndex = nextIndex + 1
end
for i = 1, _.states.qualifiersCount do
-- check if given qualifier ID is an alias and add it
_.qualifierIDs[parameters.qualifier..i] = replaceAlias(mw.text.trim(args[nextIndex] or "")):upper()
nextIndex = nextIndex + 1
end
elseif _.states[parameters.reference] then
-- do further processing if "reference(s)" command was given
end
-- not incrementing nextIndex because it is never used after this
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
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
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
_:processSeparators(args)
-- 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[
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,
-- which must exist in order to be able to determine if a claim has any references;
-- must come AFTER defining the hooks
if _.sourcedOnly and not _.states[parameters.reference] then
_:processFlagOrCommand(p.claimCommands.reference) -- use singular "reference" to minimize overhead
end
-- set the parsed format and the separators (and optional punctuation mark);
-- must come AFTER creating the additonal states
_: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
-- 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
local _ = Config.new()
_.curState = State.new(_)
local value = nil
local nextIndex = 1
while _:processFlag(args[nextIndex]) do
nextIndex = nextIndex + 1
end
_.entityID, nextIndex = extractEntityFromArgs(args, nextIndex, true)
-- if eid was explicitly set to empty, then this returns an empty string
if _.entityID == nil then
return ""
end
-- serve according to the given command
if funcName == p.generalCommands.label then
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName)
elseif funcName == p.generalCommands.title then
_.inSitelinks = true
if
value = mw.wikibase.sitelink(_.entityID)
end
if _.curState.linked and value then
value = buildWikilink(value)
end
else
local parsedFormat, formatParams
local hooks = {count = 0}
if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
_.curState.singleValue = true
end
_.entity = mw.wikibase.getEntity(_.entityID)
if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
local aliases
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
parsedFormat, formatParams = parseFormat(formats.alias)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getAlias);
-- only define the hook if the parameter ("%a") has been given
if formatParams[parameters.alias] then
hooks[parameters.alias] = getHookName(parameters.alias, 1)
hooks.count = hooks.count + 1
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
if _.entity and _.entity.aliases then aliases = _.entity.aliases[_.langCode] end
if aliases then
value = _:concatValues(_.curState:iterate(aliases, hooks))
end
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
_.inSitelinks = true
local badges
-- parse the desired format, or parse the default aliases format
if args["format"] then
parsedFormat, formatParams = parseFormat(args["format"])
else
parsedFormat, formatParams = parseFormat(formats.badge)
end
-- process overridden separator values;
-- must come AFTER tweaking the default separators
_:processSeparators(args)
-- define the hook that should be called (getBadge);
-- only define the hook if the parameter ("%b") has been given
if formatParams[parameters.badge] then
hooks[parameters.badge] = getHookName(parameters.badge, 1)
end
-- set the parsed format and the separators (and optional punctuation mark)
_:setFormatAndSeparators(_.curState, parsedFormat)
if _.entity and _.entity.sitelinks and _.entity.sitelinks[_.siteID] then badges = _.entity.sitelinks[_.siteID].badges end
if badges then
value = _:concatValues(_.curState:iterate(badges, hooks))
end
end
end
value = value or ""
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 establishCommands(commandList, commandFunc)
for commandIndex, commandName in pairs(commandList) do
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)
establishCommands(p.generalCommands, generalCommand)
-- main function that is supposed to be used by wrapper templates
Line 2,053 ⟶ 2,402:
if not frame.args[1] then
end
Line 2,059 ⟶ 2,408:
if f == "main" then
end
assert(p["_"..f],
-- copy arguments from immutable to mutable table
|