Module:Md

-- version 20200607 from https://ca.wikipedia.org/wiki/Mòdul:Wikidades

local p = {}

-- Initialization of variables

local i18n = {						-- internationalisation at Module:Wikidata/i18n ["errors"] = { ["property-not-found"] = "Property not found.", ["qualifier-not-found"] = "Qualifier not found.", },	["datetime"] = { -- $1 is a placeholder for the actual number ["beforenow"] = "$1 BCE",	-- how to format negative numbers for precisions 0 to 5 ["afternow"] = "$1 CE",		-- how to format positive numbers for precisions 0 to 5 ["bc"] = "$1 BCE",			-- how print negative years ["ad"] = "$1",				-- how print 1st century AD dates [0] = "$1 billion years",	-- precision: billion years [1] = "$100 million years",	-- precision: hundred million years [2] = "$10 million years",	-- precision: ten million years [3] = "$1 million years",	-- precision: million years [4] = "$100000 years",		-- precision: hundred thousand years; thousand separators added afterwards [5] = "$10000 years",		-- precision: ten thousand years; thousand separators added afterwards [6] = "$1 millennium",		-- precision: millennium [7] = "$1 century",			-- precision: century [8] = "$1s",				-- precision: decade -- the following use the format of #time parser function [9] = "Y",					-- precision: year, [10] = "F Y",				-- precision: month [11] = "F j, Y",			-- precision: day ["hms"] = {["hours"] = "h", ["minutes"] = "′", ["seconds"] = "″"},	-- duration: xh x′ x″	}, ["years-old"] = { ["singular"] = "",			-- year old, as in NaN plurals ["plural"] = "",			-- years old ["paucal"] = "",			-- for languages with 3 plural forms as in NaN paucals },	["cite"] = {					-- cite parameters ["title"] = "title", ["author"] = "author", ["date"] = "date", ["pages"] = "pages", ["language"] = "language", -- cite web parameters ["url"] = "url", ["website"] = "website", ["access-date"] = "access-date", ["archive-url"] = "archive-url", ["archive-date"] = "archive-date", ["publisher"] = "publisher", ["quote"] = "quote", -- cite journal parameters ["work"] = "work", ["issue"] = "issue", ["issn"] = "issn", ["doi"] = "doi" },	-- local wiki settings ["addpencil"] = false, -- adds a pencil icon linked to Wikidata statement, planned to overwrite by Wikidata Bridge ["categorylabels"] = "", -- Category:Pages with Wikidata labels not translated (void for no local category) ["addfallback"] = {} -- additional fallback language codes }

local cases = {} -- functions for local grammatical cases defined at Module:Wikidata/i18n

local required = ... -- variadic arguments from require function local wiki = {	langcode = mw.language.getContentLanguage.code, module_title = required or mw.getCurrentFrame:getTitle }

local untranslated -- used in infobox modules: nil or true local _ -- variable for unused returned values, avoiding globals

-- Module local functions

-- Credit to http://stackoverflow.com/a/1283608/2644759, cc-by-sa 3.0 local function tableMerge(t1, t2) for k, v in pairs(t2) do		if type(v) == "table" then if type(t1[k] or false) == "table" then tableMerge(t1[k] or {}, t2[k] or {}) else t1[k] = v			end else t1[k] = v		end end return t1 end

local function loadI18n(lang) local exist, res = pcall(require, wiki.module_title .. "/i18n") if exist and next(res) ~= nil then tableMerge(i18n, res.i18n) cases = res.cases end if lang ~= wiki.langcode then exist, res = pcall(require, wiki.module_title .. "/i18n/" .. lang) if exist and next(res) ~= nil then tableMerge(i18n, res.i18n) tableMerge(cases, res.cases) end end end

-- Table of language codes: requested or default and its fallbacks local function findLang(langcode) if mw.language.isKnownLanguageTag(langcode or '') == false then local cframe = mw.getCurrentFrame local pframe = cframe:getParent langcode = pframe and pframe.args.lang if mw.language.isKnownLanguageTag(langcode or '') == false then if not mw.title.getCurrentTitle.isContentPage then langcode = cframe:preprocess('') end if mw.language.isKnownLanguageTag(langcode or '') == false then langcode = wiki.langcode end end end loadI18n(langcode) local languages = mw.language.getFallbacksFor(langcode) table.insert(languages, 1, langcode) if langcode == wiki.langcode then for _, l in ipairs(i18n.addfallback) do			table.insert(languages, l)		end end return languages end

-- Argument is 'set' when it exists (not nil) or when it is not an empty string. local function isSet(var) return not (var == nil or var == '') end

-- Set local case to a label local function case(localcase, label, ...) if not isSet(label) then return label end if localcase == "smallcaps" then return ' ' .. label .. ' '	elseif cases[localcase] then return cases[localcase](label, ...) end return label end

-- get safely a serialized snak local function getSnak(statement, snaks) local ret = statement for i, v in ipairs(snaks) do		if not ret then return end ret = ret[v] end return ret end

-- mw.wikibase.getLabelWithLang or getLabelByLang with a table of languages local function getLabelByLangs(id, languages) local label local lang for _, l in ipairs(languages) do		if l == wiki.langcode then -- using getLabelWithLang when possible instead of getLabelByLang, do not solve redirects pending phab:T157868 label, l = mw.wikibase.getLabelWithLang(id) else label = mw.wikibase.getLabelByLang(id, l)		end if label then lang = l			break end end return label, lang end

-- getBestStatements if bestrank=true, else getAllStatements with no deprecated local function getStatements(entityId, property, bestrank) local claims = {} if not (entityId and mw.ustring.match(property, "^P%d+$")) then return claims end if bestrank then claims = mw.wikibase.getBestStatements(entityId, property) else local allclaims = mw.wikibase.getAllStatements(entityId, property) for _, c in ipairs(allclaims) do			if c.rank ~= "deprecated" then table.insert(claims, c)			end end end return claims end

-- Is gender femenine? true or false local function feminineGender(id) local claims = mw.wikibase.getBestStatements(id or mw.wikibase.getEntityIdForCurrentPage,'P21') if getSnak(claims, {1, "mainsnak", "datavalue"}) == nil then -- no claim, novalue or somevalue return false else local genderId = claims[1].mainsnak.datavalue.value.id		if genderId == "Q6581072" or genderId == "Q1052281" or genderId == "Q43445" then -- female, transgender female, female organism return true end end return false end

-- Fetch female form of label local function feminineForm(id, lang) local feminine_claims = getStatements(id, 'P2521') for _, feminine_claim in ipairs(feminine_claims) do		if getSnak(feminine_claim, {'mainsnak', 'datavalue', 'value', 'language'}) == lang then return feminine_claim.mainsnak.datavalue.value.text end end end

-- Add an icon for no label in requested language local function addLabelIcon(label_id, lang, uselang, icon) local ret_lang, ret_icon = ,  if icon then if lang and lang ~= uselang then ret_lang = " (" .. lang .. ")" end if label_id and (lang == nil or lang ~= uselang) then ret_icon = " " untranslated = true end if isSet(i18n.categorylabels) and lang ~= uselang and uselang == wiki.langcode then ret_icon = ret_icon ..  .. i18n.categorylabels .. (lang and  or '/Q]]') end end return ret_lang .. ret_icon end

-- Add an icon for editing a statement with requirements for Wikidata Bridge local function addEditIcon(parameters) local ret = '' if i18n.addpencil and parameters.editicon and parameters.id and parameters.property then ret = ' ' .. ""			.. " "	end return ret end -- add edit icon to the last element of a table local function addEditIconTable(thetable, parameters) if #thetable == 0 or not i18n.addpencil then return thetable end local last_element = thetable[#thetable] local the_icon = addEditIcon(parameters) -- add it before last html closing tags local tags = '' local rev_element = string.reverse(last_element) for tag in string.gmatch(rev_element, '(>%l+/<)') do if string.match(rev_element, '^' .. tags .. tag) then tags = tags .. tag else break end end local last_tags = string.reverse(tags) local offset = string.find(last_element, last_tags .. '$') if offset then thetable[#thetable] = string.sub(last_element, 1, offset - 1) .. the_icon .. last_tags else thetable[#thetable] = last_element .. the_icon end return thetable end

-- Escape Lua captures local function captureEscapes(text) return mw.ustring.gsub(text, "(%%%d)", "%%%1") end

-- expandTemplate or callParserFunction local function expandBraces(text, formatting) if text == nil or formatting == nil then return text end -- only expand braces if provided in argument, not included in value as in Q1164668 if mw.ustring.find(formatting, '{{', 1, true) == nil then return text end if type(text) ~= "string" then text = tostring(text) end for braces in mw.ustring.gmatch(text, "{{(.-)}}") do		local parts = mw.text.split(braces, "|") local title_part = parts[1] local parameters = {} for i = 2, #parts do			local subparts = mw.ustring.find(parts[i], "=") if subparts then parameters[mw.ustring.sub(parts[i], 1, subparts-1)] = mw.ustring.sub(parts[i], subparts+1, -1) else table.insert(parameters, parts[i]) end end local braces_expanded if mw.ustring.find(title_part, ":") and mw.text.split(title_part, ":")[1] ~= mw.site.namespaces[10].name -- not a prefix Template: then braces_expanded = mw.getCurrentFrame:callParserFunction{name=title_part, args=parameters} else braces_expanded = mw.getCurrentFrame:expandTemplate{title=title_part, args=parameters} end braces = mw.ustring.gsub(braces, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- escape magic characters braces_expanded = captureEscapes(braces_expanded) text = mw.ustring.gsub(text, "{{" .. braces .. "}}", braces_expanded) end return text end

-- Resolve Wikidata redirects, pending phab:T157868 local function resolveEntityId(id) if not id or not mw.wikibase.isValidEntityId(id) then return id end -- if no label in English, maybe it is a redirect -- not using mw.title.new(id).isRedirect as it is expensive -- currently getLabelByLang does not follows redirects if mw.wikibase.getLabelByLang(id, 'en') == nil then local entity = mw.wikibase.getEntity(id) -- expensive function if not entity then return nil end if id ~= entity.id then -- Qid redirected to be fixed -- see Special:WhatLinksHere/Template:Track/wikidata/redirect require(wiki.module_title .. '/debug').track('redirect') require(wiki.module_title .. '/debug').track('redirect/' .. id) else -- no redirect and no English label, fix it to avoid expensive functions require(wiki.module_title .. '/debug').track('label') require(wiki.module_title .. '/debug').track('label/' .. id) end return entity.id	end return id end

-- format data type math local function printDatatypeMath(data) return mw.getCurrentFrame:callParserFunction('#tag:math', data) end

-- format data type musical-notation local function printDatatypeMusical(data, formatting) local attr = {} if formatting == 'sound' then attr.sound = 1 end return mw.getCurrentFrame:extensionTag('score', data, attr) end

-- format data type string local function printDatatypeString(data, parameters) if mw.ustring.find((parameters.formatting or ''), '$1', 1, true) then -- formatting = a pattern return expandBraces(mw.ustring.gsub(parameters.formatting, '$1', {['$1'] = data}), parameters.formatting) elseif parameters.case then return case(parameters.case, data, parameters.lang[1]) end local data_number = string.match(data, "^%d+") if data_number then -- sort key by initial number and remaining string local sortkey = string.format("%019d", data_number * 1000) return data, sortkey .. string.sub(data, #data_number + 1) end return data end

-- format data type url local function printDatatypeUrl(data, parameters) if parameters.formatting == 'weblink' then local label_parts = mw.text.split(string.gsub(data, '/$', ''), '/') local label = string.gsub(label_parts[3], '^www%.', '') if #label_parts > 3 then label = label .. '…'		end return '[' .. data .. ' ' .. label .. ']'	end return printDatatypeString(data, parameters) end

-- format data type external-id local function printDatatypeExternal(data, parameters) if parameters.formatting == 'externalid' then local p_stat = mw.wikibase.getBestStatements(parameters.property, 'P1630') -- formatter URL local p_link_pattern = getSnak(p_stat, {1, "mainsnak", "datavalue", "value"}) if p_link_pattern then local p_link = mw.ustring.gsub(p_link_pattern, '$1', {['$1'] = data}) return '[' .. p_link .. ' ' .. data .. ']'		end end return printDatatypeString(data, parameters) end

-- format data type commonsMedia local function printDatatypeMedia(data, parameters) local icon if not string.find((parameters.formatting or ''), '$1', 1, true) then icon = "no-icon" end return printDatatypeString(data, parameters), icon end

-- format data type globe-coordinate local function printDatatypeCoordinate(data, formatting) local function globes(globe_id) local globes = {['Q3134']='callisto',['Q596']='ceres',['Q15040']='dione',['Q2']='earth',['Q3303']='enceladus', ['Q3143']='europa',['Q17975']='phoebe',['Q3169']='ganymede',['Q3123']='io',['Q17958']='iapetus', ['Q308']='mercury',['Q15034']='mimas',['Q405']='moon',['Q15050']='rhea',['Q15047']='tethys', ['Q111']='mars',['Q2565']='titan',['Q3359']='triton',['Q313']='venus',['Q3030']='vesta'} return globes[globe_id] end local function roundPrecision(num, prec) if prec == nil or prec <= 0 then return num end local sig = 10^math.floor(math.log10(prec)+.5) -- significant figure from sexagesimal precision: 0.00123 -> 0.001 return math.floor(num / sig + 0.5) * sig end local precision = data.precision local latitude = roundPrecision(data.latitude, precision) local longitude = roundPrecision(data.longitude, precision) if formatting and string.find(formatting, '$lat', 1, true) and string.find(formatting, '$lon', 1, true) then local ret = mw.ustring.gsub(formatting, '$l[ao][tn]', {['$lat'] = latitude, ['$lon'] = longitude}) if string.find(formatting, '$globe', 1, true) then local myglobe = 'earth' if isSet(data.globe) then local globenum = mw.text.split(data.globe, 'entity/')[2] -- http://www.wikidata.org/wiki/Q2 myglobe = globes(globenum) or 'earth' end ret = mw.ustring.gsub(ret, '$globe', myglobe) end return expandBraces(ret, formatting) elseif formatting == 'latitude' then return latitude, "no-icon" elseif formatting == 'longitude' then return longitude, "no-icon" elseif formatting == 'dimension' then return data.dimension, "no-icon" else --default formatting='globe' if isSet(data.globe) == false or data.globe == 'http://www.wikidata.org/entity/Q2' then return 'earth', "no-icon" else local globenum = mw.text.split(data.globe, 'entity/')[2] return globes(globenum) or globenum, "no-icon" end end end

-- Local functions for data value quantity local function unitSymbol(id, lang) -- get unit symbol or code local unit_symbol = '' if lang == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then unit_symbol = require(wiki.module_title .. "/Units").getUnit(0, '', id, true) end if unit_symbol == '' then -- fetch it		local claims = mw.wikibase.getBestStatements(id, 'P5061') if #claims > 0 then local langclaims = {} table.insert(lang, 'mul') -- multilingual as last try for _, snak in ipairs(claims) do				local snak_language = getSnak(snak, {"mainsnak", "datavalue", "value", "language"}) if snak_language and not langclaims[snak_language] then -- just the first one by language langclaims[snak_language] = snak.mainsnak.datavalue.value.text end end for _, l in ipairs(lang) do				if langclaims[l] then return langclaims[l] end end end end return unit_symbol end

local function getUnit(amount, id, parameters) -- get unit symbol or name local suffix = '' if parameters.formatting == "unitcode" then -- get unit symbol local unit_symbol = unitSymbol(id, parameters.lang) if isSet(unit_symbol) then suffix = unit_symbol end end if suffix == '' then -- formatting=unit, or formatting=unitcode not found -- get unit name local unit_label, lang = getLabelByLangs(id, parameters.lang) if lang == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then suffix = require(wiki.module_title .. "/Units").getUnit(amount, unit_label, id, false) else suffix = (unit_label or id) .. addLabelIcon(id, lang, parameters.lang[1], parameters.editicon) end end if suffix ~= '' then suffix = ' ' .. suffix end return suffix end

local function roundDefPrecision(in_num, factor) -- rounds out_num with significant figures of in_num (default precision) local out_num = in_num * factor if factor/60 == math.floor(factor/60) then -- sexagesimal integer return out_num end -- first, count digits after decimal mark, handling cases like '12.345e6' local exponent, prec local integer, dot, decimals, expstr = in_num:match('^(%d*)(%.?)(%d*)(.*)') local e = expstr:sub(1, 1) if e == 'e' or e == 'E' then exponent = tonumber(expstr:sub(2)) end if dot == '' then prec = -integer:match('0*$'):len else prec = #decimals end if exponent then -- So '1230' and '1.23e3' both give prec = -1, and '0.00123' and '1.23e-3' give 5. prec = prec - exponent end -- significant figures local in_bracket = 10^-prec -- -1 -> 10, 5 -> 0.00001 local out_bracket = in_bracket * out_num / in_num out_bracket = 10^math.floor(math.log10(out_bracket)+.5) -- 1230 -> 1000, 0.00123 -> 0.001 -- round it (credit to Luc Bloom from http://lua-users.org/wiki/SimpleRound) return math.floor(out_num/out_bracket + (out_num >=0 and 1 or -1) * 0.5) * out_bracket end

-- format data type quantity local function printDatatypeQuantity(data, parameters) local amount = data.amount amount = mw.ustring.gsub(amount, "%+", "") local suffix = "" local conv_amount, conv_suffix if string.sub(parameters.formatting or , 1, 4) == "unit" or string.sub(parameters.formatting or , 1, 8) == "duration" or parameters.convert then local unit_id = data.unit unit_id = mw.ustring.sub(unit_id, mw.ustring.find(unit_id, "Q"), -1) if string.sub(unit_id, 1, 1) == "Q" then suffix = getUnit(amount, unit_id, parameters) local convert_to if parameters.convert == "default" or parameters.convert == "default2" then local exist, units = pcall(require, wiki.module_title .. "/Units") if exist and next(units.convert_default) ~= nil then convert_to = units.convert_default[unit_id] end elseif string.sub(parameters.convert or '', 1, 1) == "Q" then convert_to = resolveEntityId(parameters.convert) elseif string.sub(parameters.formatting or '', 1, 8) == "duration" then convert_to = 'Q11574' -- seconds end if convert_to and convert_to ~= unit_id then -- convert units local conv_temp = { -- formulae for temperatures ºC, ºF, ªK: [from] = {[to] = 'formula'} ['Q25267'] = {['Q42289'] = '$1*1.8+32', ['Q11597'] = '$1+273.15'}, ['Q42289'] = {['Q25267'] = '($1-32)/1.8', ['Q11597'] = '($1+459.67)*5/9'}, ['Q11597'] = {['Q25267'] = '$1-273.15', ['Q42289'] = '($1-273.15)*1.8000+32.00'} }				if conv_temp[unit_id] and conv_temp[unit_id][convert_to] then local amount_f = mw.getCurrentFrame:callParserFunction('#expr', mw.ustring.gsub(conv_temp[unit_id][convert_to], "$1", amount)) conv_amount = math.floor(tonumber(amount_f) + 0.5) else local conversions = getStatements(unit_id, 'P2442') -- conversion to standard unit table.insert(conversions, mw.wikibase.getBestStatements(unit_id, 'P2370')[1]) -- conversion to SI unit for _, conv in ipairs(conversions) do						if conv.mainsnak.snaktype == 'value' then -- no somevalue nor novalue if conv.mainsnak.datavalue.value.unit == "http://data.nonbinary.wiki/entity/" .. convert_to then conv_amount = roundDefPrecision(amount, tonumber(conv.mainsnak.datavalue.value.amount)) break end end end end if conv_amount then conv_suffix = getUnit(conv_amount, convert_to, parameters) end elseif parameters.convert == 'M' and tonumber(amount) > 10^8 then conv_amount = math.floor(amount/10^6 + 0.5) conv_suffix = ' M' .. string.sub(suffix, 2) end end end local lang_obj = mw.language.new(parameters.lang[1]) local sortkey = string.format("%019d", tonumber(amount) * 1000) if parameters.formatting == 'duration' or parameters.formatting == 'durationhms' then local sec = tonumber(conv_amount or amount) local formathms = parameters.formatting == 'durationhms' if formathms then local intervals = {"hours", "minutes", "seconds"} local sec2table = lang_obj:getDurationIntervals(sec, intervals) sec2table["seconds"] = (sec2table["seconds"] or 0) + tonumber("." .. (tostring(sec):match("%.(%d+)") or "0")) -- add decimals local duration = '' for _, v in pairs(intervals) do				if sec2table[v] then duration = duration .. lang_obj:formatNum(sec2table[v]) .. i18n.datetime.hms[v] .. " "				end end return mw.text.trim(duration) else return lang_obj:formatDuration(sec) end end if parameters.case then amount = case(parameters.case, amount, parameters.lang[1]) else amount = lang_obj:formatNum(tonumber(amount)) end if conv_amount then local conv_sortkey = string.format("%019d", tonumber(conv_amount) * 1000) conv_amount = lang_obj:formatNum(tonumber(conv_amount)) if parameters.convert == 'default2' then return conv_amount .. conv_suffix .. ' (' .. amount .. suffix .. ')', conv_sortkey else return conv_amount .. conv_suffix, conv_sortkey end end return amount .. suffix, sortkey end

-- format data type time local function printDatatypeTime(data, parameters) -- Dates and times are stored in ISO 8601 format local timestamp = data.time local post_format local calendar_add = "" local precision = data.precision or 11 if string.sub(timestamp, 1, 1) == '-' then post_format = i18n.datetime["bc"] elseif string.sub(timestamp, 2, 3) == '00' then post_format = i18n.datetime["ad"] elseif precision > 8 then -- calendar model local calendar_model = {["Q12138"] = "gregorian", ["Q1985727"] = "gregorian", ["Q11184"] = "julian", ["Q1985786"] = "julian"} local calendar_id = mw.text.split(data.calendarmodel, 'entity/')[2] if (timestamp < "+1582-10-15T00:00:00Z" and calendar_model[calendar_id] == "gregorian") or (timestamp > "+1582-10-04T00:00:00Z" and calendar_model[calendar_id] == "julian") then calendar_add = " {{sup|(" .. mw.message.new('Wikibase-time-calendar-' .. calendar_model[calendar_id]):inLanguage(parameters.lang[1]):plain .. ")}}" end end local function formatTime(form, stamp) local pattern if type(form) == "function" then pattern = form(stamp) else pattern = form end stamp = tostring(stamp) if mw.ustring.find(pattern, "$1") then return mw.ustring.gsub(pattern, "$1", stamp) elseif string.sub(stamp, 1, 1) == '-' then -- formatDate only supports years from 0 stamp = '+' .. string.sub(stamp, 2) elseif string.sub(stamp, 1, 1) ~= '+' then -- not a valid timestamp, it is a number stamp = string.format("%04d", stamp) end local ret = mw.language.new(parameters.lang[1]):formatDate(pattern, stamp) ret = string.gsub(ret, "^(%[?%[?)0+", "%1") -- supress leading zeros ret = string.gsub(ret, "( %[?%[?)0+", "%1") return ret end local function postFormat(t) if post_format and mw.ustring.find(post_format, "$1") then return mw.ustring.gsub(post_format, "$1", t)		end return t	end local intyear = tonumber(string.match(timestamp, "[+-](%d+)")) local ret = "" if precision <= 5 then -- precision is 10000 years or more local factor = 10 ^ ((5 - precision) + 4) local y2 = math.ceil(math.abs(intyear) / factor) local relative = formatTime(i18n.datetime[precision], y2) if post_format == i18n.datetime["bc"] then ret = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative) else ret = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative) end local ret_number = string.match(ret, "%d+") if ret_number ~= nil then ret = mw.ustring.gsub(ret, ret_number, mw.language.new(parameters.lang[1]):formatNum(tonumber(ret_number))) end elseif precision == 6 or precision == 7 then -- millennia or centuries local card = math.floor((intyear - 1) / 10^(9 - precision)) + 1 ret = formatTime(i18n.datetime[precision], card) ret = postFormat(ret) elseif precision == 8 then -- decades local card = math.floor(math.abs(intyear) / 10) * 10 ret = formatTime(i18n.datetime[8], card) ret = postFormat(ret) elseif intyear > 9999 then -- not a valid timestamp return elseif precision == 9 or parameters.formatting == 'Y' then -- precision is year ret = formatTime(i18n.datetime[9], intyear) ret = postFormat(ret) .. calendar_add elseif precision == 10 then -- month ret = formatTime(i18n.datetime[10], timestamp .. " + 1 day") -- formatDate yyyy-mm-00 returns the previous month ret = postFormat(ret) .. calendar_add else -- precision 11, day ret = formatTime(parameters.formatting or i18n.datetime[11], timestamp) ret = postFormat(ret) .. calendar_add end return ret, timestamp end

-- format data value wikibase-entityid: types wikibase-item, wikibase-property local function printDatatypeEntity(data, parameters) local entity_id = data['id'] if parameters.formatting == 'raw' then return entity_id, entity_id end local entity_page = 'Special:EntityPage/' .. entity_id local label, lang = getLabelByLangs(entity_id, parameters.lang) local sitelink = mw.wikibase.getSitelink(entity_id) local parameter = parameters.formatting local labelcase = label or sitelink if parameters.gender == 'feminineform' then labelcase = feminineForm(entity_id, lang) or labelcase end if parameters.case ~= 'gender' then labelcase = case(parameters.case, labelcase, lang, parameters.lang[1], entity_id, parameters.id) end local ret1, ret2 if parameter == 'label' then ret1 = labelcase or entity_id ret2 = labelcase or entity_id elseif parameter == 'sitelink' then ret1 = (sitelink or 'massey:' .. entity_page) ret2 = sitelink or entity_id elseif mw.ustring.find((parameter or ''), '$1', 1, true) then -- formatting = a pattern ret1 = mw.ustring.gsub(parameter, '$1', labelcase or entity_id) ret1 = expandBraces(ret1, parameter) ret2 = labelcase or entity_id else if parameter == "ucfirst" or parameter == "ucinternallink" then if labelcase and lang then labelcase = mw.language.new(lang):ucfirst(labelcase) end -- only first of a list, reset formatting for next ones if parameter == "ucinterlanllink" then parameters.formatting = 'internallink' else parameters.formatting = nil -- default format end end if sitelink then ret1 =  .. labelcase ..  ret2 = labelcase elseif label and (parameter == 'internallink' or parameter == 'ucinternallink') then ret1 =  .. labelcase ..  ret2 = labelcase else ret1 =  .. (labelcase or entity_id) ..  ret2 = labelcase or entity_id end end return ret1 .. addLabelIcon(entity_id, lang, parameters.lang[1], parameters.editicon), ret2 end

-- format data type monolingualtext local function printDatatypeMonolingual(data, parameters) -- data fields: language [string], text [string] if parameters.list == "lang" and data["language"] ~= parameters.lang[1] then return elseif parameters.formatting == "language" or parameters.formatting == "text" then return data[parameters.formatting] end local result = data["text"] if data["language"] ~= wiki.langcode then result = mw.ustring.gsub('$2 ', '$[12]', {["$1"]=data["language"], ["$2"]=data["text"]}) end if mw.ustring.find((parameters.formatting or ''), '$', 1, true) then -- output format defined with $text, $language result = mw.ustring.gsub(parameters.formatting, '$text', result) result = mw.ustring.gsub(result, '$language', data["language"]) end return result end

local function getSnakValue(snak, parameters) if snak.snaktype == 'value' then -- see Special:ListDatatypes if snak.datatype == "string" then return printDatatypeString(snak.datavalue.value, parameters) -- other data value string, tabular-data not implemented elseif snak.datatype == "commonsMedia" then return printDatatypeMedia(snak.datavalue.value, parameters) elseif snak.datatype == "url" then return printDatatypeUrl(snak.datavalue.value, parameters) elseif snak.datatype == "external-id" then return printDatatypeExternal(snak.datavalue.value, parameters) elseif snak.datatype == 'math' then return printDatatypeMath(snak.datavalue.value) elseif snak.datatype == 'musical-notation' then return printDatatypeMusical(snak.datavalue.value, parameters.formatting) -- other data types elseif snak.datatype == 'wikibase-item' or snak.datatype == 'wikibase-property' then return printDatatypeEntity(snak.datavalue.value, parameters) elseif snak.datatype == 'monolingualtext' then return printDatatypeMonolingual(snak.datavalue.value, parameters) elseif snak.datatype == "globe-coordinate" then return printDatatypeCoordinate(snak.datavalue.value, parameters.formatting) elseif snak.datatype == "quantity" then return printDatatypeQuantity(snak.datavalue.value, parameters) elseif snak.datatype == "time" then return printDatatypeTime(snak.datavalue.value, parameters) end elseif snak.snaktype == 'novalue' then if parameters.formatting == 'raw' then return end return mw.message.new('Wikibase-snakview-snaktypeselector-novalue'):inLanguage(parameters.lang[1]):plain elseif snak.snaktype == 'somevalue' then if parameters.formatting == 'raw' then return end return mw.message.new('Wikibase-snakview-snaktypeselector-somevalue'):inLanguage(parameters.lang[1]):plain end return mw.wikibase.renderSnak(snak) end

local function printError(key) return ' ' .. i18n.errors[key] .. ' ' end

local function getQualifierSnak(claim, qualifierId, parameters) -- a "snak" is Wikidata terminology for a typed key/value pair -- a claim consists of a main snak holding the main information of this claim, -- as well as a list of attribute snaks and a list of references snaks if qualifierId then -- search the attribute snak with the given qualifier as key if claim.qualifiers then local qualifier = claim.qualifiers[qualifierId] if qualifier then if qualifier[1].datatype == "monolingualtext" then -- iterate over monolingualtext qualifiers to get local language for idx in pairs(qualifier) do						if getSnak(qualifier[idx], {"datavalue", "value", "language"}) == parameters.lang[1] then return qualifier[idx] end end elseif parameters.list then return qualifier else return qualifier[1] end end end return nil, printError("qualifier-not-found") else -- otherwise return the main snak return claim.mainsnak end end

local function getValueOfClaim(claim, qualifierId, parameters) local snak, error = getQualifierSnak(claim, qualifierId, parameters) if not snak then return nil, nil, error elseif snak[1] then -- a multi qualifier local result, sortkey = {}, {} local maxvals = tonumber(parameters.list) for idx in pairs(snak) do			result[#result + 1], sortkey[#sortkey + 1] = getSnakValue(snak[idx], parameters) if maxvals and maxvals == #result then break end end return mw.text.listToText(result, parameters.qseparator, parameters.qconjunction), sortkey[1] else -- a property or a qualifier return getSnakValue(snak, parameters) end end

local function getValueOfParentClaim(claim, qualifierId, parameters) local qids = mw.text.split(qualifierId, '/', true) local valueraw, parent_claims, value, sortkey if qids[1] == parameters.property then valueraw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=parameters.lang}) else valueraw, _, _ = getValueOfClaim(claim, qids[1], {["formatting"]="raw", ["lang"]=parameters.lang}) end if string.sub(valueraw or '', 1, 1) == "Q" then -- protection for 'no value' parent_claims = mw.wikibase.getBestStatements(valueraw, qids[2]) if parent_claims[1] ~= nil then value, sortkey, _ = getValueOfClaim(parent_claims[1], nil, parameters) -- raw parent value needed for while/black lists, lang for avoiding an error on types other than entity valueraw, _, _ = getValueOfClaim(parent_claims[1], nil, {["formatting"]="raw", ["lang"]=parameters.lang}) end end return value, sortkey, valueraw end

