Modul:MarkerBase

Aus Wikivoyage
Zur Navigation springen Zur Suche springen
Template-info.png Dokumentation für das Modul MarkerBase[Ansicht] [Bearbeiten] [Versionsgeschichte] [Aktualisieren]

Anwendung

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

Versionsbezeichnung auf WikiData: 2020-08-05

Wartungsfunktionen

function mb.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 mb.addWrongParameter( param )

Diese Funktion fügt den Parameter param in die Tabelle fehlerhafter Parameter ein.

function mb.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 mb.getMaintenance()

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

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

liefert eine Tabelle mit der Moduldokumentation zurück (am Anfang des Moduls definiert).

function mb.failsafe( frame )

liefert das Versionsdatum des Moduls zurück. Siehe auch Modul:Failsafe.

Funktionen

Im Projektnamensraum befindet sich die technische Dokumentation.

function mb.translateGroup( group )

liefert eine Zeichenkette mit group in Übersetzung. Die Übersetzungen werden der Tabelle groups im Modul MarkerBase/Types entnommen. Ist keine Übersetzung vorhanden, so wird group unübersetzt zurückgeliefert.

function mb.addWdClass( isFromWikidata )

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

function mb.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 mb.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 mb.addSisterIcons( sisters, name, id )

Die Funktion liefert die Zeichenkette mit Interwiki-verlinkten Symbolbildern wie Wikipedia und Wikimedia Commons zurück. sisters ist eine Tabelle mit Link-Zeichenketten zu den Wikis, name ist der Name einer Einrichtung und id die zugehörige Wikidata-Id. Es werden zwei Zeichenketten zurückgeliefert:

  • s vollständige Zeichenkette.
  • t Teilzeichenkette mit dem Wikidata-Link. Die Zeichenkette dient dazu, im Vergleich mit der ersten Zeichenkette festzustellen, ob die erste Zeichenkette nur aus dem Wikidata-Link besteht.
function mb.getWikiLink( langArray, wiki, entity )

liefert die vollständige Zeichenkette l zu einem Artikel in Wikimedia-Projekten zurück. Die Angaben stammen aus den Sitelinks der Wikidata-Entity entity. langArray ist eine Tabelle der gewünschten Sprachen. wiki = "wiki" steht für den Einsatz in der Wikipedia.

function mb.makeSisterIcons( args, country, entity )

liefert eine Zeichenkette mit den verlinkten Schwesterprojekt-Symbolen. 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 mb.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 mb.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 mb.checkTypeAndGroup( aType, group )

liefert die überprüften Werte für aType, group zurück. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten).

function mb.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 mb.checkCategory( category, catArray )

liefert den überprüften Wert für das category. Die Namenraumbezeichnung wird entfernt.

function mb.checkUrl( url )

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

function mb.makeMarkerSymbol( args, title, frame )

liefert r: HTML-Quellcode des Marker-Symbols.

function mb.getColor( aType )

liefert color, aType. Die Gruupe aType wird durch error ersetzt, wenn sie nicht vorhanden ist. color ist die zur Gruppe gehörende Farbe und wird aus der Tabelle Modul:MarkerBase/Groups bezogen.

function mb.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 mb.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.

function mb.data( s )

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

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

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

function mb.languageSpan( s, titleHint, args, country )

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.

function mb.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 mb.splitAndCheck( s, validValues )

liefert eine Tabelle kommaseparierter Werte der Zeichenkette s und überprüft, ob diese Werte in der Tabelle validValues enthalten sind. Neben der Tabelle wird als zweiter Wert eine Zeichenkette mit den fehlerhaften Werten übergeben.

function mb.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 mb.tableInsert( tab, value )

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

function mb.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 mb.makeSocial( args, fromWikidata, name, checkIt )

liefert die verlinkten Symbole von Social-Media-Diensten zurück. args ist die Tabelle der Vorlagenparameter, fromWikidata die Tabelle der Parameter, die aus Wikidata bezogen wurden, und name die Bezeichnung der Einrichtung. checkIt legt fest, ob eine Überprüfung der Werte erfolgen soll.

function mb.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 fw.typeSearch( p31 )
  • Sucht in mehreren P31-P279-Ketten nach Q-ids, deren Werte in den Tabellen Module:Wikidata2/POITypes oder Module:MarkerBase/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 mb.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 mb.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 mb.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 mb.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 mb.getArticleLink( args, entity )

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

Benötigte weitere Module

