Zum Inhalt springen

Modul:Wikidata

aus Wikipedia, der freien Enzyklopädie
Dies ist eine alte Version dieser Seite, zuletzt bearbeitet am 10. September 2025 um 07:47 Uhr durch Hgzh (Diskussion | Beiträge) (Datenfunktionen und isParent/isSubclass dokumentiert und teils vereinfacht, kleinere Anpassungen). Sie kann sich erheblich von der aktuellen Version unterscheiden.
Vorlagenprogrammierung Diskussionen Lua Test Unterseiten
Modul Deutsch

Modul: Dokumentation

Diese Seite enthält Code in der Programmiersprache Lua. Einbindungszahl Cirrus


-- 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.",
		["entity-not-valid"] = "Die an die Wikidata-Schnittstelle übergebene Item-ID ist nicht gültig.",
		["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."
	},
	["maintenance-pages"] =
	{
		["entity-not-found"] = "Wikidata/Wartung/Fehlendes Datenobjekt",
		["entity-not-valid"] = "Wikidata/Wartung/Ungültige Datenobjekt-Identifikationsnummer",
		["property-not-existing"] = "Wikidata/Wartung/Eigenschaft existiert nicht"
	},
	["datetime"] =
	{
		-- $1 is a placeholder for the actual number
		[0] = "%s Mrd. Jahren",		-- precision: billion years
		[1] = "%s00 Mio. Jahren",	-- precision: hundred million years
		[2] = "%s0 Mio. Jahren",	-- precision: ten million years
		[3] = "%s Mio. Jahren",		-- precision: million years
		[4] = "%s00.000 Jahren",	-- precision: hundred thousand years
		[5] = "%s0.000 Jahren",		-- precision: ten thousand years
		[6] = "%s. Jahrtausend", 	-- precision: millenium
		[7] = "%s. Jahrhundert",	-- precision: century
		[8] = "%ser",				-- 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 %s",	-- how to format negative numbers for precisions 0 to 5
		["afternow"] = "in %s",		-- how to format positive numbers for precisions 0 to 5
		["bc"] = '%s "v.Chr."',		-- how print negative years
		["ad"] = "%s"				-- how print positive years
	},
	["monolingualtext"] = '<span lang="%s">%s</span>',
	["errortext"] = '<span class="error">%s</span>',
	["FETCH_WIKIDATA"] = "ABFRAGE_WIKIDATA"
}

local numberIsParentCalls = 0 -- global value to count calls of expensive function isParent, including recursive calls

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

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
}

--[=[
  ==============================================================================
  GENERAL HELPERS
  ==============================================================================
]=]

--[=[
  print an error with the given error code
  
  parameters:
  - code (string): error code
  - info (opt, string): additional information

  returns: (string) error html
]=]
local function printError( code, info )
	local text
	
	text = i18n.errors[ code ] or code or ""
	if info then
		text = text .. ": " .. info
	end
	
	return mw.ustring.format( i18n.errortext, text )
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.
  
  parameters:
  - array (table): table to get items from
  - order (opt, table): order table
  
  returns: (function) iterator function
]=]
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

--[=[
  return the size of the given table
  
  parameters:
  - tbl (table): table to get size of
  
  returns: (number) number of pairs in table
]=]
local function tableSize( tbl )
	if type( tbl ) ~= "table" then
		return 0
	end
	
	local count = 0
	for _ in pairs( tbl ) do
		count = count + 1
	end
	
	return count
end

--[=[
  ==============================================================================
  WIKIBASE CLIENT LUA API HELPERS
  ==============================================================================
]=]

--[=[
  check wheter the given identifier is a valid entity id, typesafe
  
  parameters:
  - id (any): entity id
  
  returns: (bool) entity id valid or invalid
]=]
local function isValidEntityIdTypesafe( id )
	return ( type( id ) == "string" and mw.wikibase.isValidEntityId( id ) )
end

--[=[
  check wheter the entity with the given id exists, typesafe
  
  parameters:
  - id (any): entity id
  
  returns: (bool) entity id exists or not
]=]
local function entityExistsTypesafe( id )
	return ( isValidEntityIdTypesafe( id ) and mw.wikibase.entityExists( id ) )
end

--[=[
  return the entity id of the current page if given id is invalid
  
  parameters:
  - id (string|nil): entity id
  
  returns: (string|nil) entity id of current page or nil if page is not connected
]=]
local function getConnectedEntityIdIfInvalid( id )
	if isValidEntityIdTypesafe( id ) then
		return id
	else
		return mw.wikibase.getEntityIdForCurrentPage()
	end
end