-- see d:Help:Sources local function getReferences(claim, lang) local notproperref = { ["P143"] = true, -- imported from ["P3452"] = true, -- inferred from ["P887"] = true, -- based on heuristic ["P4656"] = true -- Wikimedia import URL }	local result = "" -- traverse through all references for ref in pairs(claim.references or {}) do		local refparts local refs = {} local validref = true local ref_name -- traverse through all parts of the current reference for snakkey, snakval in pairs(claim.references[ref].snaks or {}) do			for partkey, _ in pairs(claim.references[ref].snaks[snakkey] or {}) do				if notproperref[snakkey] then -- not a proper reference validref = false break end end if validref then for snakidx = 1, #snakval do if snakidx > 1 then refparts = refparts .. ", " end refparts = refparts or '' .. getSnakValue(snakval[snakidx], {lang=lang}) end refs[snakkey] = refparts refparts = nil if snakkey == "P248" then -- stated in					ref_name = snakval[1].datavalue.value.id				end end end -- fill missing values with parent item if ref_name then local function refParent(qid, pid, formatting) local snak = getSnak(mw.wikibase.getBestStatements(qid, pid), {1, "mainsnak"}) return snak and getSnakValue(snak, {formatting=formatting, lang=lang}) end refs['P50'] = refs['P50'] or refParent(ref_name, 'P50', 'label') -- author refs['P407'] = refs['P407'] or refParent(ref_name, 'P407', 'label') -- language of work refs['P123'] = refs['P123'] or refParent(ref_name, 'P123', 'label') -- publisher refs['P577'] = refs['P577'] or refParent(ref_name, 'P577') -- date refs['P1433'] = refs['P1433'] or refParent(ref_name, 'P1433', 'label') -- published in			refs['P304'] = refs['P304'] or refParent(ref_name, 'P304') -- page(s) refs['P433'] = refs['P433'] or refParent(ref_name, 'P433') -- issue refs['P236'] = refs['P236'] or refParent(ref_name, 'P236') -- ISSN refs['P356'] = refs['P356'] or refParent(ref_name, 'P356') -- DOI end -- get title of local templates for citing references local template_web = mw.wikibase.getSitelink('Q5637226') or "" template_web = mw.text.split(template_web, ":")[2] -- split off namespace from front local template_journal = mw.wikibase.getSitelink('Q5624899') or "" template_journal = mw.text.split(template_journal, ":")[2] local citeParams = {} if refs['P854'] and (refs['P1476'] or refs['P248']) and template_web then -- if both "reference URL" and "title" (or "stated in") are present, then use cite web template citeParams[i18n['cite']['url']] = refs['P854'] if refs['P248'] and refs['P1476'] == nil then citeParams[i18n['cite']['title']] = refs['P248']:match("^%[%[.-|(.-)%]%]") else citeParams[i18n['cite']['title']] = refs['P1476'] citeParams[i18n['cite']['website']] = refs['P248'] end citeParams[i18n['cite']['author']] = refs['P50'] citeParams[i18n['cite']['language']] = refs['P407'] citeParams[i18n['cite']['publisher']] = refs['P123'] citeParams[i18n['cite']['date']] = refs['P577'] citeParams[i18n['cite']['pages']] = refs['P304'] citeParams[i18n['cite']['access-date']] = refs['P813'] citeParams[i18n['cite']['archive-url']] = refs['P1065'] citeParams[i18n['cite']['archive-date']] = refs['P2960'] citeParams[i18n['cite']['quote']] = refs['P1683'] refparts = mw.getCurrentFrame:expandTemplate{title=template_web, args=citeParams} elseif refs['P1433'] and (refs['P1476'] or refs['P248']) and template_journal then -- if both "published in" and "title" (or "stated in") are present, then use cite journal template citeParams[i18n['cite']['work']] = refs['P1433'] citeParams[i18n['cite']['title']] = refs['P1476'] or refs['P248'] citeParams[i18n['cite']['author']] = refs['P50'] citeParams[i18n['cite']['date']] = refs['P577'] citeParams[i18n['cite']['issue']] = refs['P433'] citeParams[i18n['cite']['pages']] = refs['P304'] citeParams[i18n['cite']['language']] = refs['P407'] citeParams[i18n['cite']['issn']] = refs['P236'] citeParams[i18n['cite']['doi']] = refs['P356'] refparts = mw.getCurrentFrame:expandTemplate{title=template_journal, args=citeParams} elseif validref then -- raw ouput local snaksorder = claim.references[ref]["snaks-order"] local function indexed(a) for _, b in ipairs(snaksorder) do					if b == a then return true end end return false end for k, _ in pairs(refs or {}) do				if not indexed(k) then table.insert(snaksorder, k)				end end local italics = "''" for _, k in ipairs(snaksorder) do				if refs[k] then refparts = refparts and refparts .. " " or "" refparts = refparts .. mw.ustring.gsub(getLabelByLangs(k, lang), "^%l", mw.ustring.upper) .. ": "					refparts = refparts .. italics .. refs[k] .. italics .. "."					italics = "" end end end if refparts then result = mw.getCurrentFrame:extensionTag("ref", refparts, {name=ref_name}) end end return result end

