モジュールの解説[表示] [編集] [履歴] [キャッシュを破棄]

このモジュールは、モジュール:VCardで使用される関数を提供します。Wikivoyage:Marker utilities (ドイツ語版も参照。

ウィキデータでのバージョン: 2025-03-17 問題あり

使用状況

このモジュールを使用しているモジュールは以下の通りです:

関数

メンテナンス関数

function mu.initMaintenance( name ) 

この関数は、エラーメッセージ出力のための変数を初期化します。nameには、名前空間名を除いた関連モジュールの名前が入ります。また、この関数は不正なパラメータとエラー・注釈のテーブルも初期化します。

function mu.addMaintenance( key, value ) 

この関数は、keyに与えられたコードから、エラーや注釈などのメッセージを取得してエラーと注釈のテーブルに入れます。valueはメッセージの引数として、%sを置換します。上で紹介したいくつかの関数もこのテーブルに値を入れます。

function mu.getMaintenance() 

この関数は、全てのエラーと注釈のメッセージを返します。

function mu.getCategories( formatStr ) 

使用されたすべてのウィキデータプロパティについて、そのプロパティのカテゴリへのリンクを返します。

補助関数

function mu.isSet( arg ) 

引数argがセットされているかどうかについて、trueまたはfalseを返します。argに空文字列以外の値が代入されている場合のみ、trueが返ります。

function mu.convertForSort( s ) 

ソート用に、文字列sに含まれる特殊文字を置換します。置換される文字は、国際化ファイルsubstitutesテーブルに入っています。漢字は量が膨大であり列挙が不可能であるため、日本語版ウィキボヤージュではこの関数に意味がありません。

function mu.formatNumber( num ) 

数値の引数numについて、小数点を文字列として置換し、3桁ごとに桁の区切り文字を挿入します。

function mu.tableInsert( tab, value ) 

テーブルtabが定義されており、空文字列でなければ、tabvalueを挿入します。

function mu.textSplit( s, sep ) 

文字列sを区切り文字sepで分割し、空文字列を含まないテーブルに代入します。sepは正確に1文字(1バイト)の長さを持ち、パターンマッチの構文は使用できません。この関数はmw.text.split()よりも遥かに速いです。

function mu.split( s ) 

文字列sをカンマで区切った連想配列を返します。部分文字列は小文字に変換され、スペースはアンダースコアで置換されます。

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

s<span>タグまたは<bdi>タグで囲んだ文字列を返します。classCSSクラス、attrは属性のテーブル、cssは要素に直接書かれるスタイルシートのテーブルです。

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

言語とテキストの方向が指定された<span>タグにテキストsを挿入します。titleHint<span>タグのtitle属性、argsはテンプレートのパラメータが入ったテーブル、countryは国の情報のテーブルです。

function mu.addWdClass( isFromWikidata ) 

isFromWikidatatrueの場合、クラス識別子wikidata-contentを返し、そうでなければ空文字列を返します。

function mu.getAliases( tab, key ) 

テーブルtabから、別名のキーと正式名称の値を持ったテーブルを作成します。

function mu.yesno( val ) 

valが既知の値であればyまたはnを返し、なければnilを返します。モジュール:Yesnoと同じ機能です。

評価関数

function mu.checkArguments( templateArgs, validKeys ) 

テンプレートの呼び出し時に、未知のパラメータや別名によって重複したパラメータが使用されているか確認します。これらの不明または重複したパラメータは、無効なパラメータのテーブル(invalidParams)または重複した別名のテーブル(duplicateAliases)に入ります。templateArgsは確認したいパラメータのテーブルで、validKeysは既知のパラメータのテーブルです(モジュール:VCard/i18nなどを参照)。返り値は有効なパラメータのテーブルです。

function mu.checkCommonsCategory( args ) 

この関数は、コモンズのカテゴリargs.commonscatから名前空間名を除去し、コモンズのカテゴリが設定されている場合はメンテナンス用のカテゴリを追加します。

function mu.checkCoordinates( lat, long ) 

latlongの値を確認します。エラーがある場合、latとlongは空文字列となります。エラーメッセージのテーブルにも対応する部分があります(下記参照)。

function mu.checkZoom( args ) 

args.zoomの値が0~19の範囲の数字でない場合、args.zoomを既定値に設定します。

function mu.checkImage( image, entity ) 

画像の値を確認し、エラーがあった場合には空文字列を代入します。エラーメッセージのテーブルにも対応する部分があります(下記参照)。

function mu.checkStatus( args ) 

statusパラメータの値を確認し、有効な値をargs.statusTableに代入します。エラーが発生した場合、エラーメッセージのテーブルにも対応する部分があります(下記参照)。

function mu.checkTypeAndGroup( args ) 

argsのタイプとグループの値を確認し、正しい値を代入します。エラーが発生した場合、エラーメッセージのテーブルにも対応する部分があります(下記参照)。

function mu.checkUrl( args ) 

argsのURLの値を確認し、正しい値を代入します。エラーが発生した場合、エラーメッセージのテーブルにも対応する部分があります(下記参照)。

タイプとグループの関数

function mu.getTypeParams( aType ) 

aTypeで指定されたタイプのテーブルを、モジュール:Marker utilities/Typesから取得して返します。該当するものがない場合、nilが返ります。

function mu.getTypeLabel( id ) 

idに対応するタイプの最初のラベルを返します。idは、vCardのタイプかウィキデータの項目名です。エラーがある場合、空文字列かidそのものが返ります。

function mu.typeExists( aType ) 

aTypeモジュール:Marker utilities/TypesにあればaTypeを、なければnilを返します。別名は対応するタイプに変換されます。

function mu.groupWithEvents( group ) 

指定されたグループgroupが、イベントのタイプを使用できるかどうかを返します。

function mu.getColor( args ) 

グループgroupから引数argscolorinverseを追加します。

function mu.idToType( id ) 

ウィキデータIDからタイプを検索し、見つかった場合はそのタイプを、そうでなければnilを返します。

整形関数

function mu.getShow( default, args, validValues ) 

上書き可能な既定値を踏まえて、カンマ区切りでパラメータに渡されたshowの値を持つ連想配列を返します。argsはパラメータのテーブルで、この関数内ではargs.showのみが評価されます。

function mu.removeCtrls( s, onlyInline ) 

この関数は、文字列sから制御文字や改行を削除します。onlyInline = falseの場合には改行は削除されません。この関数の返り値は2つあります:

  • 整形されたs
  • <div>タグのコンテナを持つ説明文descrDiv

クエリ関数

function mu.getCoordinatesFromWikidata( entity ) 

ウィキデータのデータセットにある座標をエンティティのインスタンスと共にに返します。まず P5140 プロパティから中心座標を取得しようと試み、次に P625 プロパティから座標の取得を試みます。

function mu.typeSearch( p31 ) 

いくつかの P31~P279 チェーンで、モジュール:Marker utilities/Typesのテーブルに値が含まれているかもしれないQIDを検索します。テーブル P31 には見つかったP31の値が含まれています。はじめにヒットした値が文字列で返されます。エラーの場合は、エラー文字列が返されます。上位レベルを検索する際の深さの最大数はmi.searchLimitで設定でき、通常は4です。最初の P279 ID だけが評価され、ツリー構造全体は評価されません。

function mu.getCommonsCategory( args, entity ) 

この関数は、ウィキデータにあるエンティティのインスタンスからコモンズのカテゴリを取得しようと試みます。最初に接続されているコモンズへのリンクが調べられ、次に P373 プロパティ、最後に P910 プロパティを調べます。

function mu.getLangTable( wikiLang, localLang ) 

wikiLanglocalLangcountry.lang)、モジュール:Marker utilities/i18nの言語langsを使って言語コードの配列を作成します。

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