--[=[
  return the entity id of the current page if given id does not exist
  
  parameters:
  - id (string|nil): entity id
  
  returns: (string|nil) entity id of current page or nil if page is not connected
]=]
local function getConnectedEntityIdIfInexisting( id )
	if entityExistsTypesafe( id ) then
		return id
	else
		return mw.wikibase.getEntityIdForCurrentPage()
	end
end

--[=[
  extract the first entity id from the given string
  
  parameters:
  - text (string): input string
  - etype (opt, string): entity type, "Q" for items and "P" for properties
  
  returns: (string|nil) entity id or nil if no qid found
]=]
local function extractEntityId( text, etype )
	if not etype then
		etype = "Q"
	end
	return mw.ustring.match( text, etype .. "%d+" )
end

--[=[
  ==============================================================================
  DATETIME HELPERS
  ==============================================================================
]=]

--[=[
  transform date to normalized form
  
  parameters:
  - date (string): date
  
  returns: (string) normalized date
           (int) year
]=]
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

--[=[
  create date string from date
  
  parameters:
  - date (string): date string
  - precision (number): date precision
  - timezone (string): date timezone
  - formatstr (string): formatter string for MediaWiki's formatDate function
  
  returns: (string) date string
]=]
function formatDate( date, precision, timezone, formatstr )
	
	-- precisions: 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
	-- standard precision is days
	precision = precision or 11
	
	-- get normalized date and year
	date, year = normalizeDate( date )
	
	-- set to first day
	date = string.gsub( date, "-00%f[%D]", "-01" )
	
	-- min year precision required, but date has no year given
	if precision <= 9 and year == 0 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.format( i18n.datetime[ precision ], tostring( y2 ) )
		if year < 0 then
			relative = mw.ustring.format( i18n.datetime.beforenow, relative )
		else
			relative = mw.ustring.format( i18n.datetime.afternow, relative )
		end
		return relative
	end

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

	-- precision is years or less
	if precision >= 9 then
		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's formatDate doesn't support negative years
			date = mw.ustring.sub( date, 2 )
			formatstr = mw.ustring.gsub( formatstr, i18n.datetime[ 9 ], mw.ustring.format( i18n.datetime.bc, i18n.datetime[ 9 ] ) )
		elseif year > 0 and i18n.datetime.ad ~= "%s" then
			formatstr = mw.ustring.gsub( formatstr, i18n.datetime[ 9 ], mw.ustring.format( i18n.datetime.ad, i18n.datetime[ 9 ] ) )
		end
		
		return mw.language.new( wiki.langcode ):formatDate( formatstr, date )
	end
	
	-- return empty string by default
	return ""
end

--[=[
  split time datavalue to separated date object
  
  parameters:
  - data (table): time datavalue object
  
  returns: (table) date object
]=]
local function datavalueTimeToDateObject( data )
	-- parse timestamp
	local sign, year, month, day, hour, minute, second = string.match( data.time, "(.)(%d+)%-(%d+)%-(%d+)T(%d+):(%d+):(%d+)Z" )
	
	-- fill date object
	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$" )
	}
	
	-- handle ac/bc
	if sign == "-" then
		result.year = -result.year
	end
	
	return result
end

--[=[
  handle julian calendar
  
  parameters:
  - dateObject (table): date object, e.g. retrieved from datavalueTimeToDateObject()
  
  returns: (number) datetime representation
]=]
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

--[=[
  ==============================================================================
  SNAK RENDERERS
  ==============================================================================
]=]

