Super

-- module local variables local wiki = {	langcode = mw.language.getContentLanguage.code }

-- internationalisation local i18n = {	["errors"] = {		["property-not-found"] = "Eigenschaft nicht gefunden.", ["entity-not-found"] = "Wikidata-Eintrag nicht gefunden.", ["unknown-claim-type"] = "Unbekannter Aussagentyp.", ["unknown-entity-type"] = "Unbekannter Entity-Typ.", ["qualifier-not-found"] = "Qualifikator nicht gefunden.", ["site-not-found"] = "Wikimedia-Projekt nicht gefunden.", ["invalid-parameters"] = "Ungültige Parameter.", ["module-not-loaded"] = "Loading of additional module failed." },	["datetime"] = {		-- $1 is a placeholder for the actual number [0] = "$1 Mrd. Jahren",		-- precision: billion years [1] = "$100 Mio. Jahren",	-- precision: hundred million years [2] = "$10 Mio. Jahren",	-- precision: ten million years [3] = "$1 Mio. Jahren",		-- precision: million years [4] = "$100.000 Jahren",	-- precision: hundred thousand years [5] = "$10.000 Jahren",		-- precision: ten thousand years [6] = "$1. Jahrtausend", 	-- precision: millenium [7] = "$1. Jahrhundert",	-- precision: century [8] = "$1er",				-- precision: decade -- the following use the format of #time parser function [9] = "Y",					-- precision: year, [10] = "F Y",				-- precision: month [11] = "j. F Y",			-- precision: day [12] = 'j. F Y, G "Uhr"',	-- precision: hour [13] = "j. F Y G:i",		-- precision: minute [14] = "j. F Y G:i:s",		-- precision: second ["beforenow"] = "vor $1",	-- how to format negative numbers for precisions 0 to 5 ["afternow"] = "in $1",		-- how to format positive numbers for precisions 0 to 5 ["bc"] = '$1 "v.Chr."',		-- how print negative years ["ad"] = "$1"				-- how print positive years },	["monolingualtext"] = ' %text ', ["FETCH_WIKIDATA"] = "ABFRAGE_WIKIDATA" }

--important properties local propertyId = {	["starttime"] = "P580", ["endtime"] = "P582" }

local formatchar = {	[10] = {"n","m","M","F","xg"},				--precision: month [11] = {"W","j","d","z","D","l","N","w"},	--precision: day [12] = {"a","A","g","h","G","H"},			--precision: hour [13] = {"i"},								--precision: minute [14] = {"s","U"}							--precision: second }

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

-- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field -- use these as the second parameter and this function instead of the built-in "pairs" function -- to iterate over all qualifiers and snaks in the intended order. local function orderedpairs(array, order) if not order then return pairs(array) end

-- return iterator function local i = 0 return function i = i + 1 if order[i] then return order[i], array[order[i]] end end end

-- Function to check whether a certain item is a parent of a given item. -- If pExitItem is reached without finding the searched parent item, the search stops. -- A parent is connected via P31 or P279. -- Attention: very intensive function, use carefully! local function isParent(pItem, pParent, pExitItem, pMaxDepth, pDepth) if not pDepth then pDepth = 0 end

if type(pItem) == "number" then pItem = "Q" .. pItem end

local entity = mw.wikibase.getEntity(pItem) if not entity then return false end

local claims31 local claims279 if entity.claims then claims31 = entity.claims[mw.wikibase.resolvePropertyId('P31')] claims279 = entity.claims[mw.wikibase.resolvePropertyId('P279')] else return false end if not claims31 and not claims279 then return false end

local parentIds = {} if claims31 and #claims31 > 0 then for i, v in ipairs(claims31) do parentIds[#parentIds+1] = getSnakValue(v.mainsnak, "numeric-id") end end if claims279 and #claims279 > 0 then for i, v in ipairs(claims279) do parentIds[#parentIds+1] = getSnakValue(v.mainsnak, "numeric-id") end end

-- check if searched parent or exit item is reached or do recursive call if not parentIds[1] or #parentIds == 0 then return false end local itemString = "" local result = nil for i, v in ipairs(parentIds) do		if not v then return false end itemString = "Q" .. v

if itemString == pParent then -- successful! return true elseif itemString == pExitItem or itemString == "Q35120" then -- exit if either "exit item" or node item (Q35120) is reached return false else if pDepth+1 < pMaxDepth then result = isParent(itemString, pParent, pExitItem, pMaxDepth, pDepth+1) else return false end

if result == true then return result end end end do return false end end

local function printDatavalueCoordinate(data, parameter) -- data fields: latitude [double], longitude [double], altitude [double], precision [double], globe [wikidata URI, usually http://www.wikidata.org/entity/Q2 [earth]] if parameter then if parameter == "globe" then data.globe = mw.ustring.match(data.globe, "Q%d+") end -- extract entity id from the globe URI return data[parameter] else return data.latitude .. "/" .. data.longitude -- combine latitude and longitude, which can be decomposed using the #titleparts wiki function end end

local function printDatavalueQuantity(data, parameter) -- data fields: amount [number], unit [string], upperBound [number], lowerBound [number] if not parameter or parameter == "amount" then return tonumber(data.amount) elseif parameter == "unit" then return mw.ustring.match(data.unit, "Q%d+") else return data[parameter] end end

local function normalizeDate(date) date = mw.text.trim(date, "+") -- extract year local yearstr = mw.ustring.match(date, "^\-?%d+") local year = tonumber(yearstr) -- remove leading zeros of year return year .. mw.ustring.sub(date, #yearstr + 1), year end

-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millenia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second function formatDate(date, precision, timezone, formatstr) precision = precision or 11 date, year = normalizeDate(date) date = string.gsub(date, "-00%f[%D]", "-01") if year == 0 and precision <= 9 then return "" end

-- precision is 10000 years or more if precision <= 5 then local factor = 10 ^ ((5 - precision) + 4) local y2 = math.ceil(math.abs(year) / factor) local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2)) if year < 0 then relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative) else relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative) end return relative end