-- Set whitelist or blacklist values local function setWhiteOrBlackList(type_list, num_qual, args) local listed = false local list = {} for i = 0, num_qual do if isSet(args[type_list .. i]) then listed = true list[tostring(i)] = {} for v in string.gmatch(args[type_list .. i], 'Q%d+') do				list[tostring(i)][resolveEntityId(v)] = true end end end return list, listed end

local function tableParameters(args, parameters, column) local column_params = mw.clone(parameters) column_params.formatting = args["colformat"..column]; if column_params.formatting == "" then column_params.formatting = nil end column_params.convert = args["convert" .. column] if args["case" .. column] then column_params.case = args["case" .. column] end return column_params end

local function getEntityId(frame, unnamed) local args = frame.args or frame local pargs = frame.args and frame:getParent.args local id = args.item or args.from or (unnamed and mw.text.trim(args[1] or '') or nil) if not isSet(id) and pargs then id = pargs.item or pargs.from or (unnamed and mw.text.trim(pargs[1] or '') or nil) end if isSet(id) then if string.find(id, ":") then -- remove prefix as Property:Pid id = mw.text.split(id, ":")[2] end else id = mw.wikibase.getEntityIdForCurrentPage end return id end

-- Main function claim - -- on debug console use: =p.claim{item="Q...", property="P...", ...} function p.claim(frame) local is_sandbox = frame.args and isSet(frame:getParent.args.sandbox) if not required and is_sandbox then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain).claim(frame) end local args = frame.args or frame -- via invoke or require --If a value is already set, use it	if isSet(args.value) then if args.value == 'NONE' then return else return args.value end end -- arguments local id = getEntityId(frame) if id == nil then return end local property = string.upper(args.property or "") local qualifierId = {} qualifierId[1] = isSet(args.qualifier) and string.upper(args.qualifier) or nil local i = 2 while isSet(args["qualifier" .. i]) do qualifierId[i] = string.upper(args["qualifier" .. i]) i = i + 1 end local formatting = isSet(args.formatting) and args.formatting or nil local convert = isSet(args.convert) and args.convert or nil local case = args.case local list = args.list or true; if (list == "false" or list == "no") then list = false end if list == 'firstrank' then list = 'bestrank' end -- alias local sorting_col = args.tablesort local sorting_up = (args.sorting or "") ~= "-1" local separator = isSet(args.separator) and args.separator local conjunction = isSet(args.conjunction) and args.conjunction or separator local rowformat = args.rowformat local references = args.references local showerrors = args.showerrors local default = args.default if default then showerrors = nil end local editicon = not (args.editicon == "false" or args.editicon == "no" or formatting == "raw") local parameters = {["id"] = id, ["property"] = property, ["formatting"] = formatting, ["convert"] = convert, ["list"] = list, ["case"] = case, ["editicon"] = editicon, ["separator"] = separator, ["conjunction"] = conjunction, ["qseparator"] = separator, ["qconjunction"] = conjunction} parameters.lang = findLang(args.lang) -- fetch property local claims = {} local bestrank = (parameters.list == false or parameters.list == 'bestrank') and parameters.list ~= 'lang' for p in string.gmatch(property, 'P%d+') do		claims = getStatements(id, p, bestrank) if #claims > 0 then parameters.property = p			break end end if #claims == 0 then if showerrors then return printError("property-not-found") else return default end end -- defaults for table local preformat, postformat = "", "" local whitelisted, blacklisted = false, false local whitelist, blacklist = {}, {} if parameters.formatting == "table" then parameters.separator = parameters.separator or " " parameters.conjunction = parameters.conjunction or " " parameters.qseparator = ", " parameters.qconjunction = ", " if not rowformat then rowformat = "$0 ($1"			i = 2			while qualifierId[i] do				rowformat = rowformat .. ", $" .. i				i = i + 1			end			rowformat = rowformat .. ")" elseif mw.ustring.find(rowformat, "^[*#]") then parameters.separator = "" parameters.conjunction = "" if mw.ustring.match(rowformat, "^[*#]") == "*" then preformat = "" postformat = "" else preformat = "" postformat = "" end rowformat = mw.ustring.gsub(rowformat, "^[*#] ?", "") end -- set whitelist and blacklist values whitelist, whitelisted = setWhiteOrBlackList("whitelist", #qualifierId, args) blacklist, blacklisted = setWhiteOrBlackList("blacklist", #qualifierId, args) end -- set feminine case if gender is requested local itemgender = args.itemgender local idgender if itemgender then if string.match(itemgender, "^P%d+$") then local snak_id = getSnak(mw.wikibase.getBestStatements(id, itemgender), {1, "mainsnak", "datavalue", "value", "id"}) if snak_id then idgender = snak_id end elseif string.match(itemgender, "^Q%d+$") then idgender = itemgender end end local gender_requested = false if parameters.case == "gender" or idgender then gender_requested = true elseif parameters.formatting == "table" then for i=0, #qualifierId do if args["case" .. i] and args["case" .. i] == "gender" then gender_requested = true break end end end if gender_requested then if feminineGender(idgender or id) then parameters.gender = "feminineform" end end -- get initial sort indices local sortindices = {} for idx in pairs(claims) do		sortindices[#sortindices + 1] = idx end -- sort by claim rank local comparator = function(a, b)		local rankmap = { deprecated = 2, normal = 1, preferred = 0 } local ranka = rankmap[claims[a].rank or "normal"] .. string.format("%08d", a) local rankb = rankmap[claims[b].rank or "normal"] .. string.format("%08d", b)		return ranka < rankb end table.sort(sortindices, comparator) local result, result2 local error if parameters.list or parameters.formatting == "table" then -- convert LF to line feed, may not work on some cases parameters.separator = parameters.separator == "LF" and "\010" or parameters.separator parameters.conjunction = parameters.conjunction == "LF" and "\010" or parameters.conjunction -- i18n separators parameters.separator = parameters.separator or mw.message.new('Comma-separator'):inLanguage(parameters.lang[1]):plain parameters.conjunction = parameters.conjunction or (mw.message.new('And'):inLanguage(parameters.lang[1]):plain .. mw.message.new('Word-separator'):inLanguage(parameters.lang[1]):plain) -- iterate over all elements and return their value (if existing) local value, valueq local sortkey, sortkeyq local values = {} local sortkeys = {} local refs = {} local rowlist = {} -- rows to list with whitelist or blacklist for idx in pairs(claims) do			local claim = claims[sortindices[idx]] local reference = {} if not whitelisted then rowlist[idx] = true end if parameters.formatting == "table" then local params = tableParameters(args, parameters, "0") value, sortkey, error = getValueOfClaim(claim, nil, params) if value then values[#values + 1] = {} sortkeys[#sortkeys + 1] = {} refs[#refs + 1] = {} if whitelist["0"] or blacklist["0"] then local valueraw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=params.lang}) if whitelist["0"] and whitelist["0"][valueraw or ""] then rowlist[#values] = true elseif blacklist["0"] and blacklist["0"][valueraw or ""] then rowlist[#values] = false end end for i, qual in ipairs(qualifierId) do						local j = tostring(i) params = tableParameters(args, parameters, j)						local valueq, sortkeyq, valueraw if qual == property then -- hack for getting the property with another formatting, i.e. colformat1=raw valueq, sortkeyq, _ = getValueOfClaim(claim, nil, params) else for q in mw.text.gsplit(qual, '%s*OR%s*') do								if string.find(q, ".+/.+") then valueq, sortkeyq, valueraw = getValueOfParentClaim(claim, q, params) elseif string.find(q, "^/.+") then local claim2 = getStatements(id, string.sub(q, 2), bestrank) if #claim2 > 0 then valueq, sortkeyq, _ = getValueOfClaim(claim2[1], nil, params) end else valueq, sortkeyq, _ = getValueOfClaim(claim, q, params) end if valueq then break end end end values[#values]["col" .. j] = valueq sortkeys[#sortkeys]["col" .. j] = sortkeyq or valueq if whitelist[j] or blacklist[j] then valueq = valueraw or getValueOfClaim(claim, qual, {["formatting"]="raw", ["lang"]=params.lang}) if whitelist[j] and whitelist[j][valueq or ""] then rowlist[#values] = true elseif blacklist[j] and blacklist[j][valueq or ""] then rowlist[#values] = false end end end end else value, sortkey, error = getValueOfClaim(claim, qualifierId[1], parameters) values[#values + 1] = {} sortkeys[#sortkeys + 1] = {} refs[#refs + 1] = {} end if not value and showerrors then value = error end if value then if references and claim.references then reference = claim.references end refs[#refs]["col0"] = reference values[#values]["col0"] = value sortkeys[#sortkeys]["col0"] = sortkey or value end end -- sort and format results sortindices = {} for idx in pairs(values) do			sortindices[#sortindices + 1] = idx end if sorting_col then local sorting_table = mw.text.split(sorting_col, '%D+') local comparator = function(a, b)				local valuea, valueb local i = 1 while valuea == valueb and i <= #sorting_table do valuea = sortkeys[a]["col" .. sorting_table[i]] or '' valueb = sortkeys[b]["col" .. sorting_table[i]] or '' i = i + 1 end if sorting_up then return valueb > valuea end return valueb < valuea end table.sort(sortindices, comparator) end local maxvals = tonumber(parameters.list) result = {} for idx in pairs(values) do			local valuerow = values[sortindices[idx]] local reference = getReferences({["references"] = refs[sortindices[idx]]["col0"]}, parameters.lang) value = valuerow["col0"] if parameters.formatting == "table" then if not rowlist[sortindices[idx]] then value = nil else local rowformatting = rowformat .. "$" -- fake end character added for easy gsub value = mw.ustring.gsub(rowformatting, "$0", value) value = mw.ustring.gsub(value, "$R0", reference) -- add reference for i, _ in ipairs(qualifierId) do local valueq = valuerow["col" .. i] if args["rowsubformat" .. i] and valueq then -- add fake end character $ -- gsub $i not followed by a number so $1 doesn't match $10, $11... -- remove fake end character valueq = captureEscapes(valueq) valueq = mw.ustring.gsub(args["rowsubformat" .. i] .. "$", "$" .. i .. "(%D)", valueq .. "%1") valueq = string.sub(valueq, 1, -2) rowformatting = mw.ustring.gsub(rowformatting, "$" .. i .. "(%D)", args["rowsubformat" .. i] .. "%1") end valueq = valueq and captureEscapes(valueq) or '' value = mw.ustring.gsub(value, "$" .. i .. "(%D)", valueq .. "%1") end value = string.sub(value, 1, -2) -- remove fake end character value = expandBraces(value, rowformatting) end elseif value then value = expandBraces(value, parameters.formatting) value = value .. reference end if isSet(value) then result[#result + 1] = value if not parameters.list or (maxvals and maxvals == #result) then break end end end -- in a table, add edit icon on last element if parameters.formatting == 'table' and parameters.editicon then result = addEditIconTable(result, parameters) end result = preformat .. mw.text.listToText(result, parameters.separator, parameters.conjunction) .. postformat else -- return first element local claim = claims[sortindices[1]] result, result2, error = getValueOfClaim(claim, qualifierId[1], parameters) if result and references then result = result .. getReferences(claim, parameters.lang) end end if isSet(result) then if i18n.addpencil and not (parameters.formatting == 'table' or (result2 and result2 == 'no-icon')) then -- add edit icon, except table added previously and except explicit no-icon internal flag result = result .. addEditIcon(parameters) end else if showerrors then result = error else result = default end end return result, (required and not is_sandbox) and untranslated or '' end

-- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323) -- which are then linked to http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/01.1.00.005%20Entity%20TA98%20EN.htm -- uses the newer mw.wikibase calls instead of directly using the snaks -- formatPropertyValues returns a table with the P1323 values concatenated with ", " so we have to split them out into a table in order to construct the return string p.getTAValue = function(frame) local ent = mw.wikibase.getEntity local props = ent:formatPropertyValues('P1323') local out = {} local t = {} for k, v in pairs(props) do		if k == 'value' then t = mw.text.split( v, ", ") for k2, v2 in pairs(t) do out[#out + 1] = "[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/" .. string.sub(v2, 2) .. "%20Entity%20TA98%20EN.htm " .. v2 .. "]"			end end end local ret = table.concat(out, " ") if #ret == 0 then ret = "Invalid TA" end return ret end

-- Local functions for getParentValues ---

local function uc_first(word) return mw.ustring.upper(mw.ustring.sub(word, 1, 1)) .. mw.ustring.sub(word, 2) end

local function getPropertyValue(id, property, parameter, langs, editicon, case) local snaks = mw.wikibase.getBestStatements(id, property) local mysnak = getSnak(snaks, {1, "mainsnak"}) if mysnak == nil then return end local entity_id local result = '-' -- default for 'no value' if mysnak.datavalue then entity_id = "Q" .. tostring(mysnak.datavalue.value['numeric-id']) result, _ = getSnakValue(mysnak, {formatting=parameter, lang=langs, editicon=editicon, case=case}) end return entity_id, result end

local function getParentObjects(id,	prop_format,	label_format,	languages, 	propertySupString, 	propertyLabel,	propertyLink,	labelShow,	editicon,	upto,	upto_linkId,	last_only,	grammatical_case,	include_self) if upto_linkId == nil then upto_linkId = "" end local upto_link_ids = {} for q in mw.text.gsplit(upto_linkId, '[^Q%d]') do		upto_link_ids[resolveEntityId(q)] = true end local propertySups = mw.text.split(propertySupString, '[^P%d]') local lastlabel = uc_first(upto or '') local maxloop = tonumber(upto) or ((lastlabel .. upto_linkId) == '' and 10 or 50) local labelFilter = {} if labelShow then for _, v in ipairs(mw.text.split(labelShow, "/")) do			labelFilter[uc_first(v)] = true end end local label_self _, label_self = getPropertyValue(id, propertyLabel, label_format, languages) local result = {} for iter = 1, maxloop do		local link, label, linktext, _id, _link for _, propertySup in pairs(propertySups) do _id, _link = getPropertyValue(id, propertySup, prop_format, languages, editicon, grammatical_case) if _id and _link then id = _id; link = _link break end end if not id or not link then break end if propertyLink then _, linktext = getPropertyValue(id, propertyLink, "label", languages) if linktext then link = mw.ustring.gsub(link, "%[%[(.*)%|.+%]%]", "" .. linktext .. "") end end _, label = getPropertyValue(id, propertyLabel, label_format, languages, false, "infoboxlabel") if labelShow == nil or labelFilter[label] then result[#result + 1] = {label, link} if label then labelFilter[label] = nil -- only first label found end end if label == lastlabel or upto_link_ids[id] then break end end if last_only then result = {result[#result]} end if include_self then table.insert(result, 1, {label_self, mw.title.getCurrentTitle.text}) end return result end

local function parentObjectsToString(result,	rowformat,	cascade,	sorting) local ret = {} local first = 1 local last = #result local iter = 1 if sorting == "-1" then first = #result; last = 1; iter = -1 end for i = first, last, iter do		local rowtext = mw.ustring.gsub(rowformat, "$[01]", {["$0"] = result[i][1], ["$1"] = result[i][2]}) ret[#ret +1] = expandBraces(rowtext, rowformat) end if cascade then local prefix = "" for i = 1, #ret do ret[i] = prefix .. "• " .. ret[i] prefix = prefix .. " "		end end return ret end

-- Returns pairs of instance label and property value fetching a recursive tree function p.getParentValues(frame) if not required and frame.args and frame:getParent.args.sandbox then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain).getParentValues(frame) end local args = frame.args or frame -- via invoke or require local id = getEntityId(frame) if id == nil then return end local languages = findLang(args.lang) local propertySup = args.property; if not isSet(propertySup) then propertySup = "P131" end --administrative entity local propertyLabel = args.label; if not isSet(propertyLabel) then propertyLabel = "P31" end --instance local propertyLink = args.valuetext; if propertyLink == "" then propertyLink = nil end --internallink local property_format = args.formatting; if property_format == "" then property_format = nil end local label_format = args.labelformat; if not isSet(label_format) then label_format = "label" end local upto = args.upto; if upto == "" then upto = nil end local last_only = (args.last_only == "true" or args.last_only == "yes") local labelShow = args.labelshow; if labelShow == "" then labelShow = nil end local editicon = not (args.editicon == "false" or args.editicon == "no") local include_self = (args.include_self == "true" or args.include_self == "yes") local case = args.case; if case == "" then case = nil end if isSet(args.uptolabelid) then upto, _ = getLabelByLangs(args.uptolabelid, languages) end if isSet(args.showlabelid) then local showLabelList = {} for substring in mw.text.gsplit(args.showlabelid, '[^Q%d]') do			table.insert(showLabelList, (getLabelByLangs(substring, languages))) end if #showLabelList > 0 then labelShow = table.concat(showLabelList,"/") end end local result = getParentObjects(id,		property_format,		label_format,		languages, 		propertySup, 		propertyLabel,		propertyLink,		labelShow,		editicon,		upto,		args.uptovalueid or args.uptolinkid,		last_only,		case,		include_self) if #result == 0 then return end local rowformat = args.rowformat; if not isSet(rowformat) then rowformat = "$0 = $1" end local separator = args.separator; if not isSet(separator) then separator = " " end local sorting = args.sorting; if sorting == "" then sorting = nil end local cascade = (args.cascade == "true" or args.cascade == "yes") local ret = parentObjectsToString(result,		rowformat,		cascade,		sorting) ret = addEditIconTable(ret, {property=propertySup, editicon=editicon, id=id, lang=languages}) return mw.text.listToText(ret, separator, separator) end

-- Link with a parent label function p.linkWithParentLabel(frame) if not required and frame.args and isSet(frame:getParent.args.sandbox) then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain).linkWithParentLabel(frame) end local args = {} if frame.args then for k, v in pairs(frame.args) do -- metatable args[k] = v		end else args = frame -- via require end if isSet(args.value) then return args.value end -- get id value of property/qualifier local largs = mw.clone(args) largs.list = tonumber(args.list) and args.list or "true" largs.formatting = "raw" largs.separator = "/·/" largs.editicon = "false" local items_list, _ = p.claim(largs) if not isSet(items_list) then return end local items_table = mw.text.split(items_list, "/·/", true) -- get internal link of property/qualifier largs.formatting = "internallink" local link_list, _ = p.claim(largs) local link_table = mw.text.split(link_list, "/·/", true) -- get label of parent property local parent_claim = getSnak(getStatements(items_table[1], args.parent, true), {1, "mainsnak", "datatype"}) if parent_claim == 'monolingualtext' then largs.formatting = nil largs.list = 'lang' else largs.formatting = "label" largs.list = "false" end largs.property = args.parent largs.qualifier = nil for i, v in ipairs(items_table) do		largs.item = v		local link_label, _ = p.claim(largs) if isSet(link_label) then link_table[i] = mw.ustring.gsub(link_table[i] or '', "%[%[(.*)%|.+%]%]", "" .. link_label .. "") end end args.editicon = not (args.editicon == "false" or args.editicon == "no") args.id = getEntityId(frame) args.lang = findLang(args.lang) return mw.text.listToText(link_table) .. addEditIcon(args) end

