Module:WikidataIB/lite

-- Version: 2021-01-03

-- Module to implement getValue from Module:WikidataIB attempting to minimise resource use

local p = {}

local cdate -- initialise as nil and only load _complex_date function if needed

-- Module:Complex date is loaded lazily and has the following dependencies:

-- Module:I18n/complex date, Module:ISOdate, Module:DateI18n (alternative for Module:Date),

-- Module:Formatnum, Module:I18n/date, Module:Yesno, Module:Linguistic, Module:Calendar

-- The following, taken from https://www.mediawiki.org/wiki/Wikibase/DataModel#Dates_and_times,

-- is needed to use Module:Complex date which seemingly requires date precision as a string.

-- It would work better if only the authors of the mediawiki page could spell 'millennium'.

local dp = {

[6] = "millennium",

[7] = "century",

[8] = "decade",

[9] = "year",

[10] = "month",

[11] = "day",

}

local i18n =

{

["errors"] =

{

["property-not-found"] = "Property not found.",

["No property supplied"] = "No property supplied",

["entity-not-found"] = "Wikidata entity not found.",

["unknown-claim-type"] = "Unknown claim type.",

["unknown-entity-type"] = "Unknown entity type.",

["qualifier-not-found"] = "Qualifier not found.",

["site-not-found"] = "Wikimedia project not found.",

["labels-not-found"] = "No labels found.",

["descriptions-not-found"] = "No descriptions found.",

["aliases-not-found"] = "No aliases found.",

["unknown-datetime-format"] = "Unknown datetime format.",

["local-article-not-found"] = "Article is available on Wikidata, but not on Wikipedia",

["dab-page"] = " (dab)",

},

["months"] =

{

"January", "February", "March", "April", "May", "June",

"July", "August", "September", "October", "November", "December"

},

["century"] = "century",

["BC"] = "BC",

["BCE"] = "BCE",

["ordinal"] =

{

[1] = "st",

[2] = "nd",

[3] = "rd",

["default"] = "th"

},

["filespace"] = "File",

["Unknown"] = "Unknown",

["NaN"] = "Not a number",

-- set the following to the name of a tracking category,

-- e.g. "Category:Articles with missing Wikidata information", or "" to disable:

["missinginfocat"] = "Category:Articles with missing Wikidata information",

["editonwikidata"] = "Edit this on Wikidata",

["latestdatequalifier"] = function (date) return "before " .. date end,

-- some languages, e.g. Bosnian use a period as a suffix after each number in a date

["datenumbersuffix"] = "",

["list separator"] = ", ",

["multipliers"] = {

[0] = "",

[3] = " thousand",

[6] = " million",

[9] = " billion",

[12] = " trillion",

}

}

-- This allows an internationisation module to override the above table

if 'en' ~= mw.getContentLanguage():getCode() then

require("Module:i18n").loadI18n("Module:WikidataIB/i18n", i18n)

end

-- This piece of html implements a collapsible container. Check the classes exist on your wiki.

local collapsediv = '

'

-- Some items should not be linked.

-- Each wiki can create a list of those in Module:WikidataIB/nolinks

-- It should return a table called itemsindex, containing true for each item not to be linked

local donotlink = {}

local nolinks_exists, nolinks = pcall(mw.loadData, "Module:WikidataIB/nolinks")

if nolinks_exists then

donotlink = nolinks.itemsindex

end

-------------------------------------------------------------------------------

-- Private functions

-------------------------------------------------------------------------------

--

-------------------------------------------------------------------------------

-- findLang takes a "langcode" parameter if supplied and valid

-- otherwise it tries to create it from the user's set language ({{int:lang}})

-- failing that it uses the wiki's content language.

-- It returns a language object

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local findLang = function(langcode)

local langobj

langcode = mw.text.trim(langcode or "")

if mw.language.isKnownLanguageTag(langcode) then

langobj = mw.language.new( langcode )

else

langcode = mw.getCurrentFrame():callParserFunction('int', {'lang'})

if mw.language.isKnownLanguageTag(langcode) then

langobj = mw.language.new( langcode )

else

langobj = mw.language.getContentLanguage()

end

end

return langobj

end

-------------------------------------------------------------------------------

-- roundto takes a number (x)

-- and returns it rounded to (sf) significant figures

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local roundto = function(x, sf)

if x == 0 then return 0 end

local s = 1

if x < 0 then

x = -x

s = -1

end

if sf < 1 then sf = 1 end

local p = 10 ^ (math.floor(math.log10(x)) - sf + 1)

x = math.floor(x / p + 0.5) * p * s

-- if it's integral, cast to an integer:

if x == math.floor(x) then x = math.floor(x) end

return x

end

-------------------------------------------------------------------------------

-- decimalToDMS takes a decimal degrees (x) with precision (p)

-- and returns degrees/minutes/seconds according to the precision

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local decimalToDMS = function(x, p)

-- if p is not supplied, use a precision around 0.1 seconds

if not tonumber(p) then p = 1e-4 end

local d = math.floor(x)

local ms = (x - d) * 60

if p > 0.5 then -- precision is > 1/2 a degree

if ms > 30 then d = d + 1 end

ms = 0

end

local m = math.floor(ms)

local s = (ms - m) * 60

if p > 0.008 then -- precision is > 1/2 a minute

if s > 30 then m = m +1 end

s = 0

elseif p > 0.00014 then -- precision is > 1/2 a second

s = math.floor(s + 0.5)

elseif p > 0.000014 then -- precision is > 1/20 second

s = math.floor(10 * s + 0.5) / 10

elseif p > 0.0000014 then -- precision is > 1/200 second

s = math.floor(100 * s + 0.5) / 100

else -- cap it at 3 dec places for now

s = math.floor(1000 * s + 0.5) / 1000

end

return d, m, s

end

-------------------------------------------------------------------------------

-- decimalPrecision takes a decimal (x) with precision (p)

-- and returns x rounded approximately to the given precision

-- precision should be between 1 and 1e-6, preferably a power of 10.

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local decimalPrecision = function(x, p)

local s = 1

if x < 0 then

x = -x

s = -1

end

-- if p is not supplied, pick an arbitrary precision

if not tonumber(p) then p = 1e-4

elseif p > 1 then p = 1

elseif p < 1e-6 then p = 1e-6

else p = 10 ^ math.floor(math.log10(p))

end

x = math.floor(x / p + 0.5) * p * s