Dieses Modul benötigt folgende weitere Module: Coordinates • Failsafe • FastWikidata • MarkerBase/Currencies • MarkerBase/Groups • MarkerBase/i18n • MarkerBase/Maki icons • MarkerBase/Types • UrlCheck • Wikidata2/POITypes

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:

Hinweise
-- documentation
local MarkerBase = {
	suite  = 'MarkerBase',
	serial = '2020-08-05',
	item   = 58187612
}

-- module import
local cd = require( 'Module:Coordinates' )
local fs = require( 'Module:Failsafe' )
local fw = require( 'Module:FastWikidata' )
local mc = mw.loadData( 'Module:MarkerBase/Currencies' ) -- additional currency symbols
local mg = mw.loadData( 'Module:MarkerBase/Groups' )
local mi = require( 'Module:MarkerBase/i18n' )
local mm = require( 'Module:MarkerBase/Maki icons' )
local mt = mw.loadData( 'Module:MarkerBase/Types' ) -- types to groups like drink, eat, go, see, sleep, ...
local pt = mw.loadData( 'Module:Wikidata2/POITypes' ) -- WD types to vCard/Poi types
local uc = require( 'Module:UrlCheck' )

-- module variable
local mb = {
	name,       -- module name
	params,     -- unknown parameters' table
	maintenance -- error strings' table
}

-- maintenance tools
function mb.initMaintenance( name )
	mb.name = name
	mb.params = {}
	mb.maintenance = {}
end

function mb.addWrongParameter( param )
	table.insert( mb.params, "''" .. param .. "''" )
end

function mb.addMaintenance( s )
	table.insert( mb.maintenance, s )
end

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

function mb.getCategories( formatStr )
	return fw.getCategories( formatStr )
end

-- groups translation for map legend into Wiki language
function mb.translateGroup( group )
	if not group or group == '' then
		group = mt.error.group
	end
	local t = mg[ group ]
	if t then
		if t.map and t.map ~= '' then
			return t.map
		elseif t.label and t.label ~= '' then
			return t.label
		end
	end
	return group
end

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

-- getting DMS coordinates
function mb.dmsCoordinates( lat, long, name, fromWD, extraParams, withBracket )
-- local function outputCoordinates( lat, long, name )
	local latDMS, _, latMs = cd.getDMSString( lat, 4, 'lat', '', '', mi.defaultDmsFormat )
	local longDMS, _, 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 withBracket then
		r = '(' .. r .. ')'
	end
	return tostring( mw.html.create( 'span' )
		:attr( 'class', 'listing-dms-coordinates printNoLink plainlinks'
			.. mb.addWdClass( fromWD ) )
		:wikitext( r ) )
end