この関数は、ウィキデータの情報を用いて、ウィキの言語と現地語の名称でテーブルの引数を埋めます。fromWikidataテーブルには、名称の情報をウィキデータから取得したかどうか(fromWikidata.namefromWikidata.nameLocal)が格納されます。

function mu.getArticleLink( args, entity ) 

この記事でテンプレート呼び出しが行われない限り、この関数は関連する記事へのサイトリンクをargs.thisWikiに渡します。

マーカー関数

function mu.getMaki( key ) 

Die Funktion liefert eine Tabelle für den angegebenen key aus der Tabelle Module:Marker utilities/Maki icons zurück.

function mu.getMakiIconId( aType ) 

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

function mu.addIconToMarker( args ) 

fügt einen Text, der ein Symbol enthält, zur Parametertabelle args.

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

liefert r: HTML-Quellcode des Marker-Symbols.

アイコン関数

function mu.makeStatusIcons( args ) 

args.statusTableテーブルに従って画像の文字列を返します。

function mu.addLinkIcon( classes, link, title, text ) 

リンクされたアイコン画像を表示するためのHTMLを生成します。表示にはテンプレートのスタイルシートも必要です。listing-iconに追加するCSSクラスclassesと、リンクlink(外部リンクでも内部リンクでも)、ツールチップtitle、通常は表示されないリンクテキストtextが必要です。

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