-- if it's integral, cast to an integer:

if x == math.floor(x) then x = math.floor(x) end

-- if it's less than 1e-4, it will be in exponent form, so return a string with 6dp

-- 9e-5 becomes 0.000090

if math.abs(x) < 1e-4 then x = string.format("%f", x) end

return x

end

-------------------------------------------------------------------------------

-- dateFormat is the handler for properties that are of type "time"

-- It takes timestamp, precision (6 to 11 per mediawiki), dateformat (y/dmy/mdy), BC format (BC/BCE),

-- a plaindate switch (yes/no/adj) to en/disable "sourcing circumstances"/use adjectival form,

-- any qualifiers for the property, the language, and any adjective to use like 'before'.

-- It passes the date through the "complex date" function

-- and returns a string with the internatonalised date formatted according to preferences.

-------------------------------------------------------------------------------

-- Dependencies: findLang(); cdate(); dp[]

-------------------------------------------------------------------------------

local dateFormat = function(timestamp, dprec, df, bcf, pd, qualifiers, lang, adj, model)

-- output formatting according to preferences (y/dmy/mdy/ymd)

df = (df or ""):lower()

-- if ymd is required, return the part of the timestamp in YYYY-MM-DD form

-- but apply Year zero#Astronomers fix: 1 BC = 0000; 2 BC = -0001; etc.

if df == "ymd" then

if timestamp:sub(1,1) == "+" then

return timestamp:sub(2,11)

else

local yr = tonumber(timestamp:sub(2,5)) - 1

yr = ("000" .. yr):sub(-4)

if yr ~= "0000" then yr = "-" .. yr end

return yr .. timestamp:sub(6,11)

end

end

-- A year can be stored like this: "+1872-00-00T00:00:00Z",

-- which is processed here as if it were the day before "+1872-01-01T00:00:00Z",

-- and that's the last day of 1871, so the year is wrong.

-- So fix the month 0, day 0 timestamp to become 1 January instead:

timestamp = timestamp:gsub("%-00%-00T", "-01-01T")

-- just in case date precision is missing

dprec = dprec or 11

-- override more precise dates if required dateformat is year alone:

if df == "y" and dprec > 9 then dprec = 9 end

-- complex date only deals with precisions from 6 to 11, so clip range

dprec = dprec>11 and 11 or dprec

dprec = dprec<6 and 6 or dprec

-- BC format is "BC" or "BCE"

bcf = (bcf or ""):upper()

-- plaindate only needs the first letter (y/n/a)

pd = (pd or ""):sub(1,1):lower()

if pd == "" or pd == "n" or pd == "f" or pd == "0" then pd = false end

-- in case language isn't passed

lang = lang or findLang().code

-- set adj as empty if nil

adj = adj or ""

-- extract the day, month, year from the timestamp

local bc = timestamp:sub(1, 1)=="-" and "BC" or ""

local year, month, day = timestamp:match("[+-](%d*)-(%d*)-(%d*)T")

local iso = tonumber(year) -- if year is missing, let it throw an error

-- this will adjust the date format to be compatible with cdate

-- possible formats are Y, YY, YYY0, YYYY, YYYY-MM, YYYY-MM-DD

if dprec == 6 then iso = math.floor( (iso - 1) / 1000 ) + 1 end

if dprec == 7 then iso = math.floor( (iso - 1) / 100 ) + 1 end

if dprec == 8 then iso = math.floor( iso / 10 ) .. "0" end

if dprec == 10 then iso = year .. "-" .. month end

if dprec == 11 then iso = year .. "-" .. month .. "-" .. day end

-- add "circa" (Q5727902) from "sourcing circumstances" (P1480)

local sc = not pd and qualifiers and qualifiers.P1480

if sc then

for k1, v1 in pairs(sc) do

if v1.datavalue and v1.datavalue.value.id == "Q5727902" then

adj = "circa"

break

end

end

end

-- deal with Julian dates:

-- no point in saying that dates before 1582 are Julian - they are by default

-- doesn't make sense for dates less precise than year

-- we can suppress it by setting |plaindate, e.g. for use in constructing categories.

local calendarmodel = ""

if tonumber(year) > 1582

and dprec > 8

and not pd

and model == "http://www.wikidata.org/entity/Q1985786" then

calendarmodel = "julian"

end

if not cdate then

cdate = require("Module:Complex date")._complex_date

end

local fdate = cdate(calendarmodel, adj, tostring(iso), dp[dprec], bc, "", "", "", "", lang, 1)

-- this may have QuickStatements info appended to it in a div, so remove that

