Modul:Wikidata

Vorlagenprogrammierung Diskussionen Lua Test Unterseiten
Modul Deutsch

Modul: Dokumentation

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


local Wd = {
	-- project configuration
	wiki = {
		langcode = mw.language.getContentLanguage().code
	},

	-- internationalisation
	i18n = {
		["datetime"] = {
			-- $s 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
		},
		["errors"] = {
			["deprecated-function"] = "Aufruf veralteter Funktion",
			["entity-not-found"] = "Wikidata-Eintrag nicht gefunden.",
			["entity-not-valid"] = "Die an die Wikidata-Schnittstelle übergebene Item-ID ist nicht gültig",
			["invalid-parameters"] = "Ungültige Parameter",
			["module-not-loaded"] = "Loading of additional module failed",
			["property-not-found"] = "Eigenschaft nicht gefunden",
			["qualifier-not-found"] = "Qualifikator nicht gefunden",
			["site-not-found"] = "Wikimedia-Projekt nicht gefunden",
			["unknown-claim-type"] = "Unbekannter Aussagentyp",
			["unknown-entity-type"] = "Unbekannter Entity-Typ",
		},
		["errortext"] = '<span class="error">%s</span>',
		["FETCH_WIKIDATA"] = "ABFRAGE_WIKIDATA",
		["maintenance-pages"] = {
			["entity-not-found"] = "Wikidata/Wartung/Fehlendes Datenobjekt",
			["entity-not-valid"] = "Wikidata/Wartung/Ungültige Datenobjekt-Identifikationsnummer",
			["property-not-found"] = "Wikidata/Wartung/Eigenschaft existiert nicht"
		},
		["monolingualtext"] = '<span lang="%s">%s</span>',
	},
	
	-- global value to count calls of expensive function isParent, including recursive calls
	numberIsParentCalls = 0,

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

	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 in case of template call
  
  parameters:
  - code (string): error code
  - info (opt, string): additional information
  - frame (opt, table): template call frame for determining the error mode

  returns: (string) error text
]=]
Wd.templateCallError = function( code, info, frame )
	local mode
	local text
	
	-- get error mode
	mode = Wd.getFrameArgument( frame, "errors", false, "empty" )
	
	-- output error depending on mode
	text = ""
	if mode == "show" then
		-- show the error, reachable with #iferror in template calls
		text = Wd.i18n.errors[ code ] or code or ""
		if info then
			text = text .. ": " .. info
		end
		text = mw.ustring.format( Wd.i18n.errortext, text )
	elseif mode == "empty" then
		-- output empty string in case of error
		text = ""
	elseif mode == "link" then
		-- create maintenance link and output empty string
		Wd.createMaintenanceLink( code )
		text = ""
	elseif mode == "text" then
		-- output text given in errorsText parameter
		text = Wd.getFrameArgument( frame, "errorsText", false, "" )
	end
	
	return text
end

--[=[
  create a new error object
  
  parameters:
  - code (string): error code
  - info (opt, string): additional information

  returns: (table) error table
]=]
Wd.newError = function( code, info )
	return { 
		code = code,
		info = tostring( info )
	}
end

--[=[
  parse the given error object
  
  parameters:
  - error (table): error object created with Wd.newError

  returns:
  - (string): error code
  - (string|nil): additional information if available
]=]
Wd.parseError = function( error )
	return error.code, error.info
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
]=]
Wd.orderedPairs = function( 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
]=]
Wd.getTableSize = function( tbl )
	if type( tbl ) ~= "table" then
		return 0
	end
	
	local count = 0
	for _ in pairs( tbl ) do
		count = count + 1
	end
	
	return count
end

--[=[
  create a silent maintenance link
  
  parameters:
  - key (string): key to maintenance link title
  
  returns: (boolean) link title exists or not (to record usage)
]=]
Wd.createMaintenanceLink = function( key )
	if type( key ) == "string" and Wd.i18n[ "maintenance-pages" ][ key ] then
		return mw.title.new( Wd.i18n[ "maintenance-pages" ][ key ], "Modul" ).exists
	end
end

--[=[
  get an argument of the invocation frame with options
  
  parameters:
  - frame (table): invocation frame
  - key (string|number|table) argument key or table of keys
  - allowEmpty (opt, boolean): if true, empty strings count as valid arguments
  - default (opt, any): default value to return if argument is invalid
  
  returns: (boolean) link title exists or not (to record usage)
]=]
Wd.getFrameArgument = function( frame, key, allowEmpty, default )
	-- if frame is invalid, directly return default value
	if type( frame ) ~= "table" then
		return default
	end
	
	-- default allowEmpty to false
	if not allowEmpty then
		allowEmpty = false
	end
	
	-- get keys
	local keys
	if type( key ) == "string" or type( key ) == "number" then
		keys = { key }
	elseif type( key ) == "table" then
		keys = key
	end
	
	-- get frame argument
	local ret = nil
	for i, v in ipairs( keys ) do
		local arg = frame.args[ v ]
		if arg and ( allowEmpty == true or ( allowEmpty == false and arg ~= "") ) then
			ret = arg
			break
		end
	end
	
	-- return frame argument or default value
	if ret ~= nil then
		return ret
	else
		return default
	end
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
]=]
Wd.isValidEntityIdTypesafe = function( 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
]=]
Wd.entityExistsTypesafe = function( id )
	return ( Wd.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
]=]
Wd.getConnectedEntityIdIfInvalid = function( id )
	if Wd.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
]=]
Wd.getConnectedEntityIdIfInexisting = function( id )
	if Wd.entityExistsTypesafe( id ) then
		return id
	else
		return mw.wikibase.getEntityIdForCurrentPage()
	end