-- precision is decades, centuries and millenia local era if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end if precision == 7 then era = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(math.floor((math.abs(year) - 1) / 100) + 1)) end if precision == 8 then era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(year) / 10) * 10)) end if era then if year < 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)		elseif year > 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) end return era end

-- precision is years or less if precision >= 9 then -- the following code replaces the UTC suffix with the given negated timezone to convert the global time to the given local time		timezone = tonumber(timezone)		if timezone and timezone ~= 0 then			timezone = -timezone			timezone = string.format("%.2d%.2d", timezone / 60, timezone % 60)			if timezone[1] ~= '-' then timezone = "+" .. timezone end			date = mw.text.trim(date, "Z") .. " " .. timezone		end		-- if formatstr then for i=(precision+1), 14 do				for _, ch in pairs(formatchar[i]) do					if formatstr:find(ch) then formatstr = i18n.datetime[precision] end end end else formatstr = i18n.datetime[precision] end if year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "") elseif year < 0 then -- Mediawiki formatDate doesn't support negative years date = mw.ustring.sub(date, 2) formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.bc, "$1", i18n.datetime[9])) elseif year > 0 and i18n.datetime.ad ~= "$1" then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.ad, "$1", i18n.datetime[9])) end return mw.language.new(wiki.langcode):formatDate(formatstr, date) end end

local function printDatavalueTime(data, parameter) -- data fields: time [ISO 8601 time], timezone [int in minutes], before [int], after [int], precision [int], calendarmodel [wikidata URI] --  precision: 0 - billion years, 1 - hundred million years, ..., 6 - millenia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second --  calendarmodel: e.g. http://www.wikidata.org/entity/Q1985727 for the proleptic Gregorian calendar or http://www.wikidata.org/wiki/Q11184 for the Julian calendar] if parameter then para, formatstr = parameter:match("([^:]+):([^:]+)") if parameter == "calendarmodel" then data.calendarmodel = string.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI elseif para and para == "time" then return formatDate(data.time, data.precision, data.timezone,formatstr) elseif parameter == "time" then data.time = normalizeDate(data.time) end return data[parameter] else return formatDate(data.time, data.precision, data.timezone) end end