--[=[
  print datavalue type coordinate
  
  parameters:
  - data (table): value data
                  latitude [double], longitude [double], altitude [double], precision [double], globe [wikidata URI, usually http://www.wikidata.org/entity/Q2 [earth]]
  - parameter (string): data parameter to return
  
  returns: (string) parameter value
]=]
local function printDatavalueCoordinate( data, parameter )
	if parameter then
		if parameter == "globe" then
			-- extract entity id from the globe URI
			data.globe = extractEntityId( data.globe )
		end
		return data[ parameter ]
	else
		-- combine latitude and longitude, which can be decomposed using the #titleparts wiki function
		return data.latitude .. "/" .. data.longitude
	end
end

--[=[
  print datavalue type quantity
  
  parameters:
  - data (table): value data
                  amount [number], unit [string], upperBound [number], lowerBound [number]
  - parameter (string): data parameter to return
  
  returns: (string) parameter value
]=]
local function printDatavalueQuantity( data, parameter )
	if not parameter or parameter == "amount" then
		return tonumber( data.amount )
	elseif parameter == "unit" then
		return extractEntityId( data.unit )
	else
		return data[ parameter ]
	end
end

--[=[
  print datavalue type time
  
  parameters:
  - data (table): value data
                  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
  - parameter (string): data parameter to return
  
  returns: (string) parameter value
]=]
local function printDatavalueTime( data, parameter )
	if parameter then
		-- check if format is specified
		local para, formatstr = parameter:match( "([^:]+):([^:]+)" )
		
		if parameter == "calendarmodel" then
			-- extract entity id from the calendar model URI
			data.calendarmodel = extractEntityId( data.calendarmodel )
		elseif para and para == "time" then
			-- output formatted time string
			return formatDate( data.time, data.precision, data.timezone, formatstr )
		elseif parameter == "time" then
			-- output standard date format
			data.time = normalizeDate( data.time )
		end
		return data[ parameter ]
	else
		-- return full date
		return formatDate( data.time, data.precision, data.timezone )
	end
end

--[=[
  print datavalue type entity
  
  parameters:
  - data (table): value data
                  entity-type [string], numeric-id [int, Wikidata id]
  - parameter (string): data parameter to return
  
  returns: (string) parameter value
]=]
local function printDatavalueEntity( data, parameter )
	local id
	
	-- get entity type
	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
			-- create link
			local linkTarget = mw.wikibase.getSitelink( id )
			local linkName   = mw.wikibase.getLabel( id )
			if linkTarget then
				-- if there is a local Wikipedia page linking to it, use the label or the page title
				local link = linkTarget
				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 "[[:d:" .. id .. "|" .. id .. "]]"
				end
			end
		else
			return data[ parameter ]
		end
	else
		return mw.wikibase.getLabel( id ) or id
	end
end

--[=[
  print datavalue type monolingual text
  
  parameters:
  - data (table): value data
                  language [string], text [string]
  - parameter (string): data parameter to return
  
  returns: (string) parameter value
]=]
local function printDatavalueMonolingualText( data, parameter )
	if parameter then
		return data[ parameter ]
	else
		return mw.ustring.format( i18n.monolingualtext, data[ "language" ], data[ "text" ] )
	end
end

--[=[
  ==============================================================================
  DATA ACCESS
  ==============================================================================
]=]

--[=[
  get the value of a snak. snaks have three types: "novalue" for null/nil,
  "somevalue" for not null/not nil, or "value" for actual data
  
  parameters:
  - snak (table): snak data
  - parameter (string): data parameter to return
  
  returns: (string) rendered snak value
]=]
function getSnakValue( snak, parameter )
	if snak.snaktype == "value" then
		-- call the respective snak renderer
		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
	
	-- novalue, somevalue
	return mw.wikibase.renderSnak( snak )
end

--[=[
  get the snak of the given qualifier of the claim or its mainsnak.
  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
  
  parameters:
  - claim (table): claim data
  - qualifierId (opt, string): qualifier to get the snak from
  
  returns: (table|nil) snak data or nil if error
           (string) error message if needed
]=]
function getQualifierSnak( claim, qualifierId )
	-- return nil if claim is invalid
	-- TODO: add error message for invalid claim data
	if not claim then
		return nil
	end
	
	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
				return qualifier[ 1 ]
			end
		end
		return nil, printError( "qualifier-not-found" )
	else
		-- otherwise return the main snak
		return claim.mainsnak
	end
end

--[=[
  get the sort value of the given qualifier of the claim or its mainsnak.
  
  parameters:
  - claim (table): claim data
  - qualifierId (opt, string): qualifier to get the snak from
  
  returns: (string|nil) sort value or nil if error
]=]
function getQualifierSortValue( claim, qualifierId )
	local snak = getQualifierSnak( claim, qualifierId )
	if snak and snak.snaktype == "value" then
		if snak.datavalue.type == "time" then
			-- use datetime representation vor time datavalue
			return julianDay( datavalueTimeToDateObject( snak.datavalue.value ) )
		else
			-- use snak value for other datavalue types
			return getSnakValue( snak )
		end
	end
end

--[=[
  get the value of the mainsnak of the claim or the value of the given qualifier
  
  parameters:
  - claim (table): claim data
  - qualifierId (opt, string): qualifier to get the data from
  
  returns: (string|nil) value or nil if error
           (string) error message if needed
]=]
function getClaimValue( claim, qualifierId, parameter )
	local error
	local snak
	snak, error = getQualifierSnak( claim, qualifierId )
	
	if snak then
		return getSnakValue( snak, parameter )
	end
	
	return nil, error
end

--[=[
  convert reference object to string representation
  
  parameters:
  - ref (table): ref data

  returns: (string|nil) reference text or nil if error
]=]
function formatReference( ref )
	-- "imported from"-references are useless, skip them:
	if ref[ "P143" ] or ref[ "P4656" ] then
		return nil
	end
	
	-- load [[Modul:Zitation]]
	local ZitationSuccess, r = pcall( require, "Modul:Zitation" )
	if type( r ) == "table" then
		Zitation = r.Zitation()
		-- clear Zitation state from previous invocations
		Zitation.o = nil
	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" ] ) )
	end
	return Zitation.format()
end

--[=[
  return references of the given claim as reference string
  
  parameters:
  - frame (table): invocation frame
  - claim (table): claim data

  returns: (string) reference text
]=]
function getReferences( frame, claim )
	local result = ""
	
	-- loop through all references
	for ref in pairs( claim.references or {} ) do
		-- put snaks to table for later formatting with formatReference()
		local refTable = {}
		for snakkey, snakval in orderedPairs( claim.references[ ref ].snaks or {}, claim.references[ ref ][ "snaks-order" ] ) do
			if #snakval == 1 then
				-- only one value in snak
				refTable[ snakkey ] = getSnakValue( snakval[ 1 ] )
			else
				-- multiple values in snak
				local multival = {}
				for snakidx = 1, #snakval do
						table.insert( multival, getSnakValue( snakval[ snakidx ] ) )
				end
				refTable[ snakkey ] = multival
			end
		end
		
		-- format reference with data in refTable
		local formattedRef, f = formatReference( refTable )
		
		if formattedRef and formattedRef ~= "" then
			-- reference formatting successful, create hash from text to avoid double refs
			local hash = mw.hash.hashValue( "fnv164", formattedRef )
			
			-- output reference
			result = result .. frame:extensionTag( "ref", formattedRef, { name = "_" .. hash } )
		end
	end
	
	return result
end

--[=[
  checks if a claim has the given qualifier, and optionally, with the given value
  
  parameters:
  - claim (table): claim data
  - property (string): qualifier property
  - value (opt, string): value of qualifier

  returns: (boolean) check result
]=]
local function hasqualifier( claim, property, value )
	local invert = false
	
	-- allow inversion of the qualifier check
	if string.sub( property, 1, 1 ) == "!" then
		invert = true
		property = string.sub( property, 2 )
	end

	-- check if claim has any qualifiers.
	-- if property is empty string or literal true, check for existence of any 
	-- qualifiers only, without inversion - invert is always false in this case
	-- so will return false in the first condition
	if not claim.qualifiers then
		return invert
	elseif property == "" or property == "true" then
		return true
	end
	
	-- check if qualifier with property exists
	if not claim.qualifiers[ property ] then
		return invert
	end
	
	-- if check is only for qualifier existance return true or false inverted
	if not value then
		return ( not invert )
	end
	
	-- check for existence of the given value
	local found = false
	for key, snak in pairs( claim.qualifiers[ property ] ) do
		if snak.snaktype == "value" then
			if snak.datavalue.type == "wikibase-entityid" then
				-- wikibase id
				if snak.datavalue.value.id == value then
					found = true
					break
				end
			--TODO: elseif other types
			end
		end
	end
	
	-- return true or false inverted
	return ( found ~= invert )
end

--[=[
  checks if a claim has the given source
  
  parameters:
  - claim (table): claim data
  - property (string): source property

  returns: (boolean) check result
]=]
local function hassource( claim, property )
	
	-- check if claim has any references.
	-- if property is empty string or literal true, check for existence of any 
	-- references only
	if not claim.references then
		return false
	elseif property == "" or property == "true" then
		return true
	end
	
	-- allow inversion of the reference property check
	local invert = false
	if string.sub( property, 1, 1 ) == "!" then
		invert = true
		property = string.sub( property, 2 )
	end
	
	-- check for existence of the given source property
	for _, source in pairs( claim.references ) do
		if source.snaks[ property ] then
			return ( not invert )
		end
	end
	
	-- return true or false inverted
	return invert
end

--[=[
  checks if a claim is valid at the given date
  
  parameters:
  - claim (table): claim data
  - checkdate (opt, string): date to check, or current date

  returns: (boolean) check result
]=]
function atdate( claim, checkdate )
	local refdate, checkdateyear
	if not checkdate or checkdate == "" then
		-- date not given, use current date
		refdate = os.date( "!*t" )
		checkdateyear = 0
	else
		-- parse given date
		if string.match( checkdate, "^%d+$" ) then
			-- only year given
			refdate = { year = tonumber( checkdate ) }
			checkdateyear = tonumber( checkdate )
		else
			-- exact date given
			refdate = datavalueTimeToDateObject( { time = mw.language.getContentLanguage():formatDate( "+Y-m-d\\TH:i:s\\Z", checkdate ) } )
			checkdateyear = 0
		end
	end
	
	local refjd = julianDay( refdate )
	
	-- get date qualifiers for claim
	local exactdate = getQualifierSortValue( claim, propertyId[ "pointoftime" ] )
	local mindate   = getQualifierSortValue( claim, propertyId[ "starttime" ] )
	local maxdate   = getQualifierSortValue( claim, propertyId[ "endtime" ] )
	
	if exactdate then
		-- if there is an exact date in the qualifier, and the atdate parameter
		-- is on year precision, mindate is the beginning of the year and maxdate
		-- is 31st Dec of that particular year
		local refmaxjd
		if checkdateyear > 0 then
			refmaxjd = julianDay( { year = tonumber( checkdate ), month = 12, day = 31 } )
		else 
			refmaxjd = refjd
		end
		
		-- return false if exact date is out of bounds
		if exactdate < refjd or exactdate > refmaxjd then
			return false
		end
	else
		-- return false if date is before mindate or after maxdate
		if mindate and mindate > refjd then
			return false
		end
		if maxdate and maxdate < refjd then
			return false
		end
	end
	
	-- success, the claim was valid at "checkdate"
	return true
end

--[=[
  checks if a claim is not deprecated
  
  parameters:
  - claim (table): claim data

  returns: (boolean) check result
]=]
local function notdeprecated( claim )
  return claim.rank ~= "deprecated"
end

--[=[
  checks whether a certain item is a parent of a given item. A parent is
  connected via P31 or P279. Attention: very intensive function, use carefully!
  
  parameters:
  - item (string|number): start item
  - parent (string): parent item
  - exitItem (string): item to exit check at
  - maxDepth (number): maximum search depth
  - depth (opt, number): current search depth

  returns: (boolean) check result
]=]
local function isParent( item, parent, exitItem, maxDepth, depth )
	
	-- increment call number
	numberIsParentCalls = numberIsParentCalls + 1
	
	-- initial depth is zero
	if not depth then
		depth = 0
	end
	
	-- cast start item number to string
	if type( item ) == "number" then
		item = "Q" .. item
	end
	
	-- check if start entity exists
	if not entityExistsTypesafe( item ) then
		return false
	end
	
	-- get entity
	local entity = mw.wikibase.getEntity( item )
	if not entity then
		return false
	end
	
	-- if entity has no claims, end check
	if not entity.claims then
		return false
	end
	
	-- resolve property ids
	local claims31  = mw.wikibase.resolvePropertyId( "P31" ) 
	local claims279 = mw.wikibase.resolvePropertyId( "P279" )
	if not claims31 or not claims279 then
		return false
	end
	
	-- get connector claims, if both don't exist end check
	claims31  = entity.claims[ claims31 ]
	claims279 = entity.claims[ claims279 ]
	if not claims31 and not claims279 then
		return false
	end
	
	-- retrieve parent entity ids
	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

	-- if no parent ids found, end check
	if not parentIds[ 1 ] or #parentIds == 0 then
		return false
	end

	-- check if searched parent or exit item is reached or do recursive call
	local itemString = ""
	local result     = nil
	for i, v in ipairs( parentIds ) do
		-- create item string and check if it is valid
		itemString = "Q" .. v
		if not isValidEntityIdTypesafe( itemString ) then
			return false
		end

		if itemString == parent then
			-- parent item found
			return true
		elseif itemString == exitItem or itemString == "Q35120" then
			-- exit if either exit item or node item "entity" (Q35120) is reached
			return false
		else
			-- resursive call on parents if within maxDepth bounds
			if depth + 1 < maxDepth then
				result = isParent( itemString, parent, exitItem, maxDepth, depth + 1 )
			else
				return false
			end
			
			-- return true if recursive call returned true
			if result == true then
				return result
			end
		end
	end
	
	-- return false
	do return false end
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)
	if not frame.args.includedeprecated then
		frame.args.notdeprecated = true
    	filter("notdeprecated", notdeprecated)
    end
	
	-- use additional unnamed parameters as qualifier conditions (in pairs)
	for key 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 = {}
			local values = frame.args[key]
			local negated = string.sub(values, 1, 1) == "!"
			if negated then values = string.sub(values, 2) end
			for i, claim in pairs(claims) do
				local hasvalue = false
				for val in mw.text.gsplit(values, ",") do
					if hasqualifier(claim, frame.args[key - 1], val) then
						hasvalue = true
						break
					end
				end
				if hasvalue ~= negated then table.insert(newclaims, claim) end
			end
			claims = newclaims
		end
	end
	
	return claims
