Modul:Marker utilities

Version vom 19. Januar 2023, 13:53 Uhr von Xineohp1506 (Diskussion | Beiträge) (1 Version von wikivoyage:Modul:Marker_utilities importiert)
(Unterschied) ← Nächstältere Version | Aktuelle Version (Unterschied) | Nächstjüngere Version → (Unterschied)
Gnome-emblem-important.svg
Diese Vorlage wird auf vielen Seiten verwendet
Um große Unterbrechungen und unnötige Serverauslastung zu vermeiden, solltest du Änderungen an anderer Stelle testen, indem du beispielsweise den Quellcode dieser Vorlage kopierst und in den Benutzerbereich oder in die Sandbox einfügst. Erwäge, deine Änderungen auf der Diskussionsseite vorzuschlagen, bevor du diese Vorlage bearbeitest.

Anwendung

Das Modul stellt gemeinsame Funktionen für das Modul:Marker und das Modul:vCard zur Verfügung.

Lua-Fehler in Modul:Failsafe, Zeile 62: attempt to index field 'wikibase' (a nil value)

Benötigte weitere Module

Dieses Modul benötigt folgende weitere Module: Coordinates • Marker utilities/Groups • Marker utilities/i18n • Marker utilities/Maki icons • Marker utilities/Types • UrlCheck • Wikidata utilities

Verwendung in anderen Modulen

Dieses Modul ist notwendig für die Ausführung folgender Module. Bei Anpassungen sollte die Funktionstüchtigkeit der folgenden Module geprüft werden. Benutze dazu auch diese Tracking-Kategorie um Fehler zu finden, die sich dann auf Artikel auswirken:

Wartungsfunktionen

function mu.initMaintenance( name )

Die Funktion initialisiert die Zeichenkettenverwaltung für die Ausgabe von Fehlermeldungen. name ist der zugehörige Modulname ohne die Namensraumbezeichnung. Diese Funktion setzt auch die Tabellen fehlerhafter Paramter und der Fehlermeldungen/Hinweise zurück.

function mu.addMaintenance( s )

Diese Funktion fügt die Fehlermeldung s in die Tabelle der Fehlermeldungen und Hinweise ein. Ein Teil der oben genannten Funktionen befüllt ebenfalls diese Tabelle.

function mu.getMaintenance()

Dises Funktion liefert eine Zeichenkette mit allen Fehlermeldungen und Hinweisen zurück.

function mu.getCategories( formatStr )
  • Liefert eine Zeichenkette mit den Kategorie-Links aller verwendeten Wikidata-Eigenschaften zurück.

Funktionen

Im Projektnamensraum befindet sich die technische Dokumentation.

function mu.isSet( arg )

liefert true oder false, je nachdem, ob das Argument arg gesetzt ist oder nicht. arg muss existieren und einen Wert ungleich '' enthalten.

function mu.tableInsert( tab, value )

fügt den Wert value zur Tabelle tab hinzu, wenn er existiert und nicht leer ist.

function mu.yesno( val )

gibt y oder n zurück, wenn val einen entsprechenden Wert besitzt, anderenfalls nil.

function mu.textSplit( s, sep )

trennt die Zeichenkette s an Trennzeichen sep auf und legt sie in einer Tabelle ab. sep muss genau die Länge von einem Zeichen (ein Byte) haben und darf kein magisches Pattern-Zeichen sein. Die Funktion ist deutlich schneller als mw.text.split().

function mu.getAliases( tab, key )

erstellt aus der Tabelle tab eine Alias-Tabelle, die in tab unter dem Schlüssel key notiert sind.

function mu.checkArguments( templateArgs, validKeys )

prüft, ob im Vorlagenaufruf unbekannte oder durch Aliase entstandene doppelte Parameternamen verwendet werden. Diese unbekannten bzw. doppelten Parameter werden in die Tabelle fehlerhafter bzw. doppelter Parameter eingefügt. templateArgs ist die Tabelle der Vorlagenparamter, validKeys die Tabelle der erlaubten Parameter (siehe z. B. Modul:VCard/i18n). Die Funktion liefert eine Tabelle der gültigen Argumente zurück.

function mu.addWdClass( isFromWikidata )

liefert den Klassenbezeichner wikidata-content, wenn isFromWikidata auf true gesetzt ist, ansonsten eine leere Zeichenkette.

function mu.dmsCoordinates( lat, long, name, fromWD, extraParams, withBracket )

liefert die Zeichenkette r, die eine zu den Kartenwerkzeugen verlinkte Dezimalkoordinate enthält, die wahlweise in Klammern gesetzt oder mit einem Komma eingeleitet werden kann. name ist der Name der Einrichtung, fromWD = true besagt, dass die Koordinate aus Wikidata stammt und extraParams enthält zusätzliche Parameter wird Maßstab und Region, die in den kartenwerkzeugen ausgewertet werden.

function mu.removeCtrls( s, onlyInline )

Die Funktion entfernt Steuerzeichen und die HTML-Tags für den Zeilenumbruch und Zeilenwechsel aus der Zeichenkette s. Wenn onlyInline = false, dann bleiben Zeilenumbruch und Zeilenwechsel erhalten. Deren Vorkommen wird in der Variablen descrDiv mitgeteilt. Die Funktion liefert zwei Werte zurück:

  • s bereinigte Zeichenkette.
  • descrDiv Der Container für die Beschreibung muss ein <div>-Tag sein.
function mu.addLinkIcon( classes, link, title, text )

erstellt den HTML-Code für die Anzeige eines verlinkten Symbolbildes. Die Darstellung erfolgt im Zusammenspiel mit Stilvorlagen. Gefordert werden zu listing-icon hinzuzufügende CSS-Klassen classes, ein Link link (entweder Internet- oder Artikellink), ein Tooltip-Text title und der meist nicht sichtbare Linktext text.

function mu.makeSisterIcons( icons, args, country, entity )

liefert die verlinkten Schwesterprojekt-Symbole in der Tabelle icons zurück. Die Angaben stammen meist aus den Sitelinks der Wikidata-Entity entity. args ist die Tabelle der übergebenen Vorlagenparameter. Die Tabelle country enthält länderspezifische Daten wie die Sprachangabe.

function mu.removeStars( name )

entfernt aus der Zeichenkette name die Sternchen * und liefert die modifizierte Zeichenkette zurück.

function mu.getName( name, pageTitle )

liefert die Tabelle array, die den Namen einer Einrichtung name in verschiedenen Formen enthält. Hauptaufgabe ist es, den eigentlichen Namen aus einem möglichen Link in Wikiyntax herauszulösen. pageTitle ist der mögliche Titel des zugehörigen Artikels. Enthält der Name neben einem möglichen Link in Wikisyntax weitere Textteile, so werden diese verworfen. Folgende vier Tabellenelemente werden zurückgeliefert:

  • exists: boolean, Name liegt vor.
  • name: string, nur der Name.
  • all: string, verlinkter Name oder nur der Name, falls kein Link vorliegt.
  • pageTitle: string, Artikel, auf den verlinkt wird. Der Titel kann von name verschieden sein.
function mu.checkCoordinates( lat, long )