local function printDatavalueEntity(data, parameter) -- data fields: entity-type [string], numeric-id [int, Wikidata id] local id

if data["entity-type"] == "item" then id = "Q" .. data["numeric-id"] elseif data["entity-type"] == "property" then id = "P" .. data["numeric-id"] else return printError("unknown-entity-type") end

if parameter then if parameter == "link" then local linkTarget = mw.wikibase.sitelink(id) local linkName = mw.wikibase.label(id) if linkTarget then local link = linkTarget -- if there is a local Wikipedia article linking to it, use the label or the article title if linkName and (linkName ~= linkTarget) then link = link .. "|" .. linkName end return "" .. link .. "" else -- if there is no local Wikipedia article output the label or link to the Wikidata object to input a proper label if linkName then return linkName else return "" .. id .. "" end end else return data[parameter] end else return mw.wikibase.label(id) or id	end end

local function printDatavalueMonolingualText(data, parameter) -- data fields: language [string], text [string] if parameter then return data[parameter] else local result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"]) return result end end

function getSnakValue(snak, parameter) -- snaks have three types: "novalue" for null/nil, "somevalue" for not null/not nil, or "value" for actual data if snak.snaktype == "value" then -- call the respective snak parser if snak.datavalue.type == "string" then return snak.datavalue.value elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter) elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter) elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter) elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter) elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter) end end return mw.wikibase.renderSnak(snak) end

function getQualifierSnak(claim, qualifierId) -- 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 and claim.qualifiers then local qualifier = claim.qualifiers[qualifierId] if qualifier then return qualifier[1] end end return nil, printError("qualifier-not-found") else -- otherwise return the main snak return claim.mainsnak end end

local function datavalueTimeToDateObject(data) local sign, year, month, day, hour, minute, second = string.match(data.time, "(.)(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)Z") local result = {		year = tonumber(year), month = tonumber(month), day = tonumber(day), hour = tonumber(hour), min = tonumber(minute), sec = tonumber(second), timezone = data.timezone, julian = data.calendarmodel and string.match(data.calendarmodel, "Q11184$") }	if sign == "-" then result.year = -result.year end return result end

function julianDay(dateObject) local year = dateObject.year local month = dateObject.month or 0 local day = dateObject.day or 0

if month == 0 then month = 1 end if day == 0 then day = 1 end if month <= 2 then year = year - 1 month = month + 12 end

local time = ((((dateObject.sec or 0) / 60 + (dateObject.min or 0) + (dateObject.timezone or 0)) / 60) + (dateObject.hour or 0)) / 24

local b	if dateObject.julian then b = 0 else local century = math.floor(year / 100) b = 2 - century + math.floor(century / 4) end

return math.floor(365.25 * (year + 4716)) + math.floor(30.6001 * (month + 1)) + day + time + b - 1524.5 end

function getQualifierSortValue(claim, qualifierId) local snak = getQualifierSnak(claim, qualifierId) if snak and snak.snaktype == "value" then if snak.datavalue.type == "time" then return julianDay(datavalueTimeToDateObject(snak.datavalue.value)) else return getSnakValue(snak) end end end

function getValueOfClaim(claim, qualifierId, parameter) local error local snak snak, error = getQualifierSnak(claim, qualifierId) if snak then return getSnakValue(snak, parameter) else return nil, error end end