end

--[=[
  return the entity id by the given title and site if given id does not exist
  
  parameters:
  - id (string|nil): entity id
  - title (string|nil): page title
  - titleSite (string|nil): site for page title, or current site
  
  returns: (string|nil)
  - if given id is existing, entity id
  - if given id is inexisting and title has a connected entity on titleSite, entity id
  - if given id is inexisting and title has no connected entity on titleSite, nil
  - if given id is inexisting, no title was given and there is a connected entity, entity id
  - if given id is inexisting, no title was given and there is no connected entity, nil
]=]
Wd.getEntityIdFromTitleIfInexisting = function( id, title, titleSite )
	if Wd.entityExistsTypesafe( id ) then
		-- return existing id
		return id
	elseif title then
		-- no valid id given but title
		return mw.wikibase.getEntityIdForTitle( title, titleSite )
	else
		-- get connected entity id
		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
]=]
Wd.extractEntityId = function( 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
]=]
Wd.normalizeDate = function( 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
]=]
Wd.formatDate = function( 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 = Wd.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( Wd.i18n.datetime[ precision ], tostring( y2 ) )
		if year < 0 then
			relative = mw.ustring.format( Wd.i18n.datetime.beforenow, relative )
		else
			relative = mw.ustring.format( Wd.i18n.datetime.afternow, relative )
		end
		return relative
	end

	-- precision is decades, centuries and millenia
	local era
	if precision == 6 then
		era = mw.ustring.format( Wd.i18n.datetime[ 6 ], tostring( math.floor( ( math.abs( year ) - 1 ) / 1000 ) + 1 ) )
	end
	if precision == 7 then
		era = mw.ustring.format( Wd.i18n.datetime[ 7 ], tostring( math.floor( ( math.abs( year ) - 1 ) / 100 ) + 1 ) )
	end
	if precision == 8 then
		era = mw.ustring.format( Wd.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( Wd.i18n.datetime.bc, '"', "" ), era )
		elseif year > 0 then
			era = mw.ustring.format( mw.ustring.gsub( Wd.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( Wd.formatchar[ i ] ) do
					if formatstr:find( ch ) then
						formatstr = Wd.i18n.datetime[ precision ]
					end
				end
			end
		else
			formatstr = Wd.i18n.datetime[ precision ]
		end
		if year == 0 then
			formatstr = mw.ustring.gsub( formatstr, Wd.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, Wd.i18n.datetime[ 9 ], mw.ustring.format( Wd.i18n.datetime.bc, Wd.i18n.datetime[ 9 ] ) )
		elseif year > 0 and Wd.i18n.datetime.ad ~= "%s" then
			formatstr = mw.ustring.gsub( formatstr, Wd.i18n.datetime[ 9 ], mw.ustring.format( Wd.i18n.datetime.ad, Wd.i18n.datetime[ 9 ] ) )
		end
		
		return mw.language.new( Wd.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
]=]
Wd.datavalueTimeToDateObject = function( 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 Wd.datavalueTimeToDateObject()
  
  returns: (number) datetime representation
]=]
Wd.handleJulianDay = function( 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 string
  
  parameters:
  - data (table): value data
  - parameter (string): data parameter to return
  
  returns: (string) parameter value
]=]
Wd.printDatavalueString = function( data, parameter )
	return data
end

--[=[
  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
]=]
Wd.printDatavalueCoordinate = function( data, parameter )
	if parameter then
		if parameter == "globe" then
			-- extract entity id from the globe URI
			data.globe = Wd.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
]=]
Wd.printDatavalueQuantity = function( data, parameter )
	if not parameter or parameter == "amount" then
		return tonumber( data.amount )
	elseif parameter == "unit" then
		return Wd.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
]=]
Wd.printDatavalueTime = function( 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 = Wd.extractEntityId( data.calendarmodel )
		elseif para and para == "time" then
			-- output formatted time string
			return Wd.formatDate( data.time, data.precision, data.timezone, formatstr )
		elseif parameter == "time" then
			-- output standard date format
			data.time = Wd.normalizeDate( data.time )
		end
		return data[ parameter ]
	else
		-- return full date
		return Wd.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
]=]
Wd.printDatavalueEntity = function( 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
		-- TODO: return error object instead
		return Wd.templateCallError( "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
]=]
Wd.printDatavalueMonolingualText = function( data, parameter )
	if parameter then
		return data[ parameter ]
	else
		return mw.ustring.format( Wd.i18n.monolingualtext, data[ "language" ], data[ "text" ] )
	end