fdate = fdate:gsub('

[^<]*
', '')

-- it may also be returned wrapped in a microformat, so remove that

fdate = fdate:gsub("<[^>]*>", "")

-- there may be leading zeros that we should remove

fdate = fdate:gsub("^0*", "")

-- if a plain date is required, then remove any links (like BC linked)

if pd then

fdate = fdate:gsub("%[%[.*|", ""):gsub("]]", "")

end

-- if 'circa', use the abbreviated form *** internationalise later ***

fdate = fdate:gsub('circa ', 'c. ')

-- deal with BC/BCE

if bcf == "BCE" then

fdate = fdate:gsub('BC', 'BCE')

end

-- deal with mdy format

if df == "mdy" then

fdate = fdate:gsub("(%d+) (%w+) (%d+)", "%2 %1, %3")

end

-- deal with adjectival form *** internationalise later ***

if pd == "a" then

fdate = fdate:gsub(' century', '-century')

end

return fdate

end

-------------------------------------------------------------------------------

-- parseParam takes a (string) parameter, e.g. from the list of frame arguments,

-- and makes "false", "no", and "0" into the (boolean) false

-- it makes the empty string and nil into the (boolean) value passed as default

-- allowing the parameter to be true or false by default.

-- It returns a boolean.

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local parseParam = function(param, default)

if type(param) == "boolean" then param = tostring(param) end

if param and param ~= "" then

param = param:lower()

if (param == "false") or (param:sub(1,1) == "n") or (param == "0") then

return false

else

return true

end

else

return default

end

end

-------------------------------------------------------------------------------

-- The label in a Wikidata item is subject to vulnerabilities

-- that an attacker might try to exploit.

-- It needs to be 'sanitised' by removing any wikitext before use.

-- If it doesn't exist, return the id for the item

-- a second (boolean) value is also returned, value is true when the label exists

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local labelOrId = function(id, lang)

if lang == "default" then lang = findLang().code end

local label

if lang then

label = mw.wikibase.getLabelByLang(id, lang)

else

label = mw.wikibase.getLabel(id)

end

if label then

return mw.text.nowiki(label), true

else

return id, false

end

end

-------------------------------------------------------------------------------

-- linkedItem takes an entity-id and returns a string, linked if possible.

-- This is the handler for "wikibase-item". Preferences:

-- 1. Display linked disambiguated sitelink if it exists

-- 2. Display linked label if it is a redirect

-- 3. TBA: Display an inter-language link for the label if it exists other than in default language

-- 4. Display unlinked label if it exists

-- 5. Display entity-id for now to indicate a label could be provided

-- dtxt is text to be used instead of label, or nil.

-- lang is the current language code.

-- uselbl is boolean switch to force display of the label instead of the sitelink (default: false)

-- linkredir is boolean switch to allow linking to a redirect (default: false)

-- formatvalue is boolean switch to allow formatting as italics or quoted (default: false)

-------------------------------------------------------------------------------

-- Dependencies: labelOrId(); donotlink[]

-------------------------------------------------------------------------------

local linkedItem = function(id, args)

local lprefix = args.lprefix or "" -- toughen against nil values passed

local lpostfix = args.lpostfix or ""

local prefix = args.prefix or ""

local postfix = args.postfix or ""

local dtxt = args.dtxt

local lang = args.lang or "en" -- fallback to default if missing

local uselbl = args.uselabel or args.uselbl

uselbl = parseParam(uselbl, false)

local linkredir = args.linkredir

linkredir = parseParam(linkredir, false)

local disp

local sitelink = mw.wikibase.getSitelink(id)

local label, islabel

if dtxt then

label, islabel = dtxt, true

else

label, islabel = labelOrId(id)

end

if mw.site.siteName ~= "Wikimedia Commons" then

if sitelink then

if not dtxt then

-- if sitelink and label are the same except for case, no need to process further

if sitelink:lower() ~= label:lower() then

-- strip any namespace or dab from the sitelink

local pos = sitelink:find(":") or 0

local slink = sitelink

if pos > 0 then

local pfx = sitelink:sub(1,pos-1)

if mw.site.namespaces[pfx] then -- that prefix is a valid namespace, so remove it

slink = sitelink:sub(pos+1)

end

end

-- remove stuff after commas or inside parentheses - ie. dabs

slink = slink:gsub("%s%(.+%)$", ""):gsub(",.+$", "")

-- if uselbl is false, use sitelink instead of label

if not uselbl then

-- use slink as display, preserving label case - find("^%u") is true for 1st char uppercase

if label:find("^%u") then

label = slink:gsub("^(%l)", string.upper)

else

label = slink:gsub("^(%u)", string.lower)

end

end

end

end

if donotlink[label] then

disp = prefix .. label .. postfix

else

disp = "" .. prefix .. label .. postfix .. ""

end

elseif islabel then

-- no sitelink, label exists, so check if a redirect with that title exists, if linkredir is true

-- display plain label by default

disp = prefix .. label .. postfix

if linkredir then

local artitle = mw.title.new(label, 0) -- only nil if label has invalid chars

if not donotlink[label] and artitle and artitle.redirectTarget then

-- there's a redirect with the same title as the label, so let's link to that

disp = "" .. prefix .. label .. postfix .. ""

end

end -- test if article title exists as redirect on current Wiki

else

-- no sitelink and no label, so return whatever was returned from labelOrId for now

-- add tracking category Category:Articles with missing Wikidata information

-- for enwiki, just return the tracking category

if mw.wikibase.getGlobalSiteId() == "enwiki" then

disp = i18n.missinginfocat

else

disp = prefix .. label .. postfix .. i18n.missinginfocat

end

end

else

local ccat = mw.wikibase.getBestStatements(id, "P373")[1]

if ccat and ccat.mainsnak.datavalue then

ccat = ccat.mainsnak.datavalue.value

disp = "" .. prefix .. label .. postfix .. ""

elseif sitelink then

-- this asumes that if a sitelink exists, then a label also exists

disp = "" .. prefix .. label .. postfix .. ""

else

-- no sitelink and no Commons cat, so return label from labelOrId for now

disp = prefix .. label .. postfix

end

end

return disp

end

-------------------------------------------------------------------------------

-- sourced takes a table representing a statement that may or may not have references

-- it looks for a reference sourced to something not containing the word "wikipedia"

-- it returns a boolean = true if it finds a sourced reference.

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local sourced = function(claim)

if claim.references then

for kr, vr in pairs(claim.references) do

local ref = mw.wikibase.renderSnaks(vr.snaks)

if not ref:find("Wiki") then

return true

end

end

end

end

-------------------------------------------------------------------------------

-- setRanks takes a flag (parameter passed) that requests the values to return

-- "b[est]" returns preferred if available, otherwise normal

-- "p[referred]" returns preferred

-- "n[ormal]" returns normal

-- "d[eprecated]" returns deprecated

-- multiple values are allowed, e.g. "preferred normal" (which is the default)

-- "best" will override the other flags, and set p and n

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local setRanks = function(rank)

rank = (rank or ""):lower()

-- if nothing passed, return preferred and normal

-- if rank == "" then rank = "p n" end

local ranks = {}

for w in string.gmatch(rank, "%a+") do

w = w:sub(1,1)

if w == "b" or w == "p" or w == "n" or w == "d" then

ranks[w] = true

end

end

-- check if "best" is requested or no ranks requested; and if so, set preferred and normal

if ranks.b or not next(ranks) then

ranks.p = true

ranks.n = true

end

return ranks

end

-------------------------------------------------------------------------------

-- parseInput processes the Q-id , the blacklist and the whitelist

-- if an input parameter is supplied, it returns that and ends the call.

-- it returns (1) either the qid or nil indicating whether or not the call should continue

-- and (2) a table containing all of the statements for the propertyID and relevant Qid

-- if "best" ranks are requested, it returns those instead of all non-deprecated ranks

-------------------------------------------------------------------------------

-- Dependencies: none

-------------------------------------------------------------------------------

local parseInput = function(frame, input_parm, property_id)

-- There may be a local parameter supplied, if it's blank, set it to nil

input_parm = mw.text.trim(input_parm or "")

if input_parm == "" then input_parm = nil end

-- return nil if Wikidata is not available

if not mw.wikibase then return false, input_parm end

local args = frame.args

-- can take a named parameter |qid which is the Wikidata ID for the article.

-- if it's not supplied, use the id for the current page

local qid = args.qid or ""

if qid == "" then qid = mw.wikibase.getEntityIdForCurrentPage() end

-- if there's no Wikidata item for the current page return nil

if not qid then return false, input_parm end

-- The blacklist is passed in named parameter |suppressfields

local blacklist = args.suppressfields or args.spf or ""

-- The whitelist is passed in named parameter |fetchwikidata

local whitelist = args.fetchwikidata or args.fwd or ""

if whitelist == "" then whitelist = "NONE" end

-- The name of the field that this function is called from is passed in named parameter |name

local fieldname = args.name or ""

if blacklist ~= "" then

-- The name is compulsory when blacklist is used, so return nil if it is not supplied

if fieldname == "" then return false, nil end

-- If this field is on the blacklist, then return nil

if blacklist:find(fieldname) then return false, nil end

end

-- If we got this far then we're not on the blacklist

-- The blacklist overrides any locally supplied parameter as well

-- If a non-blank input parameter was supplied return it

if input_parm then return false, input_parm end

-- We can filter out non-valid properties

if property_id:sub(1,1):upper() ~="P" or property_id == "P0" then return false, nil end

-- Otherwise see if this field is on the whitelist:

-- needs a bit more logic because find will return its second value = 0 if fieldname is ""

-- but nil if fieldname not found on whitelist

local _, found = whitelist:find(fieldname)

found = ((found or 0) > 0)

if whitelist ~= 'ALL' and (whitelist:upper() == "NONE" or not found) then

return false, nil

end

-- See what's on Wikidata (the call always returns a table, but it may be empty):

local props = {}

if args.reqranks.b then

props = mw.wikibase.getBestStatements(qid, property_id)

else

props = mw.wikibase.getAllStatements(qid, property_id)

end

if props[1] then

return qid, props

end

-- no property on Wikidata

return false, nil

end

-------------------------------------------------------------------------------

-- createicon assembles the "Edit at Wikidata" pen icon.

-- It returns a wikitext string inside a span class="penicon"

-- if entityID is nil or empty, the ID associated with current page is used

-- langcode and propertyID may be nil or empty

-------------------------------------------------------------------------------

-- Dependencies: i18n[];

-------------------------------------------------------------------------------

local createicon = function(langcode, entityID, propertyID)

langcode = langcode or ""

if not entityID or entityID == "" then entityID= mw.wikibase.getEntityIdForCurrentPage() end

propertyID = propertyID or ""

local icon = " [["

-- " [[" -> enable Wikidata Bridge

.. i18n["filespace"]

.. ":OOjs UI icon edit-ltr-progressive.svg |frameless |text-top |10px |alt="

.. i18n["editonwikidata"]

.. "|link=https://www.wikidata.org/wiki/" .. entityID

if langcode ~= "" then icon = icon .. "?uselang=" .. langcode end

if propertyID ~= "" then icon = icon .. "#" .. propertyID end

icon = icon .. "|" .. i18n["editonwikidata"] .. "]]"

return icon

end

-------------------------------------------------------------------------------

-- assembleoutput takes the sequence table containing the property values

-- and formats it according to switches given. It returns a string or nil.

-- It uses the entityID (and optionally propertyID) to create a link in the pen icon.

-------------------------------------------------------------------------------

-- Dependencies: parseParam();

-------------------------------------------------------------------------------

local assembleoutput = function(out, args, entityID, propertyID)

-- sorted is a boolean passed to enable sorting of the values returned

-- if nothing or an empty string is passed set it false

-- if "false" or "no" or "0" is passed set it false

local sorted = parseParam(args.sorted, false)

-- noicon is a boolean passed to suppress the trailing "edit at Wikidata" icon

-- for use when the value is processed further by the infobox

-- if nothing or an empty string is passed set it false

-- if "false" or "no" or "0" is passed set it false

local noic = parseParam(args.noicon, false)

-- list is the name of a template that a list of multiple values is passed through

-- examples include "hlist" and "ubl"

-- setting it to "prose" produces something like "1, 2, 3, and 4"

local list = args.list or ""

-- sep is a string that is used to separate multiple returned values

-- if nothing or an empty string is passed set it to the default

-- any double-quotes " are stripped out, so that spaces may be passed

-- e.g. |sep=" - "

local sepdefault = i18n["list separator"]

local separator = args.sep or ""

separator = string.gsub(separator, '"', '')

if separator == "" then

separator = sepdefault

end

-- collapse is a number that determines the maximum number of returned values

-- before the output is collapsed.

-- Zero or not a number result in no collapsing (default becomes 0).

local collapse = tonumber(args.collapse) or 0

-- replacetext (rt) is a string that is returned instead of any non-empty Wikidata value

-- this is useful for tracking and debugging

local replacetext = mw.text.trim(args.rt or args.replacetext or "")

-- if there's anything to return, then return a list

-- comma-separated by default, but may be specified by the sep parameter

-- optionally specify a hlist or ubl or a prose list, etc.

local strout

if #out > 0 then

if sorted then table.sort(out) end

-- if there's something to display and a pen icon is wanted, add it the end of the last value

local hasdisplay = false

for i, v in ipairs(out) do

if v ~= i18n.missinginfocat then

hasdisplay = true

break

end

end

if not noic and hasdisplay then

out[#out] = out[#out] .. createicon(args.langobj.code, entityID, propertyID)

end

if list == "" then

strout = table.concat(out, separator)

elseif list:lower() == "prose" then

strout = mw.text.listToText( out )

else

strout = mw.getCurrentFrame():expandTemplate{title = list, args = out}

end

if collapse >0 and #out > collapse then

strout = collapsediv .. strout .. "

"

end

else

strout = nil -- no items had valid reference

end

if replacetext ~= "" and strout then strout = replacetext end

return strout

end

-------------------------------------------------------------------------------

-- rendersnak takes a table (propval) containing the information stored on one property value

-- and returns the value as a string and its language if monolingual text.

-- It handles data of type:

-- wikibase-item

-- time

-- string, url, commonsMedia, external-id

-- quantity

-- globe-coordinate

-- monolingualtext

-- It also requires linked, the link/pre/postfixes, uabbr, and the arguments passed from frame.

-- The optional filter parameter allows quantities to be be filtered by unit Qid.

-------------------------------------------------------------------------------

-- Dependencies: parseParam(); labelOrId(); i18n[]; dateFormat();

-- roundto(); decimalPrecision(); decimalToDMS(); linkedItem();

-------------------------------------------------------------------------------

local rendersnak = function(propval, args, linked, lpre, lpost, pre, post, uabbr, filter)

lpre = lpre or ""

lpost = lpost or ""

pre = pre or ""

post = post or ""

args.lang = args.lang or findLang().code

-- allow values to display a fixed text instead of label

local dtxt = args.displaytext or args.dt

if dtxt == "" then dtxt = nil end

-- switch to use display of short name (P1813) instead of label

local snak = propval.mainsnak or propval

local dtype = snak.datatype

local dv = snak.datavalue

dv = dv and dv.value

-- value and monolingual text language code returned

local val, mlt

if propval.rank and not args.reqranks[propval.rank:sub(1, 1)] then

-- val is nil: value has a rank that isn't requested

------------------------------------

elseif snak.snaktype == "somevalue" then -- value is unknown

val = i18n["Unknown"]

------------------------------------

elseif snak.snaktype == "novalue" then -- value is none

-- val = "No value" -- don't return anything

------------------------------------

elseif dtype == "wikibase-item" then -- data type is a wikibase item:

-- it's wiki-linked value, so output as link if enabled and possible

local qnumber = dv.id

if linked then

val = linkedItem(qnumber, args)

else -- no link wanted so check for display-text, otherwise test for lang code

local label, islabel

if dtxt then

label = dtxt

else

label, islabel = labelOrId(qnumber)

local langlabel = mw.wikibase.getLabelByLang(qnumber, args.lang)

if langlabel then

label = mw.text.nowiki( langlabel )

end

end

val = pre .. label .. post

end -- test for link required

------------------------------------

elseif dtype == "time" then -- data type is time:

-- time is in timestamp format

-- date precision is integer per mediawiki

-- output formatting according to preferences (y/dmy/mdy)

-- BC format as BC or BCE

-- plaindate is passed to disable looking for "sourcing cirumstances"

-- or to set the adjectival form

-- qualifiers (if any) is a nested table or nil

-- lang is given, or user language, or site language

--

-- Here we can check whether args.df has a value

-- If not, use code from Module:Sandbox/RexxS/Getdateformat to set it from templates like {{Use mdy dates}}

val = dateFormat(dv.time, dv.precision, args.df, args.bc, args.pd, propval.qualifiers, args.lang, "", dv.calendarmodel)

------------------------------------

-- data types which are strings:

elseif dtype == "commonsMedia" or dtype == "external-id" or dtype == "string" or dtype == "url" then

-- commonsMedia or external-id or string or url

-- all have mainsnak.datavalue.value as string

if (lpre == "" or lpre == ":") and lpost == "" then

-- don't link if no linkpre/postfix or linkprefix is just ":"

val = pre .. dv .. post

elseif dtype == "external-id" then

val = "[" .. lpre .. dv .. lpost .. " " .. pre .. dv .. post .. "]"

else

val = "" .. pre .. dv .. post .. ""

end -- check for link requested (i.e. either linkprefix or linkpostfix exists)

------------------------------------

-- data types which are quantities:

elseif dtype == "quantity" then

-- quantities have mainsnak.datavalue.value.amount and mainsnak.datavalue.value.unit

-- the unit is of the form http://www.wikidata.org/entity/Q829073

--

-- implement a switch to turn on/off numerical formatting later

local fnum = true

--

-- a switch to turn on/off conversions - only for en-wiki

local conv = parseParam(args.conv or args.convert, false)

-- if we have conversions, we won't have formatted numbers or scales

if conv then

uabbr = true

fnum = false

args.scale = "0"

end

--

-- a switch to turn on/off showing units, default is true

local showunits = parseParam(args.su or args.showunits, true)

--

-- convert amount to a number

local amount = tonumber(dv.amount) or i18n["NaN"]

--

-- scale factor for millions, billions, etc.

local sc = tostring(args.scale or ""):sub(1,1):lower()

local scale

if sc == "a" then

-- automatic scaling

if amount > 1e15 then

scale = 12

elseif amount > 1e12 then

scale = 9

elseif amount > 1e9 then

scale = 6

elseif amount > 1e6 then

scale = 3

else

scale = 0

end

else

scale = tonumber(args.scale) or 0

if scale < 0 or scale > 12 then scale = 0 end

scale = math.floor(scale/3) * 3

end

local factor = 10^scale

amount = amount / factor

-- ranges:

local range = ""

-- check if upper and/or lower bounds are given and significant

local upb = tonumber(dv.upperBound)

local lowb = tonumber(dv.lowerBound)

if upb and lowb then

-- differences rounded to 2 sig fig:

local posdif = roundto(upb - amount, 2) / factor

local negdif = roundto(amount - lowb, 2) / factor

upb, lowb = amount + posdif, amount - negdif

-- round scaled numbers to integers or 4 sig fig

if (scale > 0 or sc == "a") then

if amount < 1e4 then

amount = roundto(amount, 4)

else

amount = math.floor(amount + 0.5)

end

end

if fnum then amount = args.langobj:formatNum( amount ) end

if posdif ~= negdif then

-- non-symmetrical

range = " +" .. posdif .. " -" .. negdif

elseif posdif ~= 0 then

-- symmetrical and non-zero

range = " ±" .. posdif

else

-- otherwise range is zero, so leave it as ""

end

else

-- round scaled numbers to integers or 4 sig fig

if (scale > 0 or sc == "a") then

if amount < 1e4 then

amount = roundto(amount, 4)

else

amount = math.floor(amount + 0.5)

end

end

if fnum then amount = args.langobj:formatNum( amount ) end

end

-- unit names and symbols:

-- extract the qid in the form 'Qnnn' from the value.unit url

-- and then fetch the label from that - or symbol if unitabbr is true

local unit = ""

local usep = ""

local usym = ""

local unitqid = string.match( dv.unit, "(Q%d+)" )

if filter and unitqid ~= filter then return nil end

if unitqid and showunits then

local uname = mw.wikibase.getLabelByLang(unitqid, args.lang) or ""

if uname ~= "" then usep, unit = " ", uname end

if uabbr then

-- see if there's a unit symbol (P5061)

local unitsymbols = mw.wikibase.getBestStatements(unitqid, "P5061")

-- construct fallback table, add local lang and multiple languages

local fbtbl = mw.language.getFallbacksFor( args.lang )

table.insert( fbtbl, 1, args.lang )

table.insert( fbtbl, 1, "mul" )

local found = false

for idx1, us in ipairs(unitsymbols) do

for idx2, fblang in ipairs(fbtbl) do

if us.mainsnak.datavalue.value.language == fblang then

usym = us.mainsnak.datavalue.value.text

found = true

break

end

if found then break end

end -- loop through fallback table

end -- loop through values of P5061

if found then usep, unit = " ", usym end

end

end

-- format display:

if conv then

if range == "" then

val = mw.getCurrentFrame():expandTemplate{title = "cvt", args = {amount, unit}}

else

val = mw.getCurrentFrame():expandTemplate{title = "cvt", args = {lowb, "to", upb, unit}}

end

elseif unit == "$" or unit == "£" then

val = unit .. amount .. range .. i18n.multipliers[scale]

else

val = amount .. range .. i18n.multipliers[scale] .. usep .. unit

end

------------------------------------

-- datatypes which are global coordinates:

elseif dtype == "globe-coordinate" then

-- 'display' parameter defaults to "inline, title" *** unused for now ***

-- local disp = args.display or ""

-- if disp == "" then disp = "inline, title" end

--

-- format parameter switches from deg/min/sec to decimal degrees

-- default is deg/min/sec -- decimal degrees needs |format = dec

local form = (args.format or ""):lower():sub(1,3)

if form ~= "dec" then form = "dms" end -- not needed for now

--

-- show parameter allows just the latitude, or just the longitude, or both

-- to be returned as a signed decimal, ignoring the format parameter.

local show = (args.show or ""):lower()

if show ~= "longlat" then show = show:sub(1,3) end

--

local lat, long, prec = dv.latitude, dv.longitude, dv.precision

if show == "lat" then

val = decimalPrecision(lat, prec)

elseif show == "lon" then

val = decimalPrecision(long, prec)

elseif show == "longlat" then

val = decimalPrecision(long, prec) .. ", " .. decimalPrecision(lat, prec)

else

local ns = "N"

local ew = "E"

if lat < 0 then

ns = "S"

lat = - lat

end

if long < 0 then

ew = "W"

long = - long

end

if form == "dec" then

lat = decimalPrecision(lat, prec)

long = decimalPrecision(long, prec)

val = lat .. "°" .. ns .. " " .. long .. "°" .. ew

else

local latdeg, latmin, latsec = decimalToDMS(lat, prec)

local longdeg, longmin, longsec = decimalToDMS(long, prec)

if latsec == 0 and longsec == 0 then

if latmin == 0 and longmin == 0 then

val = latdeg .. "°" .. ns .. " " .. longdeg .. "°" .. ew

else

val = latdeg .. "°" .. latmin .. "′" .. ns .. " "

val = val .. longdeg .. "°".. longmin .. "′" .. ew

end

else

val = latdeg .. "°" .. latmin .. "′" .. latsec .. "″" .. ns .. " "

val = val .. longdeg .. "°" .. longmin .. "′" .. longsec .. "″" .. ew

end

end

end

------------------------------------

elseif dtype == "monolingualtext" then -- data type is Monolingual text:

-- has mainsnak.datavalue.value as a table containing language/text pairs

-- collect all the values in 'out' and languages in 'mlt' and process them later

val = pre .. dv.text .. post

mlt = dv.language

------------------------------------

else

-- some other data type so write a specific handler

val = "unknown data type: " .. dtype

end -- of datatype/unknown value/sourced check

return val, mlt

end

-------------------------------------------------------------------------------

-- propertyvalueandquals takes a property object, the arguments passed from frame,

-- and a qualifier propertyID.

-- It returns a sequence (table) of values representing the values of that property

-- and qualifiers that match the qualifierID if supplied.

-------------------------------------------------------------------------------

-- Dependencies: parseParam(); sourced(); labelOrId(); i18n.latestdatequalifier();

-- roundto(); decimalPrecision(); decimalToDMS(); assembleoutput();

-------------------------------------------------------------------------------

local function propertyvalueandquals(objproperty, args, qualID)

-- needs this style of declaration because it's re-entrant

-- onlysourced is a boolean passed to return only values sourced to other than Wikipedia

-- if nothing or an empty string is passed set it true

local onlysrc = parseParam(args.onlysourced or args.osd, true)

-- linked is a a boolean that enables the link to a local page via sitelink

-- if nothing or an empty string is passed set it true

local linked = parseParam(args.linked, true)

-- prefix is a string that may be nil, empty (""), or a string of characters

-- this is prefixed to each value

-- useful when when multiple values are returned

-- any double-quotes " are stripped out, so that spaces may be passed

local prefix = (args.prefix or ""):gsub('"', '')

-- postfix is a string that may be nil, empty (""), or a string of characters

-- this is postfixed to each value

-- useful when when multiple values are returned

-- any double-quotes " are stripped out, so that spaces may be passed

local postfix = (args.postfix or ""):gsub('"', '')

-- linkprefix is a string that may be nil, empty (""), or a string of characters

-- this creates a link and is then prefixed to each value

-- useful when when multiple values are returned and indirect links are needed

-- any double-quotes " are stripped out, so that spaces may be passed

local lprefix = (args.linkprefix or args.lp or ""):gsub('"', '')

-- linkpostfix is a string that may be nil, empty (""), or a string of characters

-- this is postfixed to each value when linking is enabled with lprefix

-- useful when when multiple values are returned

-- any double-quotes " are stripped out, so that spaces may be passed

local lpostfix = (args.linkpostfix or ""):gsub('"', '')

-- wdlinks is a boolean passed to enable links to Wikidata when no article exists

-- if nothing or an empty string is passed set it false

local wdl = parseParam(args.wdlinks or args.wdl, false)

-- unitabbr is a boolean passed to enable unit abbreviations for common units

-- if nothing or an empty string is passed set it false

local uabbr = parseParam(args.unitabbr or args.uabbr, false)

-- qualsonly is a boolean passed to return just the qualifiers

-- if nothing or an empty string is passed set it false

local qualsonly = parseParam(args.qualsonly or args.qo, false)

-- maxvals is a string that may be nil, empty (""), or a number

-- this determines how many items may be returned when multiple values are available

-- setting it = 1 is useful where the returned string is used within another call, e.g. image

local maxvals = tonumber(args.maxvals) or 0

-- pd (plain date) is a string: yes/true/1 | no/false/0 | adj

-- to disable/enable "sourcing cirumstances" or use adjectival form for the plain date

local pd = args.plaindate or args.pd or "no"

args.pd = pd

-- allow qualifiers to have a different date format; default to year

args.qdf = args.qdf or args.qualifierdateformat or args.df or "y"

local lang = args.lang or findlang().code

-- qualID is a string list of wanted qualifiers or "ALL"

qualID = qualID or ""

-- capitalise list of wanted qualifiers and substitute "DATES"

qualID = qualID:upper():gsub("DATES", "P580, P582")

local allflag = (qualID == "ALL")

-- create table of wanted qualifiers as key

local qwanted = {}

-- create sequence of wanted qualifiers

local qorder = {}

for q in mw.text.gsplit(qualID, "%p") do -- split at punctuation and iterate

local qtrim = mw.text.trim(q)

if qtrim ~= "" then

qwanted[mw.text.trim(q)] = true

qorder[#qorder+1] = qtrim

end

end

-- qsep is the output separator for rendering qualifier list

local qsep = (args.qsep or ""):gsub('"', '')

-- qargs are the arguments to supply to assembleoutput()

local qargs = {

["osd"] = "false",

["linked"] = tostring(linked),

["prefix"] = args.qprefix,

["postfix"] = args.qpostfix,

["linkprefix"] = args.qlinkprefix or args.qlp,

["linkpostfix"] = args.qlinkpostfix,

["wdl"] = "false",

["unitabbr"] = tostring(uabbr),

["maxvals"] = 0,

["sorted"] = tostring(args.qsorted),

["noicon"] = "true",

["list"] = args.qlist,

["sep"] = qsep,

["langobj"] = args.langobj,

["lang"] = args.langobj.code,

["df"] = args.qdf,

}

-- all proper values of a Wikidata property will be the same type as the first

-- qualifiers don't have a mainsnak, properties do

local datatype = objproperty[1].datatype or objproperty[1].mainsnak.datatype

-- out[] holds the a list of returned values for this property

-- mlt[] holds the language code if the datatype is monolingual text

local out = {}

local mlt = {}

for k, v in ipairs(objproperty) do

local hasvalue = true

if (onlysrc and not sourced(v)) then

-- no value: it isn't sourced when onlysourced=true

hasvalue = false

else

local val, lcode = rendersnak(v, args, linked, lprefix, lpostfix, prefix, postfix, uabbr)

if not val then

hasvalue = false -- rank doesn't match

elseif qualsonly and qualID then

-- suppress value returned: only qualifiers are requested

else

out[#out+1], mlt[#out+1] = val, lcode

end

end

-- See if qualifiers are to be returned:

local snak = v.mainsnak or v

if hasvalue and v.qualifiers and qualID ~= "" and snak.snaktype~="novalue" then

-- collect all wanted qualifier values returned in qlist, indexed by propertyID

local qlist = {}

local timestart, timeend = "", ""

-- loop through qualifiers

for k1, v1 in pairs(v.qualifiers) do

if allflag or qwanted[k1] then

if k1 == "P1326" then

local ts = v1[1].datavalue.value.time

local dp = v1[1].datavalue.value.precision

qlist[k1] = dateFormat(ts, dp, args.qdf, args.bc, pd, "", lang, "before")

elseif k1 == "P1319" then

local ts = v1[1].datavalue.value.time

local dp = v1[1].datavalue.value.precision

qlist[k1] = dateFormat(ts, dp, args.qdf, args.bc, pd, "", lang, "after")

elseif k1 == "P580" then

timestart = propertyvalueandquals(v1, qargs)[1] or "" -- treat only one start time as valid

elseif k1 == "P582" then

timeend = propertyvalueandquals(v1, qargs)[1] or "" -- treat only one end time as valid

else

local q = assembleoutput(propertyvalueandquals(v1, qargs), qargs)

-- we already deal with circa via 'sourcing circumstances' if the datatype was time

-- circa may be either linked or unlinked *** internationalise later ***

if datatype ~= "time" or q ~= "circa" and not (type(q) == "string" and q:find("circa]]")) then

qlist[k1] = q

end

end

end -- of test for wanted

end -- of loop through qualifiers

-- set date separator

local t = timestart .. timeend

-- *** internationalise date separators later ***

local dsep = "–"

if t:find("%s") or t:find(" ") then dsep = " – " end

-- set the order for the list of qualifiers returned; start time and end time go last

if next(qlist) then

local qlistout = {}

if allflag then

for k2, v2 in pairs(qlist) do

qlistout[#qlistout+1] = v2

end

else

for i2, v2 in ipairs(qorder) do

qlistout[#qlistout+1] = qlist[v2]

end

end

if t ~= "" then

qlistout[#qlistout+1] = timestart .. dsep .. timeend

end

local qstr = assembleoutput(qlistout, qargs)

if qualsonly then

out[#out+1] = qstr

else

out[#out] = out[#out] .. " (" .. qstr .. ")"

end

elseif t ~= "" then

if qualsonly then

out[#out+1] = timestart .. dsep .. timeend

else

out[#out] = out[#out] .. " (" .. timestart .. dsep .. timeend .. ")"

end

end

end -- of test for qualifiers wanted

if maxvals > 0 and #out >= maxvals then break end

end -- of for each value loop

-- we need to pick one value to return if the datatype was "monolingualtext"

-- if there's only one value, use that

-- otherwise look through the fallback languages for a match

if datatype == "monolingualtext" and #out >1 then

lang = mw.text.split( lang, '-', true )[1]

local fbtbl = mw.language.getFallbacksFor( lang )

table.insert( fbtbl, 1, lang )

local bestval = ""

local found = false

for idx1, lang1 in ipairs(fbtbl) do

for idx2, lang2 in ipairs(mlt) do

if (lang1 == lang2) and not found then

bestval = out[idx2]

found = true

break

end

end -- loop through values of property

end -- loop through fallback languages

if found then

-- replace output table with a table containing the best value

out = { bestval }

else

-- more than one value and none of them on the list of fallback languages

-- sod it, just give them the first one

out = { out[1] }

end

end

return out

end

-------------------------------------------------------------------------------

-------------------------------------------------------------------------------

-- Public functions

-------------------------------------------------------------------------------

-------------------------------------------------------------------------------

-- _getValue makes the functionality of getValue available to other modules

-------------------------------------------------------------------------------

-- Dependencies: setRanks; parseInput; propertyvalueandquals; assembleoutput; parseParam; sourced;

-- labelOrId; i18n.latestdatequalifier; roundto; decimalPrecision; decimalToDMS;

-------------------------------------------------------------------------------

p._getValue = function(args)

-- parameter sets for commonly used groups of parameters

local paraset = tonumber(args.ps or args.parameterset or 0)

if paraset == 1 then

-- a common setting

args.rank = "best"

args.fetchwikidata = "ALL"

args.onlysourced = "no"

args.noicon = "true"

elseif paraset == 2 then

-- equivalent to raw

args.rank = "best"

args.fetchwikidata = "ALL"

args.onlysourced = "no"

args.noicon = "true"

args.linked = "no"

args.pd = "true"

elseif paraset == 3 then

-- third set goes here

end

-- implement eid parameter

local eid = args.eid

if eid == "" then

return nil

elseif eid then

args.qid = eid

end

local propertyID = mw.text.trim(args[1] or "")

args.reqranks = setRanks(args.rank)

-- replacetext (rt) is a string that is returned instead of any non-empty Wikidata value

-- this is useful for tracking and debugging, so we set fetchwikidata=ALL to fill the whitelist

local replacetext = mw.text.trim(args.rt or args.replacetext or "")

if replacetext ~= "" then

args.fetchwikidata = "ALL"

end

local f = {}

f.args = args

local entityid, props = parseInput(f, f.args[2], propertyID)

if not entityid then

return props -- either the input parameter or nothing

end

-- qual is a string containing the property ID of the qualifier(s) to be returned

-- if qual == "ALL" then all qualifiers returned

-- if qual == "DATES" then qualifiers P580 (start time) and P582 (end time) returned

-- if nothing or an empty string is passed set it nil -> no qualifiers returned

local qualID = mw.text.trim(args.qual or ""):upper()

if qualID == "" then qualID = nil end

-- set a language object and code in the args table

args.langobj = findLang(args.lang)

args.lang = args.langobj.code

-- table 'out' stores the return value(s):

local out = propertyvalueandquals(props, args, qualID)

-- format the table of values and return it as a string:

return assembleoutput(out, args, entityid, propertyID)

end

-------------------------------------------------------------------------------

-- getValue is used to get the value(s) of a property

-- The property ID is passed as the first unnamed parameter and is required.

-- A locally supplied parameter may optionaly be supplied as the second unnamed parameter.

-- The function will now also return qualifiers if parameter qual is supplied

-------------------------------------------------------------------------------

-- Dependencies: _getValue; setRanks; parseInput; propertyvalueandquals; assembleoutput; parseParam; sourced;

-- labelOrId; i18n.latestdatequalifier; roundto; decimalPrecision; decimalToDMS;

-------------------------------------------------------------------------------

p.getValue = function(frame)

local args= frame.args

if not args[1] then

args = frame:getParent().args

if not args[1] then return i18n.errors["No property supplied"] end

end

return p._getValue(args)

end

-------------------------------------------------------------------------------

-- getPropOfProp takes two propertyIDs: prop1 and prop2 (as well as the usual parameters)

-- If the value(s) of prop1 are of type "wikibase-item" then it returns the value(s) of prop2

-- of each of those wikibase-items.

-- The usual whitelisting, blacklisting, onlysourced, etc. are implemented

-------------------------------------------------------------------------------

-- Dependencies: parseParam; setRanks; parseInput; sourced; propertyvalueandquals assembleoutput;

-------------------------------------------------------------------------------

p._getPropOfProp = function(args)

-- parameter sets for commonly used groups of parameters

local paraset = tonumber(args.ps or args.parameterset or 0)

if paraset == 1 then

-- a common setting

args.rank = "best"

args.fetchwikidata = "ALL"

args.onlysourced = "no"

args.noicon = "true"

elseif paraset == 2 then

-- equivalent to raw

args.rank = "best"

args.fetchwikidata = "ALL"

args.onlysourced = "no"

args.noicon = "true"

args.linked = "no"

args.pd = "true"

elseif paraset == 3 then

-- third set goes here

end

args.reqranks = setRanks(args.rank)

args.langobj = findLang(args.lang)

args.lang = args.langobj.code

local pid1 = args.prop1 or args.pid1 or ""

local pid2 = args.prop2 or args.pid2 or ""

local localval = mw.text.trim(args[1] or "")

if pid1 == "" or pid2 == "" then return nil end

local f = {}

f.args = args

local qid1, statements1 = parseInput(f, localval, pid1)

if not qid1 then return localval end

local onlysrc = parseParam(args.onlysourced or args.osd, true)

local maxvals = tonumber(args.maxvals) or 0

local qualID = mw.text.trim(args.qual or ""):upper()

if qualID == "" then qualID = nil end

local out = {}

for k, v in ipairs(statements1) do

if not onlysrc or sourced(v) then

local snak = v.mainsnak

if snak.datatype == "wikibase-item" and snak.snaktype == "value" then

local qid2 = snak.datavalue.value.id

local statements2 = {}

if args.reqranks.b then

statements2 = mw.wikibase.getBestStatements(qid2, pid2)

else

statements2 = mw.wikibase.getAllStatements(qid2, pid2)

end

if statements2[1] then

local out2 = propertyvalueandquals(statements2, args, qualID)

out[#out+1] = assembleoutput(out2, args, qid2, pid2)

end

end -- of test for valid property1 value

end -- of test for sourced

if maxvals > 0 and #out >= maxvals then break end

end -- of loop through values of property1

return assembleoutput(out, args, qid1, pid1)

end

p.getPropOfProp = function(frame)

local args= frame.args

if not args.prop1 and not args.pid1 then

args = frame:getParent().args

if not args.prop1 and not args.pid1 then return i18n.errors["No property supplied"] end

end

return p._getPropOfProp(args)

end

return p

-------------------------------------------------------------------------------

-- List of exported functions

-------------------------------------------------------------------------------

--[[

_getValue

getValue

_getPropOfProp

getPropOfProp

--]]

-------------------------------------------------------------------------------