-- removing breaks and controls from parameters
function mb.removeCtrls( s, onlyInline )
	local t = s:gsub( '</br%s*>', ' ' )
	local descrDiv = false
	if onlyInline then
		t = t:gsub( '<br[^/>]*/*>', ' ' )
			:gsub( '</*p[^/>]*/*>', ' ' )
		if not t:find( '<span', 1, true ) then
			-- keep controls in span tag
			t = t:gsub( '[%z\1-\31]', ' ' )
			-- not %c because \127 is used for Mediawiki tags (strip markers `UNIQ)
		end
	else
		t = t:gsub( '[%z\1-\9\11\12\14-\31]', ' ' )
		descrDiv = t:find( '[\10\13]' ) or t:find( '<br[^/>]*/*>' ) or
			t:find( '<p[^/>]*>' )
		if descrDiv and t ~= '' then
			mb.addMaintenance( mi.maintenance.descrDiv )
		end
	end
	if t ~= s then
		mb.addMaintenance( mi.maintenance.illegalCtrls )
	end
	return t, descrDiv
end

-- adding linked sister icons
function mb.addSisterIcons( sisters, name, id )
	local t = ''
	for key, value in pairs( sisters ) do
		if value ~= '' then
			t = t .. ' ' .. tostring( mw.html.create( 'span' )
				:attr( 'class', 'listing-sister-icon listing-sister-' .. key )
				:wikitext( '[' .. value .. ' '
					.. mw.ustring.format( mi.icons[ key ], name ) .. ']' )
			)
		end
	end

	local s = ''
	if id ~= '' then
		-- add Wikidata symbol
		s = mw.html.create( 'span' )
			:attr( 'class', 'listing-sister-icon listing-sister-wikidata' )
			:addClass( t == '' and 'listing-only-wikidata' or nil )
			:wikitext( '[' .. tostring( mw.uri.fullUrl( 'd:' .. id ) )
				.. ' ' .. mw.ustring.format( mi.icons.wikidata, name, id ) .. ']' )
		t = t .. tostring( s )
	end
	return t, s
end

-- getting sister project links
function mb.getWikiLink( langArray, wiki, entity )
	local prefix = ''
	if wiki == 'wiki' then
		prefix = 'w:' -- empty in case of Wikivoyage
	end
	local link
	for i, lang in ipairs( langArray ) do
		if lang ~= '' then
			link = fw.getSitelink( entity, lang .. wiki )
			if link then
				return tostring( mw.uri.fullUrl( prefix .. lang .. ':'
					.. mw.uri.encode( link, 'WIKI' ) ) )
			end
		end
	end
	return ''
end

-- adding Wikimedia sister project icons
function mb.makeSisterIcons( args, country, entity )
	local sisters = {
		wikivoyage = '', -- link to another branch, usually en, as a sister link
		commons    = '', -- link to Commons category
		wikipedia  = ''  -- link to Wikipedia
	}
	if args.wikidata ~= '' then
		sisters.wikipedia = mb.getWikiLink(
			{ args.wikiLang, mi.langs.name, country.lang }, 'wiki', entity
		)
		if args.thisWiki == '' then
			sisters.wikivoyage = mb.getWikiLink(
				{ mi.langs.name, country.lang }, 'wikivoyage', entity
			)
			if sisters.wikivoyage ~= '' then
				mb.addMaintenance( mi.maintenance.linkToOtherWV )
			end
		end
	end
	if args.commonscat ~= '' then
		sisters.commons = tostring( mw.uri.fullUrl( 'c:Category:'
			.. mw.uri.encode( args.commonscat, 'WIKI' ) ) )
	end

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

-- getting name table with linked and unlinked names
function mb.getName( name, pageTitle )
	local r = {
		exists = false,
		all = '', -- name or linked name
		name = '', -- only name
		pageTitle = '', -- if pageTitle ~= '' name is already linked
	}
	if not name or name == '' or type( name ) ~= 'string' 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.text.trim( mw.ustring.gsub( t, '^(.*)|(.*)$', '%1' ) )
		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
	return r
end

-- checking coordinates
function mb.checkCoordinates( args )
	local dms = false
	local t
	if type( args.lat ) == 'boolean' then
		args.lat = ''
	end
	if type( args.long ) == 'boolean' then
		args.long = ''
	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
		args.lat = ''
		args.long = ''
		mb.addMaintenance( mi.maintenance.wrongCoord )
		return
	end

	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
		args.long = ''
		mb.addMaintenance( mi.maintenance.wrongCoord )
		return
	end

	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
	if args.long == '' then
		args.lat = ''
		mb.addMaintenance( mi.maintenance.wrongCoord )
	end
	if dms then
		mb.addMaintenance( mi.maintenance.dmsCoordinate )
	end

	return
end

-- getting marker type and group
function mb.checkTypeAndGroup( aType, group )
	local s, t
	if group ~= '' then
		mb.addMaintenance( mi.maintenance.groupUsed )
		s = mg[ group ]
		if s then
			if s.alias then
				mb.addMaintenance( mi.maintenance.groupIsAlias )
				group = s.alias
			end
		else			
			mb.addMaintenance( mi.maintenance.unknownGroup )
			group = ''
		end
	end
	if aType == '' then
		aType = mt.error.group
		if group == '' then
			group = aType
		end
		mb.addMaintenance( mi.maintenance.missingType )
	elseif aType == mt.error.group then
		if group == '' then
			group = aType
		end
		mb.addMaintenance( mi.maintenance.unknownType )
	else
		-- split seperate types and analyse them
		local ar = {}
		for t in mw.ustring.gmatch( aType .. ',', '([^,]+)' ) do
			t = mw.text.trim( t:lower() )
			t = t:gsub( ' ', '_' )
			s = mg[ t ]
			if s then -- type is a group itself
				if s.is and s.is == 'color' then
					mb.addMaintenance( mi.maintenance.typeIsColor )
					if s.alias then
						t = s.alias
					end
				else
					mb.addMaintenance( mi.maintenance.typeIsGroup )
				end
				if group == '' then
					group = t
				end
			else
				s = mt[ t ]
				if s then
					if group == '' then
						group = s.group
					end
				else
					mb.addMaintenance( mi.maintenance.unknownType )
					group = mt.error.group
				end
			end
			table.insert( ar, t )
		end
		aType = table.concat( ar, ',' )
	end
	return aType, group
end

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

	-- delete namespace
	for i, value in ipairs( mi.texts.FileNS ) do
		image = image:gsub( '^' .. value .. ':', '' )
	end

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

-- remove namespace from category
function mb.checkCategory( category )
	for i = 1, #mi.texts.CategoryNS, 1 do
		category = category:gsub( '^' .. mi.texts.CategoryNS[ i ] .. ':', '' )
	end
	return category
end

-- url check
function mb.checkUrl( url )
	if url and url ~= '' then
		local c = uc.isUrl( url, mi.options.skipPathCheck ) -- getting result code
		if c > 2 then
			mb.addMaintenance( mi.maintenance.wrongUrl )
			url = ''
		elseif c == 2 then -- URL contains IP address
			mb.addMaintenance( mi.maintenance.UrlWithIP )
		elseif c == 1 then -- URL contains non-ASCII chars
			mb.addMaintenance( mi.maintenance.nonASCII )
		end
	end
	return url
end

-- making marker symbol
function mb.makeMarkerSymbol( args, title, frame )
	local lon = tonumber( args.long )
	local lat = tonumber( args.lat )
    local tagArgs = {
        zoom = tonumber( args.zoom ),
        latitude = lat,
        longitude = lon,
        show = mi.showAll,
    }
	if args.text ~= '' then
		tagArgs.text = args.text
		if not args.useIcon then
			tagArgs.class = 'no-icon'
		end
	end
	if args.mapGroup ~= '' then
		tagArgs.group = args.mapGroup
		tagArgs.show = args.mapGroup
	else
		tagArgs.group = args.groupTranslated
	end
	if args.image ~= '' then
		tagArgs.description = '[[file:' .. args.image .. '|141px|' .. 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' )
		:attr( 'class', 'plainlinks printNoLink poi listing-map' )
		:attr( 'title', mi.texts.tooltip )
		:css( {
			[ 'background-color' ] = args.color,
			[ 'border-color' ] = args.color
		} )
		-- frame:extensionTag is expensive
		:wikitext( frame:extensionTag( 'maplink', mw.text.jsonEncode( geoJson ), tagArgs ) )
	)
end

-- getting color from group type
function mb.getColor( aType )
	local c = mg[ aType ]
	if c then
		return c.color, aType
	else
		return mg[ 'error' ].color, 'error'
	end
end

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

-- generates a table with type documentation
function mb.generateTableTypeList( frame )
	local rows = {}
	local typeList = '<table class="sortable multiline" cellspacing="0">'
		.. '<tr><th>' .. mi.types.label .. '</th><th>'
		.. mi.types.group .. '</th><th>' .. mi.types.type .. '</th></tr>'
	for key, value in pairs( mt ) do 
		table.insert( rows, '<tr><td>' .. value.label .. '</td><td>' .. value.group
			.. '</td><td>' .. key:gsub( '_', ' ' ) .. '</td></tr>' )
	end
	table.sort( rows,
		function( a, b )
			function simplify( s )
				s = mw.ustring.gsub( mw.ustring.lower( s ), 'ä', 'a' )
				return mw.ustring.gsub( mw.ustring.gsub( s, 'ö', 'o' ), 'ü', 'u' )
			end
			return simplify( a ) < simplify( b )
		end
	)
	return typeList .. table.concat( rows, '' ) .. '</table>'
end

-- prepare value data parameter to a tag
function mb.data( s )
	if not s or s == '' then
		return nil
	else
		return mw.text.decode( s, true ) -- replacing all entities by characters
	end
end

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

	if country.currency ~= '' then
		args.currency = country.currency
		if mc[ country.currency ] then
			args.currency = args.currency .. ', ' .. mc[ country.currency ]
		end
	end	

	local wrapper = mw.html.create( show.inline and 'span' or 'div' )
		:attr( 'class', 'h-card ' .. aClass )
	if args.noGpx then
		wrapper:addClass( 'vcard-nogpx' )
	else
		wrapper:addClass( 'vcard' )
	end
	if show.indent then
		wrapper:addClass( 'listing-indent' )
	end
	if args.givenName.name ~= mi.maintenance.missingName then
		wrapper:attr( 'data-name', mb.data( args.givenName.name ) )
	end
	wrapper:attr( 'data-location', mb.data( args.titleObj.subpageText ) )
		:attr( 'data-region', mb.data( country.iso_3166 ) )
		:attr( 'data-country', mb.data( country.country ) )
		:attr( 'data-lang', mb.data( country.lang ) )
		:attr( 'data-lang-name', mb.data( country.langName ) )
		:attr( 'data-country-calling-code', mb.data( country.cc ) )
		:attr( 'data-dir', mb.data( country.R2L and 'rtl' or 'ltr' ) )
		:attr( 'data-wiki-dir', mb.data( args.wikiR2L and 'rtl' or 'ltr' ) )
		:attr( 'data-currency', mb.data( args.currency ) )
	local arg
	for key, value in pairs( list ) do
		arg = args[ key ]:gsub( '<[^<>]*>', '' ) -- remove html tags
		wrapper:attr( value, mb.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 args.image ~= '' then
		wrapper:node( mw.html.create( 'span' )
			:addClass( 'listing-image' )
			:css( 'display', 'none' )
			:wikitext( mw.ustring.format( '[[File:%s|350x240px|class=noviewer|%s]]',
				args.image, args.givenName.name ) )
		)
	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 mb.languageSpan( s, titleHint, args, country )
	if not s or s == '' then
		return ''
	end

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

	local dir
	if country.R2L then
		dir = 'rtl'
	elseif args.wikiR2L then
		dir = 'ltr'
	end	

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

	if country.R2L and not args.wikiR2L then
		t = '&rlm;' .. t .. '&lrm;'
	end
	if not country.R2L and args.wikiR2L 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 mb.split( s, convert, toValue )
	local arr = {}
	if not s or s == '' then
		return arr
	end
	local arr2 = mw.text.split( s, ',', true )
	local t
	for i = 1, #arr2, 1 do
		t = mw.text.trim( arr2[ i ] )
		if t ~= '' then
			if convert then
				t = t:lower():gsub( ' ', '_' )
			end
			if toValue then
				table.insert( arr, t )
			else
				arr[ t ] = ''
			end
		end
	end
	return arr
end

-- Splitting comma separated lists to an array of key items
-- checking items with allowed key values of validValues array
function mb.splitAndCheck( s, validValues )
	local arr = {}
	local err = {}
	if not s or s == '' or not validValues then
		return arr, ''
	end
	arr = mb.split( s, true )
	for k, v in pairs( arr ) do -- error handling
		if not validValues[ k ] then
			table.insert( err, k )
			arr[ k ] = nil
		end
	end
	return arr, table.concat( err, ', ' )
end

function mb.getShow( default, value, validValues )
	local show = mb.split( default, true )
	local add, err = mb.splitAndCheck( value, validValues )
	if err ~= '' then
		mb.addMaintenance( mw.ustring.format( mi.maintenance.unknownShow, err ) )
	end
	if add.none or add.coord then
		show.poi = nil -- overwriting default
	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 mb.tableInsert( tab, value )
	if type( tab ) == 'table' and value and value ~= '' then
		table.insert( tab, value )
	end
end

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

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

function mb.makeSocial( args, fromWikidata, name, checkIt )
	local domain, span, t
	local s = ''

	for _, service in ipairs( mi.services ) do
		-- check values first
		t = args[ service.key ] or ''
		domain = service.url:gsub( 'com/.*', 'com/' )
		if t ~= '' and checkIt then
			if t:match( '^http' ) then
				if not t:match( '^https' ) then
					t = t:gsub( '^http', 'https' )
					mb.addMaintenance( mw.ustring.format(
						mi.maintenance.wrongSocialUrl, service.key ) )
				end
				if uc.isUrl( t, mi.options.skipPathCheck ) > 1 or
					not t:match( '^' .. domain .. '.+$' ) then
					t = ''
					mb.addMaintenance( mw.ustring.format(
						mi.maintenance.wrongSocialUrl, service.key ) )
				end
				if t ~= '' and not fromWikidata[ service.key ] then
					mb.addMaintenance( mw.ustring.format(
						mi.maintenance.socialUrlUsed, service.key ) )
				end
			else
				local match = false
				if type( service.pattern ) == 'string' then
					service.pattern = { service.pattern }
				end
				for _, pattern in ipairs( service.pattern ) do
					if mw.ustring.match( t, pattern ) then
						match = true
						break
					end
				end
				if not match then
					t = ''
					mb.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 = mw.html.create( 'span' )
				:attr( 'class', 'listing-social-media '
					.. 'listing-social-media-' .. service.key
					.. mb.addWdClass( fromWikidata[ key ] ) )
				:attr( 'style', mi.texts.socialStyle )
				:wikitext( '[' .. t .. ' '
					.. mw.ustring.format( mi.icons[ service.key ], name ) .. ']' )
			s = s .. tostring( span )
		end
	end
	return s
end

local function _makeAirport( code, args, fromWikidata )
	local span = mw.html.create( 'span' )
		:attr( 'class', 'listing-' .. code .. '-code'
			.. mb.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 mb.makeAirport( args, fromWikidata )
	local airport = ''
	if args.iata ~= '' then
		airport = _makeAirport( 'iata', args, fromWikidata )
	elseif args.icao ~= '' then
		airport = _makeAirport( 'icao', args, fromWikidata )
	end
	return airport
end

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

	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 value and value ~= '' then
				value = mw.ustring.lower( value:gsub( ' ', '_' ) )
				if mt[ value ] then
					return value
				end
			end
		end
		return nil
	end

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

			-- checking English label and aliases
			t = compareLabels( mw.wikibase.getLabelByLang( id, 'en' ) )
			if t then
				return t
			end
			t = compareLabels( fw.getAliases( id, 'en' ) )
			if t then
				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 fw.getValues calls
	local id, ids
	for i = 1, #p31, 1 do -- step 2: analyse P279 chains of first ids
		id = p31[ i ].id -- start id
		local j = 0
		repeat
			ids = fw.getValues( id, mi.properties.subclassOf, nil )
			if #ids > 0 then
				aType = compareIds( ids )
				if aType then
					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 'error'
end

function mb.typeSearch( p31, entity )
	if p31 and #p31 == 0 then
		p31 = fw.getValues( entity, mi.properties.subclassOf, mi.p31Limit )
	end
	local t = _typeSearch( p31 )
	if t ~= 'error' then
		mb.addMaintenance( mi.maintenance.typeFromWD )
	end
	return t
end

function mb.getMakiIconName( aType )
	local mType
	if mm[ aType ] then
		mType = aType
	elseif mt[ aType ] and mt[ aType ].icon then
		mType = mt[ aType ].icon
	end
	if mType and mm[ mType ].im and mm[ mType ].im ~= '' then
		return mType
	end
	return nil
end

function mb.checkCommonsCategory( args )
	-- remove namespace from category
	if args.commonscat ~= '' then
		args.commonscat = mb.checkCategory( args.commonscat )
		if args.commonscat ~= '' then
			mb.addMaintenance( mi.maintenance.commonscat )
		end
	end
end

function mb.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 = fw.getSitelink( entity, 'commonswiki' ) or ''
	if t:match( '^Category:.+$' ) then
		t = t:gsub( '^Category:', '' )
	else
		t = fw.getValue( entity, mi.properties.commonsCategory )
		if t == '' then
			local id = fw.getId( entity, mi.properties.mainCategory )
			if id ~= '' then
				t = fw.getSitelink( id, 'commonswiki' ) or ''
				t = t:gsub( '^Category:', '' )
			end
		end
	end
	if t ~= '' and args.commonscat ~= '' then
		mb.addMaintenance( mi.maintenance.commonscatWD )
	end
	if args.commonscat == '' then
		args.commonscat = t
	end
end

-- getting names from Wikidata
function mb.getNamesFromWikidata( args, fromWikidata, country, entity )
	-- getting official names
	local officialNames =
		fw.getValuesWithLanguages( entity, mi.properties.officialName )

	if type( args.name ) == 'boolean' or args.name == '' then
		args.name = officialNames[ args.wikiLang ]
			or mi.langs.name ~= '' and officialNames[ mi.langs.name ]
			or ''
		-- if failed then get labels
		if args.name == '' then
			if args.wikiLang == mi.langs.name then
				args.name = fw.getLabel( entity ) or ''
			else
				args.name = fw.getLabel( entity )
					or mi.langs.name ~= '' and fw.getLabel( entity, mi.langs.name )
					or ''
			end
		end
		if args.name ~= '' then
			mb.addMaintenance( mi.maintenance.nameFromWD )
			fromWikidata.name = true
		end
	end

	if args.name == '' and
		not ( type( args.nameLocal ) == 'string' and args.nameLocal ~= '' ) then
		args.nameLocal = true
	end

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

-- getting link to Wikivoyage
function mb.getArticleLink( args, entity )
	local t = fw.getSitelink( entity, args.wikiLang .. 'wikivoyage' )
	if t and t ~= args.titleObj.text then  -- no link to the article itself
		args.thisWiki = t
	end
end

-- module administration
function mb.MarkerBase()
	return MarkerBase
end

function mb.failsafe( version )
	return fs._failsafe( version, MarkerBase ) or ''
end

return mb