end

--[=[
  ==============================================================================
  CLAIMS
  ==============================================================================
]=]

--[=[
  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
]=]
Wd.getSnakValue = function( snak, parameter )
	if snak.snaktype == "value" then
		-- define snak renderers
		local snakRendererList = {
			["string"] = Wd.printDatavalueString,
			["globecoordinate"] = Wd.printDatavalueCoordinate,
			["quantity"] = Wd.printDatavalueQuantity,
			["time"] = Wd.printDatavalueTime,
			["wikibase-entityid"] = Wd.printDatavalueEntity,
			["monolingualtext"] = Wd.printDatavalueMonolingualText
		}
		
		-- get the respective snak renderer and call it
		local snakRenderer = snakRendererList[ snak.datavalue.type ]
		if snakRenderer then
			return snakRenderer( snak.datavalue.value, parameter )
		end
	end
	
	-- any other snak type or datavalue type
	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
]=]
Wd.getQualifierSnak = function( claim, qualifierId )
	-- return nil if claim is invalid
	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
		-- TODO: return error object instead
		return nil, Wd.templateCallError( "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
]=]
Wd.getQualifierSortValue = function( claim, qualifierId )
	local snak = Wd.getQualifierSnak( claim, qualifierId )
	if snak and snak.snaktype == "value" then
		if snak.datavalue.type == "time" then
			-- use datetime representation vor time datavalue
			return Wd.handleJulianDay( Wd.datavalueTimeToDateObject( snak.datavalue.value ) )
		else
			-- use snak value for other datavalue types
			return Wd.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
]=]
Wd.getClaimValue = function( claim, qualifierId, parameter )
	local error
	local snak
	snak, error = Wd.getQualifierSnak( claim, qualifierId )
	
	if snak then
		return Wd.getSnakValue( snak, parameter )
	end
	
	return nil, error
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
]=]
Wd.claimHasQualifier = function( 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 existence 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
]=]
Wd.claimHasSource = function( 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
]=]
Wd.claimValidAtDate = function( 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 = Wd.datavalueTimeToDateObject( { time = mw.language.getContentLanguage():formatDate( "+Y-m-d\\TH:i:s\\Z", checkdate ) } )
			checkdateyear = 0
		end
	end
	
	local refjd = Wd.handleJulianDay( refdate )
	
	-- get date qualifiers for claim
	local exactdate = Wd.getQualifierSortValue( claim, Wd.propertyId[ "pointoftime" ] )
	local mindate   = Wd.getQualifierSortValue( claim, Wd.propertyId[ "starttime" ] )
	local maxdate   = Wd.getQualifierSortValue( claim, Wd.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 = Wd.handleJulianDay( { 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
]=]
Wd.claimNotDeprecated = function( claim )
	return claim.rank ~= "deprecated"
end

--[=[
  return a table of claims excluding claims not passed the filters
  
  parameters:
  - frame (table): call arguments
  - claims (table): claims to filter

  returns: (table) filtered claim table
]=]
Wd.filterClaims = function( frame, claims )
	-- subfunction that calls the check functions and does the filtering
	-- parameters:
	-- - condition (string): call argument that contains the filter condition
	-- - filterfunction (function): check function for the condition
	local function filter( condition, filterfunction )
		-- if condition not found in call arguments, do nothing
		if not frame.args[ condition ] then
			return
		end
		
		-- filter claim table
		local newclaims = {}
		for i, claim in pairs( claims ) do
			-- call filter function and check for result
			if filterfunction( claim, frame.args[ condition ] ) then
				table.insert( newclaims, claim )
			end
		end
		claims = newclaims
	end
	
	-- call available filters
	filter( "hasqualifier", Wd.claimHasQualifier )
	filter( "hassource", Wd.claimHasSource )
	filter( "atdate", Wd.claimValidAtDate )
	if not frame.args.includedeprecated then
		frame.args.notdeprecated = true
    	filter( "notdeprecated", Wd.claimNotDeprecated )
    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 ]
			
			-- allow inversion of values
			local invert = string.sub( values, 1, 1 ) == "!"
			if invert then
				values = string.sub( values, 2 )
			end
			
			-- filter claims
			for i, claim in pairs( claims ) do
				local hasvalue = false
				for val in mw.text.gsplit( values, "," ) do
					-- check if qualifier has current value
					if Wd.claimHasQualifier( claim, frame.args[ key - 1 ], val ) then
						hasvalue = true
						break
					end
				end
				if hasvalue ~= invert then
					table.insert( newclaims, claim )
				end
			end
			
			-- replace claims table
			claims = newclaims
		end
	end
	
	-- return filtered claim table
	return claims
end

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

  returns: (string|nil) reference text or nil if error
]=]
Wd.formatReference = function( 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, Wd.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
]=]
Wd.getReferences = function( frame, claim )
	local result = ""
	
	-- loop through all references
	for ref in pairs( claim.references or {} ) do
		-- put snaks to table for later formatting with Wd.formatReference()
		local refTable = {}
		for snakkey, snakval in Wd.orderedPairs( claim.references[ ref ].snaks or {}, claim.references[ ref ][ "snaks-order" ] ) do
			if #snakval == 1 then
				-- only one value in snak
				refTable[ snakkey ] = Wd.getSnakValue( snakval[ 1 ] )
			else
				-- multiple values in snak
				local multival = {}
				for snakidx = 1, #snakval do
						table.insert( multival, Wd.getSnakValue( snakval[ snakidx ] ) )
				end
				refTable[ snakkey ] = multival
			end
		end
		
		-- format reference with data in refTable
		local formattedRef, f = Wd.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 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
]=]
Wd.isParent = function( item, parent, exitItem, maxDepth, depth )
	
	-- increment call number
	Wd.numberIsParentCalls = Wd.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 Wd.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] = Wd.getSnakValue( v.mainsnak, "numeric-id" )
		end
	end
	if claims279 and #claims279 > 0 then
		for i, v in ipairs( claims279 ) do
			parentIds[ #parentIds + 1 ] = Wd.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 Wd.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 = Wd.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

--[=[
  ==============================================================================
  ENTITY PROPERTIES
  ==============================================================================
]=]

--[=[
  return the badges of the given entity for the given site
  
  parameters (frame):
  - id (opt, string): entity id
  - title (opt, string): title of a local page that is connected to the entity
  - titleSite (opt, string): site name to look for title, or current site
  - site (opt, string): site name, or current site
  One of id and title has to be specified. id takes precedence over title.
  
  returns:
  - (boolean) success indicator
  - (table|boolean) badges list if successful or error object
]=]
Wd.getBadges = function( id, title, titleSite, site )
	
	-- determine and validate entity id
	id = Wd.getEntityIdFromTitleIfInexisting( id, title, titleSite )
	if id == false then
		-- no id and no title given, invalid call
		return false, Wd.newError( "invalid-parameters", "Wd.getBadges" )
	elseif not Wd.entityExistsTypesafe( id ) then
		-- entity not found
		return false, Wd.newError( "entity-not-found", id )
	end
	
	-- if site is nil, the badges of the current wiki will be returned
	return true, mw.wikibase.getBadges( id, site ) or false
end

--[=[
  return the description of a Wikidata entity in the given language or the default language of this Wikipedia site
  
  parameters:
  - id (opt, string): entity id
  - title (opt, string): title of a local page that is connected to the entity
  - titleSite (opt, string): site name to look for title, or current site
  - langcode (opt, string): language code, or language code of current site
  - noFallback (opt, boolean): if true, suppress language fallbacks
  One of id and title has to be specified. id takes precedence over title.
  
  returns:
  - (boolean) success indicator
  - (string|boolean|table) description text if successful or error object
]=]
Wd.getDescription = function( id, title, titleSite, langcode, noFallback )
	
	-- determine and validate entity id
	id = Wd.getEntityIdFromTitleIfInexisting( id, title, titleSite )
	if id == false then
		-- no id and no title given, invalid call
		return false, Wd.newError( "invalid-parameters", "Wd.getDescription" )
	elseif not Wd.entityExistsTypesafe( id ) then
		-- entity not found
		return false, Wd.newError( "entity-not-found", id )
	end
	
	-- get description
	if noFallback then
		-- in given language or wiki language without fallbacks
		return true, mw.wikibase.getDescriptionByLang( id, langcode or Wd.wiki.langcode ) or false
	elseif not langcode then
		-- in wiki language with fallbacks
		return true, mw.wikibase.getDescription( id ) or false
	else
		-- in given language with fallbacks
		local entity = mw.wikibase.getEntity( id )
		if entity then
			return true, entity:getDescription( langcode ) or false
		else
			return false, Wd.newError( "entity-not-found", id )
		end
	end
end

--[=[
  return the label of a Wikidata entity in the given language or the default language of this Wikipedia site
  
  parameters:
  - id (opt, string): entity id
  - title (opt, string): title of a local page that is connected to the entity
  - titleSite (opt, string): site name to look for title, or current site
  - langcode (opt, string): language code, or language code of current site
  - noFallback (opt, boolean): if true, suppress language fallbacks
  One of id and title has to be specified. id takes precedence over title.
  
  returns:
  - (boolean) success indicator
  - (string|boolean|table) label text if successful or error object
]=]
Wd.getLabel = function( id, title, titleSite, langcode, noFallback )
	
	-- determine and validate entity id
	id = Wd.getEntityIdFromTitleIfInexisting( id, title, titleSite )
	if id == false then
		-- no id and no title given, invalid call
		return false, Wd.newError( "invalid-parameters", "Wd.getLabel" )
	elseif not Wd.entityExistsTypesafe( id ) then
		-- entity not found
		return false, Wd.newError( "entity-not-found", id )
	end
	
	-- get label
	if noFallback then
		-- in given language or wiki language without fallbacks
		return true, mw.wikibase.getLabelByLang( id, langcode or Wd.wiki.langcode ) or false
	elseif not langcode then
		-- in wiki language with fallbacks
		return true, mw.wikibase.getLabel( id ) or false
	else
		-- in given language with fallbacks
		local entity = mw.wikibase.getEntity( id )
		if entity then
			return true, entity:getLabel( langcode ) or false
		else
			return false, Wd.newError( "entity-not-found", id )
		end
	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:
  - id (opt, string): entity id
  - title (opt, string): title of a local page that is connected to the entity
  - titleSite (opt, string): site name to look for title, or current site
  One of id and title has to be specified. id takes precedence over title.
  
  returns:
  - (boolean) success indicator
  - (string|boolean|table) page id if successful or error object
]=]
Wd.getPageId = function( id, title, titleSite )
	
	-- determine page id
	if Wd.entityExistsTypesafe( id ) then
		-- get the given entity to check for redirects
		local entity = mw.wikibase.getEntity( id )
		if entity then
			id = entity:getId()
		else
			return false, Wd.newError( "entity-not-found", id )
		end
	elseif title then
		id = mw.wikibase.getEntityIdForTitle( title, titleSite )
	else
		id = mw.wikibase.getEntityIdForCurrentPage()
	end
	
	-- return determined id or false if id not found
	return true, id or false
