Dokumentation für das Modul Marker utilities[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Anwendung

Das Modul stellt gemeinsame Funktionen für das Modul:Marker und das Modul:vCard zur Verfügung. Im Projektnamensraum befindet sich die technische Dokumentation.

Versionsbezeichnung auf Wikidata: 2025-03-17 Ok!

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( key, value ) 

Diese Funktion fügt die Fehlermeldung mit dem Schlüssel key in die Tabelle der Fehlermeldungen und Hinweise ein. value dient als Ersatz für einen Platzhalter %s. Ein Teil der oben genannten Funktionen befüllt ebenfalls diese Tabelle.

function mu.makeMaintenance( page, modules ) 

Diese Funktion liefert eine Zeichenkette mit allen Fehlermeldungen und Hinweisen zurück. modules ist eine Tabelle mit den Modulvariablen, aus denen weitere Wartungskategorien erhalten werden sollen.

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

Funktionen für allgemeine Nutzung

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.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.formatNumber( num ) 

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

function mu.tableInsert( tab, value ) 

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

function mu.textSplit( s, sep ) 

trennt die Zeichenkette s an Trennzeichen sep auf und legt sie in einer Tabelle ab, die aber keine leeren Zeichenketten enthält. 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.split( s ) 

liefert eine assoziative Tabelle kommaseparierter Werte der Zeichenkette s. Die Teilzeichenketten werden in Kleinbuchstaben umgewandelt, und Leerräume werden durch den Unterstrich ersetzt.

function mu.makeSpan( s, class, isBdi, attr, css ) 

liefert eine Zeichenkette, die von einem <span>-Tag oder <bdi>-Tag umschlossen ist. class ist das Klassenattribut des Tags, während die Tabellen attr und css weitere Tap-parameter liefern.

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.addWdClass( isFromWikidata ) 

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

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.yesno( val ) 

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

Prüffunktionen

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.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.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.checkZoom( args ) 

ersetzt args.zoom mit einem Standardwert, falls args.zoom nicht im Intervall von [0, 19] liegt.

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.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.checkStyles( args ) 

ersetzt Stil-Aliase durch CSS-Stile.

function mu.checkId( args ) 

prüft die Gültigkeit von args.id und die nicht gleichzeitige Nutzung mit args.wikidata. Im Fehlerfall wird args.id gelöscht und eine Fehlermeldung ausgegeben.

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.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).

Typ- und Gruppenfunktionen

function mu.getTypeParams( aType ) 

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

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.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.groupWithEvents( group ) 

prüft, ob in der angegebenen Gruppe group Ereignisse als Typen vorgesehen sind.

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.idToType( id ) 

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

Funktionen zur Parameteraufbereitung

function mu.getShow( default, args, validValues ) 

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

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.

Wikidata-Abfragefunktionen

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 Eigenschaft P5140 zu erhalten, danach die Koordinate aus der Eigenschaft P625.

function mu.typeSearch( p31 ) 

Sucht in mehreren P31-P279-Ketten nach Q-ids, deren Werte in der Tabelle 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.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 Eigenschaft P373 und zuletzt die Eigenschaft P910.

function mu.getLangTable( wikiLang, localLang ) 

erstellt eine Tabelle mit Sprachbezeichnern unter Nutzung von wikiLang, localLang (country.lang) und den Sprachen langs aus dem Modul Marker utilities/i18n.

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.

Marker-Funktionen

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

liefert r: HTML-Quellcode des Marker-Symbols.

Symbolfunktionen

function mu.makeStatusIcons( args ) 

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

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.makeIcons( args, page, country, entity, show, fromWikidata ) 

liefert die verlinkten Schwesterprojekt-Symbole und die verlinkten Symbole von Social-Media-Diensten 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. fromWikidata die Tabelle der Parameter, die aus Wikidata bezogen wurden.

Ausgabefunktionen

function mu.prepareNames( args ) 

fügt der Argumente-Tabelle den Tabellen displayName und givenName hinzu, die aus den Parametern name und nameMap gebildet werden. Hauptaufgabe ist es, den eigentlichen Namen aus einem möglichen Link in Wikiyntax herauszulösen. Beide Tabellen bestehen aus folgenden drei Elementen:

  • 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.makeName( result, args, show, page, country, nameClass, localClass ) 

fügt den Namen und Namensergänzungen zur Tabelle result hinzu. Zu den Namensergänzungen gehören der alternative Name, der lokale Name, Kommentar und Flughafencodes, die in Klammern geschrieben und mit einem span-Tag umschlossen werden. nameClass und localClass sind zusätzliche Klassenattribute für den Namen und den lokalen Namen.

function mu.parentheses( s, trim ) 

fügt die Zeichenkette s in Klammern ein. Wenn trim = true wird die Formatierungszeichenkette (üblicherweise ' (%s)') getrimmt.

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

liefert die Zeichenkette r, die eine zu den Kartenwerkzeugen verlinkte Dezimalkoordinate enthält, die wahlweise in Klammern gesetzt 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.makeWrapper( result, args, country, show, list, aClass, frame ) 

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

Sonstige Funktionen