function formatReference(ref) -- "imported from"-references are useless, skip them: if ref["P143"] then return nil end -- load Modul:Zitation local ZitationSuccess, r = pcall(require, "Modul:Zitation") if type(r) == "table" then Zitation = r.Zitation end -- assert (ZitationSuccess, i18n["errors"]["module-not-loaded"]) -- assignments of Wikidata properties to Zitation parameters local wdZmap = { P1433 = {"bas", "Werk"}, P248 = {"bas", "Werk"}, P1476 = {"bas", "Titel"}, P1680 = {"bas", "TitelErg"}, P407 = {"bas", "Sprache"}, P364 = {"bas", "Sprache"}, P2439 = {"bas", "Sprache"}, P123 = {"bas", "Verlag"}, P577 = {"bas", "Datum"}, P98  = {"bas", "Hrsg"}, P2093 = {"bas", "Autor"}, P50  = {"bas", "Autor"}, P1683 = {"bas", "Zitat"}, P854 = {"www", "URL"}, P813 = {"www", "Abruf"}, P1065 = {"www", "ArchivURL"}, P2960 = {"www", "ArchivDatum"}, P2701 = {"www", "Format"}, P393 = {"print", "Auflage"}, P291 = {"print", "Ort"}, P304 = {"fragment", "Seiten"}, P792 = {"fragment", "Kapitel"}, P629 = {"orig", "Titel"} }	for prop, value in pairs(ref) do		if wdZmap[prop] then if type(value) == "table" then -- More snaks with same property, we concatenate using a comma value = table.concat(value, ", ") end -- value should be string now, so we can call Zitation if type(value) == "string" and string.len(value) > 0 then Zitation.fill(wdZmap[prop][1], wdZmap[prop][2], value, prop) end end end -- if no title on Wikidata, try to use the URL as title if (not ref["P1476"]) and ref["P854"] then local URLutil = Zitation.fetch("URLutil") Zitation.fill("bas", "Titel", URLutil.getHost(ref["P854"])) elseif not (ref["P1476"]) then Zitation.fill("bas", "Titel", ref["P854"]) end refFormatted, f = Zitation.format return refFormatted end

function getReferences(frame, claim) local result = "" -- traverse through all references for ref in pairs(claim.references or {}) do		local refTable = {} for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) do			if #snakval == 1 then refTable[snakkey] = getSnakValue(snakval[1]) else --				multival = {} for snakidx = 1, #snakval do						table.insert(multival, getSnakValue(snakval[snakidx])) end refTable[snakkey] = multival end end local formattedRef = formatReference(refTable) if formattedRef then result = result .. frame:extensionTag("ref", formattedRef) end end return result end

local function hasqualifier(claim, qualifierproperty) local invert if string.sub(qualifierproperty, 1, 1) == "!" then invert = true else invert = false end if not claim.qualifiers and not invert then return false end if not claim.qualifiers and invert then return true end if qualifierproperty == '' then return true end if not invert and not claim.qualifiers[qualifierproperty] then return false end if invert and claim.qualifiers[string.sub(qualifierproperty, 2)] then return false end return true end

local function qualifierhasvalue(claim, property, value) if not claim.qualifiers then return false end if not claim.qualifiers[property] then return false end for key, snak in pairs(claim.qualifiers[property]) do		if snak.snaktype == "value" then if snak.datavalue.type == "wikibase-entityid" then if snak.datavalue.value.id == value then return true end --TODO: elseif other types end end end return false end

local function hassource(claim, sourceproperty) if not claim.references then return false end if sourceproperty == '' then return true end if string.sub(sourceproperty,1,1) ~= "!" then for _, source in pairs(claim.references) do			if source.snaks[sourceproperty] then return true end end return false else for _, source in pairs(claim.references) do			for key in pairs(source.snaks) do				if key ~= string.sub(sourceproperty,2) then return true end end end return false end end

function atdate(claim, mydate) local refdate if not mydate or mydate == "" then refdate = os.date("!*t") else if string.match(mydate, "^%d+$") then refdate = { year = tonumber(mydate) } else refdate = datavalueTimeToDateObject({ time = mw.language.getContentLanguage:formatDate("+Y-m-d\\TH:i:s\\Z", mydate) }) end end local refjd = julianDay(refdate)

local mindate = getQualifierSortValue(claim, propertyId["starttime"]) local maxdate = getQualifierSortValue(claim, propertyId["endtime"])

if mindate and mindate > refjd then return false end if maxdate and maxdate < refjd then return false end

return true end

--returns a table of claims excluding claims not passed the filters function filterClaims(frame, claims) local function filter(condition, filterfunction) if not frame.args[condition] then return end local newclaims = {} for i, claim in pairs(claims) do			if filterfunction(claim, frame.args[condition]) then table.insert(newclaims, claim) end end claims = newclaims end