end

--[=[
  return the sitelink name of the given entity on the given site.
  
  parameters:
  - id (opt, string): entity id
  - title (opt, string): title of a local page that is connected to the entity
  - titleSite (opt, string): site name to look for title, or current site
  - site (opt, string): site name, or current site
  One of id and title has to be specified. id takes precedence over title.
  
  returns:
  - (boolean) success indicator
  - (string|boolean|table) sitelink if successful or error object
]=]
Wd.getSitelink = function( id, title, titleSite, site )
	
	-- determine and validate entity id
	id = Wd.getEntityIdFromTitleIfInexisting( id, title, titleSite )
	if id == false then
		-- no id and no title given, invalid call
		return false, Wd.newError( "invalid-parameters", "Wd.getSitelink" )
	elseif not Wd.entityExistsTypesafe( id ) then
		-- entity not found
		return false, Wd.newError( "entity-not-found", id )
	end
		
	-- if site is nil, the sitelink to the current wiki will be returned
	return true, mw.wikibase.getSitelink( id, site ) or false
end

--[=[
  return the number of sitelinks in the given entity.
  
  parameters:
  - id (opt, string): entity id
  - title (opt, string): title of a local page that is connected to the entity
  - titleSite (opt, string): site name to look for title, or current site
  - filter (opt, string): filter regexp
  One of id and title has to be specified. id takes precedence over title.
  
  returns:
  - (boolean) success indicator
  - (number|table) sitelink count if successful or error object
]=]
Wd.getSitelinkCount = function( id, title, titleSite, filter )
	
	-- determine and validate entity id
	id = Wd.getEntityIdFromTitleIfInexisting( id, title, titleSite )
	if id == false then
		-- no id and no title given, invalid call
		return false, Wd.newError( "invalid-parameters", "Wd.getSitelinkCount" )
	elseif not Wd.entityExistsTypesafe( id ) then
		-- entity not found
		return false, Wd.newError( "entity-not-found", id )
	end
	
	-- get entity
	local entity = mw.wikibase.getEntity( id )
	if not entity then
		return false, Wd.newError( "entity-not-found", id )
	end

	-- create filter regexp
	if filter then
		filter = "^.*" .. filter .. "$"
	else
		filter = false
	end

	-- count sitelinks
	local count  = 0
	if entity.sitelinks then
		if filter == false then
			count = Wd.getTableSize( entity.sitelinks )
		else
			for project, _ in pairs( entity.sitelinks ) do
				if string.find( project, filter ) then
					count = count + 1
				end
			end
		end
	end
	
	return true, count