end

--[=[
  ==============================================================================
  TEMPLATE EXPORT
  ==============================================================================
]=]

local p = {}

--[=[
  checks whether a certain item is a subclass of a given item.
  
  parameters (frame):
  - id (string): start item
  - parent (string): parent item
  - exitItem (string): item to exit check at
  - maxDepth (opt, string): maximum search depth
  - returnInt (opt, string): if set, return 1 or empty instead of true/false

  returns: (boolean|number) check result in
]=]
function p.isSubclass( frame )
	local result
	
	-- check for validity of start and parent item
	if entityExistsTypesafe( frame.args[ "id" ] ) and entityExistsTypesafe( frame.args[ "parent" ] ) then
		-- check if id and parent are same
		if frame.args[ "id" ] ~= frame.args[ "parent" ] then
			-- set maximum depth
			local maxDepth
			maxDepth = tonumber( frame.args[ "maxDepth" ] ) or 5
			
			-- getParent() call
			result = isParent( frame.args[ "id" ], frame.args[ "parent" ], frame.args[ "exitItem" ], maxDepth)
			-- mw.log(numberIsParentCalls) --uncomment to load number of isParent() calls into log
		else
			result = true
		end
	else
		result = false
	end
	
	-- return result
	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.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 countValues = frame.args["countValues"]
	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 sortEmptiesFirst = frame.args["sortEmptiesFirst"]
	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
	if id then
		if not mw.wikibase.isValidEntityId(id) then
			if showerrors then 
				return printError("entity-not-valid") 
			else 
				local temp = mw.title.new(i18n["maintenance-pages"]["entity-not-valid"], "Modul").exists
				return default 
			end
		elseif not mw.wikibase.entityExists(id) then
			if showerrors then
				return printError("entity-not-found") 
			else 
				local temp = mw.title.new(i18n["maintenance-pages"]["entity-not-found"], "Modul").exists
				return default 
			end
		end
	end
	
	local entity = mw.wikibase.getEntity(id)
	if not entity then
		if showerrors then return printError("entity-not-found") else return default end
	end

	-- check if property exists
	local realProp = mw.wikibase.resolvePropertyId(property)
	if not realProp then
		local temp = mw.title.new(i18n["maintenance-pages"]["property-not-existing"], "Modul").exists
	end

	-- fetch the first claim of satisfying the given property
	local claims
	if entity.claims then claims = entity.claims[realProp] end
	if not claims or not claims[1] then
		if countValues then return 0
		elseif showerrors then return printError("property-not-found") 
		else return default end
	end

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

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

	local comparator
	if sort then
		comparator = function(a, b) --comparator function for sorting statements based on qualifier value
			-- load qualifier values
			local QualifierSortValueA = getQualifierSortValue(claims[a], sort)
			local QualifierSortValueB = getQualifierSortValue(claims[b], sort)
			
			-- if either of the two statements does not have this qualifer: 
			---- if sortEmptiesFirst=true: sort it to the beginning 
			---- else: always sort it to the end
			if not QualifierSortValueB then 
				if not QualifierSortValueA then
					-- if neither of the two statements has this qualifier, arbitrarily but consistently return a < b
					return a < b
				elseif sortEmptiesFirst then
					return false
				else
					return true
				end
			elseif not QualifierSortValueA then 
				if sortEmptiesFirst then return true else return false end
			end
			
			if type(QualifierSortValueA) ~= type(QualifierSortValueB) and not (tonumber(QualifierSortValueA) and tonumber(QualifierSortValueB)) then
				if tonumber(QualifierSortValueA) then return true
				elseif tonumber(QualifierSortValueB) then return false
				elseif tostring(QualifierSortValueA) and tostring(QualifierSortValueB) then
					if inverse then return tostring(QualifierSortValueA) > tostring(QualifierSortValueB) else return tostring(QualifierSortValueA) < tostring(QualifierSortValueB) end
				else return false end -- different types, neither numbers nor strings, no chance to compare => random result to avoid script error
			elseif tonumber(QualifierSortValueA) and tonumber(QualifierSortValueB) then
				QualifierSortValueA = tonumber(QualifierSortValueA)
				QualifierSortValueB = tonumber(QualifierSortValueB)
			end
			if inverse then
				return QualifierSortValueA > QualifierSortValueB
			else
				return QualifierSortValueA < QualifierSortValueB
			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] = getClaimValue(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 countValues then
		local count = 0
		for _ in pairs(claims) do count = count + 1 end
		result = count
	elseif 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 =  getClaimValue(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(#result, listMaxItems))
		else
			result = table.concat(result, list)
		end
	else
		-- return first element

		local claim = claims[sortindices[1]]
		if language == "Q" then
			result, error = "Q" .. getSnakValue(getQualifierSnak(claim), "numeric-id")
		elseif 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 = getClaimValue(claim, qualifierId, parameter)
					break
				end
			end
		else
			result, error = getClaimValue(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

--[=[
  return the entity id of the current page, or check the given entity id
  for redirects and return the redirect target, if existing
  
  parameters (frame):
  - 1 (opt, string): entity id
  
  returns: (string) entity id or empty string
]=]
function p.pageId( frame )
	local id = frame.args[ 1 ]
	
	-- if id not given, return the id of the current page.
	-- mw.wikibase.getEntityIdForCurrentPage() loads less data than getEntity(),
	-- so it's preferable to use it in this case.
	if not id then
		return mw.wikibase.getEntityIdForCurrentPage() or ""
	end
	
	-- get the given entity to check for redirects
	local entity = mw.wikibase.getEntity( id )
	if entity then
		return entity:getId() or ""
	end
	
	-- return empty string by default
	return ""
end

--[=[
  return label of a Wikidata entity in the given language or the default language of this Wikipedia site
  
  parameters (frame):
  - 1 (opt, string): language code
  - 2 (opt, string): entity id
  - noFallback (opt, string): suppress language fallbacks
  
  returns: (string) label text or empty string
]=]
function p.labelIn( frame )
	local langcode   = frame.args[ 1 ]
	local id         = frame.args[ 2 ]
	local noFallback = frame.args[ "noFallback" ]
		
	if noFallback then
		-- get label in language without fallbacks.
		-- id is required mw.wikibase.getLabelByLang(), so get the connected entity
		-- if not given or invalid
		id = getConnectedEntityIdIfInexisting( id )
		if not id then
			return ""
		end
		return mw.wikibase.getLabelByLang( id, langcode or wiki.langcode ) or ""
	elseif not langcode then
		-- no langcode given, return label in wiki language with fallbacks
		return mw.wikibase.getLabel( id ) or ""
	else
		-- get label in language with fallbacks
		local entity = mw.wikibase.getEntity( id )
		if entity then
			return entity:getLabel( langcode ) or ""
		end
	end
	
	-- return empty string by default
	return ""
end

--[=[
  return the label of the given entity or property id

  parameters (frame):
  - 1 (opt, string): entity id, or entity id of current page
  
  returns: (string) label text, error text or empty string
]=]
function p.labelOf( frame )
	local id = frame.args[ 1 ]
	
	-- mw.wikibase.getLabel() would support omitting the id, but we want
	-- to display an error message if the page isn't connected.
	id = getConnectedEntityIdIfInexisting( id )
	if not id then
		return printError( "entity-not-found" )
	end
	
	return mw.wikibase.getLabel( id ) or ""
end

--[=[
  return description of a Wikidata entity in the given language or the default language of this Wikipedia site
  
  parameters (frame):
  - 1 (opt, string): language code
  - 2 (opt, string): entity id
  - noFallback (opt, string): suppress language fallbacks
  
  returns: (string) description text or empty string
]=]
function p.descriptionIn( frame )
	local langcode   = frame.args[ 1 ]
	local id         = frame.args[ 2 ]
	local noFallback = frame.args[ "noFallback" ]
	
	if noFallback then
		-- get description in language without fallbacks
		-- id is required mw.wikibase.getDescriptionByLang(), so get the connected entity
		-- if not given or invalid
		id = getConnectedEntityIdIfInexisting( id )
		if not id then
			return ""
		end
		return mw.wikibase.getDescriptionByLang( id, langcode or wiki.langcode ) or ""
	elseif not langcode then
		-- no langcode given, return description in wiki language with fallbacks
		return mw.wikibase.getDescription( id ) or ""
	else
		-- get description in language with fallbacks
		local entity = mw.wikibase.getEntity( id )
		if entity then
			return entity:getDescription( langcode ) or ""
		end
	end
	
	-- return empty string by default
	return ""
end

--[=[
  return the Wikipedia page name of the given entity on the given site.
  
  parameters (frame):
  - 1 (opt, string): entity id, or entity id of current page
  - 2 (opt, string): site name, or current site
  
  returns: (string) page name, error text or empty string
]=]
function p.sitelinkOf( frame )
	local id   = frame.args[ 1 ]
	local site = frame.args[ 2 ]

	-- mw.wikibase.getSitelink() doesn't support omitting the id parameter, so
	-- get the current page's entity id manually and fail if not found.
	id = getConnectedEntityIdIfInexisting( id )
	if not id then
		return printError( "entity-not-found" )
	end
	
	-- if site is nil, the sitelink to the current wiki will be returned
	return mw.wikibase.getSitelink( id, site ) or ""
end

--[=[
  return the number of sitelinks in the given entity
  
  parameters (frame):
  - 1 (opt, string): sitelink filter for projects
  - 2 (opt, string): entity id, or entity id of current page 
  
  returns: (number) sitelink count
]=]
function p.sitelinkCount( frame )
	local filter = frame.args[ 1 ]
	local id     = frame.args[ 2 ]
	
	if filter then
		filter = "^.*" .. filter .. "$"
	else
		filter = false
	end
	
	local count  = 0
	local entity = mw.wikibase.getEntity( id )
	if entity and entity.sitelinks then
		if filter == false then
			count = tableSize( entity.sitelinks )
		else
			for project, _ in pairs( entity.sitelinks ) do
				if string.find( project, filter ) then
					count = count + 1
				end
			end
		end
	end
	
	return count
end

--[=[
  return the badges of the given entity for the given site
  
  parameters (frame):
  - 1 (opt, string): site id, or current site
  - 2 (opt, string): entity id, or entity id of current page
  
  returns: (string) badges
]=]
function p.badges( frame )
	local site = frame.args[ 1 ]
	local id   = frame.args[ 2 ]
	
	-- entity is required for mw.wikibase.getBadges()
	id = getConnectedEntityIdIfInexisting( id )
	if not id then
		return printError( "entity-not-found" )
	end
	
	-- get badges, site is allowed to be nil
	badges = mw.wikibase.getBadges( id, site )
	if type( badges ) == "table" then
		return table.concat( badges, "/" )
	end
	
	-- return empty string by default
	return ""
end

--[=[
  ==============================================================================
  DEBUGGING
  ==============================================================================
]=]

-- call this in cases of script errors within a function instead of {{#invoke:Wikidata|<method>|...}} call {{#invoke:Wikidata|debug|<method>|...}}
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 '<span class="error">' .. result .. '</span>' end -- revert
		if status then return result else return '<span class="error">' .. result .. '</span>' 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 "<pre>" .. mw.text.jsonEncode(entity, mw.text.JSON_PRETTY) .. "</pre>" end
end

--[=[
  ==============================================================================
  COORDINATE TEMPLATE HELPERS
  ==============================================================================
]=]

-- formfill Template:Coordinate (NS, EW, name from WikidataEntity) and expand it
-- füllt Vorlage:Coordinate (NS, EW, name mit Wikidata-Werten) + expandiert sie
--  1st frame.arg                            .. Q prefixed entity id (mandatory)
--  named frame.arg "type", "region", "text" .. see doc of 'Coordinate' template
function p.ffCoordinate(frame)
	local f = frame
	local id = f.args[1] or f.args.Q
	local name = f.args.name or p.labelIn{ args = { nil, id, id = id }}
	
	local coord = mw.text.split(p.claim{ args = { "P625", id, id = id }}, "/")
	coord[1] = tonumber(coord[1])
	coord[2] = tonumber(coord[2])

	local t, r = f.args.type, f.args.region
	if not t
	then t = p.claim{ args = { "P31", id, id = id, language = "Q" }}
		 t = t and t:gsub("Q.*", {
			Q8502  = "mountain",
			Q54050 = "landmark"
		 })
		 if not t or t and t:find("Q", 1, true)
		 then t="" -- no default, let Coordinate warn about unset type= param
		 end
	end
	if not r
	then r = p.claim{ args = { "P17", id, id = id, language = "Q" }}
		 r = r and p.claim{ args = { "P297", r, id = r }}
		 if not r
		 then r="" -- no default, let Coordinate warn about unset region= param
		 end
	end
	
	return ('<span data-sort-value="%010.6f"></span>'):format((f.args.sortkey
		or "EW"):find("EW", 1, true) and coord[2]+360.0 or coord[1]+180.0
	) .. f:expandTemplate{ title = "Coordinate", args = {
		NS = coord[1], EW = coord[2], type = t, region = r,
		text = f.args.text or (f.args.maplink and "ICON0" or "/"),
		name = name, simple = f.args.simple
	}} .. (not f.args.maplink and "" or (" " ..
		--f:callParserFunction{ name="#statements", args={ "P625", from = id } }
		f:callParserFunction{ name="#tag:maplink", args={ "",
			class = "no-icon", text = f.args.mlname and name,
			zoom = 12, latitude = coord[1], longitude = coord[2]
		}}
	))
end

function p.ffCoordinateAndLatLonMaplink(frame)
	frame.args.maplink = 1
	--frame.args.mlname = nil
	return p.ffCoordinate(frame)
end

function p.ffCoordinateAndMaplink(frame)
	frame.args.maplink = 1
	frame.args.mlname = 1
	return p.ffCoordinate(frame)
end


return p