liefert die überprüften Koordinaten lat, long. Im Fehlerfall sind lat und long leere Zeichenketten. Die Fehlermeldungstabelle enthält zusätzlich einen entsprechenden Eintrag (siehe unten).

function mu.typeExists( aType )

liefert den Typ aType oder nil zurück, je nachdem, ob der Typ in der Typentabelle enthalten ist. Aliase werden in den zugehörigen Typ umgewandelt.

function mu.getTypeParams( aType )

liefert den Parametersatz aus Module:Marker utilities/Types für den Typ aType oder nil zurück.

function mu.idToType( id )

liefert zur Wikidata-Id den zugehörigen Typ oder nil zurück.

function mu.checkTypeAndGroup( args )

liefert die überprüften Werte für den Typ und die Gruppen in args zurück. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten).

function mu.checkStatus( args )

prüft die übergebenen Werte im Parameter status und legt die gültigen Werte in args.statusTable ab. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten).

function mu.makeStatusIcons( args )

liefert eine Zeichenkette mit der Bildersyntax gemäß der Tabelle args.statusTable zurück.

function mu.checkImage( image, entity )

liefert den überprüften Wert für das image oder eine leere Zeichenkette zurück. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten). Die Variable mi.options.imageCheck legt fest, ob überhaupt einen rechenzeitintensive Prüfung vorgenommen wird.

function mu.checkUrl( args )

liefert die überprüfte Internetaddresse url in der Argumenttabelle args zurück. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten).

function mu.setCopyMarker( args, show )

prüft bei gesetztem show.copy-Parameter, dass args.wikidata gesetzt und args.copyMarker nicht gesetzt sind. Ansonsten werden show.copy auf false gesetzt und eine Fehlerausschrift ausgegeben.

function mu.makeMarkerSymbol( args, title, frame, show )

liefert r: HTML-Quellcode des Marker-Symbols.

function mu.getColor( args )

fügt color und inverse zu den Argumenten args aus der Gruppe group hinzu. color ist die zur Gruppe gehörende Farbe und wird aus der Tabelle Modul:Marker utilities/Groups bezogen.

function mu.replaceBrackets( s )

liefert eine Zeichenkette, in der eckige Klammern durch XML-Zeichenreferenz ersetzt wurden. Wird benötigt, wenn die Zeichenkette in einem Link verwendet werden soll.

function mu.convertForSort( s )

Wandelt die Zeichenkette s so um, dass eine korrekte Sortierung ermöglicht wird. Die auszutauschenden Buchstaben sind in der sprachabhängigen Tabelle substitutes im Modul Marker utilities/i18n enthalten.

function mu.data( s )

liefert die Zeichenkette s mit aufgelösten XML- und SGML-Enitäten, wenn sie existiert und nicht leer ist, ansonsten nil.

function mu.makeWrapper( result, args, country, show, list, aClass, frame )

umgibt zum Inhalt eines Markers oder einer vCard mit einem umschließenden Tag (span, div).

function mu.languageSpan( s, titleHint, args, country, addClass )

fügt den Text in ein span-Tag, in dem die Sprache und Textrichtung des des Texts s angegeben ist. titleHint ist das title-Attribut des span-Tags, args die Vorlagenparamtetertabelle und country eine Tabelle mit landesspezifischen Angaben. addClass stellt einen zusätzlichen Klassenbezeichner dar.

function mu.split( s, convert, toValue )

liefert eine Tabelle kommaseparierter Werte der Zeichenkette s. Bei convert = true wird die Zeichenkette in Kleinbuchstaben umgewandelt, und Leerräume werden durch den Unterstrich ersetzt. Bei toValue = false, werden die Werte als Schlüssel einer assoziierten Tabelle übergeben.

function mu.getShow( default, value, validValues )

liefert eine assoziierte Tabelle mit den übergebenen kommaseparierten show-Attributen, wobei der überschreibbare Standardwert default berücksichtigt wird. Die show-Attribute werden auf Gültigkeit hin überprüft.

function mu.getCoordinatesFromWikidata( entity )

liefert die im Wikidata-Datensatz mit der Einität entity enthaltene Koordinate zurück. Zuerst wird versucht, die Zentrumskoordinate aus der Eigenbschaft P5140 zu erhalten, danach die Koordinate aus der Eigenschaft P625.

function mu.makeSocial( icons, args, fromWikidata, name )

liefert die verlinkten Symbole von Social-Media-Diensten in der Tabelle icons zurück. args ist die Tabelle der Vorlagenparameter, fromWikidata die Tabelle der Parameter, die aus Wikidata bezogen wurden, und name die Bezeichnung der Einrichtung.

function mu.makeAirport( args, fromWikidata )

liefert den formatierten Flughafencode nach IATA oder, falls dieser fehlt, den nach ICAO. args ist die Tabelle der Vorlagenparameter und fromWikidata die Tabelle der Parameter, die aus Wikidata bezogen

function mu.typeSearch( p31 )
  • Sucht in mehreren P31-P279-Ketten nach Q-ids, deren Werte in den Tabellen Module:Wikidata2/POITypes oder Module:Marker utilities/Types enthalten sein könnten. Die Tabelle p31 enthält die vorgefunden P31-Werte. Der erste Treffer wird als Zeichenkette zurückgegeben, im Fehlerfall die Zeichenkette error. Die Maximalanzahl höherer Ebenen für die Suche ist mit mi.searchLimit vorgegeben, dies sind üblicherweise 4. Es wird nur jeweils die erste P279-Id ausgewertet, also nicht die gesamte Baumstruktur.
function mu.getMakiIconName( aType )

Die Funktion liefert den Namen einer MAKI-Ikone zum Type aType oder nil zurück, wenn es keinen gibt oder eine Abbildung für den Fließtext fehlt.

function mu.checkCommonsCategory( args )

Die Funktion löscht eine evtl. vorhandene Namensraumangabe in der Commons-Kategorie args.commonscat und fügt eine Wartungskategorie hinzu, wenn die Commons-Kategorie gesetzt wurde.

function mu.getCommonsCategory( args, entity )

Die Funktion versucht, eine Commons-Kategorie aus Wikidata über die Wikidata-Entität entity zu beziehen. Zuerst wird der Commons-Site-Link analysiert, dann die Eigenschft P373 und zuletzt die Eigenschaft P910.

function mu.getNamesFromWikidata( args, fromWikidata, country, entity )

Die Funktion befüllt die Tabelle args mit dem Namen der Einrichtung in der Wiki-Sprache und in der Landessprache mit den Angaben aus Wikidata. Die Tabelle fromWikidata enthält die Information (fromWikidata.name, fromWikidata.nameLocal), ob der Name aus Wikidata stammt.

function mu.getArticleLink( args, entity )

Die Funktion übergibt den Sitelink zum zugehörigen Arikel an args.thisWiki, außer der Vorlagenaufruf wurde in diesem Arikel vorgenommen.

function mu.formatNumber( num )

Die Funktion ersetzt in der als Zeichenkette vorliegenden Zahl das Dezimalzeichen und fügt Tausendertrennzeichen ein.

function mu.getTypeLabel( id )

liefert das erste Label aus der Typenliste zum id. id kann ein Marker- oder vCard-Typ bzw. eine Wikidata-Id sein. Im Fehlerfall wird eine leere Zeichenkette oder der id selbst zurückgegeben.

