Module:Wd
![]() | This Lua module is used on many pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
![]() | This module is rated as alpha. It is ready for third-party input, and may be used on a few pages to see if problems arise, but should be watched. Suggestions for new features or changes in their input and output mechanisms are welcome. |
This module is intended to fetch data from விக்கித்தரவுகள் with or without a link to the connected Wikipedia article.
The module is under development and is not yet complete. Of the available datatypes, it currently supports strings, quantities, monolingual text, time and globe coordinates.
Usage
[தொகு]{{#invoke:wd|command1|flag1a|flag1b|flag1c|command2|flag2a|flag2b|flag2c|flag0a|flag0b|flag0c|arg1|arg2|arg3}}
Commands
[தொகு]Value class
[தொகு]The value class commands can be combined, meaning that multiple commands of different types from this class can be given at one time.
Combine multiple commands into one call to this module instead of making multiple calls to this module with one command each to be sure that all the returned pieces of information belong to each other.
Type | Command | Returns | Basic usage | Description |
---|---|---|---|---|
1 | property | first match* | {{#invoke:wd|property|P1}} | Returns the requested property – or list of properties – from the current item, or a given item. |
properties | all matches | {{#invoke:wd|properties|P1}} | ||
2 | qualifier | first match* | {{#invoke:wd|qualifier|P1|P2}} | Returns the requested qualifier – or list of qualifiers – from the given property of the current item, or a given item. |
qualifiers | all matches | {{#invoke:wd|qualifiers|P1|P2}} | ||
3 | reference | first match* | {{#invoke:wd|reference|P1}} | Returns a reference – or list of references – from the given property of the current item, or a given item. |
references | all matches | {{#invoke:wd|references|P1}} | ||
* Returns only a single value instead of multiple (if multiple claims or statements match). The returned value is the first match found from the best-ranked claims. |
Name class
[தொகு]The name class commands cannot be combined.
Command | Basic usage | Description |
---|---|---|
label | {{#invoke:wd|label}} | Returns the label of the current item, or a given item or property. In case of an item that has no label, the title of the linked page will be returned instead if it exists. |
title | {{#invoke:wd|title}} | Returns the title of the page linked to the current item, or a given item, if such page exists. The label of the item will never be returned instead. |
Main class
Command | Basic usage | Description |
---|---|---|
main | {{#invoke:wd|main|command|P1}} | Intended for use by a wrapper template (around this module) to invoke one of the above commands, returning their respective output. The parameters passed to the wrapper template are the ones that will be used by the module, e.g. Therefore, any parameters set by the wrapper template itself will be discarded, e.g. |
Flags
[தொகு]The following (optional) flags are available which can be used to alter this module's behaviour. They must be given after the (first) command and before the other arguments. For convenience, empty flags (i.e. ||
) are allowed and will simply be ignored.
Command flags
[தொகு]These flags (flag1*
, flag2*
, ...) apply to the command that precedes them directly.
Flag | Description |
---|---|
linked | Creates a link to the Wikipedia article that is connected to the property or qualifier if it exists. Also links units of measurement that may be appended to values. If this parameter is omitted, then the plain property or qualifier value will be returned. |
short | EXPENSIVE Gets the short name (P1813) of any item returned if they have one attached. If that is not the case, then the default behaviour of returning the item label will occur. |
raw | Returns the raw value if applicable. If this flag is used with item or property datatypes, then this will return the Q-identifier or P-identifier. For quantity datatypes, this flag will strip off any units of measurement. If this flag is used with time datatypes, then the returned date will be in the format of If it is used with globe coordinate datatypes, then it replaces the various symbols with forward slashes in the returned value (e.g. |
Configuration flags
[தொகு]These flags (flag0*
) are general configuration flags and can be given anywhere after the first command (but before the other arguments).
Flag | Description | |
---|---|---|
Combination of: | preferred | Sets a rank constraint for the selected claim(s). The first three set the ranks for which claim(s) will be selected. They can optionally be followed by a If the The default is Output is always sorted from highest rank to lowest (regardless of any of these flags being set). |
normal | ||
deprecated | ||
best | ||
Combination of: | future | Sets a time constraint for the selected claim(s). Uses the claims' qualifiers of "start time" (P580) and "end time" (P582) to determine if the claim is valid for the selected time period(s). The default is |
current | ||
former | ||
mdy | Returns date values in month_day_year order instead of day_month_year order. |
Arguments
[தொகு]The following table shows the available arguments (arg*
) in their fixed order. For each command, the minimal set of arguments is marked (i.e. if the commands properties
and qualifiers
have been given, then at least both the arguments property_id
and qualifier_id
should be given as well).
(required) | (optional) | (optional) | (required) | (optional) | (required) | ||
{{#invoke:wd | commands | flags | item_id | property_id | claim_id_or_lit_value | qualifier_id | }} |
---|---|---|---|---|---|---|---|
label , title | |||||||
property /properties | |||||||
reference /references | |||||||
qualifier /qualifiers |
Below follows a description of all arguments.
Argument | Description |
---|---|
item_id (optional) | EXPENSIVE Q-identifier of the item to be accessed (e.g. Q55 ). In case of the If this parameter is omitted, then the item connected to the current page will be used. |
property_id | P-identifier or an available alias of the property to be accessed (e.g. P38 ). |
claim_id_or_lit_value (optional) | Either the Q-identifier equal to the property value of the particular claim to be accessed (e.g. Q6655 ) or a literal value (i.e. string or quantity etc., no item label) equal to the property value (in its raw form) of the claim to be accessed. Dates as literal values must be formatted Globe coordinates as literal values must be formatted with forward slashes (i.e. The special type 'no value' can be given by entering the empty string (i.e. If this parameter is omitted, then all claims (matching any other constraints) within the property will be accessed. |
qualifier_id | P-identifier or an available alias of the qualifier to be accessed (e.g. P518 ). |
Named properties
[தொகு]Named properties are aliases for P-identifiers. The following named properties (which are case-sensitive) are currently available:
Alias | translates to | P-identifier |
---|---|---|
coord | → | P625 |
author | → | P50 |
publisher | → | P123 |
importedFrom | → | P143 |
statedIn | → | P248 |
publicationDate | → | P577 |
startTime | → | P580 |
endTime | → | P582 |
retrieved | → | P813 |
referenceURL | → | P854 |
archiveURL | → | P1065 |
title | → | P1476 |
quote | → | P1683 |
shortName | → | P1813 |
language | → | P2439 |
archiveDate | → | P2960 |
Advanced usage
[தொகு]The layout of the output from value class commands can be customized by using a number of named flags, which are name-value pairs (i.e. |flag=value
). The table below shows the available named flags.
Named flags can be given anywhere after the first command. To insert a space at the beginning or end of a value
, use the HTML entity 
.
Named flag | Default | Default condition | Description | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
format= | %p[%s][%r] | if the property /properties command was given and the qualifier /qualifiers command was not given | The format of a single claim. The available parameters are as follows.
Optional parameters can be given by encapsulating them between square brackets: At least one parameter must be given that is not optional, while the To get a literal | ||||||||||
%q[%s][%r] | if the property /properties command was not given and the qualifier /qualifiers command was given | ||||||||||||
%r | if only the reference /references command was given | ||||||||||||
%p[ <span style="font-size:smaller">(%q)</span>][%s][%r] | if the property /properties command was given and the qualifier /qualifiers command was given | ||||||||||||
sep= | வார்ப்புரு:Dfn | default | The fixed separator between each pair of claims. | ||||||||||
வார்ப்புரு:Dfn | if only the reference /references command was given without the raw flag | ||||||||||||
sep%s= | , | default | The movable separator between each pair of claims. This will be the value of the %s parameter applied to all claims except for the last in the list. | ||||||||||
sep%q= | வார்ப்புரு:Dfn | default | The separator between each pair of qualifiers of a single claim. This is the value separator for the %q parameter. | ||||||||||
sep%r= | வார்ப்புரு:Dfn | default | The separator between each pair of references of a single claim. This is the value separator for the %r parameter. | ||||||||||
வார்ப்புரு:Dfn | if the raw flag was given for the reference /references command | ||||||||||||
punc= | வார்ப்புரு:Dfn | default | A punctuation mark placed at the end of the output. This will be placed on the %s parameter applied to the last claim in the list. This allows any references to be placed after the punctuation mark when the output is used as part of a sentence. |
Examples
[தொகு]Parameters and output types | Example | Description |
---|---|---|
Q55 = "நெதர்லாந்து", P1549 = ""
[monolingual text] | {{#invoke:wd|property|Q55|P1549}}
| Gets a monolingual text value in the current wiki's language. |
P1549 = ""
[monolingual text] | {{#invoke:wd|property|P1549}}
| If the module is transcluded on the நெதர்லாந்து page (which is linked to Q55), then the Q55 can be omitted. |
Q55 = "நெதர்லாந்து", P395 = ""
[string] | {{#invoke:wd|property|Q55|P395}}
| Gets a literal string value. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை"
[quantity] | {{#invoke:wd|property|normal+|Q55|P1082}}
| Gets a single property value from claims with a 'normal' rank or higher. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை"
[quantity] | {{#invoke:wd|properties|normal+|Q55|P1082}}
| Gets multiple property values from claims with a 'normal' rank or higher. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை", P585 = "கால கட்டம்"
[quantity], [time] | {{#invoke:wd|properties|qualifier|normal+|Q55|P1082|P585}}
| Gets a single qualifier value for each claim, additional to the property value. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை", P585 = "கால கட்டம்"
[quantity], [time], [reference] | {{#invoke:wd|properties|qualifier|references|normal+|Q55|P1082|P585}} | Gets references for each claim. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை"
[quantity], [reference] | A total of {{#invoke:wd|property|references|Q55|P1082}} people live in the Netherlands.
| Gets a property with its references. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை"
[quantity], [reference] | The Netherlands has a population of {{#invoke:wd|property|references|Q55|P1082|punc=.}}
| Adds a punctuation mark at the end of the output, in front of the references. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை", P585 = "கால கட்டம்"
[quantity], [time], [reference] | <ul>{{#invoke:wd|properties|qualifier|references|normal+|Q55|P1082|P585|format=<li>%p[%r][<ul><li>%q</li></ul>]</li>}}</ul>
| Returns the output in a custom format. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை", P585 = "கால கட்டம்"
[time] | {{#invoke:wd|qualifier|normal+|Q55|P1082|P585}}
| Gets a single qualifier per claim, by default for multiple matching claims. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை", P585 = "கால கட்டம்"
[time] | {{#invoke:wd|property|qualifier|normal+|Q55|P1082|P585|format=%q[%s][%r]}}
| To get a single qualifier for only a single claim, give the property command too along with the regular qualifier format. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை", P585 = "கால கட்டம்"
[time] | {{#invoke:wd|qualifier|Q55|P1082|17590672|P585}}
| Gets a qualifier from claims for which the (raw) property value matches a given literal value. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை", P585 = "கால கட்டம்"
[time] | {{#invoke:wd|qualifier|mdy|Q55|P1082|17590672|P585}}
| Gets dates in month_day_year order. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை", P585 = "கால கட்டம்"
[time] | {{#invoke:wd|qualifier|raw|Q55|P1082|17590672|P585}}
| Gets a raw date value. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை"
[reference] | {{#invoke:wd|references|Q55|P1082|17590672}}
| Gets the references from a particular claim. |
Q55 = "நெதர்லாந்து", P1082 = "மக்கள் தொகை"
[reference] | {{#invoke:wd|references|raw|Q55|P1082|17590672}} | Gets references from a particular claim in their raw form. |
Q55 = "நெதர்லாந்து", P2855 = "", P518 = ""
[item label] | {{#invoke:wd|qualifier|Q55|P2855|P518}}
| Gets a single qualifier value (for each matching claim). |
Q55 = "நெதர்லாந்து", P2855 = "", P518 = ""
[item label] | {{#invoke:wd|qualifiers|Q55|P2855|P518}}
| Gets multiple qualifier values (for each matching claim). |
Q55 = "நெதர்லாந்து", P2855 = "", P518 = ""
[quantity], [item label] | {{#invoke:wd|properties|qualifiers|Q55|P2855|P518}}
| Gets multiple property values along with multiple qualifier values. |
Q55 = "நெதர்லாந்து", P2855 = "", P518 = ""
[quantity], [item label] | {{#invoke:wd|properties|qualifiers|Q55|P2855|P518|sep= + |sep%s=|sep%q= / }}
| Returns the output with custom separators. |
Q55 = "நெதர்லாந்து", P38 = "நாணயம்", P518 = ""
[item label] | {{#invoke:wd|properties|qualifiers|normal+|current|Q55|P38|P518}}}}
| Gets claims that are currently valid. |
Q55 = "நெதர்லாந்து", P38 = "நாணயம்", P518 = ""
[item label] | {{#invoke:wd|properties|linked|qualifiers|normal+|current|Q55|P38|P518}}}} | Gets claims with linked property values. |
Q55 = "நெதர்லாந்து", P38 = "நாணயம்", P518 = ""
[item label] | {{#invoke:wd|properties|qualifiers|linked|normal+|current|Q55|P38|P518}}}}
| Gets claims with linked qualifier values. |
Q55 = "நெதர்லாந்து", P38 = "நாணயம்", P518 = ""
[item label] | {{#invoke:wd|properties|linked|short|qualifiers|linked|normal+|current|Q55|P38|P518}}}} | Gets claims with linked property and qualifier values, with short property values wherever available. |
Q55 = "நெதர்லாந்து", P38 = "நாணயம்", Q4917 = "அமெரிக்க டாலர்", P518 = ""
[item label] | {{#invoke:wd|qualifiers|linked|normal+|current|Q55|P38|Q4917|P518}}}}
| Gets qualifiers from claims for which the (raw) property value matches a given item ID. |
Q55 = "நெதர்லாந்து", P38 = "நாணயம்", P518 = ""
[item label] | {{#invoke:wd|properties|qualifiers|normal+|former|Q55|P38|P518}}}}
| Gets claims that were valid in the past. |
Q55 = "நெதர்லாந்து", P38 = "நாணயம்", P518 = ""
[item label] | {{#invoke:wd|properties|raw|qualifiers|normal+|former|Q55|P38|P518}}}}
| Gets raw property values. |
Q55 = "நெதர்லாந்து", P38 = "நாணயம்", P518 = ""
[item label] | {{#invoke:wd|properties|raw|linked|qualifiers|normal+|former|Q55|P38|P518}}}} | Gets raw property values that are linked to Wikidata. |
Q55 = "நெதர்லாந்து", P2884 = ""
[quantity] | {{#invoke:wd|property|Q55|P2884}}
| Gets a quantity value with its associated unit of measurement. |
Q55 = "நெதர்லாந்து", P2884 = ""
[quantity] | {{#invoke:wd|property|linked|Q55|P2884}}
| Gets a quantity value with a linked unit of measurement. |
Q55 = "நெதர்லாந்து", P2884 = ""
[quantity] | {{#invoke:wd|property|raw|Q55|P2884}}
| Gets a raw quantity value. |
Q55 = "நெதர்லாந்து", P625 = "ஆயமுறை இடங்குறிப்பு"
[globe coordinate] | {{#invoke:wd|property|Q55|P625}}
| Gets a globe coordinate value. |
Q55 = "நெதர்லாந்து", P625 = "ஆயமுறை இடங்குறிப்பு"
[globe coordinate] | {{#invoke:wd|property|linked|Q55|P625}} | Gets a linked globe coordinate value. |
Q55 = "நெதர்லாந்து", P625 = "ஆயமுறை இடங்குறிப்பு"
[globe coordinate] | {{#invoke:wd|property|raw|Q55|P625}}
| Gets a raw globe coordinate value. |
Q55 = "நெதர்லாந்து", P625 = "ஆயமுறை இடங்குறிப்பு"
[globe coordinate] | {{#invoke:wd|property|Q55|coord}}
| A named property (or alias) can be used instead of the P-identifier. |
Q4917 = "அமெரிக்க டாலர்"
[item label] | {{#invoke:wd|label|Q4917}}
| Gets an item's label. |
Q4917 = "அமெரிக்க டாலர்"
[item label] | {{#invoke:wd|label|short|linked|Q4917}} | Gets an item's short and linked label. |
P38 = "நாணயம்"
[property label] | {{#invoke:wd|label|P38}}
| Gets a property's label. |
P38 = "நாணயம்"
[property label] | {{#invoke:wd|label|linked|P38}} | Gets a property's label that is linked to Wikidata. |
Q776 = "யூடிரெக்ட்"
[item label] | {{#invoke:wd|label|Q776}}
| Gets an item's label. |
Q776 = "யூடிரெக்ட்"
[item label] | {{#invoke:wd|label|linked|Q776}} | Gets an item's linked label. |
[item label] | {{#invoke:wd|label}}
| If the module is transcluded on the உத்ரத் page (which is linked to Q776), then the Q776 can be omitted. |
Q776 = "யூடிரெக்ட்"
[page title] | {{#invoke:wd|title|Q776}}
| Gets the title of the page on the current wiki that is linked to the given item. |
Q776 = "யூடிரெக்ட்"
[page title] | {{#invoke:wd|title|linked|Q776}} | Gets the linked title of the page on the current wiki that is linked to the given item. |
[page title] | {{#invoke:wd|title}}
| If the module is transcluded on the உத்ரத் page (which is linked to Q776), then the Q776 can be omitted. |
Example references
[தொகு]- ↑ 1.01.11.21.3"Bevolking; kerncijfers (1950-2024)". Retrieved 22 ஆகத்து 2024.
- ↑ 2.02.12.22.32.42.52.62.72.8"Bevolking; kerncijfers, 1950-2022".
- ↑ 3.03.1"Bevolking; kerncijfers, 1950-2022". Retrieved 18 மார்ச் 2023.
- ↑ 4.04.1"Nederland telt 17 miljoen inwoners". 21 மார்ச் 2016.
- ↑ 5.05.1"CBS StatLine - Bevolking; kerncijfers". Archived from the original on 3 செப்டெம்பர் 2014. Retrieved 26 ஆகத்து 2014.
- ↑ 6.06.16.26.3"CBS StatLine - Bevolking; kerncijfers". Archived from the original on 27 ஆகத்து 2015. Retrieved 22 ஆகத்து 2015.
See also
[தொகு]- {{Wikidata}}, a user-friendly wrapper template for this module.
- {{WikidataOI}}, a wrapper template for this module that adds an opt-in toggle.
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].require("strict")localp={}localarg=...locali18nlocalfunctionloadI18n(aliasesP,frame)localtitleifframethen-- current module invoked by page/template, get its title from frametitle=frame:getTitle()else-- current module included by other module, get its title from ...title=argendifnoti18ntheni18n=require(title.."/i18n").init(aliasesP)endendp.claimCommands={property="property",properties="properties",qualifier="qualifier",qualifiers="qualifiers",reference="reference",references="references"}p.generalCommands={label="label",title="title",description="description",alias="alias",aliases="aliases",badge="badge",badges="badges"}p.flags={linked="linked",short="short",raw="raw",multilanguage="multilanguage",unit="unit",-------------preferred="preferred",normal="normal",deprecated="deprecated",best="best",future="future",current="current",former="former",edit="edit",editAtEnd="edit@end",mdy="mdy",single="single",sourced="sourced"}p.args={eid="eid",page="page",date="date"}localaliasesP={coord="P625",-----------------------image="P18",author="P50",authorNameString="P2093",publisher="P123",importedFrom="P143",statedIn="P248",pages="P304",language="P407",hasPart="P527",publicationDate="P577",startTime="P580",endTime="P582",chapter="P792",retrieved="P813",referenceURL="P854",sectionVerseOrParagraph="P958",archiveURL="P1065",title="P1476",formatterURL="P1630",quote="P1683",shortName="P1813",definingFormula="P2534",archiveDate="P2960",inferredFrom="P3452",typeOfReference="P3865",column="P3903"}localaliasesQ={percentage="Q11229",prolepticJulianCalendar="Q1985786",citeWeb="Q5637226",citeQ="Q22321052"}localparameters={property="%p",qualifier="%q",reference="%r",alias="%a",badge="%b",separator="%s",general="%x"}localformats={property="%p[%s][%r]",qualifier="%q[%s][%r]",reference="%r",propertyWithQualifier="%p[ <span style=\"font-size:85\\%\">(%q)</span>][%s][%r]",alias="%a[%s]",badge="%b[%s]"}localhookNames={-- {level_1, level_2}[parameters.property]={"getProperty"},[parameters.reference]={"getReferences","getReference"},[parameters.qualifier]={"getAllQualifiers"},[parameters.qualifier.."\\d"]={"getQualifiers","getQualifier"},[parameters.alias]={"getAlias"},[parameters.badge]={"getBadge"}}-- default value objects, should NOT be mutated but instead copiedlocaldefaultSeparators={["sep"]={" "},["sep%s"]={","},["sep%q"]={"; "},["sep%q\\d"]={", "},["sep%r"]=nil,-- none["punc"]=nil-- none}localrankTable={["preferred"]=1,["normal"]=2,["deprecated"]=3}localfunctionreplaceAlias(id)ifaliasesP[id]thenid=aliasesP[id]endreturnidendlocalfunctionerrorText(code,param)localtext=i18n["errors"][code]ifparamthentext=mw.ustring.gsub(text,"$1",param)endreturntextendlocalfunctionthrowError(errorMessage,param)error(errorText(errorMessage,param))endlocalfunctionreplaceDecimalMark(num)returnmw.ustring.gsub(num,"[.]",i18n['numeric']['decimal-mark'],1)endlocalfunctionpadZeros(num,numDigits)localnumZeroslocalnegative=falseifnum<0thennegative=truenum=num*-1endnum=tostring(num)numZeros=numDigits-num:len()for_=1,numZerosdonum="0"..numendifnegativethennum="-"..numendreturnnumendlocalfunctionreplaceSpecialChar(chr)ifchr=='_'then-- replace underscores with spacesreturn' 'elsereturnchrendendlocalfunctionreplaceSpecialChars(str)localchrlocalesc=falselocalstrOut=""fori=1,#strdochr=str:sub(i,i)ifnotescthenifchr=='\\'thenesc=trueelsestrOut=strOut..replaceSpecialChar(chr)endelsestrOut=strOut..chresc=falseendendreturnstrOutendlocalfunctionbuildWikilink(target,label)ifnotlabelortarget==labelthenreturn"[["..target.."]]"elsereturn"[["..target.."|"..label.."]]"endend-- used to make frame.args mutable, to replace #frame.args (which is always 0)-- with the actual amount and to simply copy tableslocalfunctioncopyTable(tIn)ifnottInthenreturnnilendlocaltOut={}fori,vinpairs(tIn)dotOut[i]=vendreturntOutend-- used to merge output arrays together;-- note that it currently mutates the first input arraylocalfunctionmergeArrays(a1,a2)fori=1,#a2doa1[#a1+1]=a2[i]endreturna1endlocalfunctionsplit(str,del)localout={}locali,j=str:find(del)ifiandjthenout[1]=str:sub(1,i-1)out[2]=str:sub(j+1)elseout[1]=strendreturnoutendlocalfunctionparseWikidataURL(url)localidifurl:match('^http[s]?://')thenid=split(url,"Q")ifid[2]thenreturn"Q"..id[2]endendreturnnilendlocalfunctionparseDate(dateStr,precision)precision=precisionor"d"locali,j,index,ptrlocalparts={nil,nil,nil}ifdateStr==nilthenreturnparts[1],parts[2],parts[3]-- year, month, dayend-- 'T' for snak values, '/' for outputs with '/Julian' attachedi,j=dateStr:find("[T/]")ifithendateStr=dateStr:sub(1,i-1)endlocalfrom=1ifdateStr:sub(1,1)=="-"then-- this is a negative number, look further aheadfrom=2endindex=1ptr=1i,j=dateStr:find("-",from)ifithen-- yearparts[index]=tonumber(mw.ustring.gsub(dateStr:sub(ptr,i-1),"^\+(.+)$","%1"),10)-- remove '+' sign (explicitly give base 10 to prevent error)ifparts[index]==-0thenparts[index]=tonumber("0")-- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string insteadendifprecision=="y"then-- we're donereturnparts[1],parts[2],parts[3]-- year, month, dayendindex=index+1ptr=i+1i,j=dateStr:find("-",ptr)ifithen-- monthparts[index]=tonumber(dateStr:sub(ptr,i-1),10)ifprecision=="m"then-- we're donereturnparts[1],parts[2],parts[3]-- year, month, dayendindex=index+1ptr=i+1endendifdateStr:sub(ptr)~=""then-- day if we have month, month if we have year, or yearparts[index]=tonumber(dateStr:sub(ptr),10)endreturnparts[1],parts[2],parts[3]-- year, month, dayendlocalfunctiondatePrecedesDate(aY,aM,aD,bY,bM,bD)ifaY==nilorbY==nilthenreturnnilendaM=aMor1aD=aDor1bM=bMor1bD=bDor1ifaY<bYthenreturntrueendifaY>bYthenreturnfalseendifaM<bMthenreturntrueendifaM>bMthenreturnfalseendifaD<bDthenreturntrueendreturnfalseendlocalfunctiongetHookName(param,index)ifhookNames[param]thenreturnhookNames[param][index]elseifparam:len()>2thenreturnhookNames[param:sub(1,2).."\\d"][index]elsereturnnilendendlocalfunctionalwaysTrue()returntrueend-- The following function parses a format string.---- 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.---- Example:-- "A %p B [%s[%q1]] C [%r] D"---- Structure:-- [-- {-- str = "A "-- },-- {-- str = "%p"-- },-- {-- str = " B ",-- child =-- [-- {-- str = "%s",-- child =-- [-- {-- str = "%q1"-- }-- ]-- }-- ]-- },-- {-- str = " C ",-- child =-- [-- {-- str = "%r"-- }-- ]-- },-- {-- str = " D"-- }-- ]--localfunctionparseFormat(str)localchr,esc,param,root,cur,prev,newlocalparams={}localfunctionnewObject(array)localobj={}-- new objectobj.str=""array[#array+1]=obj-- array{object}obj.parent=arrayreturnobjendlocalfunctionendParam()ifparam>0thenifcur.str~=""thencur.str="%"..cur.strcur.param=trueparams[cur.str]=truecur.parent.req[cur.str]=trueprev=curcur=newObject(cur.parent)endparam=0endendroot={}-- arrayroot.req={}cur=newObject(root)prev=nilesc=falseparam=0fori=1,#strdochr=str:sub(i,i)ifnotescthenifchr=='\\'thenendParam()esc=trueelseifchr=='%'thenendParam()ifcur.str~=""thencur=newObject(cur.parent)endparam=2elseifchr=='['thenendParam()ifprevandcur.str==""thentable.remove(cur.parent)cur=prevendcur.child={}-- new arraycur.child.req={}cur.child.parent=curcur=newObject(cur.child)elseifchr==']'thenendParam()ifcur.parent.parentthennew=newObject(cur.parent.parent.parent)ifcur.str==""thentable.remove(cur.parent)endcur=newendelseifparam>1thenparam=param-1elseifparam==1thenifnotchr:match('%d')thenendParam()endendcur.str=cur.str..replaceSpecialChar(chr)endelsecur.str=cur.str..chresc=falseendprev=nilendendParam()-- make sure that at least one required parameter has been definedifnotnext(root.req)thenthrowError("missing-required-parameter")end-- make sure that the separator parameter "%s" is not amongst the required parametersifroot.req[parameters.separator]thenthrowError("extra-required-parameter",parameters.separator)endreturnroot,paramsendlocalfunctionsortOnRank(claims)localrankPoslocalranks={{},{},{},{}}-- preferred, normal, deprecated, (default)localsorted={}for_,vinipairs(claims)dorankPos=rankTable[v.rank]or4ranks[rankPos][#ranks[rankPos]+1]=vendsorted=ranks[1]sorted=mergeArrays(sorted,ranks[2])sorted=mergeArrays(sorted,ranks[3])returnsortedendlocalConfig={}-- allows for recursive callsfunctionConfig:new()localcfg={}setmetatable(cfg,self)self.__index=selfcfg.separators={-- single value objects wrapped in arrays so that we can pass by reference["sep"]={copyTable(defaultSeparators["sep"])},["sep%s"]={copyTable(defaultSeparators["sep%s"])},["sep%q"]={copyTable(defaultSeparators["sep%q"])},["sep%r"]={copyTable(defaultSeparators["sep%r"])},["punc"]={copyTable(defaultSeparators["punc"])}}cfg.entity=nilcfg.entityID=nilcfg.propertyID=nilcfg.propertyValue=nilcfg.qualifierIDs={}cfg.qualifierIDsAndValues={}cfg.bestRank=truecfg.ranks={true,true,false}-- preferred = true, normal = true, deprecated = falsecfg.foundRank=#cfg.rankscfg.flagBest=falsecfg.flagRank=falsecfg.periods={true,true,true}-- future = true, current = true, former = truecfg.flagPeriod=falsecfg.atDate={parseDate(os.date('!%Y-%m-%d'))}-- today as {year, month, day}cfg.mdyDate=falsecfg.singleClaim=falsecfg.sourcedOnly=falsecfg.editable=falsecfg.editAtEnd=falsecfg.inSitelinks=falsecfg.langCode=mw.language.getContentLanguage().codecfg.langName=mw.language.fetchLanguageName(cfg.langCode,cfg.langCode)cfg.langObj=mw.language.new(cfg.langCode)cfg.siteID=mw.wikibase.getGlobalSiteId()cfg.states={}cfg.states.qualifiersCount=0cfg.curState=nilcfg.prefetchedRefs=nilreturncfgendlocalState={}functionState:new(cfg,type)localstt={}setmetatable(stt,self)self.__index=selfstt.conf=cfgstt.type=typestt.results={}stt.parsedFormat={}stt.separator={}stt.movSeparator={}stt.puncMark={}stt.linked=falsestt.rawValue=falsestt.shortName=falsestt.anyLanguage=falsestt.unitOnly=falsestt.singleValue=falsereturnsttend-- if id == nil then item connected to current page is usedfunctionConfig:getLabel(id,raw,link,short)locallabel=nillocalprefix,title="",nilifnotidthenid=mw.wikibase.getEntityIdForCurrentPage()ifnotidthenreturn""endendid=id:upper()-- just to be sureifrawthen-- check if given id actually existsifmw.wikibase.isValidEntityId(id)andmw.wikibase.entityExists(id)thenlabel=idendprefix,title="d:Special:EntityPage/",label-- may be nilelse-- try short name first if requestedifshortthenlabel=p._property{aliasesP.shortName,[p.args.eid]=id}-- get short nameiflabel==""thenlabel=nilendend-- get labelifnotlabelthenlabel=mw.wikibase.getLabelByLang(id,self.langCode)-- XXX: should use fallback labels?endendifnotlabelthenlabel=""elseiflinkthen-- build a link if requestedifnottitlethenifid:sub(1,1)=="Q"thentitle=mw.wikibase.getSitelink(id)elseifid:sub(1,1)=="P"then-- properties have no sitelink, link to Wikidata insteadprefix,title="d:Special:EntityPage/",idendendlabel=mw.text.nowiki(label)-- escape raw label text so it cannot be wikitext markupiftitlethenlabel=buildWikilink(prefix..title,label)endendreturnlabelendfunctionConfig:getEditIcon()localvalue=""localprefix=""localfront=" "localback=""ifself.entityID:sub(1,1)=="P"thenprefix="Property:"endifself.editAtEndthenfront='<span style="float:'ifself.langObj:isRTL()thenfront=front..'left'elsefront=front..'right'endfront=front..'">'back='</span>'endvalue="[[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.langCodeifself.propertyIDthenvalue=value.."#"..self.propertyIDelseifself.inSitelinksthenvalue=value.."#sitelinks-wikipedia"endvalue=value.."|"..i18n['info']['edit-on-wikidata'].."]]"returnfront..value..backend-- 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 outputfunctionConfig:concatValues(valuesArray)localoutString=""localj,skipfori=1,#valuesArraydo-- check if this is a referenceifvaluesArray[i].refHashthenj=i-1skip=false-- skip this reference if it is part of a continuous row of references that already contains the exact same referencewhilevaluesArray[j]andvaluesArray[j].refHashdoifvaluesArray[i].refHash==valuesArray[j].refHashthenskip=truebreakendj=j-1endifnotskipthen-- 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})endelseoutString=outString..valuesArray[i][1]endendreturnoutStringendfunctionConfig:convertUnit(unit,raw,link,short,unitOnly)localspace=" "locallabel=""localitemIDifunit==""orunit=="1"thenreturnnilendifunitOnlythenspace=""enditemID=parseWikidataURL(unit)ifitemIDthenifitemID==aliasesQ.percentagethenreturn"%"elselabel=self:getLabel(itemID,raw,link,short)iflabel~=""thenreturnspace..labelendendendreturn""endfunctionState:getValue(snak)returnself.conf:getValue(snak,self.rawValue,self.linked,self.shortName,self.anyLanguage,self.unitOnly,false,self.type:sub(1,2))endfunctionConfig:getValue(snak,raw,link,short,anyLang,unitOnly,noSpecial,type)ifsnak.snaktype=='value'thenlocaldatatype=snak.datavalue.typelocalsubtype=snak.datatypelocaldatavalue=snak.datavalue.valueifdatatype=='string'thenifsubtype=='url'andlinkthen-- create link explicitlyifrawthen-- will render as a linked number like [1]return"["..datavalue.."]"elsereturn"["..datavalue.." "..datavalue.."]"endelseifsubtype=='commonsMedia'theniflinkthenreturnbuildWikilink("c:File:"..datavalue,datavalue)elseifnotrawthenreturn"[[File:"..datavalue.."]]"elsereturndatavalueendelseifsubtype=='geo-shape'andlinkthenreturnbuildWikilink("c:"..datavalue,datavalue)elseifsubtype=='math'andnotrawthenlocalattribute=nilif(type==parameters.propertyor(type==parameters.qualifierandself.propertyID==aliasesP.hasPart))andsnak.property==aliasesP.definingFormulathenattribute={qid=self.entityID}endreturnmw.getCurrentFrame():extensionTag("math",datavalue,attribute)elseifsubtype=='external-id'andlinkthenlocalurl=p._property{aliasesP.formatterURL,[p.args.eid]=snak.property}-- get formatter URLifurl~=""thenurl=mw.ustring.gsub(url,"$1",datavalue)return"["..url.." "..datavalue.."]"elsereturndatavalueendelsereturndatavalueendelseifdatatype=='monolingualtext'thenifanyLangordatavalue['language']==self.langCodethenreturndatavalue['text']elsereturnnilendelseifdatatype=='quantity'thenlocalvalue=""localunitifnotunitOnlythen-- get value and strip + signs from frontvalue=mw.ustring.gsub(datavalue['amount'],"^\+(.+)$","%1")ifrawthenreturnvalueend-- replace decimal mark based on localevalue=replaceDecimalMark(value)-- add delimiters for readabilityvalue=i18n.addDelimiters(value)endunit=self:convertUnit(datavalue['unit'],raw,link,short,unitOnly)ifunitthenvalue=value..unitendreturnvalueelseifdatatype=='time'thenlocaly,m,d,p,yDiv,yRound,yFull,value,calendarID,dateStrlocalyFactor=1localsign=1localprefix=""localsuffix=""localmayAddCalendar=falselocalcalendar=""localprecision=datavalue['precision']ifprecision==11thenp="d"elseifprecision==10thenp="m"elsep="y"yFactor=10^(9-precision)endy,m,d=parseDate(datavalue['time'],p)ify<0thensign=-1y=y*signend-- if precision is tens/hundreds/thousands/millions/billions of yearsifprecision<=8thenyDiv=y/yFactor-- if precision is tens/hundreds/thousands of yearsifprecision>=6thenmayAddCalendar=trueifprecision<=7then-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)yRound=math.ceil(yDiv)ifnotrawthenifprecision==6thensuffix=i18n['datetime']['suffixes']['millennium']elsesuffix=i18n['datetime']['suffixes']['century']endsuffix=i18n.getOrdinalSuffix(yRound)..suffixelse-- if not verbose, take the first year of the century/millennium-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)yRound=(yRound-1)*yFactor+1endelse-- precision == 8-- round decades down (e.g. 2010s)yRound=math.floor(yDiv)*yFactorifnotrawthenprefix=i18n['datetime']['prefixes']['decade-period']suffix=i18n['datetime']['suffixes']['decade-period']endendifrawandsign<0then-- if BCE then compensate for "counting backwards"-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)yRound=yRound+yFactor-1endelselocalyReFactor,yReDiv,yReRound-- round to nearest for tens of thousands of years or moreyRound=math.floor(yDiv+0.5)ifyRound==0thenifprecision<=2andy~=0thenyReFactor=1e6yReDiv=y/yReFactoryReRound=math.floor(yReDiv+0.5)ifyReDiv==yReRoundthen-- change precision to millions of years only if we have a whole number of themprecision=3yFactor=yReFactoryRound=yReRoundendendifyRound==0then-- otherwise, take the unrounded (original) number of yearsprecision=5yFactor=1yRound=ymayAddCalendar=trueendendifprecision>=1andy~=0thenyFull=yRound*yFactoryReFactor=1e9yReDiv=yFull/yReFactoryReRound=math.floor(yReDiv+0.5)ifyReDiv==yReRoundthen-- change precision to billions of years if we're in that rangeprecision=0yFactor=yReFactoryRound=yReRoundelseyReFactor=1e6yReDiv=yFull/yReFactoryReRound=math.floor(yReDiv+0.5)ifyReDiv==yReRoundthen-- change precision to millions of years if we're in that rangeprecision=3yFactor=yReFactoryRound=yReRoundendendendifnotrawthenifprecision==3thensuffix=i18n['datetime']['suffixes']['million-years']elseifprecision==0thensuffix=i18n['datetime']['suffixes']['billion-years']elseyRound=yRound*yFactorifyRound==1thensuffix=i18n['datetime']['suffixes']['year']elsesuffix=i18n['datetime']['suffixes']['years']endendelseyRound=yRound*yFactorendendelseyRound=ymayAddCalendar=trueendifmayAddCalendarthencalendarID=parseWikidataURL(datavalue['calendarmodel'])ifcalendarIDandcalendarID==aliasesQ.prolepticJulianCalendarthenifnotrawtheniflinkthencalendar=" ("..buildWikilink(i18n['datetime']['julian-calendar'],i18n['datetime']['julian'])..")"elsecalendar=" ("..i18n['datetime']['julian']..")"endelsecalendar="/"..i18n['datetime']['julian']endendendifnotrawthenlocalce=nilifsign<0thence=i18n['datetime']['BCE']elseifprecision<=5thence=i18n['datetime']['CE']endifcetheniflinkthence=buildWikilink(i18n['datetime']['common-era'],ce)endsuffix=suffix.." "..ceendvalue=tostring(yRound)ifmthendateStr=self.langObj:formatDate("F","1-"..m.."-1")ifdthenifself.mdyDatethendateStr=dateStr.." "..d..","elsedateStr=d.." "..dateStrendendvalue=dateStr.." "..valueendvalue=prefix..value..suffix..calendarelsevalue=padZeros(yRound*sign,4)ifmthenvalue=value.."-"..padZeros(m,2)ifdthenvalue=value.."-"..padZeros(d,2)endendvalue=value..calendarendreturnvalueelseifdatatype=='globecoordinate'then-- logic from https://github.com/DataValues/Geo (v4.0.1)localprecision,unitsPerDegree,numDigits,strFormat,value,globelocallatitude,latConv,latValue,latLinklocallongitude,lonConv,lonValue,lonLinklocallatDirection,latDirectionN,latDirectionS,latDirectionENlocallonDirection,lonDirectionE,lonDirectionW,lonDirectionENlocaldegSymbol,minSymbol,secSymbol,separatorlocallatDegrees=nillocallatMinutes=nillocallatSeconds=nillocallonDegrees=nillocallonMinutes=nillocallonSeconds=nillocallatDegSym=""locallatMinSym=""locallatSecSym=""locallonDegSym=""locallonMinSym=""locallonSecSym=""locallatDirectionEN_N="N"locallatDirectionEN_S="S"locallonDirectionEN_E="E"locallonDirectionEN_W="W"ifnotrawthenlatDirectionN=i18n['coord']['latitude-north']latDirectionS=i18n['coord']['latitude-south']lonDirectionE=i18n['coord']['longitude-east']lonDirectionW=i18n['coord']['longitude-west']degSymbol=i18n['coord']['degrees']minSymbol=i18n['coord']['minutes']secSymbol=i18n['coord']['seconds']separator=i18n['coord']['separator']elselatDirectionN=latDirectionEN_NlatDirectionS=latDirectionEN_SlonDirectionE=lonDirectionEN_ElonDirectionW=lonDirectionEN_WdegSymbol="/"minSymbol="/"secSymbol="/"separator="/"endlatitude=datavalue['latitude']longitude=datavalue['longitude']iflatitude<0thenlatDirection=latDirectionSlatDirectionEN=latDirectionEN_Slatitude=math.abs(latitude)elselatDirection=latDirectionNlatDirectionEN=latDirectionEN_Nendiflongitude<0thenlonDirection=lonDirectionWlonDirectionEN=lonDirectionEN_Wlongitude=math.abs(longitude)elselonDirection=lonDirectionElonDirectionEN=lonDirectionEN_Eendprecision=datavalue['precision']ifnotprecisionorprecision<=0thenprecision=1/3600-- precision not set (correctly), set to arcsecondend-- remove insignificant detaillatitude=math.floor(latitude/precision+0.5)*precisionlongitude=math.floor(longitude/precision+0.5)*precisionifprecision>=1-(1/60)andprecision<1thenprecision=1elseifprecision>=(1/60)-(1/3600)andprecision<(1/60)thenprecision=1/60endifprecision>=1thenunitsPerDegree=1elseifprecision>=(1/60)thenunitsPerDegree=60elseunitsPerDegree=3600endnumDigits=math.ceil(-math.log10(unitsPerDegree*precision))ifnumDigits<=0thennumDigits=tonumber("0")-- for some reason, 'numDigits = 0' may actually store '-0', so parse from string insteadendstrFormat="%."..numDigits.."f"ifprecision>=1thenlatDegrees=strFormat:format(latitude)lonDegrees=strFormat:format(longitude)ifnotrawthenlatDegSym=replaceDecimalMark(latDegrees)..degSymbollonDegSym=replaceDecimalMark(lonDegrees)..degSymbolelselatDegSym=latDegrees..degSymbollonDegSym=lonDegrees..degSymbolendelselatConv=math.floor(latitude*unitsPerDegree*10^numDigits+0.5)/10^numDigitslonConv=math.floor(longitude*unitsPerDegree*10^numDigits+0.5)/10^numDigitsifprecision>=(1/60)thenlatMinutes=latConvlonMinutes=lonConvelselatSeconds=latConvlonSeconds=lonConvlatMinutes=math.floor(latSeconds/60)lonMinutes=math.floor(lonSeconds/60)latSeconds=strFormat:format(latSeconds-(latMinutes*60))lonSeconds=strFormat:format(lonSeconds-(lonMinutes*60))ifnotrawthenlatSecSym=replaceDecimalMark(latSeconds)..secSymbollonSecSym=replaceDecimalMark(lonSeconds)..secSymbolelselatSecSym=latSeconds..secSymbollonSecSym=lonSeconds..secSymbolendendlatDegrees=math.floor(latMinutes/60)lonDegrees=math.floor(lonMinutes/60)latDegSym=latDegrees..degSymbollonDegSym=lonDegrees..degSymbollatMinutes=latMinutes-(latDegrees*60)lonMinutes=lonMinutes-(lonDegrees*60)ifprecision>=(1/60)thenlatMinutes=strFormat:format(latMinutes)lonMinutes=strFormat:format(lonMinutes)ifnotrawthenlatMinSym=replaceDecimalMark(latMinutes)..minSymbollonMinSym=replaceDecimalMark(lonMinutes)..minSymbolelselatMinSym=latMinutes..minSymbollonMinSym=lonMinutes..minSymbolendelselatMinSym=latMinutes..minSymbollonMinSym=lonMinutes..minSymbolendendlatValue=latDegSym..latMinSym..latSecSym..latDirectionlonValue=lonDegSym..lonMinSym..lonSecSym..lonDirectionvalue=latValue..separator..lonValueiflinkthenglobe=parseWikidataURL(datavalue['globe'])ifglobethenglobe=mw.wikibase.getLabelByLang(globe,"en"):lower()elseglobe="earth"endlatLink=table.concat({latDegrees,latMinutes,latSeconds},"_")lonLink=table.concat({lonDegrees,lonMinutes,lonSeconds},"_")value="[https://geohack.toolforge.org/geohack.php?language="..self.langCode.."¶ms="..latLink.."_"..latDirectionEN.."_"..lonLink.."_"..lonDirectionEN.."_globe:"..globe.." "..value.."]"endreturnvalueelseifdatatype=='wikibase-entityid'thenlocallabellocalitemID=datavalue['numeric-id']ifsubtype=='wikibase-item'thenitemID="Q"..itemIDelseifsubtype=='wikibase-property'thenitemID="P"..itemIDelsereturn'<strong class="error">'..errorText('unknown-data-type',subtype)..'</strong>'endlabel=self:getLabel(itemID,raw,link,short)iflabel==""thenlabel=nilendreturnlabelelsereturn'<strong class="error">'..errorText('unknown-data-type',datatype)..'</strong>'endelseifsnak.snaktype=='somevalue'andnotnoSpecialthenifrawthenreturn" "-- single space represents 'somevalue'elsereturni18n['values']['unknown']endelseifsnak.snaktype=='novalue'andnotnoSpecialthenifrawthenreturn""-- empty string represents 'novalue'elsereturni18n['values']['none']endelsereturnnilendendfunctionConfig:getSingleRawQualifier(claim,qualifierID)localqualifiersifclaim.qualifiersthenqualifiers=claim.qualifiers[qualifierID]endifqualifiersandqualifiers[1]thenreturnself:getValue(qualifiers[1],true)-- raw = trueelsereturnnilendendfunctionConfig:snakEqualsValue(snak,value)localsnakValue=self:getValue(snak,true)-- raw = trueifsnakValueandsnak.snaktype=='value'andsnak.datavalue.type=='wikibase-entityid'thenvalue=value:upper()endreturnsnakValue==valueendfunctionConfig:setRank(rank)localrankPosifrank==p.flags.bestthenself.bestRank=trueself.flagBest=true-- mark that 'best' flag was givenreturnendifrank:sub(1,9)==p.flags.preferredthenrankPos=1elseifrank:sub(1,6)==p.flags.normalthenrankPos=2elseifrank:sub(1,10)==p.flags.deprecatedthenrankPos=3elsereturnend-- one of the rank flags was given, check if another one was given beforeifnotself.flagRankthenself.ranks={false,false,false}-- no other rank flag given before, so unset ranksself.bestRank=self.flagBest-- unsets bestRank only if 'best' flag was not given beforeself.flagRank=true-- mark that a rank flag was givenendifrank:sub(-1)=="+"thenfori=rankPos,1,-1doself.ranks[i]=trueendelseifrank:sub(-1)=="-"thenfori=rankPos,#self.ranksdoself.ranks[i]=trueendelseself.ranks[rankPos]=trueendendfunctionConfig:setPeriod(period)localperiodPosifperiod==p.flags.futurethenperiodPos=1elseifperiod==p.flags.currentthenperiodPos=2elseifperiod==p.flags.formerthenperiodPos=3elsereturnend-- one of the period flags was given, check if another one was given beforeifnotself.flagPeriodthenself.periods={false,false,false}-- no other period flag given before, so unset periodsself.flagPeriod=true-- mark that a period flag was givenendself.periods[periodPos]=trueendfunctionConfig:qualifierMatches(claim,id,value)localqualifiersifclaim.qualifiersthenqualifiers=claim.qualifiers[id]endifqualifiersthenfor_,vinpairs(qualifiers)doifself:snakEqualsValue(v,value)thenreturntrueendendelseifvalue==""then-- if the qualifier is not present then treat it the same as the special value 'novalue'returntrueendreturnfalseendfunctionConfig:rankMatches(rankPos)ifself.bestRankthenreturn(self.ranks[rankPos]andself.foundRank>=rankPos)elsereturnself.ranks[rankPos]endendfunctionConfig:timeMatches(claim)localstartTime=nillocalstartTimeY=nillocalstartTimeM=nillocalstartTimeD=nillocalendTime=nillocalendTimeY=nillocalendTimeM=nillocalendTimeD=nilifself.periods[1]andself.periods[2]andself.periods[3]then-- any timereturntrueendstartTime=self:getSingleRawQualifier(claim,aliasesP.startTime)ifstartTimeandstartTime~=""andstartTime~=" "thenstartTimeY,startTimeM,startTimeD=parseDate(startTime)endendTime=self:getSingleRawQualifier(claim,aliasesP.endTime)ifendTimeandendTime~=""andendTime~=" "thenendTimeY,endTimeM,endTimeD=parseDate(endTime)endifstartTimeY~=nilandendTimeY~=nilanddatePrecedesDate(endTimeY,endTimeM,endTimeD,startTimeY,startTimeM,startTimeD)then-- invalidate end time if it precedes start timeendTimeY=nilendTimeM=nilendTimeD=nilendifself.periods[1]then-- futureifstartTimeYanddatePrecedesDate(self.atDate[1],self.atDate[2],self.atDate[3],startTimeY,startTimeM,startTimeD)thenreturntrueendendifself.periods[2]then-- currentif(startTimeY==nilornotdatePrecedesDate(self.atDate[1],self.atDate[2],self.atDate[3],startTimeY,startTimeM,startTimeD))and(endTimeY==nilordatePrecedesDate(self.atDate[1],self.atDate[2],self.atDate[3],endTimeY,endTimeM,endTimeD))thenreturntrueendendifself.periods[3]then-- formerifendTimeYandnotdatePrecedesDate(self.atDate[1],self.atDate[2],self.atDate[3],endTimeY,endTimeM,endTimeD)thenreturntrueendendreturnfalseendfunctionConfig:processFlag(flag)ifnotflagthenreturnfalseendifflag==p.flags.linkedthenself.curState.linked=truereturntrueelseifflag==p.flags.rawthenself.curState.rawValue=trueifself.curState==self.states[parameters.reference]then-- raw reference values end with periods and require a separator (other than none)self.separators["sep%r"][1]={" "}endreturntrueelseifflag==p.flags.shortthenself.curState.shortName=truereturntrueelseifflag==p.flags.multilanguagethenself.curState.anyLanguage=truereturntrueelseifflag==p.flags.unitthenself.curState.unitOnly=truereturntrueelseifflag==p.flags.mdythenself.mdyDate=truereturntrueelseifflag==p.flags.singlethenself.singleClaim=truereturntrueelseifflag==p.flags.sourcedthenself.sourcedOnly=truereturntrueelseifflag==p.flags.editthenself.editable=truereturntrueelseifflag==p.flags.editAtEndthenself.editable=trueself.editAtEnd=truereturntrueelseifflag==p.flags.bestorflag:match('^'..p.flags.preferred..'[+-]?$')orflag:match('^'..p.flags.normal..'[+-]?$')orflag:match('^'..p.flags.deprecated..'[+-]?$')thenself:setRank(flag)returntrueelseifflag==p.flags.futureorflag==p.flags.currentorflag==p.flags.formerthenself:setPeriod(flag)returntrueelseifflag==""then-- ignore empty flags and carry onreturntrueelsereturnfalseendendfunctionConfig:processFlagOrCommand(flag)localparam=""ifnotflagthenreturnfalseendifflag==p.claimCommands.propertyorflag==p.claimCommands.propertiesthenparam=parameters.propertyelseifflag==p.claimCommands.qualifierorflag==p.claimCommands.qualifiersthenself.states.qualifiersCount=self.states.qualifiersCount+1param=parameters.qualifier..self.states.qualifiersCountself.separators["sep"..param]={copyTable(defaultSeparators["sep%q\\d"])}elseifflag==p.claimCommands.referenceorflag==p.claimCommands.referencesthenparam=parameters.referenceelsereturnself:processFlag(flag)endifself.states[param]thenreturnfalseend-- create a new state for each commandself.states[param]=State:new(self,param)-- use "%x" as the general parameter nameself.states[param].parsedFormat=parseFormat(parameters.general)-- will be overwritten for param=="%p"-- set the separatorself.states[param].separator=self.separators["sep"..param]-- will be nil for param=="%p", which will be set separatelyifflag==p.claimCommands.propertyorflag==p.claimCommands.qualifierorflag==p.claimCommands.referencethenself.states[param].singleValue=trueendself.curState=self.states[param]returntrueendfunctionConfig:processSeparators(args)localsepfori,vinpairs(self.separators)doifargs[i]thensep=replaceSpecialChars(args[i])ifsep~=""thenself.separators[i][1]={sep}elseself.separators[i][1]=nilendendendendfunctionConfig:setFormatAndSeparators(state,parsedFormat)state.parsedFormat=parsedFormatstate.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 usefunctionState:isSourced(claim)self.conf.prefetchedRefs=self:getReferences(claim)return(#self.conf.prefetchedRefs>0)endfunctionState:resetCaches()-- any prefetched references of the previous claim must not be usedself.conf.prefetchedRefs=nilendfunctionState:claimMatches(claim)localmatches,rankPos-- first of all, reset any cached values used for the previous claimself:resetCaches()-- if a property value was given, check if it matches the claim's property valueifself.conf.propertyValuethenmatches=self.conf:snakEqualsValue(claim.mainsnak,self.conf.propertyValue)elsematches=trueend-- if any qualifier values were given, check if each matches one of the claim's qualifier valuesfori,vinpairs(self.conf.qualifierIDsAndValues)domatches=(matchesandself.conf:qualifierMatches(claim,i,v))end-- check if the claim's rank and time period matchrankPos=rankTable[claim.rank]or4matches=(matchesandself.conf:rankMatches(rankPos)andself.conf:timeMatches(claim))-- if only claims with references must be returned, check if this one has anyifself.conf.sourcedOnlythenmatches=(matchesandself:isSourced(claim))-- prefetches and caches referencesendreturnmatches,rankPosendfunctionState:out()localresult-- collection of arrays with value objectslocalvaluesArray-- array with value objectslocalsep=nil-- value objectlocalout={}-- array with value objectslocalfunctionwalk(formatTable,result)localvaluesArray={}-- array with value objectsfori,vinpairs(formatTable.req)doifnotresult[i]ornotresult[i][1]then-- 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 resultreturn{}endendfor_,vinipairs(formatTable)doifv.paramthenvaluesArray=mergeArrays(valuesArray,result[v.str])elseifv.str~=""thenvaluesArray[#valuesArray+1]={v.str}endifv.childthenvaluesArray=mergeArrays(valuesArray,walk(v.child,result))endendreturnvaluesArrayend-- iterate through the results from back to front, so that we know when to add separatorsfori=#self.results,1,-1doresult=self.results[i]-- if there is already some output, then add the separatorsif#out>0thensep=self.separator[1]-- fixed separatorresult[parameters.separator]={self.movSeparator[1]}-- movable separatorelsesep=nilresult[parameters.separator]={self.puncMark[1]}-- optional punctuation markendvaluesArray=walk(self.parsedFormat,result)if#valuesArray>0thenifsepthenvaluesArray[#valuesArray+1]=sependout=mergeArrays(valuesArray,out)endend-- reset state before next iterationself.results={}returnoutend-- level 1 hookfunctionState:getProperty(claim)localvalue={self:getValue(claim.mainsnak)}-- create one value objectif#value>0thenreturn{value}-- wrap the value object in an array and return itelsereturn{}-- return empty array if there was no valueendend-- level 1 hookfunctionState:getQualifiers(claim,param)localqualifiersifclaim.qualifiersthenqualifiers=claim.qualifiers[self.conf.qualifierIDs[param]]endifqualifiersthen-- iterate through claim's qualifier statements to collect their values;-- return array with multiple value objectsreturnself.conf.states[param]:iterate(qualifiers,{[parameters.general]=hookNames[parameters.qualifier.."\\d"][2],count=1})-- pass qualifier state with level 2 hookelsereturn{}-- return empty arrayendend-- level 2 hookfunctionState:getQualifier(snak)localvalue={self:getValue(snak)}-- create one value objectif#value>0thenreturn{value}-- wrap the value object in an array and return itelsereturn{}-- return empty array if there was no valueendend-- level 1 hookfunctionState:getAllQualifiers(claim,param,result,hooks)localout={}-- array with value objectslocalsep=self.conf.separators["sep"..parameters.qualifier][1]-- value object-- iterate through the output of the separate "qualifier(s)" commandsfori=1,self.conf.states.qualifiersCountdo-- if a hook has not been called yet, call it nowifnotresult[parameters.qualifier..i]thenself:callHook(parameters.qualifier..i,hooks,claim,result)end-- if there is output for this particular "qualifier(s)" command, then add itifresult[parameters.qualifier..i]andresult[parameters.qualifier..i][1]then-- if there is already some output, then add the separatorif#out>0andsepthenout[#out+1]=sependout=mergeArrays(out,result[parameters.qualifier..i])endendreturnoutend-- level 1 hookfunctionState:getReferences(claim)ifself.conf.prefetchedRefsthen-- return references that have been prefetched by isSourcedreturnself.conf.prefetchedRefsendifclaim.referencesthen-- iterate through claim's reference statements to collect their values;-- return array with multiple value objectsreturnself.conf.states[parameters.reference]:iterate(claim.references,{[parameters.general]=hookNames[parameters.reference][2],count=1})-- pass reference state with level 2 hookelsereturn{}-- return empty arrayendend-- level 2 hookfunctionState:getReference(statement)localkey,citeWeb,citeQ,labellocalparams={}localciteParams={['web']={},['q']={}}localciteMismatch={}localuseCite=nillocaluseParams=nillocalvalue=""localref={}localreferenceEmpty=true-- will be set to false if at least one parameter is left unremovedlocalnumAuthorParameters=0localnumAuthorNameStringParameters=0localversion=2-- increment this each time the below logic is changed to avoid conflict errorsifstatement.snaksthen-- don't include "imported from", which is added by a botifstatement.snaks[aliasesP.importedFrom]thenstatement.snaks[aliasesP.importedFrom]=nilend-- don't include "inferred from", which is added by a botifstatement.snaks[aliasesP.inferredFrom]thenstatement.snaks[aliasesP.inferredFrom]=nilend-- don't include "type of reference"ifstatement.snaks[aliasesP.typeOfReference]thenstatement.snaks[aliasesP.typeOfReference]=nilend-- don't include "image" to prevent litteringifstatement.snaks[aliasesP.image]thenstatement.snaks[aliasesP.image]=nilend-- don't include "language" if it is equal to the local oneifself:getReferenceDetail(statement.snaks,aliasesP.language)==self.conf.langNamethenstatement.snaks[aliasesP.language]=nilend-- retrieve all the parametersforiinpairs(statement.snaks)dolabel=""-- multiple authors may be givenifi==aliasesP.authorori==aliasesP.authorNameStringthenparams[i]=self:getReferenceDetails(statement.snaks,i,false,self.linked,true)-- link = true/false, anyLang = trueelseparams[i]={self:getReferenceDetail(statement.snaks,i,false,(self.linkedor(i==aliasesP.statedIn))and(statement.snaks[i][1].datatype~='url'),true)}-- link = true/false, anyLang = trueendif#params[i]==0thenparams[i]=nilelsereferenceEmpty=falseifstatement.snaks[i][1].datatype=='external-id'thenkey="external-id"label=self.conf:getLabel(i)iflabel~=""thenlabel=label.." "endelsekey=iend-- add the parameter to each matching type of citationforjinpairs(citeParams)do-- do so if there was no mismatch with a previous parameterifnotciteMismatch[j]then-- check if this parameter is not mismatching itselfifi18n['cite'][j][key]then-- continue if an option is available in the corresponding cite templateifi18n['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)if(i~=aliasesP.authorandi~=aliasesP.authorNameString)or(i18n['cite'][j][aliasesP.author]~=i18n['cite'][j][aliasesP.authorNameString])thenciteParams[j][i18n['cite'][j][key]]=label..params[i][1]-- to avoid problems with non-author multiple parameters (if existent), the following old code is retainedfork=2,#params[i]dociteParams[j][i18n['cite'][j][key]..k]=label..params[i][k]end-- handle "author" and "author name string" specially if they use the same template parameterelseifi==aliasesP.authorori==aliasesP.authorNameStringthenifparams[aliasesP.author]~=nilthennumAuthorParameters=#params[aliasesP.author]elsenumAuthorParameters=0endifparams[aliasesP.authorNameString]~=nilthennumAuthorNameStringParameters=#params[aliasesP.authorNameString]elsenumAuthorNameStringParameters=0end-- 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 loopif((statement.snaks[aliasesP.author]==nil)==(numAuthorParameters==0))and((statement.snaks[aliasesP.authorNameString]==nil)==(numAuthorNameStringParameters==0))thenfork=1,numAuthorParameters+numAuthorNameStringParametersdoifk<=numAuthorParametersthen-- now handling the authors from the "author" propertyciteParams[j][i18n['cite'][j][aliasesP.author]..k]=label..params[aliasesP.author][k]else-- now handling the authors from "author name string"citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k]=label..params[aliasesP.authorNameString][k-numAuthorParameters]endendendendendelseciteMismatch[j]=trueendendendendend-- get title of general template for citing web referencesciteWeb=split(mw.wikibase.getSitelink(aliasesQ.citeWeb)or"",":")[2]-- split off namespace from front-- get title of template that expands stated-in references into citationsciteQ=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 presentifciteWebandnotciteMismatch['web']andciteParams['web'][i18n['cite']['web'][aliasesP.referenceURL]]andciteParams['web'][i18n['cite']['web'][aliasesP.title]]thenuseCite=citeWebuseParams=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 presentelseifciteQandnotciteMismatch['q']andciteParams['q'][i18n['cite']['q'][aliasesP.statedIn]]then-- we need the raw "stated in" Q-identifier for the this templateciteParams['q'][i18n['cite']['q'][aliasesP.statedIn]]=self:getReferenceDetail(statement.snaks,aliasesP.statedIn,true)-- raw = trueuseCite=citeQuseParams=citeParams['q']endifuseCiteanduseParamsthen-- if this module is being substituted then build a regular template call, otherwise expand the templateifmw.isSubsting()thenfori,vinpairs(useParams)dovalue=value.."|"..i.."="..vendvalue="{{"..useCite..value.."}}"elsevalue=mw.getCurrentFrame():expandTemplate{title=useCite,args=useParams}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 errorelseifnotreferenceEmptythenvalue="<span style=\"color: crimson\">"..errorText("malformed-reference").."</span>"endifvalue~=""thenvalue={value}-- create one value objectifnotself.rawValuethen-- this should become a <ref> tag, so save the reference's hash for latervalue.refHash="wikidata-"..statement.hash.."-v"..(tonumber(i18n['cite']['version'])+version)endref={value}-- wrap the value object in an arrayendendreturnrefend-- gets a detail of one particular type for a referencefunctionState:getReferenceDetail(snaks,dType,raw,link,anyLang)localswitchLang=anyLanglocalvalue=nilifnotsnaks[dType]thenreturnnilend-- if anyLang, first try the local language and otherwise any languagerepeatfor_,vinipairs(snaks[dType])dovalue=self.conf:getValue(v,raw,link,false,anyLangandnotswitchLang,false,true)-- noSpecial = trueifvaluethenbreakendendifvalueornotanyLangthenbreakendswitchLang=notswitchLanguntilanyLangandswitchLangreturnvalueend-- gets the details of one particular type for a referencefunctionState:getReferenceDetails(snaks,dType,raw,link,anyLang)localvalues={}ifnotsnaks[dType]thenreturn{}endfor_,vinipairs(snaks[dType])do-- if nil is returned then it will not be added to the tablevalues[#values+1]=self.conf:getValue(v,raw,link,false,anyLang,false,true)-- noSpecial = trueendreturnvaluesend-- level 1 hookfunctionState:getAlias(object)localvalue=object.valuelocaltitle=nilifvalueandself.linkedthenifself.conf.entityID:sub(1,1)=="Q"thentitle=mw.wikibase.getSitelink(self.conf.entityID)elseifself.conf.entityID:sub(1,1)=="P"thentitle="d:Property:"..self.conf.entityIDendiftitlethenvalue=buildWikilink(title,value)endendvalue={value}-- create one value objectif#value>0thenreturn{value}-- wrap the value object in an array and return itelsereturn{}-- return empty array if there was no valueendend-- level 1 hookfunctionState:getBadge(value)value=self.conf:getLabel(value,self.rawValue,self.linked,self.shortName)ifvalue==""thenvalue=nilendvalue={value}-- create one value objectif#value>0thenreturn{value}-- wrap the value object in an array and return itelsereturn{}-- return empty array if there was no valueendendfunctionState:callHook(param,hooks,statement,result)localvaluesArray,refHash-- call a parameter's hook if it has been defined and if it has not been called beforeifnotresult[param]andhooks[param]thenvaluesArray=self[hooks[param]](self,statement,param,result,hooks)-- array with value objects-- add to the resultif#valuesArray>0thenresult[param]=valuesArrayresult.count=result.count+1elseresult[param]={}-- an empty array to indicate that we've tried this hook alreadyreturntrue-- miss == trueendendreturnfalseend-- iterate through claims, claim's qualifiers or claim's references to collect valuesfunctionState:iterate(statements,hooks,matchHook)matchHook=matchHookoralwaysTruelocalmatches=falselocalrankPos=nillocalresult,gotRequiredfor_,vinipairs(statements)do-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)matches,rankPos=matchHook(self,v)ifmatchesthenresult={count=0}-- collection of arrays with value objectslocalfunctionwalk(formatTable)localmissfori2,v2inpairs(formatTable.req)do-- call a hook, adding its return value to the resultmiss=self:callHook(i2,hooks,v,result)ifmissthen-- we miss a required value for this level, so return falsereturnfalseendifresult.count==hooks.countthen-- we're done if all hooks have been called;-- returning at this point breaks the loopreturntrueendendfor_,v2inipairs(formatTable)doifresult.count==hooks.countthen-- we're done if all hooks have been called;-- returning at this point prevents further childs from being processedreturntrueendifv2.childthenwalk(v2.child)endendreturntrueendgotRequired=walk(self.parsedFormat)-- only append the result if we got values for all required parameters on the root levelifgotRequiredthen-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRankifrankPosandself.conf.foundRank>rankPosthenself.conf.foundRank=rankPosend-- append the resultself.results[#self.results+1]=result-- break if we only need a single valueifself.singleValuethenbreakendendendendreturnself:out()endlocalfunctiongetEntityId(arg,eid,page,allowOmitPropPrefix)localid=nillocalprop=nilifargthenifarg:sub(1,1)==":"thenpage=argeid=nilelseifarg:sub(1,1):upper()=="Q"orarg:sub(1,9):lower()=="property:"orallowOmitPropPrefixtheneid=argpage=nilelseprop=argendendifeidthenifeid:sub(1,9):lower()=="property:"thenid=replaceAlias(mw.text.trim(eid:sub(10)))ifid:sub(1,1):upper()~="P"thenid=""endelseid=replaceAlias(eid)endelseifpagethenifpage:sub(1,1)==":"thenpage=mw.text.trim(page:sub(2))endid=mw.wikibase.getEntityIdForTitle(page)or""endifnotidthenid=mw.wikibase.getEntityIdForCurrentPage()or""endid=id:upper()ifnotmw.wikibase.isValidEntityId(id)thenid=""endreturnid,propendlocalfunctionnextArg(args)localarg=args[args.pointer]ifargthenargs.pointer=args.pointer+1returnmw.text.trim(arg)elsereturnnilendendlocalfunctionclaimCommand(args,funcName)localcfg=Config:new()cfg:processFlagOrCommand(funcName)-- process first command (== function name)locallastArg,parsedFormat,formatParams,claims,valuelocalhooks={count=0}-- set the date if given;-- must come BEFORE processing the flagsifargs[p.args.date]thencfg.atDate={parseDate(args[p.args.date])}cfg.periods={false,true,false}-- change default time constraint to 'current'end-- process flags and commandsrepeatlastArg=nextArg(args)untilnotcfg:processFlagOrCommand(lastArg)-- get the entity ID from either the positional argument, the eid argument or the page argumentcfg.entityID,cfg.propertyID=getEntityId(lastArg,args[p.args.eid],args[p.args.page])ifcfg.entityID==""thenreturn""-- we cannot continue without a valid entity IDendcfg.entity=mw.wikibase.getEntity(cfg.entityID)ifnotcfg.propertyIDthencfg.propertyID=nextArg(args)endcfg.propertyID=replaceAlias(cfg.propertyID)ifnotcfg.entityornotcfg.propertyIDthenreturn""-- we cannot continue without an entity or a property IDendcfg.propertyID=cfg.propertyID:upper()ifnotcfg.entity.claimsornotcfg.entity.claims[cfg.propertyID]thenreturn""-- there is no use to continue without any claimsendclaims=cfg.entity.claims[cfg.propertyID]ifcfg.states.qualifiersCount>0then-- do further processing if "qualifier(s)" command was givenif#args-args.pointer+1>cfg.states.qualifiersCountthen-- claim ID or literal value has been givencfg.propertyValue=nextArg(args)endfori=1,cfg.states.qualifiersCountdo-- check if given qualifier ID is an alias and add itcfg.qualifierIDs[parameters.qualifier..i]=replaceAlias(nextArg(args)or""):upper()endelseifcfg.states[parameters.reference]then-- do further processing if "reference(s)" command was givencfg.propertyValue=nextArg(args)end-- check for special property value 'somevalue' or 'novalue'ifcfg.propertyValuethencfg.propertyValue=replaceSpecialChars(cfg.propertyValue)ifcfg.propertyValue~=""andmw.text.trim(cfg.propertyValue)==""thencfg.propertyValue=" "-- single space represents 'somevalue', whereas empty string represents 'novalue'elsecfg.propertyValue=mw.text.trim(cfg.propertyValue)endend-- parse the desired format, or choose an appropriate formatifargs["format"]thenparsedFormat,formatParams=parseFormat(args["format"])elseifcfg.states.qualifiersCount>0then-- "qualifier(s)" command givenifcfg.states[parameters.property]then-- "propert(y|ies)" command givenparsedFormat,formatParams=parseFormat(formats.propertyWithQualifier)elseparsedFormat,formatParams=parseFormat(formats.qualifier)endelseifcfg.states[parameters.property]then-- "propert(y|ies)" command givenparsedFormat,formatParams=parseFormat(formats.property)else-- "reference(s)" command givenparsedFormat,formatParams=parseFormat(formats.reference)end-- if a "qualifier(s)" command and no "propert(y|ies)" command has been given, make the movable separator a semicolonifcfg.states.qualifiersCount>0andnotcfg.states[parameters.property]thencfg.separators["sep"..parameters.separator][1]={";"}end-- if only "reference(s)" has been given, set the default separator to none (except when raw)ifcfg.states[parameters.reference]andnotcfg.states[parameters.property]andcfg.states.qualifiersCount==0andnotcfg.states[parameters.reference].rawValuethencfg.separators["sep"][1]=nilend-- if exactly one "qualifier(s)" command has been given, make "sep%q" point to "sep%q1" to make them equivalentifcfg.states.qualifiersCount==1thencfg.separators["sep"..parameters.qualifier]=cfg.separators["sep"..parameters.qualifier.."1"]end-- process overridden separator values;-- must come AFTER tweaking the default separatorscfg: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 givenfori,vinpairs(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"ifformatParams[i]orformatParams[i:sub(1,2)]thenhooks[i]=getHookName(i,1)hooks.count=hooks.count+1endend-- 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 givenifformatParams[parameters.qualifier]andcfg.states.qualifiersCount>0thenhooks[parameters.qualifier]=getHookName(parameters.qualifier,1)hooks.count=hooks.count+1end-- 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 hooksifnotcfg.states[parameters.property]thencfg.states[parameters.property]=State:new(cfg,parameters.property)-- if the "single" flag has been given then this state should be equivalent to "property" (singular)ifcfg.singleClaimthencfg.states[parameters.property].singleValue=trueendend-- if the "sourced" flag has been given then create a state for "reference" if it doesn't exist yet, using default values,-- which must exist in order to be able to determine if a claim has any references;-- must come AFTER defining the hooksifcfg.sourcedOnlyandnotcfg.states[parameters.reference]thencfg:processFlagOrCommand(p.claimCommands.reference)-- use singular "reference" to minimize overheadend-- set the parsed format and the separators (and optional punctuation mark);-- must come AFTER creating the additonal statescfg:setFormatAndSeparators(cfg.states[parameters.property],parsedFormat)-- process qualifier matching values, analogous to cfg.propertyValuefori,vinpairs(args)doi=tostring(i)ifi:match('^[Pp]%d+$')oraliasesP[i]thenv=replaceSpecialChars(v)-- check for special qualifier value 'somevalue'ifv~=""andmw.text.trim(v)==""thenv=" "-- single space represents 'somevalue'endcfg.qualifierIDsAndValues[replaceAlias(i):upper()]=vendend-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)claims=sortOnRank(claims)-- then iterate through the claims to collect valuesvalue=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 Wikidataifcfg.editableandvalue~=""thenvalue=value..cfg:getEditIcon()endreturnvalueendlocalfunctiongeneralCommand(args,funcName)localcfg=Config:new()cfg.curState=State:new(cfg)locallastArglocalvalue=nilrepeatlastArg=nextArg(args)untilnotcfg:processFlag(lastArg)-- get the entity ID from either the positional argument, the eid argument or the page argumentcfg.entityID=getEntityId(lastArg,args[p.args.eid],args[p.args.page],true)ifcfg.entityID==""ornotmw.wikibase.entityExists(cfg.entityID)thenreturn""-- we cannot continue without an entityend-- serve according to the given commandiffuncName==p.generalCommands.labelthenvalue=cfg:getLabel(cfg.entityID,cfg.curState.rawValue,cfg.curState.linked,cfg.curState.shortName)elseiffuncName==p.generalCommands.titlethencfg.inSitelinks=trueifcfg.entityID:sub(1,1)=="Q"thenvalue=mw.wikibase.getSitelink(cfg.entityID)endifcfg.curState.linkedandvaluethenvalue=buildWikilink(value)endelseiffuncName==p.generalCommands.descriptionthenvalue=mw.wikibase.getDescription(cfg.entityID)elselocalparsedFormat,formatParamslocalhooks={count=0}cfg.entity=mw.wikibase.getEntity(cfg.entityID)iffuncName==p.generalCommands.aliasorfuncName==p.generalCommands.badgethencfg.curState.singleValue=trueendiffuncName==p.generalCommands.aliasorfuncName==p.generalCommands.aliasesthenifnotcfg.entity.aliasesornotcfg.entity.aliases[cfg.langCode]thenreturn""-- there is no use to continue without any aliassesendlocalaliases=cfg.entity.aliases[cfg.langCode]-- parse the desired format, or parse the default aliases formatifargs["format"]thenparsedFormat,formatParams=parseFormat(args["format"])elseparsedFormat,formatParams=parseFormat(formats.alias)end-- process overridden separator values;-- must come AFTER tweaking the default separatorscfg:processSeparators(args)-- define the hook that should be called (getAlias);-- only define the hook if the parameter ("%a") has been givenifformatParams[parameters.alias]thenhooks[parameters.alias]=getHookName(parameters.alias,1)hooks.count=hooks.count+1end-- set the parsed format and the separators (and optional punctuation mark)cfg:setFormatAndSeparators(cfg.curState,parsedFormat)-- iterate to collect valuesvalue=cfg:concatValues(cfg.curState:iterate(aliases,hooks))elseiffuncName==p.generalCommands.badgeorfuncName==p.generalCommands.badgesthenifnotcfg.entity.sitelinksornotcfg.entity.sitelinks[cfg.siteID]ornotcfg.entity.sitelinks[cfg.siteID].badgesthenreturn""-- there is no use to continue without any badgesendlocalbadges=cfg.entity.sitelinks[cfg.siteID].badgescfg.inSitelinks=true-- parse the desired format, or parse the default aliases formatifargs["format"]thenparsedFormat,formatParams=parseFormat(args["format"])elseparsedFormat,formatParams=parseFormat(formats.badge)end-- process overridden separator values;-- must come AFTER tweaking the default separatorscfg:processSeparators(args)-- define the hook that should be called (getBadge);-- only define the hook if the parameter ("%b") has been givenifformatParams[parameters.badge]thenhooks[parameters.badge]=getHookName(parameters.badge,1)hooks.count=hooks.count+1end-- set the parsed format and the separators (and optional punctuation mark)cfg:setFormatAndSeparators(cfg.curState,parsedFormat)-- iterate to collect valuesvalue=cfg:concatValues(cfg.curState:iterate(badges,hooks))endendvalue=valueor""ifcfg.editableandvalue~=""then-- if desired, add a clickable icon that may be used to edit the returned value on Wikidatavalue=value..cfg:getEditIcon()endreturnvalueend-- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)localfunctionestablishCommands(commandList,commandFunc)for_,commandNameinpairs(commandList)dolocalfunctionwikitextWrapper(frame)localargs=copyTable(frame.args)args.pointer=1loadI18n(aliasesP,frame)returncommandFunc(args,commandName)endp[commandName]=wikitextWrapperlocalfunctionluaWrapper(args)args=copyTable(args)args.pointer=1loadI18n(aliasesP)returncommandFunc(args,commandName)endp["_"..commandName]=luaWrapperendendestablishCommands(p.claimCommands,claimCommand)establishCommands(p.generalCommands,generalCommand)-- main function that is supposed to be used by wrapper templatesfunctionp.main(frame)ifnotmw.wikibasethenreturnnilendlocalf,argsloadI18n(aliasesP,frame)-- get the parent frame to take the arguments that were passed to the wrapper templateframe=frame:getParent()orframeifnotframe.args[1]thenthrowError("no-function-specified")endf=mw.text.trim(frame.args[1])iff=="main"thenthrowError("main-called-twice")endassert(p["_"..f],errorText('no-such-function',f))-- copy arguments from immutable to mutable tableargs=copyTable(frame.args)-- remove the function name from the listtable.remove(args,1)returnp["_"..f](args)endreturnp