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