function mu.getPageData()

stellt seitenbezogene Daten zur Verfügung.

Invoke-Funktionen

function mu.generateTableTypeList( frame )

liefert eine sortierbare Tabelle aller für die Vorlagen {{Marker}} und {{vCard}} einsetzbaren Typen. Die Tabelle zeigt die deutsche Übersetzung, die zugehörige Gruppe und die englische Typbezeichnung an.


--[[
	Functions library for Marker and vCard modules
	In non-Wikivoyage projects, sister-project links functions have to be adapted.
]]--

-- module variable and administration
local mu = {
	moduleInterface = {
		suite  = 'Marker utilities',
		serial = '2022-10-22',
		item   = 58187612
	}
}

-- require( 'strict' )
local cd = require( 'Module:Coordinates' )
local mg = mw.loadData( 'Module:Marker utilities/Groups' )
local mi = require( 'Module:Marker utilities/i18n' )
local mm -- MAKI icons
local mt = require( 'Module:Marker utilities/Types' )   -- types to groups like drink, eat, go, see, sleep, ...
local uc -- URL check
local wu = require( 'Module:Wikidata utilities' )

function mu.isSet( arg )
	return arg and arg ~= ''
end

function mu.tableInsert( tab, value )
	if mu.isSet( value ) then
		table.insert( tab, value )
	end
end

function mu.yesno( val )
	return mi.yesno[ mw.ustring.lower( val ) ]
end

-- sep is a single character separator but no magic character
function mu.textSplit( s, sep )
	local result = {}
	for str in s:gmatch( '([^' .. sep .. ']+)' ) do
		mu.tableInsert( result, mw.text.trim( str ) )
	end
	return result
end

function mu.getAliases( tab, key )
	local result = {}
	if not tab then
		return result
	end
	local v
	for k, tb in pairs( tab ) do
		v = tb[ key ]
		if v then
			if type( v ) == 'table' then
				for i = 1, #v do
					result[ v[ i ] ] = k
				end
			else
				result[ v ] = k
			end
		end
	end
	return result
end

-- maintenance tools
function mu.initMaintenance( name )
	mu.name             = name -- module name
	mu.invalidParams    = {}   -- table of unknown parameters
	mu.duplicateAliases = {}   -- table of duplicate parameter aliases
	mu.maintenance      = {}   -- table of error strings

	mu.typeAliases      = mu.getAliases( mt.types, 'alias' )  -- table for type aliases
	mu.groupAliases     = mu.getAliases( mg.groups, 'alias' ) -- table for group aliases
end

function mu.addMaintenance( s )
	local function contains( new )
		for i = 1, #mu.maintenance do
			if mu.maintenance[ i ] == new then
				return true
			end
		end
		return false
	end

	if not contains( s ) then
		table.insert( mu.maintenance, s )
	end
end

function mu.getMaintenance()
	local s = ''
	if #mu.invalidParams > 0 then
		if #mu.invalidParams == 1 then
			s = mw.ustring.format( mi.maintenance.unknownParam, mu.invalidParams[ 1 ] )
		else
			s = mw.ustring.format( mi.maintenance.unknownParams,
				table.concat( mu.invalidParams, ', ' ) )
		end
		s = s .. mw.ustring.format( mi.maintenance.wrongParam, mu.name )
	end
	if #mu.duplicateAliases > 0 then
		s = s .. mw.ustring.format( mi.maintenance.duplicateAliases,
			table.concat( mu.duplicateAliases, ', ' ) )
	end
	return s .. table.concat( mu.maintenance, ' ' )
end

function mu.getCategories( formatStr )
	return wu.getCategories( formatStr )
end

-- args: template arguments consisting of argument name as key and a value
-- validKeys: table with argument name as key used by the script and
--    a string or a table of strings for argument names used by the local wiki
function mu.checkArguments( templateArgs, validKeys )
	local args = {}
	if not templateArgs or not validKeys or not next( validKeys ) then
		return args
	end

	local keys = {} -- list of wiki-dependent parameter names
	for key, params in pairs( validKeys ) do
		if type( params ) == 'string' then
			keys[ params ] = key
		else
			for i = 1, #params do
				keys[ params[ i ] ] = key
			end
		end
	end
	
	local targetKey
	for key, arg in pairs( templateArgs ) do
		targetKey = keys[ key ]
		if targetKey then
			if args[ targetKey ] then -- prevents duplicates
				table.insert( mu.duplicateAliases, "''" .. key .. "''" )
			else
				args[ targetKey ] = arg
			end
		else
			table.insert( mu.invalidParams, "''" .. key .. "''" )
		end
	end
	return args
end

function mu.addWdClass( isFromWikidata )
	return isFromWikidata and ' voy-wikidata-content' or ''
end

-- getting DMS coordinates
function mu.dmsCoordinates( lat, long, name, fromWD, extraParams, withBrackets )
	local dec
	local latDMS, dec, latMs = cd.getDMSString( lat, 4, 'lat', '', '', mi.defaultDmsFormat )
	local longDMS, dec, longMs = cd.getDMSString( long, 4, 'long', '', '', mi.defaultDmsFormat )

	local r = '[' .. mi.coordURL .. latMs .. '_' .. longMs .. '_'
		.. mw.uri.encode( extraParams )	.. '&locname=' .. mw.uri.encode( name )
		.. ' ' .. tostring( mw.html.create( 'span' )
				:addClass( 'coordStyle' )
				:attr( 'title', mi.texts.latitude )
				:wikitext( latDMS )
			)
		.. ' ' .. tostring( mw.html.create( 'span' )
				:addClass( 'coordStyle' )
				:attr( 'title', mi.texts.longitude )
				:wikitext( longDMS )
			)
		.. ']'
	if withBrackets then
		r = '(' .. r .. ')'
	end
	return tostring( mw.html.create( 'span' )
		:attr( 'class', 'listing-dms-coordinates printNoLink plainlinks'
			.. mu.addWdClass( fromWD ) )
		:wikitext( r ) )
end

local function replaceWithSpace( s, pattern )
	s = s:find( pattern ) and s:gsub( pattern, ' ' ) or s
	return s
end

