「モジュール:Wd」の版間の差分
ナビゲーションに移動
検索に移動
en:Module:Wd 2018年3月18日 (日) 10:53(UTC)より
template>K-iczn (en:Module:Wd 2017年12月25日 (月) 17:06(UTC)より) |
template>K-iczn (en:Module:Wd 2018年3月18日 (日) 10:53(UTC)より) |
||
20行目: | 20行目: | ||
end | end | ||
p. | p.claimCommands = { | ||
property = "property", | property = "property", | ||
properties = "properties", | properties = "properties", | ||
29行目: | 29行目: | ||
} | } | ||
p. | p.generalCommands = { | ||
label = "label", | label = "label", | ||
title = "title", | title = "title", | ||
alias = "alias", | alias = "alias", | ||
aliases = "aliases" | aliases = "aliases", | ||
badge = "badge", | |||
badges = "badges" | |||
} | } | ||
73行目: | 75行目: | ||
reference = "%r", | reference = "%r", | ||
alias = "%a", | alias = "%a", | ||
badge = "%b", | |||
separator = "%s", | separator = "%s", | ||
general = "%x" | general = "%x" | ||
82行目: | 85行目: | ||
reference = "%r", | reference = "%r", | ||
propertyWithQualifier = "%p[ <span style=\"font-size:smaller\">(%q)</span>][%s][%r]", | propertyWithQualifier = "%p[ <span style=\"font-size:smaller\">(%q)</span>][%s][%r]", | ||
alias = "%a[%s]" | alias = "%a[%s]", | ||
badge = "%b[%s]" | |||
} | } | ||
90行目: | 94行目: | ||
[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"} | |||
} | } | ||
148行目: | 153行目: | ||
cfg.editAtEnd = false | cfg.editAtEnd = false | ||
cfg. | 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) | ||
-- somewhat reliable way of determining global site ID in the absence of a library function, targeting the Wikipedia project (i.e. appending "wiki") | |||
cfg.siteID = (function() for i,v in pairs(mw.site.interwikiMap("local")) do if v.isCurrentWiki then return mw.ustring.gsub(i,"-","_").."wiki" end end end)() | |||
cfg.states = {} | cfg.states = {} | ||
197行目: | 205行目: | ||
end | end | ||
function errorText(code, param) | 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 | ||
684行目: | 692行目: | ||
if self.propertyID then | if self.propertyID then | ||
value = value .. "#" .. self.propertyID | value = value .. "#" .. self.propertyID | ||
elseif self. | elseif self.inSitelinks then | ||
value = value .. "#sitelinks-wikipedia" | value = value .. "#sitelinks-wikipedia" | ||
end | end | ||
1,093行目: | 1,101行目: | ||
end | end | ||
precision = datavalue['precision'] | precision = datavalue['precision'] or 1 | ||
latitude = math.floor(latitude / precision + 0.5) * precision | latitude = math.floor(latitude / precision + 0.5) * precision | ||
1,282行目: | 1,290行目: | ||
end | end | ||
function Config: | function Config:qualifierMatches(claim, id, value) | ||
local qualifiers | |||
if | if claim.qualifiers then qualifiers = claim.qualifiers[id] end | ||
if qualifiers then | |||
for i, v in pairs(qualifiers) do | |||
if self:snakEqualsValue(v, value) then | |||
return true | |||
end | |||
end | end | ||
elseif value == "" then | |||
-- if the qualifier is not present then treat it the same as the special value 'novalue' | |||
return true | return true | ||
end | |||
self. | |||
return false | |||
end | |||
function Config:rankMatches(rankPos) | |||
if self.bestRank then | |||
return (self.ranks[rankPos] and self.foundRank >= rankPos) | |||
else | |||
return self.ranks[rankPos] | |||
end | |||
end | |||
function Config:timeMatches(claim) | |||
local startTime = nil | |||
local startTimeY = nil | |||
local startTimeM = nil | |||
local startTimeD = nil | |||
local endTime = nil | |||
local endTimeY = nil | |||
local endTimeM = nil | |||
local endTimeD = nil | |||
if self.periods[1] and self.periods[2] and self.periods[3] then | |||
-- any time | |||
return true | return true | ||
end | end | ||
local now = os.date('!*t') | |||
startTime = self:getSingleRawQualifier(claim, p.aliasesP.startTime) | |||
if startTime and startTime ~= "" and startTime ~= " " then | |||
if | startTimeY, startTimeM, startTimeD = parseDate(startTime) | ||
end | |||
endTime = self:getSingleRawQualifier(claim, p.aliasesP.endTime) | |||
if endTime and endTime ~= "" and endTime ~= " " then | |||
endTimeY, endTimeM, endTimeD = parseDate(endTime) | |||
elseif endTime == " " then | |||
-- end time is 'unknown', assume it is somewhere in the past; | |||
-- we can do this by taking the current date as a placeholder for the end time | |||
endTimeY = now['year'] | |||
endTimeM = now['month'] | |||
endTimeD = now['day'] | |||
end | end | ||
if | if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then | ||
-- invalidate end time if it precedes start time | |||
endTimeY = nil | |||
endTimeM = nil | |||
endTimeD = nil | |||
end | end | ||
if self. | if self.periods[1] then | ||
return | -- future | ||
if startTimeY and datePrecedesDate(now['year'], now['month'], now['day'], startTimeY, startTimeM, startTimeD) then | |||
return true | |||
end | |||
end | end | ||
-- | if self.periods[2] then | ||
-- current | |||
if (startTimeY == nil or not datePrecedesDate(now['year'], now['month'], now['day'], startTimeY, startTimeM, startTimeD)) and | |||
(endTimeY == nil or datePrecedesDate(now['year'], now['month'], now['day'], endTimeY, endTimeM, endTimeD)) then | |||
return true | |||
end | |||
end | |||
if self.periods[3] then | |||
-- former | |||
if endTimeY and not datePrecedesDate(now['year'], now['month'], now['day'], endTimeY, endTimeM, endTimeD) then | |||
return true | |||
end | |||
end | end | ||
return false | |||
return | |||
end | end | ||
function Config: | function Config:processFlag(flag) | ||
if not flag then | |||
return false | |||
else | |||
flag = mw.text.trim(flag) | |||
end | |||
if | if flag == p.flags.linked then | ||
self.curState.linked = true | |||
return true | |||
elseif flag == p.flags.raw then | |||
self.curState.rawValue = true | |||
if self.curState == self.states[parameters.reference] then | |||
-- raw reference values end with periods and require a separator (other than none) | |||
self.separators["sep%r"][1] = {" "} | |||
end | end | ||
elseif | |||
return true | |||
elseif flag == p.flags.short then | |||
self.curState.shortName = true | |||
return true | return true | ||
elseif flag == p.flags.multilanguage then | |||
self.curState.anyLanguage = true | |||
return true | |||
elseif flag == p.flags.unit then | |||
self.curState.unitOnly = true | |||
return | |||
return true | return true | ||
elseif flag == p.flags.mdy then | |||
self.mdyDate = true | |||
return true | |||
elseif flag == p.flags.single then | |||
self.singleClaim = true | |||
return true | |||
elseif flag == p.flags.sourced then | |||
self.sourcedOnly = true | |||
return true | |||
elseif flag == p.flags.edit then | |||
self.editable = true | |||
return true | |||
elseif flag == p.flags.editAtEnd then | |||
self.editable = true | |||
self.editAtEnd = true | |||
return true | |||
elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then | |||
self:setRank(flag) | |||
return true | |||
elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then | |||
self:setPeriod(flag) | |||
return true | |||
elseif flag == "" then | |||
-- ignore empty flags and carry on | |||
return true | |||
else | |||
return false | |||
end | end | ||
end | |||
function Config:processFlagOrCommand(flag) | |||
local param = "" | |||
if not flag then | |||
return false | |||
else | |||
if | flag = mw.text.trim(flag) | ||
end | end | ||
if | if flag == p.claimCommands.property or flag == p.claimCommands.properties then | ||
param = parameters.property | |||
elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then | |||
self.states.qualifiersCount = self.states.qualifiersCount + 1 | |||
param = parameters.qualifier .. self.states.qualifiersCount | |||
self.separators["sep"..param] = {copyTable(defaultSeparators["sep%q\\d"])} | |||
elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then | |||
param = parameters.reference | |||
else | |||
return self:processFlag(flag) | |||
end | end | ||
if self. | if self.states[param] then | ||
return false | |||
end | end | ||
-- create a new state for each command | |||
self.states[param] = State.new(self) | |||
-- use "%x" as the general parameter name | |||
self.states[param].parsedFormat = parseFormat(parameters.general) -- will be overwritten for param=="%p" | |||
-- set the separator | |||
self.states[param].separator = self.separators["sep"..param] -- will be nil for param=="%p", which will be set separately | |||
if flag:sub(-1) ~= 's' then | |||
self.states[param].singleValue = true | |||
end | end | ||
self.curState = self.states[param] | |||
return | return true | ||
end | end | ||
-- determines if a claim has references by prefetching them from the claim using getReferences, | function Config:processSeparators(args) | ||
-- which applies some filtering that determines if a reference is actually returned, | local sep | ||
-- and caches the references for later use | |||
for i, v in pairs(self.separators) do | |||
if args[i] then | |||
sep = replaceSpecialChars(args[i]) | |||
if sep ~= "" then | |||
self.separators[i][1] = {sep} | |||
else | |||
self.separators[i][1] = nil | |||
end | |||
end | |||
end | |||
end | |||
function Config:setFormatAndSeparators(state, parsedFormat) | |||
state.parsedFormat = parsedFormat | |||
state.separator = self.separators["sep"] | |||
state.movSeparator = self.separators["sep"..parameters.separator] | |||
state.puncMark = self.separators["punc"] | |||
end | |||
-- determines if a claim has references by prefetching them from the claim using getReferences, | |||
-- which applies some filtering that determines if a reference is actually returned, | |||
-- and caches the references for later use | |||
function State:isSourced(claim) | function State:isSourced(claim) | ||
self.conf.prefetchedRefs = self:getReferences(claim) | self.conf.prefetchedRefs = self:getReferences(claim) | ||
1,886行目: | 1,917行目: | ||
value = buildWikilink(title, value) | value = buildWikilink(title, value) | ||
end | end | ||
end | |||
value = {value} -- create one value object | |||
if #value > 0 then | |||
return {value} -- wrap the value object in an array and return it | |||
else | |||
return {} -- return empty array if there was no value | |||
end | |||
end | |||
-- level 1 hook | |||
function State:getBadge(value) | |||
value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName) | |||
if value == "" then | |||
value = nil | |||
end | end | ||
1,988行目: | 2,036行目: | ||
end | end | ||
function extractEntityFromInput(id, | function extractEntityFromInput(id, allowOmitPropPrefix) | ||
if id:sub(1,1):upper() == "Q" then | if id:sub(1,1):upper() == "Q" then | ||
return id:upper() | return id:upper() -- entity ID of an item was given | ||
elseif id:sub(1,9):lower() == "property:" then | elseif id:sub(1,9):lower() == "property:" then | ||
return replaceAlias(mw.text.trim(id:sub(10))):upper() -- entity ID of a property was given | return replaceAlias(mw.text.trim(id:sub(10))):upper() -- entity ID of a property was given | ||
elseif | elseif allowOmitPropPrefix and id ~= "" then | ||
return replaceAlias(id):upper() | return replaceAlias(id):upper() -- could be an entity ID of a property without "Property:" prefix | ||
else | else | ||
return nil | return nil | ||
2,000行目: | 2,048行目: | ||
end | end | ||
function extractEntityFromArgs(args, nextIndex, | function extractEntityFromArgs(args, nextIndex, allowOmitPropPrefix) | ||
local id, eidArg | local id, eidArg | ||
2,009行目: | 2,057行目: | ||
end | end | ||
id = extractEntityFromInput(args[nextIndex], | id = extractEntityFromInput(args[nextIndex], allowOmitPropPrefix) | ||
eidArg = args[p.args.eid] | eidArg = args[p.args.eid] | ||
if id then | if id then | ||
return id, nextIndex + 1 | return id, nextIndex + 1 | ||
elseif | elseif eidArg then | ||
return extractEntityFromInput(eidArg, true), nextIndex -- if no positional id was found but eid was given, use eid without a default | |||
else | |||
return mw.wikibase.getEntityIdForCurrentPage(), nextIndex -- by default, use item-entity connected to current page | return mw.wikibase.getEntityIdForCurrentPage(), nextIndex -- by default, use item-entity connected to current page | ||
end | end | ||
end | end | ||
function | function claimCommand(args, funcName) | ||
local _ = Config.new() | local _ = Config.new() | ||
_:processFlagOrCommand(funcName) -- process first command (== function name) | _:processFlagOrCommand(funcName) -- process first command (== function name) | ||
local parsedFormat, formatParams, claims | local parsedFormat, formatParams, claims, value | ||
local hooks = {count = 0} | local hooks = {count = 0} | ||
2,037行目: | 2,085行目: | ||
_.entityID, nextIndex = extractEntityFromArgs(args, nextIndex, false) | _.entityID, nextIndex = extractEntityFromArgs(args, nextIndex, false) | ||
-- if eid was | -- if eid was explicitly set to empty, then this returns an empty string | ||
if _.entityID == nil then | if _.entityID == nil then | ||
return "" | return "" | ||
2,107行目: | 2,155行目: | ||
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 _.states.qualifiersCount == 1 then | if _.states.qualifiersCount == 1 then | ||
_.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"] | _.separators["sep"..parameters.qualifier] = _.separators["sep"..parameters.qualifier.."1"] | ||
2,114行目: | 2,161行目: | ||
-- process overridden separator values; | -- process overridden separator values; | ||
-- must come AFTER | -- must come AFTER tweaking the default separators | ||
_:processSeparators(args) | |||
-- 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 | |||
-- 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 | |||
for i, v in pairs(_.states) do | for i, v in pairs(_.states) do | ||
-- e.g. 'formatParams["%q1"] or formatParams["%q"]' to define hook even if "%q1" was not defined to be able to build a complete value for "%q" | -- 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" | ||
2,159行目: | 2,196行目: | ||
-- must come AFTER defining the hooks | -- must come AFTER defining the hooks | ||
if _.sourcedOnly and not _.states[parameters.reference] then | if _.sourcedOnly and not _.states[parameters.reference] then | ||
_:processFlagOrCommand(p. | _: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 | |||
_ | _:setFormatAndSeparators(_.states[parameters.property], parsedFormat) | ||
-- process qualifier matching values, analogous to _.propertyValue | -- process qualifier matching values, analogous to _.propertyValue | ||
2,203行目: | 2,238行目: | ||
end | end | ||
function | function generalCommand(args, funcName) | ||
local _ = Config.new() | local _ = Config.new() | ||
_.curState = State.new(_) | _.curState = State.new(_) | ||
2,216行目: | 2,251行目: | ||
_.entityID, nextIndex = extractEntityFromArgs(args, nextIndex, true) | _.entityID, nextIndex = extractEntityFromArgs(args, nextIndex, true) | ||
-- if eid was | -- if eid was explicitly set to empty, then this returns an empty string | ||
if _.entityID == nil then | if _.entityID == nil then | ||
return "" | return "" | ||
2,222行目: | 2,257行目: | ||
-- serve according to the given command | -- serve according to the given command | ||
if funcName == p. | if funcName == p.generalCommands.label then | ||
value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName) | value = _:getLabel(_.entityID, _.curState.rawValue, _.curState.linked, _.curState.shortName) | ||
elseif funcName == p. | elseif funcName == p.generalCommands.title then | ||
_. | _.inSitelinks = true | ||
if _.entityID:sub(1,1) == "Q" then | if _.entityID:sub(1,1) == "Q" then | ||
2,234行目: | 2,269行目: | ||
value = buildWikilink(value) | value = buildWikilink(value) | ||
end | end | ||
else | |||
local | local parsedFormat, formatParams | ||
local hooks = {count = 0} | local hooks = {count = 0} | ||
if funcName == p. | if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then | ||
_.curState.singleValue = true | _.curState.singleValue = true | ||
end | end | ||
_.entity = mw.wikibase.getEntity(_.entityID) | |||
-- process overridden separator values; | if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then | ||
local aliases | |||
if args[ | -- parse the desired format, or parse the default aliases format | ||
if args["format"] then | |||
parsedFormat, formatParams = parseFormat(args["format"]) | |||
else | |||
parsedFormat, formatParams = parseFormat(formats.alias) | |||
end | |||
-- process overridden separator values; | |||
-- must come AFTER tweaking the default separators | |||
_:processSeparators(args) | |||
-- define the hook that should be called (getAlias); | |||
-- only define the hook if the parameter ("%a") has been given | |||
if formatParams[parameters.alias] then | |||
hooks[parameters.alias] = getHookName(parameters.alias, 1) | |||
hooks.count = hooks.count + 1 | |||
end | |||
-- set the parsed format and the separators (and optional punctuation mark) | |||
_:setFormatAndSeparators(_.curState, parsedFormat) | |||
if _.entity and _.entity.aliases then aliases = _.entity.aliases[_.langCode] end | |||
if aliases then | |||
value = _:concatValues(_.curState:iterate(aliases, hooks)) | |||
end | |||
elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then | |||
_.inSitelinks = true | |||
local badges | |||
-- parse the desired format, or parse the default aliases format | |||
if args["format"] then | |||
parsedFormat, formatParams = parseFormat(args["format"]) | |||
else | |||
parsedFormat, formatParams = parseFormat(formats.badge) | |||
end | |||
-- process overridden separator values; | |||
-- must come AFTER tweaking the default separators | |||
_:processSeparators(args) | |||
-- define the hook that should be called (getBadge); | |||
-- only define the hook if the parameter ("%b") has been given | |||
if formatParams[parameters.badge] then | |||
hooks[parameters.badge] = getHookName(parameters.badge, 1) | |||
hooks.count = hooks.count + 1 | |||
end | |||
-- set the parsed format and the separators (and optional punctuation mark) | |||
_:setFormatAndSeparators(_.curState, parsedFormat) | |||
if _.entity and _.entity.sitelinks and _.entity.sitelinks[_.siteID] then badges = _.entity.sitelinks[_.siteID].badges end | |||
if badges then | |||
value = _:concatValues(_.curState:iterate(badges, hooks)) | |||
end | end | ||
end | end | ||
end | end | ||
2,297行目: | 2,353行目: | ||
function establishCommands(commandList, commandFunc) | function establishCommands(commandList, commandFunc) | ||
for commandIndex, commandName in pairs(commandList) do | for commandIndex, commandName in pairs(commandList) do | ||
local function wikitextWrapper(frame) | local function wikitextWrapper(frame) | ||
loadSubmodules(frame) | loadSubmodules(frame) | ||
return commandFunc(copyTable(frame.args), commandName) | return commandFunc(copyTable(frame.args), commandName) | ||
2,311行目: | 2,367行目: | ||
end | end | ||
establishCommands(p. | establishCommands(p.claimCommands, claimCommand) | ||
establishCommands(p. | 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 |