filter('hasqualifier', hasqualifier) filter('hassource', hassource) filter('atdate', atdate) for key, val in pairs(frame.args) do		if type(key) == "number" and key > 2 and key % 2 == 1 then -- key = 3, 5, 7 and so on			local newclaims = {} for i, claim in pairs(claims) do				if qualifierhasvalue(claim, frame.args[key - 1], frame.args[key]) then table.insert(newclaims, claim) end end claims = newclaims end end return claims end

local p = {}

function p.isSubclass(frame) if not frame.args["parent"] then return "" end

local maxDepth maxDepth = frame.args["maxDepth"] or 5 if not type(maxDepth) == "number" then maxDepth = 5 end

local result result = isParent(frame.args["id"], frame.args["parent"], frame.args["exitItem"], maxDepth) if frame.args["returnInt"] then if result == true then return 1 else return "" end else if result then return result else return false end end end

function p.descriptionIn(frame) local langcode = frame.args[1] local id = frame.args[2] -- return description of a Wikidata entity in the given language or the default language of this Wikipedia site local entity = mw.wikibase.getEntity(id) if entity and entity.descriptions then local desc = entity.descriptions[langcode or wiki.langcode] if desc then return desc.value end else return ""; end end

function p.labelIn(frame) local langcode = frame.args[1] local id = frame.args[2] -- return label of a Wikidata entity in the given language or the default language of this Wikipedia site local entity = mw.wikibase.getEntity(id) if entity and entity.labels then local label = entity.labels[langcode or wiki.langcode] if label then return label.value end else return ""; end end

function p.claim(frame) local property = frame.args[1] or "" local id = frame.args["id"] local qualifierId = frame.args["qualifier"] local parameter = frame.args["parameter"] local language = frame.args["language"] local list = frame.args["list"] local includeempty = frame.args["includeempty"] local listMaxItems = tonumber(frame.args["listMaxItems"]) or 0 local references = frame.args["references"] local sort = frame.args["sort"] local sortInItem = frame.args["sortInItem"] local inverse = frame.args["inverse"] local showerrors = frame.args["showerrors"] local default = frame.args["default"] if default then showerrors = nil end

-- get wikidata entity local entity = mw.wikibase.getEntity(id) if not entity then if showerrors then return printError("entity-not-found") else return default end end -- fetch the first claim of satisfying the given property local claims if entity.claims then claims = entity.claims[mw.wikibase.resolvePropertyId(property)] end if not claims or not claims[1] then if showerrors then return printError("property-not-found") else return default end end

--filter claims claims = filterClaims(frame, claims) if not claims[1] then return default end