end

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

-- export table
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
]=]
p.isSubclass = function( frame )
	local result
	
	-- check for validity of start and parent item
	if Wd.entityExistsTypesafe( frame.args[ "id" ] ) and Wd.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 = Wd.isParent( frame.args[ "id" ], frame.args[ "parent" ], frame.args[ "exitItem" ], maxDepth)
			-- mw.log(Wd.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

--[=[
  get a claim
  
  parameters (frame):
  - 1 (string): property of claim
  - id (string): entity id
  - qualifier (string): qualifier to return
  - parameter (string): value parameter to return
  - language (string): value language to return
  - countValues (string): return number of results instead of returning the actual result
  - list (string): concatenate all results with the given separator
  - listMaxItems (string): maximum items to list
  - includeempty (string): also include empty values
  - references (string): return also references, if "only", return references only
  - sort (string): sort results
  - sortEmptiesFirst (string): sort empty values first
  - sortInItem (string): sort based on property value in given item
  - inverse (string): inverse sorting order
  - showerrors (string): show errors instead of returning empty string
  - default (string): default return value if empty or error

  returns: (string) claim or empty or error
]=]
p.claim = function( frame )
	-- (A) PARAMETER ASSIGNMENTS --
	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" ]
	
	-- don't show errors if default value is set
	if default then
		showerrors = nil
	end
	
	-- (B) VALIDITY CHECKS --
	-- get wikidata entity
	if id then
		if not Wd.isValidEntityIdTypesafe( id ) then
			-- invalid entity id
			if showerrors then
				return Wd.templateCallError( "entity-not-valid" ) 
			else
				Wd.createMaintenanceLink( "entity-not-valid" )
				return default
			end
		elseif not Wd.entityExistsTypesafe( id ) then
			-- entity id does not exist
			if showerrors then
				return Wd.templateCallError( "entity-not-found" ) 
			else
				-- create maintenance link
				Wd.createMaintenanceLink( "entity-not-found" )
				return default 
			end
		end
	end
	
	-- get entity object
	local entity = mw.wikibase.getEntity( id )
	if not entity then
		if showerrors then
			return Wd.templateCallError( "entity-not-found" )
		else
			return default
		end
	end

	-- check if property exists
	local realProp = mw.wikibase.resolvePropertyId( property )
	if not realProp then
		Wd.createMaintenanceLink( "property-not-found" )
	end

	-- fetch the first claim 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 Wd.templateCallError( "property-not-found" ) 
		else
			return default
		end
	end
	
	-- (C) FILTERING AND SORTING --
	-- filter claims
	claims = Wd.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
	
	-- get comparator for sorting
	local comparator
	if sort then
		-- comparator function for sorting statements based on qualifier value
		comparator = function( a, b )
			-- load qualifier values
			local qualSortValA = Wd.getQualifierSortValue( claims[ a ], sort )
			local qualSortValB = Wd.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 qualSortValB then 
				if not qualSortValA then
					-- if neither of the two statements has this qualifier, arbitrarily but consistently return a < b
					return a < b
				else
					-- true if sortEmptiesFirst is false
					return ( not sortEmptiesFirst )
				end
			elseif not qualSortValA then
				-- true if sortEmptiesFirst is not false
				return ( not not sortEmptiesFirst )
			end
			
			if type( qualSortValA ) ~= type( qualSortValB ) and not ( tonumber( qualSortValA ) and tonumber( qualSortValB ) ) then
				if tonumber( qualSortValA ) then
					return true
				elseif tonumber( qualSortValB ) then
					return false
				elseif tostring( qualSortValA ) and tostring( qualSortValB ) then
					if inverse then
						return tostring( qualSortValA ) > tostring( qualSortValB )
					else
						return tostring( qualSortValA ) < tostring( qualSortValB )
					end
				else
					-- different types, neither numbers nor strings, no chance to compare => random result to avoid script error
					return false
				end
			elseif tonumber( qualSortValA ) and tonumber( qualSortValB ) then
				qualSortValA = tonumber( qualSortValA )
				qualSortValB = tonumber( qualSortValB )
			end
			if inverse then
				return qualSortValA > qualSortValB
			else
				return qualSortValA < qualSortValB
			end
		end
	elseif sortInItem then
		-- get sortkeys of claims in referenced entity and given property
		local sortkeys = {}
		local snakSingle
		local claimContainingValue
		local claimContainingProperty
		local claimContainingEntity
		
		-- check if given property is valid
		claimContainingProperty = mw.wikibase.resolvePropertyId( sortInItem )
		if not claimContainingProperty then
			if showerrors then
				return Wd.templateCallError( "property-not-found" ) 
			else
				return default
			end
		end
		
		-- loop trough all claims and retrieve the sortkeys
		for idx, claim in pairs( claims ) do
			-- get referenced entity
			snakSingle = Wd.getQualifierSnak( claim )
			claimContainingEntity = "Q" .. Wd.getSnakValue( snakSingle, "numeric-id" )
			
			-- get value if entity exists and has the property
			if Wd.entityExistsTypesafe( claimContainingEntity ) then
				claimContainingEntity = mw.wikibase.getEntity( claimContainingEntity )
				if claimContainingEntity and claimContainingEntity.claims and claimContainingEntity.claims[ claimContainingProperty ] then
					claimContainingValue = claimContainingEntity.claims[ claimContainingProperty ]
				end
			end
			
			-- use value as sortkey, or empty string if no property value found
			if claimContainingValue then
				sortkeys[ #sortkeys + 1 ] = Wd.getClaimValue( claimContainingValue[ 1 ] )
			else
				sortkeys[ #sortkeys + 1 ] = ""
			end
		end
		-- comparator function based on retrieved sortkeys
		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
	
	-- use comparator to sort indices table
	table.sort( sortindices, comparator )

	-- (D) RETURN VALUE --
	local result
	local error
	if countValues then
		-- return claim count
		result = Wd.getTableSize( claims )
	elseif list then
		-- list claims
		
		-- if a newline is provided (whose backslash will be escaped) unescape it
		list = string.gsub( list, "\\n", "\n" )
		
		-- loop through all elements and return their value (if existing)
		local value
		result = {}
		for idx in pairs( claims ) do
			local claim = claims[ sortindices[ idx ] ]
			
			-- get claim value and handle result
			value, error = Wd.getClaimValue( claim, qualifierId, parameter )
			if not value then
				if showerrors then
					value = error
				end
				if includeempty then
					-- overwrite error to empty string
					value = ""
				end
			end
			
			-- add references
			if value and references then
				value = value .. Wd.getReferences( frame, claim )
			end
			
			-- add to result list
			result[ #result + 1 ] = value
		end
		
		-- concat result table to string
		if listMaxItems and listMaxItems > 0 then
			-- return until maximum results is reached
			result = table.concat( result, list, 1, math.min( #result, listMaxItems ) )
		else
			-- return everything
			result = table.concat( result, list )
		end
	else
		-- return first element only
		local claim = claims[ sortindices[ 1 ] ]
		
		-- get value with language support
		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 = Wd.getClaimValue( claim, qualifierId, parameter )
					break
				end
			end
		else
			-- no language given, get claim value
			result, error = Wd.getClaimValue( claim, qualifierId, parameter )
		end
		
		-- handle references
		if references == "only" then
			-- only return references
			result = Wd.getReferences( frame, claim )
		elseif result and references then
			-- add references
			result = result .. Wd.getReferences( frame, claim ) 
		end
	end
	
	-- return result
	if result then
		return result
	else
		if showerrors then
			return error
		else
			return default
		end
	end
end

--[=[
  returns the given value or runs claim() if keyword is found

  parameters (frame):
  - 2 (opt, string): value to return or keyword
  
  returns: (string) value or return of claim()
]=]
p.getValue = function( frame )
	local param = frame.args[ 2 ]
	if param == "FETCH_WIKIDATA" or param == Wd.i18n["FETCH_WIKIDATA"] then
		return p.claim( frame )
	else
		return param
	end
end

--[=[
  return the badges of the given entity for the given site
  
  parameters (frame):
  - id (opt, string): entity id, or entity id of current page
  - title (opt, string): page name to retrieve entity id from
  - titleSite (opt, string): sitename to title
  - site (opt, string): site id, or current site
  id takes precedence over title.

  returns: (string) badges
]=]
p.badges = function( frame )
	local id        = Wd.getFrameArgument( frame, "id" )
	local title     = Wd.getFrameArgument( frame, "title" )
	local titleSite = Wd.getFrameArgument( frame, "titleSite" )
	local site      = Wd.getFrameArgument( frame, "site" )
	
	-- get badges, site is allowed to be nil
	local success, result = Wd.getBadges( id, title, titleSite, site )
	if not success then
		return Wd.templateCallError( Wd.parseError( result ), frame )
	elseif type( result ) == "table" then
		-- concatenate all badges with "/" delimiter for usage with #titleparts
		return table.concat( result, "/" )
	end
	
	-- return empty string by default
	return ""
end

--[=[
  return description of a Wikidata entity in the given language or the default language of this Wikipedia site
  
  parameters (frame):
  - id (opt, string): entity id
  - title (opt, string): page name to retrieve entity id from
  - titleSite (opt, string): sitename to title
  - language (opt, string): language code
  - noFallback (opt, string): suppress language fallbacks
  id takes precedence over title.
  
  returns: (string) description text or empty string
]=]
p.descriptionIn = function( frame )
	local id         = Wd.getFrameArgument( frame, "id" )
	local title      = Wd.getFrameArgument( frame, "title" )
	local titleSite  = Wd.getFrameArgument( frame, "titleSite" )
	local langcode   = Wd.getFrameArgument( frame, "language" )
	local noFallback = Wd.getFrameArgument( frame, "noFallback" )
	
	-- get the description
	local success, result = Wd.getDescription( id, title, titleSite, langcode, noFallback )
	if not success then
		return Wd.templateCallError( Wd.parseError( result ), frame )
	else
		return result or ""
	end
end

--[=[
  return label of a Wikidata entity in the given language or the default language of this Wikipedia site
  
  parameters (frame):
  - id (opt, string): entity id
  - title (opt, string): page name to retrieve entity id from
  - titleSite (opt, string): sitename to title
  - language (opt, string): language code
  - noFallback (opt, string): suppress language fallbacks
  id takes precedence over title.
  
  returns: (string) label text or empty string
]=]
p.labelIn = function( frame )
	local id         = Wd.getFrameArgument( frame, "id" )
	local title      = Wd.getFrameArgument( frame, "title" )
	local titleSite  = Wd.getFrameArgument( frame, "titleSite" )
	local langcode   = Wd.getFrameArgument( frame, "language" )
	local noFallback = Wd.getFrameArgument( frame, "noFallback" )
	
	-- get the label
	local success, result = Wd.getLabel( id, title, titleSite, langcode, noFallback )
	if not success then
		return Wd.templateCallError( Wd.parseError( result ), frame )
	else
		return result or ""
	end
end

--[=[
  [DEPRECTAED] 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) error text
]=]
p.labelOf = function( frame )
	-- soft-deprecate labelOf in favor of labelIn
	return Wd.templateCallError( "deprecated-function", "labelOf", frame )
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):
  - id (opt, string): entity id
  - title (opt, string): page name to retrieve entity id from
  - titleSite (opt, string): sitename to title
  id takes precedence over title.
  
  returns: (string) entity id or empty string
]=]
p.pageId = function( frame )
	local id        = Wd.getFrameArgument( frame, "id" )
	local title     = Wd.getFrameArgument( frame, "title" )
	local titleSite = Wd.getFrameArgument( frame, "titleSite" )
	
	-- get the id
	local success, result = Wd.getPageId( id, title, titleSite )
	if not success then
		return Wd.templateCallError( Wd.parseError( result ), frame )
	else
		return result or ""
	end
end

--[=[
  return the number of sitelinks in the given entity
  
  parameters (frame):
  - id (opt, string): entity id, or entity id of current page
  - title (opt, string): page name to retrieve entity id from
  - titleSite (opt, string): sitename to title
  - filter (opt, string): sitelink filter for projects
  id takes precedence over title.
  
  returns: (number) sitelink count
]=]
p.sitelinkCount = function( frame )
	local id        = Wd.getFrameArgument( frame, "id" )
	local title     = Wd.getFrameArgument( frame, "title" )
	local titleSite = Wd.getFrameArgument( frame, "titleSite" )
	local filter    = Wd.getFrameArgument( frame, "filter" )
	
	-- get the sitelink count
	local success, result = Wd.getSitelinkCount( id, title, titleSite, filter )
	if not success then
		return Wd.templateCallError( Wd.parseError( result ), frame )
	else
		return result or ""
	end
end

--[=[
  return the Wikipedia page name of the given entity on the given site.
  
  parameters (frame):
  - id (opt, string): entity id, or entity id of current page
  - title (opt, string): page name to retrieve entity id from
  - titleSite (opt, string): sitename to title
  - site (opt, string): site name, or current site
  id takes precedence over title.

  returns: (string) page name, error text or empty string
]=]
p.sitelinkOf = function( frame )
	local id        = Wd.getFrameArgument( frame, "id" )
	local title     = Wd.getFrameArgument( frame, "title" )
	local titleSite = Wd.getFrameArgument( frame, "titleSite" )
	local site      = Wd.getFrameArgument( frame, "site" )
	
	-- get the sitelink
	local success, result = Wd.getSitelink( id, title, titleSite, site )
	if not success then
		return Wd.templateCallError( Wd.parseError( result ), frame )
	else
		return result or ""
	end
end

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

--[=[
  call this in cases of script errors within a function:
  instead of {{#invoke:Wikidata|<method>|...}} call {{#invoke:Wikidata|debug|<method>|...}}

  parameters (frame):
  - (...) call parameters of function to debug
  
  returns: (...) return value of function to debug
]=]
p.debug = function( frame )
	-- get function name
	local func = frame.args[ 1 ]
	if not func then
		return Wd.templateCallError( "invalid-parameters" )
	end
	
	-- 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
			-- skip first unnamed param
			if key > 1 then
				newargs[ key - 1 ] = val
			end
		else
			newargs[ key ] = val
		end
	end
	
	-- replace frame args and call the given function
	frame.args = newargs
	local status, result = pcall( p[ func ], frame )
	
	-- output return value or state
	if status then
		return result
	else
		return mw.ustring.format( "<span class=\"error\">%s</span>", result )
	end
end

--[=[
  print the content of an entity

  parameters (frame):
  - 1 (opt, string): entity id, or entity id of current page
  
  returns: (string) entity json content
]=]
p.printEntity = function( frame )
	local id = Wd.getConnectedEntityIdIfInexisting( frame.args[ 1 ] );
	if id then
		-- get entity and dump it
		local entity = mw.wikibase.getEntity( id )
		if entity then
			return mw.ustring.format( "<pre>%s</pre>", mw.dumpObject( entity ) )
		end
	end
	
	-- return empty string by default
	return ""
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
p.ffCoordinate = function(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, parameter = "numeric-id", default = "" }}
		 if t and t ~= "" then
			t = "Q" .. t
			t = t:gsub("Q.*", {
				Q8502  = "mountain",
				Q54050 = "landmark"
			})
		 end
		 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, parameter = "numeric-id", default = "" }}
		 if r and r ~= "" then
		 	r = "Q" .. r
		 	r = p.claim{ args = { "P297", r, id = r }}
		 else
		 	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

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

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

return p

Content Disclaimer

Informasi ini disarikan dari Wikipedia dan disajikan kembali untuk tujuan edukasi. Konten tersedia di bawah lisensi CC BY-SA 3.0. Kami tidak bertanggung jawab atas ketidakakuratan data yang bersumber dari kontribusi publik tersebut.

  1. The information displayed on this website is sourced in part or in whole from Wikipedia and has been adapted for the purpose of restating it. We strive to provide accurate and relevant information, however:
  2. There is no guarantee of absolute accuracy. Wikipedia is an open, collaborative project that can be edited by anyone, so information is subject to change.
  3. It is not intended to constitute professional advice. The content displayed is for informational and educational purposes only. For important decisions (e.g., medical, legal, or financial), please consult a professional.
  4. Content copyright. Wikipedia is licensed under the Creative Commons Attribution-ShareAlike License (CC BY-SA). This means that content may be reused with appropriate attribution and shared under a similar license.
  5. Responsible use. Any risk arising from the use of information from this website is entirely the responsibility of the user.