-- Calculate number of years old function p.yearsOld(frame) if not required and frame.args and frame:getParent.args.sandbox then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain).yearsOld(frame) end local args = frame.args or frame -- via invoke or require local id = getEntityId(frame) local lang = mw.language.new('en') local function getBestValue(id, prop) local snak_value = getSnak(mw.wikibase.getBestStatements(id, prop), {1, "mainsnak", "datavalue", "value"}) return snak_value end local birth = getBestValue(id, 'P569') if type(birth) ~= 'table' or birth.time == nil or birth.precision == nil or birth.precision < 8 then return end local death = getBestValue(id, 'P570') if type(death) ~= 'table' or death.time == nil or death.precision == nil then death = {['time'] = lang:formatDate('c'), ['precision'] = 11} -- current date elseif death.precision < 8 then return end local dates = {} dates[1] = {['min'] = {}, ['max'] = {}, ['precision'] = birth.precision} dates[1].min.year = tonumber(mw.ustring.match(birth.time, "^[+-]?%d+")) dates[1].min.month = tonumber(mw.ustring.match(birth.time, "\-(%d%d)\-")) dates[1].min.day = tonumber(mw.ustring.match(birth.time, "\-(%d%d)T")) dates[1].max = mw.clone(dates[1].min) dates[2] = {['min'] = {}, ['max'] = {}, ['precision'] = death.precision} dates[2].min.year = tonumber(mw.ustring.match(death.time, "^[+-]?%d+")) dates[2].min.month = tonumber(mw.ustring.match(death.time, "\-(%d%d)\-")) dates[2].min.day = tonumber(mw.ustring.match(death.time, "\-(%d%d)T")) dates[2].max = mw.clone(dates[2].min) for i, d in ipairs(dates) do		if d.precision == 10 then -- month d.min.day = 1 local timestamp = string.format("%04d", tostring(math.abs(d.max.year))) .. string.format("%02d", tostring(d.max.month)) .. "01"			d.max.day = tonumber(lang:formatDate("j", timestamp .. " + 1 month - 1 day")) elseif d.precision < 10 then -- year or decade d.min.day = 1 d.min.month = 1 d.max.day = 31 d.max.month = 12 if d.precision == 8 then -- decade d.max.year = d.max.year + 9 end end end local function age(d1, d2) local years = d2.year - d1.year if d2.month < d1.month or (d2.month == d1.month and d2.day < d1.day) then years = years - 1 end if d2.year > 0 and d1.year < 0 then years = years - 1 -- no year 0 end return years end local old_min = age(dates[1].max, dates[2].min) local old_max = age(dates[1].min, dates[2].max) local old, old_expr if old_min == 0 and old_max == 0 then old = "< 1" old_max = 1 -- expression in singular elseif old_min == old_max then old = old_min else old = old_min .. "/" .. old_max end if args.formatting == 'unit' then local langs = findLang(args.lang) local yo, yo_sg, yo_pl, yo_pau if langs[1] == wiki.langcode then yo_sg = i18n["years-old"].singular yo_pl = i18n["years-old"].plural yo_pau = i18n["years-old"].paucal end if not isSet(yo_pl) then yo_pl, _ = getLabelByLangs('Q24564698', langs) yo_sg = yo_pl end if not isSet(yo_pau) then yo_pau = yo_pl end yo = mw.language.new(langs[1]):plural(old_max, {yo_sg, yo_pau, yo_pl}) if mw.ustring.find(yo, '$1', 1, true) then old_expr = mw.ustring.gsub(yo, "$1", old) else old_expr = old .. ' ' .. yo		end elseif args.formatting then old_expr = expandBraces(mw.ustring.gsub(args.formatting, '$1', old), args.formatting) else old_expr = old end return old_expr end