リンクされた姉妹プロジェクトのアイコンをテーブルに代入します。通常、情報はウィキデータ・エンティティのサイトリンクから取得します。argsは、テンプレートに渡されたパラメータのテーブルです。テーブルcountryには言語をはじめとした国のデータが入っています。

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

リンクされたソーシャルメディアサービスのアイコンをテーブルに代入します。argsは、テンプレートに渡されたパラメータのテーブルです。fromWikidataはウィキデータから取得したパラメータのテーブル、nameはマーカーやvCardが紹介している場所の名称です。

出力関数

function mu.prepareNames( args ) 

nameパラメータとnameMapから形成されるdisplayNameテーブルとgivenNameテーブルを引数テーブルに追加します。ウィキ構文内の有効なリンクから実際の名前を抽出することが主な目的です。どちらのテーブルも次の3つの要素で構成されます:

  • namestring):表示される名前
  • allstring):テンプレートにリンク付きで渡された場合はリンクを含めたウィキ構文、そうでない場合は名前だけ
  • pageTitlestring):リンクの名前。nameと異なるかもしれません。
function mu.makeName( result, args, show, page, country, nameClass, localClass ) 

resultテーブルに名前と追加の名前を追加します。追加の名前には、別名、現地語での名前、コメント、空港コードが含まれ、括弧とspanタグに挟まれています。nameClasslocalClassは、名前と現地語名の追加クラスです。

function mu.parentheses( s, trim ) 

この関数は、括弧内に文字列sを挿入します。ブール値trimtrueならば、書式の文字列(通常は (%s))が切り抜かれます。

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

文字列rを返します。この文字列は、マップツールへのリンクを含む10進数の座標で、任意で括弧内に入れることもできます。nameは場所の名前で、fromWD = trueは座標がウィキデータ由来であることを意味します。また、extraParamsにはマップツールで使用される縮尺と領域のデータが入ります。

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

マーカーやvCardの内容をタグ(<span><div>)で囲みます。

その他の関数

function mu.getPageData() 

この関数は、ページに関連したデータを返します。

関連項目

--[[ 	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-02-14', 		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( ' ', '_' ) 	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 normalizeValues( args ) 	for i, param in ipairs( mi.options.normalizeValues ) do 		if mu.isSet( args[ param ] ) then 			args[ param ] = args[ param ]:ulower():gsub( '[_%s]+', ' ' ) 		end 	end end  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 	normalizeValues( args ) 	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 = id:gsub( '[_%s]+', '_' ) 	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( ',' ) 		if at then 			t = t:sub( 1, at - 1 ) 		end 	else 		t = id:gsub( '_', ' ' ) 	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  local function replaceWithSpace( s, pattern ) 	s = s:find( pattern ) and s:gsub( pattern, ' ' ) or s 	return s end  -- removing line breaks and controls from parameter strings function mu.removeCtrls( s, onlyInline ) 	local descrDiv = false -- div tag instead of span tag for description needed?  	-- remove controls from tags before test 	s = s:gsub( '(<[^>]+>)', function( t ) 			return replaceWithSpace( t, '[%z\1-\31]' ) 		end )  	local t = replaceWithSpace( s, '</br%s*>' ) 	if onlyInline then 		t = replaceWithSpace( t, '<br[^/>]*/*>' ) 		t = replaceWithSpace( t, '</*p[^/>]*/*>' ) 		t = replaceWithSpace( t, '[%z\1-\31]' ) 		-- not %c because \127 is used for Mediawiki tags (strip markers `UNIQ) 	else 		t = replaceWithSpace( t, '[%z\1-\9\11\12\14-\31]' ) 		descrDiv = t:find( '[\10\13]' ) or t:find( '<br[^/>]*/*>' ) or 			t:find( '<p[^/>]*>' ) 	end 	if t ~= s then 		mu.addMaintenance( '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 = args.sectionFrom:gsub( '[_]+', ' ' ) 		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