-- removing line breaks and controls from parameter strings
function mu.removeCtrls( s, onlyInline )
	local descrDiv = false -- div tag instead of span tag for description needed?

	-- remove controls from tags before test
	s = s:gsub( '(<[^>]+>)', function( t )
			return replaceWithSpace( t, '[%z\1-\31]' )
		end )

	local t = replaceWithSpace( s, '</br%s*>' )
	if onlyInline then
		t = replaceWithSpace( t, '<br[^/>]*/*>' )
		t = replaceWithSpace( t, '</*p[^/>]*/*>' )
		t = replaceWithSpace( t, '[%z\1-\31]' )
		-- not %c because \127 is used for Mediawiki tags (strip markers `UNIQ)
	else
		t = replaceWithSpace( t, '[%z\1-\9\11\12\14-\31]' )
		descrDiv = t:find( '[\10\13]' ) or t:find( '<br[^/>]*/*>' ) or
			t:find( '<p[^/>]*>' )
	end
	if t ~= s then
		mu.addMaintenance( mi.maintenance.illegalCtrls )
	end

	if descrDiv then
		mu.addMaintenance( mi.maintenance.descrDiv )
		-- unify line breaks to Linux mode
		t = t:gsub( '\13\10', '\10' ):gsub( '\13', '\10' )
		-- replace line breaks by <br> in block mode
		t = t:gsub( '([^>%]\10])\10+([^<%[\10])', '%1<br class="listing-next-paragraph" />%2' )
	end

	return replaceWithSpace( t, '%s%s+' ), descrDiv
end

local colorAdjust = { ['-webkit-print-color-adjust'] = 'exact', ['color-adjust'] = 'exact',
	['print-color-adjust'] = 'exact' }

function mu.addLinkIcon( classes, link, title, text )
	local span = tostring( mw.html.create( 'span' )
		:attr( 'title', title )
		:css( colorAdjust )
		:wikitext( ' ' .. text )
	)
	local lFormat = ( link:find( '^https?://' ) or link:find( '^//' ) )
		and '[%s %s]' or '[[%s|%s]]'
	return tostring( mw.html.create( 'span' )
		:attr( 'class', 'listing-icon ' .. classes )
		:wikitext( mw.ustring.format( lFormat, link, span ) )
	)
end

-- adding linked sister icons
local function addSisterIcons( icons, sisters, name, id )
	local link, span
	for i, key in ipairs( { 'wikivoyage', 'wikipedia', 'commons', 'wikidata' } ) do
		if mu.isSet( sisters[ key ] ) then
			span = mu.addLinkIcon( 'listing-sister-icon listing-sister-' .. key, sisters[ key ],
				mw.ustring.format( mi.iconTitles[ key ], name, id ), key )
			table.insert( icons, span )
		end
	end

	-- return true if only Wikidata icon
	return mu.isSet( sisters.wikidata ) and #icons == 1
end

-- getting sister project links
local function getWikiLink( langArray, wiki, entity, wikilang )
	local prefix = wiki == 'wiki' and 'w:' or 'voy:'
	local link
	for i, lang in ipairs( langArray ) do
		if lang ~= '' then
			link = wu.getSitelink( entity, lang .. wiki )
			if link then
				prefix = prefix .. ( lang ~= wikilang and ( lang .. ':' ) or '' )
				return prefix .. link
			end
		end
	end
	return ''
end

function mu.getLangTable( wikiLang, localLang )
	local langs = { wikiLang }
	for i, lang in ipairs( mi.langs ) do
		table.insert( langs, lang )
	end
	if mu.isSet( localLang ) and localLang ~= wikiLang then
		table.insert( langs, localLang )
	end
	return langs
end

-- adding Wikimedia sister project icons
function mu.makeSisterIcons( icons, args, page, country, entity )
	local sisters = {
		commons    = '', -- link to Commons category
		wikidata   = '', -- link to Wikidata
		wikipedia  = '', -- link to Wikipedia
		wikivoyage = ''  -- link to another branch, usually en, as a sister link
	}

	if mu.isSet( args.wikipedia ) then
		sisters.wikipedia = 'w:' .. args.wikipedia
	end
	if mu.isSet( args.wikidata ) then
		local langs = mu.getLangTable( page.lang, country.lang )

		if sisters.wikipedia == '' then
			sisters.wikipedia = getWikiLink( langs, 'wiki', entity, page.lang )
		end
		if args.wikiPage == '' then
			table.remove( langs, 1 ) -- exclude page.lang
			sisters.wikivoyage = getWikiLink( langs, page.globalProject, entity, page.lang )
			if sisters.wikivoyage ~= '' then
				mu.addMaintenance( mi.maintenance.linkToOtherWV )
			end
		end
		sisters.wikidata = 'd:' .. args.wikidata
	end
	if args.commonscat ~= '' then
		sisters.commons = 'c:Category:' .. args.commonscat
	end

	return addSisterIcons( icons, sisters, args.givenName.name, args.wikidata )
end

function mu.removeStars( args, params )
	for i, param in ipairs( params ) do
		if args[ param ]:find( '*', 1, true ) then
			args[ param ] = args[ param ]:gsub( '%*+', '' )
			args[ param ] = mw.text.trim( args[ param ] )
			mu.addMaintenance( mi.maintenance.nameWithStar )
		end
	end
end

-- getting name table with linked and unlinked names
function mu.getName( name, pageTitle )
	local r = {
		exists = false,
		all = '', -- name or linked name
		name = '', -- only name
		pageTitle = '', -- if pageTitle ~= '' name is already linked
	}
	if type( name ) ~= 'string' or name == '' then
		return r
	end

	r.exists = true
	local s
	local t, c = mw.ustring.gsub( name, '^(.*)%[%[(.*)%]%](.*)$', '%2' )
	if c > 0 then -- is / contains [[...]]
		t = mw.text.trim( t )
		r.all = '[[' .. t .. ']]'
		s, c = mw.ustring.gsub( t, '^(.*)|(.*)$', '%2' )
		if c > 0 then -- is [[...|...]]
			r.name = mw.text.trim( s )
			r.pageTitle = mw.ustring.gsub( t, '^(.*)|(.*)$', '%1' )
			r.pageTitle = mw.text.trim( r.pageTitle )
		else
			r.name = t
			r.pageTitle = t
		end
	else
		r.name = name
		r.all = name
		if pageTitle ~= '' then
			r.pageTitle = pageTitle
			r.all = '[[' .. pageTitle .. '|' .. name .. ']]'
		end
	end
	mu.removeStars( r, { 'name', 'all' } )
	return r
end

-- checking coordinates
function mu.checkCoordinates( args )
	local function clearCoordinates()
		args.lat = ''
		args.long = ''
	end

	local t
	if type( args.lat ) == 'boolean' or type( args.long ) == 'boolean' then
		clearCoordinates()
	end
	if args.lat == '' and args.long == '' then
		return
	elseif args.lat ~= '' and args.long == '' then
		t = args.lat:find( ',', 1, true )
		if t then
			args.long = mw.text.trim( args.lat:sub( t + 1, #args.lat ) )
			args.lat = mw.text.trim( args.lat:sub( 1, t - 1 ) )
		end
	end
	if args.lat == '' or args.long == '' then
		clearCoordinates()
		mu.addMaintenance( mi.maintenance.wrongCoord )
		return
	end

	local dms = false
	t = tonumber( args.lat )
	if t then
		args.lat = math.abs( t ) <= 90 and t or ''
	else
		t = cd.toDec( args.lat, 'lat', 6 )
		args.lat = t.error == 0 and t.dec or ''
		dms = args.lat ~= ''
	end

	if args.lat ~= '' then
		t = tonumber( args.long )
		if t then
			args.long = ( t > -180 and t <= 180 ) and t or ''
		else
			t = cd.toDec( args.long, 'long', 6 )
			args.long = t.error == 0 and t.dec or ''
			dms = dms or args.long ~= ''
		end
	end

	if args.lat == '' or args.long == '' then
		clearCoordinates()
		mu.addMaintenance( mi.maintenance.wrongCoord )
	elseif dms then
		mu.addMaintenance( mi.maintenance.dmsCoordinate )
	end

	return
end

-- getting a set of parameters for a given type
function mu.getTypeParams( aType )
	return mt.types[ aType ]
end

local function encodeSpaces( s )
	if s:find( '[_%s]+' ) then
		s = s:gsub( '[_%s]+', '_' )
	end
	return s
end

function mu.typeExists( aType )
	return mt.types[ aType ] and aType or mu.typeAliases[ aType ]
end

-- groups translation for map legend into Wiki language
local function translateGroup( group )
	if not mu.isSet( group ) then
		group = mt.types.error.group
	end
	local t = mg.groups[ group ]
	if t then
		t = t.map or t.label or t.alias or group
		if type( t ) == 'table' then
			t = t[ 1 ]
		end
		return t
	end
	return group
end

-- getting marker type and group
function mu.checkTypeAndGroup( args )
	local s, t
	args.typeTable = {}
	args.subtypeTable = {}
	if mu.isSet( args.group ) then
		mu.addMaintenance( mi.maintenance.groupUsed )
		if mu.groupAliases[ args.group ] then
			args.group = mu.groupAliases[ args.group ]
		end
		s = mg.groups[ args.group ]
		if not s then			
			mu.addMaintenance( mi.maintenance.unknownGroup )
			args.group = ''
		elseif s.is and s.is == 'color' then
			mu.addMaintenance( mi.maintenance.groupIsColor )
			if mi.options.useTypeCateg then
				mu.addMaintenance( mw.ustring.format( mi.maintenance.type, args.group ) )
			end
		end
	end
	if not mu.isSet( args.type ) then
		args.type = mt.types.error.group
		if args.group == '' then
			args.group = mt.types.error.group
		end
		mu.addMaintenance( mi.maintenance.missingType )
	elseif args.type == mt.types.error.group then
		if args.group == '' then
			args.group = mt.types.error.group
		end
		mu.addMaintenance( mi.maintenance.unknownType )
	else
		-- split seperate types and analyse them
		for i, t in ipairs( mu.textSplit( args.type, ',' ) ) do
			t = encodeSpaces( t:lower() )
			if next( mu.typeAliases ) then
				t = mu.typeAliases[ t ] or t
			end
			s = mu.groupAliases[ t ]
			if s then
				t = s
			end
			s = mg.groups[ t ]
			if s then -- type is a group itself
				if s.is and s.is == 'color' then
					mu.addMaintenance( mi.maintenance.typeIsColor )
				elseif not mi.options.noTypeMsgs then
					mu.addMaintenance( mi.maintenance.typeIsGroup )
				end
				if args.group == '' then
					args.group = t
				end
			else
				s = mt.types[ t ]
				if s then
					if args.group == '' then
						args.group = s.group
					end
					if mu.isSet( s.subtype ) then
						table.insert( args.subtypeTable, t )
					end
				else
					mu.addMaintenance( mi.maintenance.unknownType )
					args.group = mt.types.error.group
				end
			end
			table.insert( args.typeTable, t )
		end
		args.type = table.concat( args.typeTable, ',' )
	end
	args.groupTranslated = translateGroup( args.group )
end

function mu.checkStatus( args )
	args.statusTable = {}
	local hash = {}
	if mu.isSet( args.status ) then
		local statusAliases = mu.getAliases( mi.statuses, 'alias' )
		for i, t in ipairs( mu.textSplit( args.status, ',' ) ) do
			t = encodeSpaces( t:lower() )
			if next( statusAliases ) then
				t = statusAliases[ t ] or t
			end
			if mi.statuses[ t ] then
				if not hash[ t ] then
					table.insert( args.statusTable, t )
					hash[ t ] = 'x'
				end
			else
				mu.addMaintenance( mi.maintenance.unknownStatus )
			end
		end
	end
end

function mu.makeStatusIcons( args )
	local result = ''
	if args.statusTable and #args.statusTable > 0 then
		for k, v in pairs( args.statusTable ) do
			result = result .. tostring( mw.html.create( 'span' )
				:addClass( 'listing-status listing-status-' .. v )
				:attr( 'title', mi.statuses[ v ].label )
				:css( colorAdjust )
				:wikitext( v )
			)
		end
	end
	return result
end

local function removeNS( s, nsTable )
	s = s or ''
	if not s:find( ':', 1, true ) then
		return s
	end

	local t = s
	for i = 1, #nsTable do
		t = mw.ustring.gsub( t, '^' .. nsTable[ i ] .. ':', '' )
		if s ~= t then
			break
		end
	end
	return t
end

-- image check
function mu.checkImage( args, entity )
	if type( args.image ) == 'boolean' or args.image == '' then
		return
	end

	-- formal checks
	if args.image:find( '^https?:' ) then
		args.image = ''
	else
		-- remove namespace
		args.image = removeNS( args.image, mi.texts.FileNS )
		local extensionExists = false
		local im = args.image:lower()
		for i = 1, #mi.fileExtensions do
			if im:find( '%.' .. mi.fileExtensions[ i ] .. '$' ) then
				extensionExists = true
				break
			end
		end
		if not extensionExists then
			args.image = ''
		end
	end
	if args.image == '' then
		mu.addMaintenance( mi.maintenance.wrongImgName )
		return
	end

	local alreadyChecked = false
	if mi.options.mediaCheck and args.image ~= '' then
		if not mi.options.WDmediaCheck and entity then
			-- check if image is stored in Wikidata
			local imgs = wu.getValues( entity, mi.properties.image, nil )
			for i = 1, #imgs do
				if imgs[ i ] == args.image then
					alreadyChecked = true
					break
				end
			end
		end
		if not alreadyChecked then
			-- expensive function call
			local title = mw.title.new( 'Media:' .. args.image )
			if not title or not title.exists then
				mu.addMaintenance( mw.ustring.format( mi.maintenance.missingImg, args.image ) )
				args.image = ''
			end
		end
	end
end

local function isUrl( url )
	if not uc then
		uc = require( 'Module:UrlCheck' )
	end
	return uc.isUrl( url, mi.options.skipPathCheck )
end

-- url check in args
function mu.checkUrl( args )
	if mu.isSet( args.url ) then
		local c = isUrl( args.url ) -- getting result code
		if c > 2 then
			mu.addMaintenance( mi.maintenance.wrongUrl )
			args.url = ''
		elseif c == 2 then -- URL contains IP address
			mu.addMaintenance( mi.maintenance.urlWithIP )
		else
			for i = 1, #mi.services do
				if args.url:find( mi.services[ i ].key .. '.com', 1, true ) then
					mu.addMaintenance( mi.maintenance.urlIsSocialMedia )
					args.url = ''
				end
			end
		end
	end
end

function mu.setCopyMarker( args, show )
	if show.copy and ( mu.isSet( args.copyMarker ) or not mu.isSet( args.wikidata ) ) then
		show.copy = false
		mu.addMaintenance( mi.maintenance.deleteShowCopy )
	end
	if show.copy then
		args.copyMarker = args.wikidata
	end
end

-- making marker symbol
function mu.makeMarkerSymbol( args, title, frame )
	local inverseClass = args.inverse and ' listing-map-inverse' or ''
	local copyClass = 'listing-map plainlinks printNoLink voy-copy-marker'
		.. inverseClass

	if mu.isSet( args.copyMarker ) then
		return tostring( mw.html.create( 'span' )
			:addClass( copyClass )
			:attr( 'title', mi.texts.tooltip )
			:css( colorAdjust )
			-- display will be replaced by [[MediaWiki:Gadget-GeneralChanges.js]] script
			:css( { display = 'none' } )
			:attr( 'data-copy-marker-attribute', args.copyMarker:match( 'Q%d+' )
				and 'data-wikidata' or 'data-name' )
			:attr( 'data-copy-marker-content', args.copyMarker )
		)
	end
	
	local lon = tonumber( args.long )
	local lat = tonumber( args.lat )
    local tagArgs = {
        zoom = tonumber( args.zoom ),
        latitude = lat,
        longitude = lon,
        show = mg.showAll,
    }
	if mu.isSet( args.text ) then
		tagArgs.text = args.text
		if not args.useIcon then
			tagArgs.class = 'no-icon'
		end
	end
	if mu.isSet( args.mapGroup ) then
		tagArgs.group = args.mapGroup
		tagArgs.show = args.mapGroup
	else
		tagArgs.group = args.groupTranslated
	end
	if mu.isSet( args.image ) then
		tagArgs.description = '[[File:' .. args.image .. '|100x100px|' .. title .. ']]'
	end

	local geoJson = {
		type = 'Feature',
		geometry = {
			type = 'Point',
			coordinates = { lon, lat }
		},
		properties = {
			title = title,
			description = tagArgs.description,
			['marker-symbol'] = args.symbol,
			['marker-color'] = args.color,
			['marker-size'] = 'medium',
		}
	}

	return tostring( mw.html.create( 'span' )
		:addClass( 'listing-map plainlinks printNoLink' .. inverseClass )
		:attr( 'title', mi.texts.tooltip )
		:css( colorAdjust )
		:css( 'background-color', args.color )
		-- frame:extensionTag is expensive
		:wikitext( frame:extensionTag( 'maplink', mw.text.jsonEncode( geoJson ), tagArgs ) )
	)
end

-- getting color from group in args
function mu.getColor( args )
	local c = mg.groups[ args.group ] or mg.groups[ 'error' ]
	args.color = c.color
	args.inverse = c.inverse
end

-- replace brackets in names
function mu.replaceBrackets( s )
	local count
	s, count = s:gsub( '%[', '&#x005B;' )
		:gsub( '%]', '&#x005D;' )
	return s
end

function mu.convertForSort( s )
	s = mw.ustring.lower( s )
	for i, obj in ipairs( mi.substitutes ) do
		s = mw.ustring.gsub( s, obj.l, obj.as )
	end
	return s
end

-- prepare value s for data attribute and replace all entities by characters
function mu.data( s )
	return mu.isSet( s ) and mw.text.decode( s, true ) or nil
end

-- adding wrapper and microformats
function mu.makeWrapper( result, args, page, country, show, list, aClass, frame )
	if type( result ) == 'table' then
		result = table.concat( result, ' ' )
	end

	local wrapper = mw.html.create( show.inline and 'span' or 'div' )
		:attr( 'class', 'vcard h-card ' .. aClass )
	if args.noGpx then
		wrapper:addClass( 'listing-no-gpx' )
	end
	if show.outdent and not show.inline then
		wrapper:addClass( 'listing-outdent' )
	end
	if show.inlineSelected then
		wrapper:addClass( 'listing-inline' )
	end
	if mu.isSet( args.copyMarker ) or not show.poi then
		wrapper:addClass( 'voy-without-marker' )
	end
	if args.givenName.name ~= mi.maintenance.missingName then
		wrapper:attr( 'data-name', mu.data( args.givenName.name ) )
	end
	if args.statusTable and #args.statusTable > 0 then
		wrapper:addClass( 'listing-with-status' )
	end
	if mu.isSet( args.wikidata ) then
		wrapper:attr( 'id', 'vCard_' .. args.wikidata )
	end
	wrapper:attr( 'data-location', mu.data( page.subpageText ) )
		:attr( 'data-location-qid', page.entityId )
		:attr( 'data-wikilang', page.lang )
		:attr( 'data-country', mu.data( country.iso_3166 ) )
		:attr( 'data-country-name', mu.data( country.country ) )
		:attr( 'data-lang', mu.data( country.lang ) )
		:attr( 'data-lang-name', mu.data( country.langName ) )
		:attr( 'data-country-calling-code', mu.data( country.cc ) )
		:attr( 'data-trunk-prefix', mu.data( country.trunkPrefix ) )
		:attr( 'data-dir', mu.data( country.isRTL and 'rtl' or 'ltr' ) )
		:attr( 'data-wiki-dir', mu.data( page.isRTL and 'rtl' or 'ltr' ) )
		:attr( 'data-currency', mu.data( country.addCurrency ) )
	local arg
	for key, value in pairs( list ) do
		arg = args[ key ]:gsub( '<[^<>]*>', '' ) -- remove html tags
		wrapper:attr( value, mu.data( arg ) )
	end
	if not show.noCoord then
		wrapper:node( mw.html.create( 'span' )
			:attr( 'class', 'p-geo geo listing-coordinates' )
			:css( 'display', 'none' )
			:node( mw.html.create( 'span' )
				:attr( 'class', 'p-latitude latitude' )
				:wikitext( args.lat )
			)
			:node( mw.html.create( 'span' )
				:attr( 'class', 'p-longitude longitude' )
				:wikitext( args.long )
			)	
		)
	end
	if not show.name then
		wrapper:node( mw.html.create( 'span' )
			:attr( 'class', 'p-name fn org listing-name' )
			:css( 'display', 'none' )
			:wikitext( args.givenName.name )
		)
	end

	wrapper = tostring( wrapper:wikitext( result ) )

	-- adding coordinates to Mediawiki database
	-- frame:callParserFunction is expensive
	if not show.noCoord and mi.options.secondaryCoords then
		wrapper = wrapper .. frame:callParserFunction{ name = '#coordinates',
			args = { args.lat, args.long, country.extra, name = args.givenName.name } }
	end

	return wrapper
end

-- bdi and bdo tags are not working properly on all browsers. Adding marks
-- (lrm, rlm) is maybe the only way for a correct output
function mu.languageSpan( s, titleHint, page, country, addClass )
	if not mu.isSet( s ) then
		return ''
	end

	local c = country.lang
	if c == '' or c == page.lang then
		return s
	end

	local dir
	if country.isRTL then
		dir = 'rtl'
	elseif page.isRTL then
		dir = 'ltr'
	end	

	local t = tostring( mw.html.create( 'bdi' )
		:attr( 'class', addClass )
		:addClass( 'voy-lang' )
		:addClass( 'voy-lang-' .. c )
		:attr( 'lang', c )
		:attr( 'dir', dir )
		:attr( 'title', mw.ustring.format( titleHint , country.langName ) )
		:wikitext( s )
	)

	if country.isRTL and not page.isRTL then
		t = '&rlm;' .. t .. '&lrm;'
	end
	if not country.isRTL and page.isRTL then
		t = '&lrm;' .. t .. '&rlm;'
	end
	return t
end

-- Splitting comma separated lists to an array
-- convert = true: conversion to lowercase characters, spaces to low lines
-- toValue = true: list items handled as array values
-- toValue = false: list items handled as array keys
function mu.split( s, convert, toValue )
	local arr = {}
	if not mu.isSet( s ) then
		return arr
	end
	for i, str in ipairs( mu.textSplit( s, ',' ) ) do
		if convert then
			str = encodeSpaces( str:lower() )
		end
		if toValue then
			table.insert( arr, str )
		else
			arr[ str ] = ''
		end
	end
	return arr
end

-- Splitting comma separated lists to a table of key items
-- checking items with allowed key values of validValues table
local function splitAndCheck( s, validValues )
	local values = {}
	if not mu.isSet( s ) or not validValues then
		return values, ''
	end

	local errors = {}
	for item, v in pairs( mu.split( s, true ) ) do
		-- value check
		if validValues[ item ] then
			values[ item ] = ''
		else
			table.insert( errors, item )
		end
	end
	return values, table.concat( errors, ', ' )
end

function mu.getShow( default, value, validValues )
	local show = mu.split( default, true )
	local add, err = splitAndCheck( value, validValues )
	if err ~= '' then
		mu.addMaintenance( mw.ustring.format( mi.maintenance.unknownShow, err ) )
	end
	if add.none or add.coord or add.poi or add.all then
		show.all   = nil -- overwriting defaults
		show.coord = nil
		show.poi   = nil
	end
	for key, value in pairs( add ) do
		show[ key ] = value
	end
	if show.none then
		show.none  = nil
		show.all   = nil
		show.coord = nil
		show.poi   = nil
	end
	if show.all then
		show.all   = nil
		show.coord = ''
		show.poi   = ''
	end

	return show
end

function mu.getCoordinatesFromWikidata( args, fromWikidata, entity )
	if not entity or ( args.lat ~= '' and args.long ~= '' ) then
		return
	end

	-- center coordinates from Wikidata
	local c = wu.getValue( entity, mi.properties.centerCoordinates )
	if c == '' then
		-- coordinates from Wikidata
		c = wu.getValue( entity, mi.properties.coordinates )
	end
	if c ~= '' then
		args.lat = c.latitude
		args.long = c.longitude
		fromWikidata.lat = true
	end
end

-- create social media icons including value check
function mu.makeSocial( icons, args, fromWikidata, name )
	local domain, span, t

	for i, service in ipairs( mi.services ) do
		-- check values first
		t = args[ service.key ] or ''
		domain = service.url:gsub( 'com/.*', 'com/' )
		if t ~= '' then
			if t:match( '^http' ) then
				if not t:find( 'https', 1, true ) then
					t = t:gsub( '^http', 'https' )
					mu.addMaintenance( mw.ustring.format(
						mi.maintenance.wrongSocialUrl, service.key ) )
				end
				if isUrl( t ) > 1 or
					not t:match( '^' .. domain .. '.+$' ) then
					t = ''
					mu.addMaintenance( mw.ustring.format(
						mi.maintenance.wrongSocialUrl, service.key ) )
				end
				if t ~= '' and not fromWikidata[ service.key ] then
					mu.addMaintenance( mw.ustring.format(
						mi.maintenance.socialUrlUsed, service.key ) )
				end
			else
				local match = false
				local sp = service.pattern
				if type( sp ) == 'string' then
					sp = { sp }
				end
				for i = 1, #sp do
					if mw.ustring.match( t, sp[ i ] ) then
						match = true
						break
					end
				end
				if not match then
					t = ''
					mu.addMaintenance( mw.ustring.format(
						mi.maintenance.wrongSocialId, service.key ) )
				end
			end
		end
		args[ service.key ] = t

		-- create symbol link
		if t ~= '' then
			if not t:find( domain, 1, true ) then
				t = mw.ustring.format( service.url, t )				
			end

			span = mu.addLinkIcon( 'listing-social-media listing-social-media-' ..
				service.key .. mu.addWdClass( fromWikidata[ service.key ] ), t,
				mw.ustring.format( mi.iconTitles[ service.key ], name ), service.key )
			table.insert( icons, span )
		end
	end
end

local function _makeAirport( code, args, fromWikidata )
	local span = mw.html.create( 'span' )
		:attr( 'class', 'listing-' .. code .. '-code'
			.. mu.addWdClass( fromWikidata[ code ] ) )
		:wikitext( args[ code ] )
	return tostring( mw.html.create( 'span' )
		:attr( 'class', 'listing-airport listing-' .. code )
		:wikitext( mw.ustring.format( mi.texts[ code ], tostring( span ) ) )
	)
end

function mu.makeAirport( args, fromWikidata )
	if mu.isSet( args.iata ) then
		return _makeAirport( 'iata', args, fromWikidata )
	elseif mu.isSet( args.icao ) then
		return _makeAirport( 'icao', args, fromWikidata )
	else
		return ''
	end
end

function mu.idToType( id )
	if not mu.types then
		mu.types = mu.getAliases( mt.types, 'wd' ) -- Q id to type table
	end
	return mu.types[ id ]
end

local function _typeSearch( p31 )
	-- p31: array of Wikidata values
	if not p31 or #p31 == 0 then
		return ''
	end

	local firstStep = true

	local function compareLabels( ar )
		if not ar then
			return nil
		elseif type( ar ) == 'string' then
			ar = { ar }
		end
		for i, value in ipairs( ar ) do
			if mu.isSet( value ) then
				value = mw.ustring.lower( encodeSpaces( value ) )
				if mt.types[ value ] then
					return value
				end
			end
		end
		return nil
	end

	local function compareIds( ar )
		local id, t
		for i = 1, #ar do
			id = ar[ i ].id
			-- md: indexed array of q id - types relations
			t = mu.idToType( id )
			if t then
				return t
			end

			-- checking English label and aliases
			t = compareLabels( mw.wikibase.getLabelByLang( id, 'en' ) )
				or compareLabels( wu.getAliases( id, 'en' ) )
			if t then
				if firstStep and not mi.options.noTypeMsgs then
					firstStep = false
					mu.addMaintenance( mi.maintenance.typeFromWDchain )
				end
				return t
			end
		end
		return nil
	end

	local aType = compareIds( p31 ) -- check p31 ids first, maybe step 2 is not nessary
	if aType then
		if not mi.options.noTypeMsgs then
			mu.addMaintenance( mi.maintenance.typeFromWD )
		end
		return aType
	end

	-- now functions becomes expensive because of multiple wu.getValues calls
	local id, ids
	firstStep = false
	for i = 1, #p31 do -- step 2: analyse P279 chains of first ids
		id = p31[ i ].id -- start id
		local j = 0
		repeat
			ids = wu.getValues( id, mi.properties.subclassOf, nil )
			if #ids > 0 then
				aType = compareIds( ids )
				if aType then
					if not mi.options.noTypeMsgs then
						mu.addMaintenance( mi.maintenance.typeFromWD )
						mu.addMaintenance( mi.maintenance.typeFromWDchain )
					end
					return aType
				end
				id = ids[ 1 ].id
			end
			j = j + 1

		-- limit: maximum levels to analyse
		until j >= mi.searchLimit or #ids == 0
	end

	return ''
end

function mu.typeSearch( p31, entity )
	if p31 and #p31 == 0 then
		p31 = wu.getValues( entity, mi.properties.subclassOf, mi.p31Limit )
	end
	return _typeSearch( p31 )
end

-- returns a single data set from Module:Marker utilities/Maki icons
function mu.getMaki( key )
	if not mm then
		mm = require( 'Module:Marker utilities/Maki icons' )
	end
	return mm.icons[ key ]
end

function mu.getMakiIconName( aType )
	local mType
	if mu.getMaki( aType ) then
		mType = aType
	elseif mt.types[ aType ] and mt.types[ aType ].icon then
		mType = mt.types[ aType ].icon
	end
	if mType and mu.isSet( mu.getMaki( mType ).im ) then
		return mType
	end
	return nil
end

function mu.checkCommonsCategory( args )
	-- remove namespace from category
	if type( args.commonscat ) == 'string' and args.commonscat ~= '' then
		args.commonscat = removeNS( args.commonscat, mi.texts.CategoryNS )
		if args.commonscat ~= '' then
			mu.addMaintenance( mi.maintenance.commonscat )
		end
	end
end

function mu.getCommonsCategory( args, entity )
	-- getting commonscat from commonswiki sitelink before P373
	-- because sitelink is checked by Wikidata
	if type( args.commonscat ) == 'boolean' then
		args.commonscat = ''
	end

	local t = wu.getSitelink( entity, 'commonswiki' ) or ''
	if t:match( '^Category:.+$' ) then
		t = t:gsub( '^Category:', '' )
	else
		t = wu.getValue( entity, mi.properties.commonsCategory )
		if t == '' then
			local id = wu.getId( entity, mi.properties.mainCategory )
			if id ~= '' then
				t = wu.getSitelink( id, 'commonswiki' ) or ''
				t = t:gsub( '^Category:', '' )
			end
		end
	end
	if t ~= '' and args.commonscat ~= '' then
		mu.addMaintenance( mi.maintenance.commonscatWD )
	end
	if args.commonscat == '' then
		args.commonscat = t
	end
end

-- getting names from Wikidata
function mu.getNamesFromWikidata( args, fromWikidata, page, country, entity )
	-- getting official names
	local officialNames =
		wu.getMonolingualValues( entity, mi.properties.officialName )

	if type( args.name ) == 'boolean' or args.name == '' then
		args.name = ''
		local langs = mu.getLangTable( page.lang )
		for i, lang in ipairs( langs ) do
			args.name = officialNames[ lang ]
			if args.name then
				break
			end
		end
		-- if failed then get labels
		if not mu.isSet( args.name ) then
			for i, lang in ipairs( langs ) do
				args.name = wu.getLabel( entity, lang, true )
				if args.name then
					break
				end
			end
			args.name = args.name or ''
		end
		if args.name ~= '' then
			mu.addMaintenance( mi.maintenance.nameFromWD )
			fromWikidata.name = true
		end
	end

	-- get local name if no name is available
	if args.name == '' and
		not ( type( args.nameLocal ) == 'string' and args.nameLocal ~= '' ) then
		args.nameLocal = true
	-- no local name if country and wiki language are identical
	elseif args.nameLocal == true and page.lang == country.lang then
		args.nameLocal = ''	
	end

	if args.nameLocal == true then
		args.nameLocal = officialNames[ country.lang ] or ''
		if args.nameLocal == '' then
			args.nameLocal = wu.getLabel( entity, country.lang, true ) or ''
		end
		if args.name == '' and args.nameLocal ~= '' then
			args.name = args.nameLocal
			args.nameLocal = ''
			mu.addMaintenance( mi.maintenance.nameFromWD )
			fromWikidata.name = true
		end
		if args.name:lower() == args.nameLocal:lower() then
			args.nameLocal = ''
		end
		fromWikidata.nameLocal = args.nameLocal ~= ''
		if fromWikidata.nameLocal then
			mu.addMaintenance( mi.maintenance.localNameFromWD )
		end
	end
end

-- getting link to Wikivoyage
function mu.getArticleLink( args, entity, page )
	local t = wu.getSitelinkTable( entity, page.lang .. page.globalProject )
	if t and t.title ~= page.text then  -- no link to the article itself
		local isLink = false
		for i, badge in ipairs( t.badges ) do
			if badge == mi.qualifiers.intentionalSitelink or 
				badge == mi.qualifiers.redirectSitelink then
				isLink = true
				break
			end
		end
		if not isLink then
			args.wikiPage = t.title
		end
	end
end

-- replacing decimal separator and inserting group separators
function mu.formatNumber( num )
	if mu.isSet( mi.formatnum.decimalPoint ) and mi.formatnum.decimalPoint ~= '.' then
		num = num:gsub( '%.', mi.formatnum.decimalPoint )
	end

	if mu.isSet( mi.formatnum.groupSeparator ) then
		local count
		repeat
			num, count = num:gsub( '^([-+]?%d+)(%d%d%d)',
				'%1%' .. mi.formatnum.groupSeparator .. '%2' ) 
		until count == 0
	end
    return num
end

function mu.getTypeLabel( id )
	if not mu.isSet( id ) then
		return ''
	end
	if id:match( '^Q%d+$' ) then
		id = mu.idToType( id )
		if not id then
			return ''
		end
	end

	local at, t
	id = id:gsub( '[_%s]+', '_' )
	if mu.typeAliases[ id ] then
		id = mu.typeAliases[ id ]
	end
	t = mt.types[ id ]
	if t then
		t = t.label or id
		at = t:find( ',' )
		if at then
			t = t:sub( 1, at - 1 )
		end
	else
		t = id:gsub( '_', ' ' )
	end
	return t
end

function mu.getPageData()
	local page = mw.title.getCurrentTitle()
	page.langObj = mw.getContentLanguage()
	page.lang = page.langObj:getCode()
	page.langName = mw.language.fetchLanguageName( page.lang, page.lang )
	page.isRTL = page.langObj:isRTL()
	page.entityId = mw.wikibase.getEntityIdForCurrentPage() -- can be nil
	page.siteName = mw.site.siteName
	page.globalProject = page.siteName:lower()
	if page.globalProject == 'wikipedia' then
		page.globalProject = 'wiki'
	end

	return page
end

-- generates a table with type documentation
function mu.generateTableTypeList( frame )
	local rows = {}
	local typeList = '<table class="wikitable sortable multiline" cellspacing="0">'
		.. '<tr><th>' .. mi.types.label .. '</th><th>'
		.. mi.types.group .. '</th><th>' .. mi.types.type .. '</th></tr>'
	local label
	for key, value in pairs( mt.types ) do
		label = value.label or value.alias or key
		if type( label ) == 'table' then
			label = label[ 1 ] or ''
		end
		label = label:gsub( '_', ' ' )
		table.insert( rows, '<tr><td>' .. label .. '</td><td>' .. value.group
			.. '</td><td>' .. key:gsub( '_', ' ' ) .. '</td></tr>' )
	end
	table.sort( rows,
		function( a, b )
			return mu.convertForSort( a ) < mu.convertForSort( b )
		end
	)
	return typeList .. table.concat( rows, '' ) .. '</table>'
end

return mu