function mu.getPageData() 
stellt seitenbezogene Daten zur Verfügung.
Hinweise
--[[ 	Functions library for Marker and vCard modules 	In non-Wikivoyage projects, sister-project links functions have to be adapted. ]]--  -- require( 'strict' ) local cd = require( 'Module:Coordinates' ) local mg = require( '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' )  -- module variable and administration local mu = { 	moduleInterface = { 		serial = '2025-03-17', 		item   = 58187612 	}, 	comma = mi.texts.comma, 	commaSeparator = mi.texts.commaSeparator }  local colorAdjust = { ['-webkit-print-color-adjust'] = 'exact', ['color-adjust'] = 'exact', 	['print-color-adjust'] = 'exact' }  -- maintenance tools function mu.initMaintenance( name ) 	mu.invalidParams    = {}   -- table of unknown parameters 	mu.duplicateAliases = {}   -- table of duplicate parameter aliases 	mu.maintenance      = {}   -- table of error strings  	mu.types            = mt.types 	mu.groups           = mg.groups 	mu.typeAliases      = nil  -- table for type aliases. Create on demand 	mu.groupAliases     = nil  -- table for group aliases end  local function contains( new ) 	for i = 1, #mu.maintenance do 		if mu.maintenance[ i ] == new then 			return true 		end 	end 	return false end  function mu.addMaintenance( key, value ) 	local s = key -- fallback 	local tab = mi.maintenance[ key ] 	if tab then 		s = mi.formats.category:format( tab.category ) .. 			( tab.err and mi.formats.error:format( tab.err ) or '' ) .. 			( tab.hint and mi.formats.hint:format( tab.hint ) or '' ) 	end 	s = value and mw.ustring.format( s, value ) or s  	if not contains( s ) then 		table.insert( mu.maintenance, s ) 	end end  function mu.getCategories( formatStr ) 	return wu.getCategories( formatStr ) end  local function getMaintenance() 	if #mu.invalidParams == 1 then 		mu.addMaintenance( 'unknownParam', mu.invalidParams[ 1 ] ) 	elseif #mu.invalidParams > 1 then 		mu.addMaintenance( 'unknownParams', 			table.concat( mu.invalidParams, mu.commaSeparator ) ) 	end 	if #mu.duplicateAliases > 0 then 		mu.addMaintenance( 'duplicateAliases', 			table.concat( mu.duplicateAliases, mu.commaSeparator ) ) 	end 	return table.concat( mu.maintenance, '' ) end  function mu.makeMaintenance( page, modules ) 	if mi.nsNoMaintenance[ page.namespace ] then 		return '' 	end  	local r = getMaintenance() 	if mi.options.usePropertyCateg then 		local m = mi.maintenance.properties -- format string 		for i, aModule in ipairs( modules ) do 			if aModule then 				r = r .. aModule.getCategories( m ) 			end 		end 	end 	return r end  -- general-use functions function mu.isSet( arg ) 	return arg and arg ~= '' end  function mu.convertForSort( s ) 	s = s:ulower() 	for i, obj in ipairs( mi.substitutes ) do 		s = mw.ustring.gsub( s, obj.l, obj.as ) 	end 	return s end  -- replacing decimal separator and inserting group separators function mu.formatNumber( num ) 	if mu.isSet( mi.texts.decimalPoint ) and mi.texts.decimalPoint ~= '.' then 		num = num:gsub( '%.', mi.texts.decimalPoint ) 	end  	if mu.isSet( mi.texts.groupSeparator ) then 		local count 		repeat 			num, count = num:gsub( '^([-+]?%d+)(%d%d%d)', 				'%1%' .. mi.texts.groupSeparator .. '%2' )  		until count == 0 	end     return num end  function mu.tableInsert( tab, value ) 	if mu.isSet( value ) then 		table.insert( tab, value ) 	end end  -- splitting string s at sep, removing empty substrings -- sep is a single character separator but no magic pattern 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  local function encodeSpaces( s ) 	s = s:gsub( '[_%s]+', '_' ) 	return s end  local function replaceWithSpace( s, pattern ) 	s = s:find( pattern ) and s:gsub( pattern, ' ' ) or s 	return s end  -- Splitting comma separated lists to a table and converting items function mu.split( s ) 	local arr = {} 	if not mu.isSet( s ) then 		return arr 	end 	for i, str in ipairs( mu.textSplit( s, ',' ) ) do 		arr[ encodeSpaces( str ) ] = 1 	end 	return arr end  function mu.makeSpan( s, class, isBdi, attr, css ) 	return tostring( mw.html.create( isBdi and 'bdi' or 'span' ) 		:addClass( class ) 		:attr( attr or {} ) 		:css( css or {} ) 		:wikitext( s ) 	) 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 bdi = mw.html.create( 'bdi' ) 		:addClass( addClass ) 		:wikitext( s ) 	local c = country.lang 	if c == '' or c == page.lang then 		return tostring( bdi ) 	end  	local dir 	local fStr = '%s' 	if country.isRTL and not page.isRTL then 		dir = 'rtl' 		fStr = '&rlm;%s&lrm;' 	elseif not country.isRTL and page.isRTL then 		dir = 'ltr' 		fStr = '&lrm;%s&rlm;' 	end	  	return fStr:format( tostring( bdi 		:addClass( 'voy-lang voy-lang-' .. c ) 		:attr( { title = mw.ustring.format( titleHint , country.langName ), 			lang = c, dir = dir } ) 	) ) end  function mu.addWdClass( isFromWikidata ) 	return isFromWikidata and ' voy-wikidata-content' or '' 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  function mu.yesno( val ) 	return mi.yesno[ val:ulower() ] end  -- check functions  local function emphasize( s ) 	return mw.ustring.format( mi.texts.emph, s ) 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, emphasize( key ) ) 			else 				args[ targetKey ] = arg 			end 		else 			table.insert( mu.invalidParams, emphasize( key ) ) 		end 	end  	-- normalize values 	for i, param in ipairs( mi.options.normalizeValues ) do 		if mu.isSet( args[ param ] ) then 			args[ param ] = replaceWithSpace( args[ param ]:ulower(), '[_%s]+' ) 		end 	end  	return args end  local function removeNS( s, nsTable ) 	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 			return t 		end 	end 	return t end  function mu.checkCommonsCategory( args ) 	-- remove namespace from category 	if mu.isSet( args.commonscat )  then 		args.commonscat = removeNS( args.commonscat, mi.texts.CategoryNS ) 	end 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( '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( 'wrongCoord' ) 	elseif dms then 		mu.addMaintenance( 'dmsCoordinate' ) 	end end  -- check zoom level function mu.checkZoom( args ) 	args.zoom = math.floor( tonumber( args.zoom ) or mi.map.defaultZoomLevel ) 	if args.zoom < 0 or args.zoom > mi.map.maxZoomLevel then 		args.zoom = mi.map.defaultZoomLevel 	end 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( '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( 'missingImg', args.image ) 				args.image = '' 			end 		end 	end 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 ) 			t = statusAliases[ t ] or t 			if mi.statuses[ t ] then 				if not hash[ t ] then 					table.insert( args.statusTable, t ) 					hash[ t ] = 'x' 				end 			else 				mu.addMaintenance( 'unknownStatus' ) 			end 		end 		if #args.statusTable > 1 then 			table.sort( args.statusTable, 				function( a, b ) 					local at = mi.statuses[ a ] 					local bt = mi.statuses[ b ] 					return ( at.g < bt.g ) or ( at.g == bt.g and a < b ) 				end 			) 		end 	end end  function mu.checkStyles( args ) 	if mu.isSet( args.styles ) then 		if mi.nameStyles[ args.styles:lower() ] then 			args.styles = args.styles:lower() 			args.styleClass = ' listing-name-style-' .. args.styles 			args.styles = mi.nameStyles[ args.styles ] 		end 	else 		args.styles = nil 	end end  function mu.checkId( args ) 	if mu.isSet( args.id ) and mu.isSet( args.wikidata ) then 		mu.addMaintenance( 'wikidataWithId' ) 		args.id = '' 	end 	if mu.isSet( args.id ) and args.id:find( '[!-,.-/:-?[-^{-\127%z\1-\31]' ) then 		mu.addMaintenance( 'wrongId' ) 		args.id = '' 	end 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  local function getTypeFromTypeAliases( aType ) 	if not mu.typeAliases then 		mu.typeAliases = mu.getAliases( mt.types, 'alias' ) 	end 	return mu.typeAliases[ aType ] end  local function getGroupFromGroupAliases( group ) 	if not mu.groupAliases then 		mu.groupAliases = mu.getAliases( mg.groups, 'alias' ) 	end 	return mu.groupAliases[ group ] end  -- getting marker type and group function mu.checkTypeAndGroup( args ) 	local s, t 	args.typeTable = {} 	args.subtypeTable = {}  	-- check group 	if mu.isSet( args.group ) then 		mu.addMaintenance( 'parameterUsed', 'group' ) 		s = mg.groups[ args.group ] 		if not s then 			s = getGroupFromGroupAliases( args.group ) 			if s then 				args.group = s 				s = mg.groups[ args.group ] 			end 		end 		if not s then			 			mu.addMaintenance( 'unknownGroup' ) 			args.group = '' 		elseif s.is and s.is == 'color' and mi.options.useTypeCateg then 			mu.addMaintenance( 'group', args.group ) 		end 	end  	-- check type 	if not mu.isSet( args.type ) then 		args.type = mt.types.error.group 		mu.addMaintenance( 'missingType' ) 	elseif args.type == mt.types.error.group then 		mu.addMaintenance( 'unknownType', args.type ) 	end 	if args.type == mt.types.error.group then 		args.group = mt.types.error.group 	else 		-- split seperate types and analyse them 		for i, t in ipairs( mu.textSplit( args.type, ',' ) ) do 			-- try to find the type t in types or groups 			t = encodeSpaces( t ) 			if not mt.types[ t ] then 				t = getTypeFromTypeAliases( t ) or getGroupFromGroupAliases( t ) or t 			end  			s = mg.groups[ t ] 			if s then -- type is a group itself 				if s.is and s.is == 'color' then 					if mi.options.excludeColorTypes then 						args.group = mt.types.error.group 						mu.addMaintenance( 'unknownType', t ) 					else 						mu.addMaintenance( 'typeIsColor' ) 					end 				elseif not mi.options.noTypeMsgs then 					mu.addMaintenance( '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 					args.group = mt.types.error.group 					mu.addMaintenance( 'unknownType', t ) 				end 			end 			table.insert( args.typeTable, t ) 			if mi.options.useTypeCateg then 				mu.addMaintenance( 'type', t ) 			end 		end 		args.type = table.concat( args.typeTable, ',' ) 	end 	args.groupTranslated = translateGroup( args.group ) 	mu.getColor( args ) end  local function isUrl( url ) 	uc = uc or require( 'Module:UrlCheck' ) 	return uc.isUrl( url, mi.options.skipPathCheck ) end  -- url check in args function mu.checkUrl( args ) 	if not mu.isSet( args.url ) then 		return 	end  	local c = isUrl( args.url ) -- getting result code 	if c > 2 then 		mu.addMaintenance( 'wrongUrl' ) 		args.url = '' 	elseif c == 2 then -- URL contains IP address 		mu.addMaintenance( 'urlWithIP' ) 	else 		for i = 1, #mi.services do 			if args.url:find( mi.services[ i ].key .. '.com', 1, true ) then 				mu.addMaintenance( 'urlIsSocialMedia' ) 				args.url = '' 			end 		end 	end end  -- type and group functions -- getting a set of parameters for a given type function mu.getTypeParams( aType ) 	return mt.types[ aType ] end  function mu.idToType( id ) 	if not mu.typeIds then 		mu.typeIds = mu.getAliases( mt.types, 'wd' ) -- Q id to type table 	end 	return mu.typeIds[ id ] 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 	else 		id = encodeSpaces( id ) 	end  	local at, t 	t = mt.types[ id ] 	if not t then 		t = getTypeFromTypeAliases( id ) 		t = t and mt.types[ t ] 	end 	if t then 		t = t.label or id 		at = t:find( ',', 1, true ) 		if at then 			t = t:sub( 1, at - 1 ) 		end 	else 		t = replaceWithSpace( id, '_' ) 	end 	return t end  function mu.typeExists( aType ) 	return mt.types[ aType ] and aType or getTypeFromTypeAliases( aType ) end  -- check if the specified group can have events function mu.groupWithEvents( group ) 	return mg.groups[ group ] and mg.groups[ group ].withEvents end  -- see: https://www.w3.org/TR/WCAG20/#relativeluminancedef local function hexToLinear( hex ) 	hex = tonumber( hex, 16 ) / 255.0 	if hex <= 0.03928 then 		return hex / 12.92 	else 		return ( ( hex + 0.055 ) / 1.055 ) ^ 2.4 	end end  -- relative luminance of a color -- 6 digit hex color local function hexToLuminance( color ) 	local r = hexToLinear( color:sub( 1, 2 ) ) 	local g = hexToLinear( color:sub( 3, 4 ) ) 	local b = hexToLinear( color:sub( 5, 6 ) )  	return 0.2126 * r + 0.7152 * g + 0.0722 * b end  local function isInverse( backgroundColor ) 	-- magenta, error: 0.2848 	-- the threshold should not be greater than 0.4  	local luminanceThreshold = hexToLuminance( 'FF00FF' )  	backgroundColor = backgroundColor:sub( 2 ) -- remove # 		:gsub( '^(%x)(%x)(%x)$', '%1%1%2%2%3%3' ) 	local luminance = hexToLuminance( backgroundColor )  	return hexToLuminance( backgroundColor ) > luminanceThreshold 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 = isInverse( c.color ) 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 validValues then 		return values, '' 	end  	local errors = {} 	for item, v in pairs( mu.split( s ) ) do 		-- value check 		if validValues[ item ] then 			values[ item ] = 1 		else 			table.insert( errors, item ) 		end 	end 	return values, table.concat( errors, mu.commaSeparator ) end  local function setCopyMarker( args, show ) 	if show.copy and ( mu.isSet( args.copyMarker ) or not mu.isSet( args.wikidata ) ) then 		show.copy = nil 		mu.addMaintenance( 'deleteShowCopy' ) 	end 	if show.copy then 		args.copyMarker = args.wikidata 	end 	if mu.isSet( args.copyMarker ) then 		show.symbol = nil 	end end  -- treatment of social media services if Wikidata is available local function setSocialMedia( args, value ) 	for i, service in ipairs( mi.services ) do 		args[ service.key ] = value 	end end  function mu.getShow( default, args, validValues ) 	local show = mu.split( default ) 	local add, err = splitAndCheck( args.show, validValues ) 	if err ~= '' then 		mu.addMaintenance( 'unknownShow', err ) 	end 	-- maintenance 	if add.inline then 		show.inlineSelected = true 		mu.addMaintenance( 'showInlineUsed' ) 	end 	if add.poi then 		mu.addMaintenance( 'showPoiUsed' ) -- is default 	end 	-- overwriting defaults 	if add.none or add.coord or add.poi or add.all then 		show.all, show.coord, show.poi = nil, nil, nil 	end 	-- merge show with add values 	for key, value in pairs( add ) do 		show[ key ] = value 	end 	if show.none then 		show.none, show.all, show.coord, show.poi = nil, nil, nil, nil 	end 	if show.all then 		show.all   = nil 		show.coord, show.poi = 1, 1 	end 	if show.noname then 		show.noname, show.name = nil, nil 	else 		show.name = 1 	end 	setCopyMarker( args, show ) 	if args.wikidata ~= '' then 		if show.socialmedia then 			setSocialMedia( args, 'y' ) 		elseif show.nosocialmedia then 			setSocialMedia( args, 'n' ) 		end 	end 	if not show.nosocialmedia then 		show.socialmedia = 1 	end  	return show 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( 'illegalCtrls' ) 	end  	-- remove LTR and RTL marks 	t = mw.ustring.gsub( t, '[‎‏]+', '' )  	if descrDiv then 		-- 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  -- fetch data from Wikidata 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, args.long = c.latitude, c.longitude 		fromWikidata.lat = true 	end 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 = ( 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( '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 		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( 'typeFromWDchain' ) 					end 					return aType 				end 				id = ids[ 1 ].id 			end 			j = j + 1  		-- limit: maximum levels to analyse 		until j >= mi.options.searchLimit or #ids == 0 	end  	return '' end  function mu.getTypeFromWikidata( args, entity ) 	if mu.isSet( args.type ) then 		return 	end  	local p31 = wu.getValues( entity, mi.properties.instanceOf ) 	if #p31 == 0 then 		p31 = wu.getValues( entity, mi.properties.subclassOf ) 	end 	args.type = typeSearch( p31 ) 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( '^[Cc]ategory:', '' ) 	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( '^[Cc]ategory:', '' ) 			end 		end 	end 	if t ~= '' and args.commonscat ~= '' then 		mu.addMaintenance( 'commonscatWD' ) 	end 	if args.commonscat == '' then 		args.commonscat = t 	end 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  -- 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 ) -- no fallback 				if args.name then 					break 				end 			end 			args.name = args.name or '' 		end 		if args.name ~= '' then 			mu.addMaintenance( '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 type( args.nameLocal ) == 'string' and args.nameLocal ~= '' then 		-- keeping local name in any case for html data 		args.addNameLocal = args.nameLocal 		return 	end  	local nameLocal = officialNames[ country.lang ] or '' 	if nameLocal == '' then 		nameLocal = wu.getLabel( entity, country.lang, true ) or '' 	end  	if args.nameLocal == true then 		args.nameLocal = nameLocal 		if args.name == '' and args.nameLocal ~= '' then 			args.name = args.nameLocal 			args.nameLocal = '' 			mu.addMaintenance( 'nameFromWD' ) 			fromWikidata.name = true 		end 		if args.name:ulower() == args.nameLocal:ulower() then 			args.nameLocal = '' 		end 		fromWikidata.nameLocal = args.nameLocal ~= '' 	elseif page.lang ~= country.lang and args.name ~= nameLocal then 		args.addNameLocal = nameLocal 	end end  -- getting link to Wikivoyage function mu.getArticleLink( args, entity, page ) 	local title, isRedirect = 		wu.getCheckedSitelink( entity, page.lang .. page.globalProject ) 	if title and title ~= page.text then 		args.wikiPage = title 		if isRedirect == true then 			args.linkIsRedirect = true 		end 	end end  -- marker functions -- returns a single data set from Module:Marker utilities/Maki icons local function getMaki( key ) 	mm = mm or require( 'Module:Marker utilities/Maki icons' ) 	key = key:lower():gsub( '[_%s]+', '-' ) 	return mm.icons[ key ] end  local function getMakiIconId( key ) 	if not mu.isSet( key ) then 		return nil 	end 	key = key:lower():gsub( '[_%s]+', '-' ) 	if getMaki( key ) then 		return key 	end 	key = mt.types[ key ] and mt.types[ key ].icon 	if key and getMaki( key ) then 		return key 	end 	return nil end  local function addIconToMarker( args ) 	args.text = ( '[[File:Maki7-%s.svg|x14px|link=|class=noviewer]]' ):format( args.symbol ) 	args.isIcon = true end  -- distinguishing marker symbols, default: number local function makeMarkerProperties( args, show ) 	args.symbol = args.symbol or '' 	local noSymbol = args.symbol == '' 	if args.symbol == '' and show.poi and show.symbol then 		args.symbol = getMakiIconId( args.typeTable[ 1 ] ) or '' 	end 	local isIcon = getMaki( args.symbol ) 	if args.symbol == '' or args.symbol == 'number' then 		args.symbol = '-number-' .. args.group 	elseif args.symbol == 'letter' then 		args.symbol = '-letter-' .. args.group 	elseif args.symbol:len() == 1 and args.symbol:match( '%w' ) then 		args.text = args.symbol:upper() 		mu.addMaintenance( 'numberUsed' ) 	elseif args.symbol ~= '' and args.text == '' and isIcon then 		addIconToMarker( args ) 	elseif args.symbol ~= '' and not isIcon then 		args.text = mi.texts.closeX 		args.isIcon = true 		args.group = 'error' 		mu.getColor( args ) 		mu.addMaintenance( noSymbol and 'unknownMAKI' or 'unknownIcon' ) 	end end  -- making marker symbol function mu.makeMarkerSymbol( args, show, frame ) 	local extraClasses = args.inverse and ' listing-map-inverse' or 		' listing-map-not-inverse' 	if args.group == 'error' then 		extraClasses = extraClasses .. ' listing-map-is-error' 	end 	local title = args.givenName.all  	if mu.isSet( args.copyMarker ) then 		local copyClass = 'listing-map plainlinks printNoLink voy-copy-marker' 			.. extraClasses 		return tostring( mw.html.create( 'span' ) 			:addClass( copyClass ) 			: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', title = mi.texts.tooltip, 				['data-copy-marker-content'] = args.copyMarker } ) 		) 	end 	 	makeMarkerProperties( args, show ) 	if args.isIcon then 		extraClasses = extraClasses .. ' listing-map-is-symbol' 	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 		tagArgs.class = 'no-icon' 	end 	if mu.isSet( args.mapGroup ) then 		tagArgs.group, tagArgs.show = args.mapGroup, 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' .. extraClasses ) 		:attr( 'title', mi.texts.tooltip ) 		:css( colorAdjust ) 		:css( { ['background-color'] = args.color, 			color = args.inverse and '#000' or '#fff' } ) 		-- frame:extensionTag is expensive 		:wikitext( frame:extensionTag( 'maplink', mw.text.jsonEncode( geoJson ), tagArgs ) ) 	) end  -- icon functions function mu.makeStatusIcons( args ) 	local result = '' 	for i, v in ipairs( args.statusTable ) do 		result = result .. mu.makeSpan( ' ', 'listing-status listing-status-' .. v, 			false, { title = mi.statuses[ v ].label }, colorAdjust ) 		if mi.statuses[ v ].category then 			result = result .. ( '[[Category:%s]]' ):format( mi.statuses[ v ].label ) 		end 	end 	return result end  function mu.addLinkIcon( classes, link, title, text, addSpace ) 	local span = mu.makeSpan( ' ', nil, false, { title = title, ['data-icon'] = text }, 		colorAdjust ) -- space to keep the span tag 	local lFormat = ( link:find( '^https?://' ) or link:find( '^//' ) ) 		and '[%s %s]' or '[[%s|%s]]' 	local iconLink = mw.ustring.format( lFormat, link, span ) 	if addSpace then 		iconLink = mu.makeSpan( '​ ', 'listing-icon-with-space', true ) .. iconLink 	end 	return mu.makeSpan( iconLink, 'listing-icon ' .. classes ) end  -- adding linked sister icons local function addSisterIcons( icons, sisters, name, id ) 	local icon 	for i, key in ipairs( { 'wikivoyage', 'wikipedia', 'commons', 'wikidata' } ) do 		if mu.isSet( sisters[ key ] ) then 			icon = mu.addLinkIcon( 'listing-sister-icon listing-sister-' .. key, sisters[ key ], 				mw.ustring.format( mi.iconTitles[ key ], name, id ), key, 				key == 'wikidata' ) -- add leading space 			table.insert( icons, icon ) 		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.getFilteredSitelink( entity, lang .. wiki ) 			if link then 				prefix = prefix .. ( lang ~= wikilang and ( lang .. ':' ) or '' ) 				return prefix .. link 			end 		end 	end 	return '' end  -- adding Wikimedia sister project icons local function 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( '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  -- creating social media icons including value check local function makeSocial( icons, args, fromWikidata, name ) 	local domain, span, t, which  	for i, service in ipairs( mi.services ) do 		-- check values first 		t = args[ service.key ] or '' 		domain = type( service.url ) == 'string' and service.url or service.url[ 1 ] 		domain = domain: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( 'wrongSocialUrl', service.key ) 				end 				if isUrl( t ) > 1 or 					not t:match( '^' .. domain .. '.+$' ) then 					t = '' 					mu.addMaintenance( 'wrongSocialUrl', service.key ) 				end 				if t ~= '' and not fromWikidata[ service.key ] then 					mu.addMaintenance( '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 						which = i 						break 					end 				end 				if not match then 					t = '' 					mu.addMaintenance( 'wrongSocialId', service.key ) 				end 			end 		end 		args[ service.key ] = t  		-- create symbol link 		if t ~= '' then 			if not t:find( domain, 1, true ) then 				-- multiple service urls 				local formatStr = type( service.url ) == 'string' and service.url or 					service.url[ which ] 				t = mw.ustring.format( formatStr, 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  function mu.makeIcons( args, page, country, entity, show, fromWikidata ) 	local icons = {} 	local onlyWikidata = mi.options.showSisters and not show.nositelinks and 		makeSisterIcons( icons, args, page, country, entity ) 	if show.socialmedia then 		makeSocial( icons, args, fromWikidata, args.givenName.name ) 	end 	if #icons > 0 then 		return ( onlyWikidata and '' or mi.texts.space ) .. table.concat( icons, '' ) 	else 		return '' 	end end  -- output functions local function removeStars( args ) 	for i, param in ipairs( mi.options.noStarParams ) do 		if mu.isSet( args[ param ] ) and args[ param ]:find( '*', 1, true ) then 			args[ param ] = args[ param ]:gsub( '%*+', '' ) 			args[ param ] = mw.text.trim( args[ param ] ) 			mu.addMaintenance( 'nameWithStar' ) 		end 	end end  local function makeAnchorId( name ) 	if name and not name:match( '^Q%d+$' ) then 		name = mw.uri.anchorEncode( name ) 	end 	return mw.ustring.format( mi.texts.anchor, name ) end  -- getting name table with linked and unlinked names local function getName( name, pageTitle ) 	local r = { 		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  	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 mu.isSet( pageTitle ) then 			r.pageTitle = pageTitle 			r.all = '[[' .. pageTitle .. '|' .. name .. ']]' 			r.fromWD = true 		end 	end 	removeStars( r, { 'name', 'all' } ) 	return r end  -- create givenName, displayName tables function mu.prepareNames( args ) 	local function simplifyString( s, length ) 		s = mw.ustring.sub( s, 1, length ) 		s = mw.ustring.gsub( s, "[%.'" .. '"“”„‟«»‘’‚‛‹›]', '' ) 		s = mw.ustring.gsub( s, '[-‐‑‒–—―]', ' ' ) 		return s:ulower():gsub( '%s%s+', ' ' ) 	end  	local function removeDuplicate( value1, key2 ) 		if not mu.isSet( value1 ) or not mu.isSet( args[ key2 ] ) then 			return 		end 		local minLen = math.min( mw.ustring.len( value1 ), mw.ustring.len( args[ key2 ] ) ) 		if simplifyString( value1, minLen ) == simplifyString( args[ key2 ], minLen ) then 			args[ key2 ] = '' 		end 	end  	-- use local name if name is not given 	if args.name == '' and args.nameLocal ~= '' then 		args.name = args.nameLocal 		args.nameLocal = '' 	end 	if args.name == args.nameMap then 		args.nameMap = '' 	end 	-- missing name 	if args.name == '' then 		args.name = mi.texts.missingName 		mu.addMaintenance( 'missingNameMsg' ) 	end 	-- names shall not contain tags or template calls 	if args.name:find( '<', 1, true ) or args.name:find( '{{', 1, true ) or 		args.nameMap:find( '<', 1, true ) or args.nameMap:find( '{{', 1, true ) or 		args.nameLocal:find( '<', 1, true ) or args.nameLocal:find( '{{', 1, true ) then 		mu.addMaintenance( 'malformedName' ) 	end 	removeStars( args ) 	-- handling linked names like [[article|text]] 	args.displayName = getName( args.name, args.wikiPage ) 	if mu.isSet( args.nameMap ) then 		args.givenName = getName( args.nameMap, args.wikiPage ) 	else 		-- real copy 		args.givenName = {} 		args.givenName.all = args.displayName.all 		args.givenName.name = args.displayName.name 		args.givenName.pageTitle = args.displayName.pageTitle 	end 	-- reset args.linkIsRedirect if Wikidata link is not used 	-- args.linkIsRedirect is set by mu.getArticleLink() 	if not args.displayName.fromWD then 		args.linkIsRedirect = nil 	end  	-- remove identical names 	removeDuplicate( args.givenName.name, 'nameLocal' ) 	removeDuplicate( args.givenName.name, 'alt' ) 	removeDuplicate( args.givenName.name, 'comment' ) 	removeDuplicate( args.nameLocal, 'alt' ) 	removeDuplicate( args.alt, 'comment' ) 	removeDuplicate( args.directions, 'directionsLocal' ) 	removeDuplicate( args.address, 'addressLocal' ) end  local function _makeAirport( code, args ) 	local span = mu.makeSpan( args[ code ], 		'listing-' .. code .. '-code' .. mu.addWdClass( true ) ) 	return mu.makeSpan( mw.ustring.format( mi.texts[ code ], span ), 		'listing-airport listing-' .. code ) end  local function makeAirport( args, show ) 	if show.noairport then 		return '' 	else 		local t = args.type:gsub( ',.*$', '' ) -- only first type 		if mt.types[ t ] and not mt.types[ t ].useIATA then 			return '' 		end 	end  	if mu.isSet( args.iata ) then 		return _makeAirport( 'iata', args ) 	elseif mu.isSet( args.icao ) then 		return _makeAirport( 'icao', args ) 	else 		return '' 	end end  -- creating a list of nick names etc. local function makeNameAffix( args, page, country, addClass, show ) 	local result = {} 	if mi.options.showLocalData then 		if mu.isSet( args.nameLocal ) then 			table.insert( result,  				mu.languageSpan( args.nameLocal, mi.texts.hintName, page, country, 					'listing-name-local' .. addClass ) ) 		end 		if mu.isSet( args.nameLatin ) then 			table.insert( result, mu.makeSpan( args.nameLatin, 				'listing-name-latin', false, { title = mi.texts.hintLatin, 					lang = mu.isSet( country.lang ) and ( country.lang .. '-Latn' ) or nil } ) ) 		end 	end 	for i, key in ipairs( { 'alt', 'comment' } ) do 		if mu.isSet( args[ key ] ) then 			table.insert( result, mu.makeSpan( args[ key ], 'listing-' .. key ) ) 		end 	end 	mu.tableInsert( result, makeAirport( args, show ) ) 	if #result == 0 then 		return '' 	end 	return mu.makeSpan( mu.parentheses( table.concat( result, mu.comma ) ), 		'p-nickname nickname listing-add-names' ) end  -- replace brackets in names local function replaceBrackets( s ) 	s = s:gsub( '%[', '&#x005B;' ):gsub( '%]', '&#x005D;' ) 	return s end  -- creating (linked) name and its affix function mu.makeName( result, args, show, page, country, nameClass, localClass ) 	local s = '' 	local r = {}  	nameClass = nameClass .. 		( args.displayName.fromWD and ' listing-link-from-wd' or '' )  	-- clear redirect wiki link if required 	if args.linkIsRedirect and not show.wikilink then 		args.displayName.pageTitle = '' 		args.displayName.all = args.displayName.name 		args.linkIsRedirect = nil  		nameClass = nameClass .. ' listing-unused-redirect' 		mu.addMaintenance( 'unusedRedirect' ) 	end 	if args.linkIsRedirect then 		nameClass = nameClass .. ' mw-redirect listing-link-is-redirect' 		mu.addMaintenance( 'linkIsRedirect' ) 	end 	if mu.isSet( args.url ) and args.displayName.pageTitle == '' then 		s = '[' .. args.url .. ' ' 			.. replaceBrackets( args.displayName.name ) .. ']' 	else 		s = args.displayName.all 	end 	if mu.isSet( args.nameExtra ) then 		s = s .. ' ' .. args.nameExtra 	end  	-- insert name 	if s ~= '' then 		local attr = { style = args.styles } 		s = mu.makeSpan( s,	'p-name fn org listing-name' .. nameClass .. 			( args.styleClass and args.styleClass or '' ), true, attr ) 		if mu.isSet( args.metadata ) then 			s = s .. args.metadata 		end 		table.insert( r, s ) 	end  	-- insert separate url icon 	if mu.isSet( args.url ) and args.displayName.pageTitle ~= '' then 		-- both article and web links 		table.insert( r, mu.addLinkIcon( 'listing-url', args.url, 			mi.iconTitles.internet, 'internet' ) ) 	end  	-- insert name affix 	mu.tableInsert( r, makeNameAffix( args, page, country, localClass, show ) ) 	 	if #r > 0 then 		table.insert( result, table.concat( r, mi.texts.space ) ) 	end end  function mu.parentheses( s, trim ) 	if not mu.isSet( s ) then 		return '' 	end  	local formatter = mi.texts.parentheses 	if trim then 		formatter = mw.text.trim( formatter ) 	end 	return mw.ustring.format( formatter, s ) end  -- getting DMS coordinates function mu.dmsCoordinates( lat, long, name, fromWD, extraParams, noBrackets ) 	local function coordSpan( title, text ) 		return mu.makeSpan( text, 'coordStyle', false, { title = title } ) 	end  	local dec 	local latDMS, dec, latMs = cd.getDMSString( lat, 4, 'lat', '', '', mi.map.defaultDmsFormat ) 	local longDMS, dec, longMs = cd.getDMSString( long, 4, 'long', '', '', mi.map.defaultDmsFormat )  	local r = '[' .. mi.map.coordURL .. latMs .. '_' .. longMs .. '_' .. 		mw.uri.encode( extraParams ) .. '&locname=' .. mw.uri.encode( name ) .. 		' ' .. coordSpan( mi.texts.latitude, latDMS ) .. 		' ' .. coordSpan( mi.texts.longitude, longDMS ) .. ']' 	r = noBrackets and r or mu.parentheses( r ) 	return mu.makeSpan( r, 'listing-dms-coordinates printNoLink plainlinks' 		.. mu.addWdClass( fromWD ) ) end  -- prepare value s for data attribute and replace all entities by characters local function 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, frame ) 	if not mu.isSet( args.nameLocal ) and mu.isSet( args.addNameLocal ) then 		args.nameLocal = args.addNameLocal 	end 	if not mu.isSet( args.addressLocal ) and mu.isSet( args.addAddressLocal ) then 		args.addressLocal = args.addAddressLocal 	end 	local wrapper = mw.html.create( show.inline and 'span' or 'div' ) 		:addClass( 'vcard h-card ' .. args.template ) 	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 or args.template == 'Marker' then 		wrapper:addClass( 'listing-inline' ) 	end 	if mu.isSet( args.copyMarker ) or not show.poi then 		wrapper:addClass( 'voy-without-marker' ) 	end 	if #args.statusTable > 0 then 		wrapper:addClass( 'listing-with-status' ) 	end 	if args.givenName.name ~= mi.texts.missingName then 		wrapper:attr( 'data-name', data( args.givenName.name ) ) 	end  	if not ( mu.isSet( args.copyMarker ) and args.copyMarker == args.wikidata ) then 		local id = mu.isSet( args.wikidata ) and args.wikidata or args.id 		if mu.isSet( id ) then 			wrapper:attr( 'id', makeAnchorId( id ) ) 		end 	end  	local editClass = 'listing-edit' 	if mu.isSet( args.sectionFrom ) then 		args.sectionFrom = replaceWithSpace( args.sectionFrom, '[_]+' ) 		if args.sectionFrom ~= page.text then 			editClass = 'listing-no-edit' 		end 	end 	wrapper:addClass( editClass ) 	wrapper:attr( 'data-location', data( page.subpageText ) ) 		:attr( 'data-location-qid', page.entityId ) 		:attr( 'data-wikilang', page.lang ) 		:attr( 'data-country', data( country.iso_3166 ) ) 		:attr( 'data-country-name', data( country.country ) ) 		:attr( 'data-lang', data( country.lang ) ) 		:attr( 'data-lang-name', data( country.langName ) ) 		:attr( 'data-country-calling-code', data( country.cc ) ) 		:attr( 'data-trunk-prefix', data( country.trunkPrefix ) ) 		:attr( 'data-dir', data( country.isRTL and 'rtl' or 'ltr' ) ) 		:attr( 'data-wiki-dir', data( page.isRTL and 'rtl' or 'ltr' ) ) 		:attr( 'data-currency', data( country.addCurrency ) ) 	local arg 	for key, value in pairs( list ) do 		if mu.isSet( args[ key ] ) then 			arg = args[ key ]:gsub( '<[^<>]*>', '' ) -- remove html tags 			wrapper:attr( value, data( arg ) ) 		end 	end 	if not show.name then 		wrapper:node( mw.html.create( 'span' ) 			:addClass( 'p-name fn org listing-name' ) 			:css( 'display', 'none' ) 			:wikitext( args.givenName.name ) 		) 	end 	wrapper:wikitext( result ) 	if not show.noCoord then 		wrapper:node( mw.html.create( 'span' ) 			:addClass( 'p-geo geo listing-coordinates' ) 			:css( 'display', 'none' ) 			:node( mw.html.create( 'span' ) 				:addClass( 'p-latitude latitude' ) 				:wikitext( args.lat ) 			) 			:node( mw.html.create( 'span' ) 				:addClass( 'p-longitude longitude' ) 				:wikitext( args.long ) 			)	 		) 	end  	wrapper = tostring( wrapper )  	-- 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  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  return mu