-- get initial sort indices local sortindices = {} for idx in pairs(claims) do		sortindices[#sortindices + 1] = idx end

local comparator if sort then -- sort by time qualifier comparator = function(a, b)			local timea = getQualifierSortValue(claims[a], sort) local timeb = getQualifierSortValue(claims[b], sort) if type(timea) ~= type(timeb) and not (tonumber(timea) and tonumber(timeb)) then if tonumber(timea) then return true elseif tonumber(timeb) then return false elseif tostring(timea) and tostring(timeb) then if inverse then return tostring(timea) > tostring(timeb) else return tostring(timea) < tostring(timeb) end else return false end -- different types, neither numbers nor strings, no chance to compare => random result to avoid script error elseif tonumber(timea) and tonumber(timeb) then timea = tonumber(timea) timeb = tonumber(timeb) end if inverse then return timea > timeb else return timea < timeb end end elseif sortInItem then -- fill table sortkeys local sortkeys = {} local snakSingle local sortkeyValueId local claimContainingValue for idx, claim in pairs(claims) do			snakSingle = getQualifierSnak(claim) sortkeyValueId = "Q" .. getSnakValue(snakSingle, "numeric-id") claimContainingValue = mw.wikibase.getEntity(sortkeyValueId).claims[mw.wikibase.resolvePropertyId(sortInItem)] if claimContainingValue then sortkeys[#sortkeys + 1] = getValueOfClaim(claimContainingValue[1]) else sortkeys[#sortkeys + 1] = "" end end comparator = function(a, b)			if inverse then return sortkeys[a] > sortkeys [b] else return sortkeys[a] < sortkeys [b] end end else -- sort by claim rank 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 end table.sort(sortindices, comparator)

local result local error if list then list = string.gsub(list, "\\n", "\n") -- if a newline is provided (whose backslash will be escaped) unescape it		local value -- iterate over all elements and return their value (if existing) result = {} for idx in pairs(claims) do			local claim = claims[sortindices[idx]] value, error = getValueOfClaim(claim, qualifierId, parameter) if not value and value ~= 0 and showerrors then value = error end if not value and value ~= 0 and includeempty then value = "" end if value and references then value = value .. getReferences(frame, claim) end result[#result + 1] = value end if listMaxItems and listMaxItems > 0 then result = table.concat(result, list, 1, math.min(table.getn(result), listMaxItems)) else result = table.concat(result, list) end else -- return first element

local claim = claims[sortindices[1]] if language and claim.mainsnak.datatype == "monolingualtext" then -- iterate over claims to find adequate language for idx, claim in pairs(claims) do				if claim.mainsnak.datavalue.value.language == language then result, error = getValueOfClaim(claim, qualifierId, parameter) break end end else result, error = getValueOfClaim(claim, qualifierId, parameter) end if references == "only" then result = getReferences(frame, claim) elseif result and references then result = result .. getReferences(frame, claim) end end

if result then return result else if showerrors then return error else return default end end end

function p.getValue(frame) local param = frame.args[2] if param == "FETCH_WIKIDATA" or param == i18n["FETCH_WIKIDATA"] then return p.claim(frame) else return param end end

function p.pageId(frame) local id = frame.args[1] local entity = mw.wikibase.getEntity(id) if not entity then return "" else return entity.id end end

function p.labelOf(frame) local id = frame.args[1] -- returns the label of the given entity/property id	-- if no id is given, the one from the entity associated with the calling Wikipedia article is used if not id then local entity = mw.wikibase.getEntity if not entity then return printError("entity-not-found") end id = entity.id	end return mw.wikibase.label(id) end

function p.sitelinkOf(frame) local id = frame.args[1] -- returns the Wikipedia article name of the given entity -- if no id is given, the one from the entity associated with the calling Wikipedia article is used if not id then local entity = mw.wikibase.getEntity if not entity then return printError("entity-not-found") end id = entity.id	end return mw.wikibase.sitelink(id) end

function p.badges(frame) local site = frame.args[1] local id = frame.args[2] if not site then return printError("site-not-found") end local entity = mw.wikibase.getEntity(id) if not entity then return printError("entity-not-found") end local badges = entity.sitelinks[site].badges if badges then local result for idx = 1, #badges do if result then result = result .. "/" .. badges[idx] else result = badges[idx] end end return result end end

function p.sitelinkCount(frame) local filter = "^.*" .. (frame.args[1] or "") .. "$"	local id = frame.args[2]

local entity = mw.wikibase.getEntity(id) local count = 0 if entity and entity.sitelinks then for project, _ in pairs(entity.sitelinks) do			if string.find(project, filter) then count = count + 1 end end end return count end

-- call this in cases of script errors within a function instead of call function p.debug(frame) local func = frame.args[1] if func then -- create new parameter set, where the first parameter with the function name is removed local newargs = {} for key, val in pairs(frame.args) do			if type(key) == "number" then if key > 1 then newargs[key - 1] = val end else newargs[key] = val end end frame.args = newargs local status, result = pcall(p[func], frame) -- if status then return tostring(result) or "" else return ' ' .. result .. ' ' end -- revert if status then return result else return ' ' .. result .. ' ' end else return printError("invalid-parameters") end end

function p.printEntity(frame) local id = frame.args[1] local entity = mw.wikibase.getEntity(id) if entity then return " " .. mw.text.jsonEncode(entity, mw.text.JSON_PRETTY) .. " " end end

return p