-- Gets a label in a given language (content language by default) or its fallbacks, optionnally linked. function p.getLabel(frame) if not required and frame.args and frame:getParent.args.sandbox then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain).getLabel(frame) end local args = frame.args or frame -- via invoke or require local id = getEntityId(frame, true) if id == nil then return end local editicon = not (args.editicon == "false" or args.editicon == "no") and mw.wikibase.isValidEntityId(id) local label_icon = '' local label, lang if args.label then label = args.label else local languages = findLang(args.lang) -- exceptions or labels fixed local exist, labels = pcall(require, wiki.module_title .. "/labels" .. (languages[1] == wiki.langcode and '' or '/' .. languages[1])) if exist and next(labels.infoboxLabelsFromId) ~= nil then label = labels.infoboxLabelsFromId[id] end if label == nil then label, lang = getLabelByLangs(id, languages) if label then if args.itemgender and feminineGender(args.itemgender) then label = feminineForm(id, lang) or label end label = mw.language.new(lang):ucfirst(mw.text.nowiki(label)) -- sanitize end label_icon = addLabelIcon(id, lang, languages[1], editicon) end end local linked = args.linked if isSet(linked) and linked ~= "no" then local article = mw.wikibase.getSitelink(id) or ("massey:Special:EntityPage/" .. id) return "" .. (label or id) .. "" .. label_icon, not required and '' or untranslated else return (label or id) .. label_icon, not required and '' or untranslated end end

-- Utilities - -- See also module ../debug.

-- Copied from Module:Wikibase function p.getSiteLink(frame) local id = getEntityId(frame, 1) if id == nil then return end return mw.wikibase.getSitelink(id, mw.text.trim(frame.args[2] or '')) end

-- Helper function for the default language code used function p.lang(frame) local lang = frame and frame.args[1] -- nil via require return findLang(lang)[1] end

-- Number of statements function p.numStatements(frame) local id = getEntityId(frame) if id == nil then return 0 end local prop = mw.text.trim(frame.args[1]) local num = {} if frame.args[2] then -- qualifier local qual = mw.text.trim(frame.args[2]) local values = p.claim{item=id, property=prop, qualifier=qual, formatting='raw', separator='/·/'} if values then num = mw.text.split(values, '/·/') end else num = mw.wikibase.getBestStatements(id, prop) end return #num end

-- Returns true if property datavalue is found excluding novalue/somevalue function p.validProperty(frame) local property = mw.text.trim(frame.args[1]) local item = getEntityId(frame) if item == nil then return end local prop_data = getSnak(mw.wikibase.getBestStatements(item, property), {1, "mainsnak", "datavalue"}) return prop_data and true or nil end

return p