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

Anwendung

Das Modul wird direkt von der Vorlage {{vCard}} aufgerufen. Parameterbeschreibung siehe dort. Im Projektnamensraum befindet sich die technische Dokumentation Wikivoyage:VCard.

Versionsbezeichnung auf Wikidata: 2025-04-23 Ok!

Benötigte weitere Module

Dieses Modul benötigt folgende weitere Module: CountryData • CountryData/Currencies • Exchange rate • Hours • Hours/i18n • Languages • LinkMail • LinkPhone • LinkSkype • Marker utilities • Marker utilities/i18n • VCard/Cards • VCard/i18n • VCard/Params • VCard/Qualifiers • VCard/Subtypes • VCard/Unesco • Wikidata utilities

Anmerkungen

Experimentalbeispiele

* {{vCard | name= Ein Hotel | type = hostel | url= http://hotel.de | lat = 52.5144 | long = 13.389722 | show = all | address = Hauptstraße 1 | directions = Abzweig von der Nebenstraße | description = Das in der Innenstadt gelegene Hotel besitzt großzügige Räume, bietet aber nur ÜF. | phone = +49 (0)30 2345 1234 | fax = +49 (0)30 2345 9876, +49 0176 345 1234 | before = [[File:Flag of Germany.svg|border|20px|class=noviewer|Flag of Germany]] | image = Berlin Friedrichstraße Galeries Lafayette.jpg | email = [email protected] | comment = j | lastedit = 2020-09-18 | hours = 7/24. | checkin = ab 14 Uhr | checkout = bis 12 Uhr | payment = Visa, Master, AmEx, Maestro | subtype = wlan, bar, pool, room:89 | skype = nutzer.name; nutzer2.name }}  * {{vCard | wikidata = Q201219 | name = Ägyptisches Museum Kairo | comment = auch Nationalmuseum | alt = Egyptian Museum | name-latin = al-Matḥaf al-Miṣrī | address = Mīdān et-Taḥrīr | address-local = ميدان التحرير | directions = im Stadtzentrum | directions-local = بوسط البلد | status = top-sight }} * {{vCard | wikidata = Q201219 | name = Ägyptisches Museum Kairo | auto = n | facebook = j }} <​!-- Marker-Modus --> * {{vCard | wikidata = Q19675 | name = Musée du Louvre | description = Noch weiterer Text. }} * {{vCard | wikidata = Q19675 | price = n | hours = n | payment = n }} * {{vCard | wikidata = Q257342 }} * {{vCard | wikidata = Q28934 | description = Test Durchhangeln. }} * {{vCard | wikidata = Q4872 }} * {{vCard | wikidata = Q10697 }} * {{vCard | wikidata = Q12508 | description = Test Durchhangeln. }} * {{vCard | wikidata = Q46033 | auto = n | show = symbol | description = Test Flughafen mit Ikone. }} * {{vCard | wikidata = Q13218762 | description = Anzeige englische Wikipedia. }} * {{vCard | wikidata = Q56506795 | description = Ausgabe Anschriften. }} * {{vCard | wikidata = Q47429618 | auto = n | address = | address-local = | directions = | phone = | subtype = n | description = Test eingeschränkte WD-Ausgabe. }} * {{vCard | wikidata = Q637739 | description = Rollstuhl aus WD. }} * {{vCard | name = Landesmuseum für Vorgeschichte | type = museum | wikidata = Q1332407 | description = Uhrzeiten, Kommentare. }} * {{vCard | name = Museo Nacional de Antropología | type = museum | wikidata = Q2917041 | description = Zusammenfassung Kommentare. }} 
  • Flag of Germany 1 Ein Hotel, Hauptstraße 1 (Abzweig von der Nebenstraße). Tel.: +49 (0)30 2345 1234, Fax: +49 (0)30 2345 9876, +49 (0)176 345 1234, E-Mail: Skype: nutzer.name, nutzer2.name. Das in der Innenstadt gelegene Hotel besitzt großzügige Räume, bietet aber nur ÜF. Merkmale: WLAN, Bar, Schwimmbecken, 89 Zimmer. Geöffnet: 7/24. Check-in: ab 14 Uhr. Check-out: bis 12 Uhr. Akzeptierte Zahlungsarten: Visa, Master, AmEx, Maestro. (52° 30′ 52″ N 13° 23′ 23″ O)

Parameter show

  • Mögliche Werte: none, poi, coord, all, inline, noairport, outdent, symbol. Eine kommaseparierte Liste mehrerer Werte ist möglich.
  • Man kann die Standardwerte überschreiben.
  • Darstellung von Marker und Koordinaten.
    • none ist stärker als poi, coord und all. all ist stärker als poi und coord.
  • Block-/Inline-Darstellung
    • Zukünftig wird die vCard standardmäßig im Block-Modus (css: display = block) angezeigt werden. Gegenwärtig Inline-Modus aufgrund zahlreicher Verwendung in diesem Modus. Dies schließt Beschränkungen bei den Darstellungsmöglichkeiten der Beschreibung description ein. inline schaltet vom Block- in den Inline-Modus.
    • outdent bewirkt im Blockmodus einen eingerückten Absatz mit hängender erster Zeile. Das POI-Symbol wirkt dann wie ein Aufzählungszeichen.
Hinweise
-- module variable and administration local vc = { 	moduleInterface = { 		suite  = 'vCard', 		serial = '2025-04-23', 		item   = 58187507 	}, 	 	-- table containing parameters fetched from Wikidata 	fromWD = {}, 	-- Wikidata to subtype table 	subtypeIds = nil, 	-- complete subtype table including Wikidata subtypes 	subtypes = {} }  -- module import -- require( 'strict' ) local mi = require( 'Module:Marker utilities/i18n' ) local mu = require( 'Module:Marker utilities' ) local vp = require( 'Module:VCard/Params' ) -- parameter lists local vi = require( 'Module:VCard/i18n' ) -- parameter translations local vq = mw.loadData( 'Module:VCard/Qualifiers' ) -- comment tables  local cm = require( 'Module:CountryData' ) local er -- modules will be loaded later if needed local hi local hr local lg local lp = require( 'Module:LinkPhone' ) local vs local wu = require( 'Module:Wikidata utilities' )  local function addWdClass( key ) 	return mu.addWdClass( vc.fromWD[ key ] ) end  local function forceFetchFromWikidata( tab ) 	for key, value in pairs( tab ) do 		vp.ParMap[ key ] = true 	end end  -- copying frameArgs parameters to args = vp.ParMap parameters local function copyParameters( args, show ) 	local t, value 	local exclude = { auto = 1, show = 1, subtype = 1, wikidata = 1 } 	local copy = { subtype = 1 }  	vp.ParMap.wikidata = args.wikidata  	-- force getting data from Wikidata for missing parameters 	show.inlineDescription = true -- description with div or span tag 	if vp.ParMap.auto == true then 		forceFetchFromWikidata( vp.ParWD ) 		forceFetchFromWikidata( vp.ParWDAdd ) 	end  	-- copying args parameters to vp.ParMap parameters 	for key, v in pairs( vi.p ) do 		value = args[ key ]  		if value then 			value, t = 				mu.removeCtrls( value, show.inline or key ~= 'description' ) 			if t then 				show.inlineDescription = false 			end  			if not exclude[ key ] then 				if value == '' and key ~= 'type' then 					value = 'y' 				end 				t = mu.yesno( value ) 				if t then 					if vp.ParMap.wikidata ~= '' then 						vp.ParMap[ key ] = t == 'y' 					else 						vp.ParMap[ key ] = '' 					end 				else 					vp.ParMap[ key ] = value 				end 			end 			if copy[ key ] then 				vp.ParMap[ key ] = value 			end 		end 	end  	return vp.ParMap end  -- checking subtypes local function checkSubtypes( args, subtypesTable ) 	if not mu.isSet( args.subtype ) then 		return {} 	end  	local function aliasToSubtype( alias ) 		if not vc.subtypeAliases then -- alias to subtype table 			vc.subtypeAliases = mu.getAliases( subtypesTable, 'alias' ) 		end 		return vc.subtypeAliases[ alias ] 	end  	local function subtypeExists( subtype ) 		return subtypesTable[ subtype ] and subtype or aliasToSubtype( subtype ) 	end  	local subtypes = {} 	local invalidSubtypes = {} 	local at, count, invalidCount, item 	for subtype, v in pairs( mu.split( args.subtype ) ) do 		invalidCount = false 		count = '' 		item = subtype 		-- split item from count 		at = item:find( ':', 1, true ) 		if at then 			count = tonumber( item:sub( at + 1, #item ) ) or '' 			item = mw.text.trim( item:sub( 1, at - 1 ) ) 			if count == '' then 				invalidCount = true -- ':' without count or not a number 			else 				count = math.floor( count ) 				if count < 2 then 					count = '' 				end 			end 		end 		item = subtypeExists( item ) or mu.typeExists( item ) 		if item then 			subtypes[ item ] = count 		end 		if invalidCount or not item then 			table.insert( invalidSubtypes, subtype ) 		end 	end 	if #invalidSubtypes > 0 then 		mu.addMaintenance( 'unknownSubtype', table.concat( invalidSubtypes, mu.commaSeparator ) ) 	end 	return subtypes end  local function initialParametersCheck( frame, page ) 	local country, email, entity, param, show, t, v, web, wrongQualifier 	local frameArgs = mu.checkArguments( frame:getParent().args, vi.p )  	frameArgs.wikidata, entity, wrongQualifier = wu.getEntity( frameArgs.wikidata or '' ) 	if wrongQualifier then 		mu.addMaintenance( 'wrongQualifier' ) 	end 	if mu.isSet( frameArgs.wikidata ) then 		mu.addMaintenance( 'wikidata' ) 		v = mu.yesno( frameArgs.auto or '' ) 		if v then 			vp.ParMap.auto = v == 'y' 		else 			vp.ParMap.auto = vi.options.defaultAuto 		end 	else 		vp.ParMap.auto = false 	end  	-- making phone number table 	t = {} 	for i, key in ipairs( vp.phones ) do 		mu.tableInsert( t, frameArgs[ key ] ) 	end 	-- making web addresses table 	web = {} 	mu.tableInsert( web, frameArgs.url ) 	email = frameArgs.email or '' 	email = email:gsub( ',.*$', '' ) -- first email 	mu.tableInsert( web, email )  	-- getting country-specific technical parameters 	country = cm.getCountryData( entity, t, frameArgs.country, frameArgs.wikidata, web ) 	if country.fromWD then 		mu.addMaintenance( 'countryFromWD' ) 	end 	if country.unknownCountry then 		mu.addMaintenance( 'unknownCountry' ) 	end 	if country.cc ~= '' then 		country.trunkPrefix = lp.getTrunkPrefix( country.cc ) 	end 	-- for map support 	country.extra = mi.map.defaultSiteType 	if mu.isSet( country.iso_3166 ) then 		country.extra = country.extra .. '_region:' .. country.iso_3166 		-- country-specific default show 	end 	if mu.isSet( country.show ) then 		vp.ParMap.show = vp.ParMap.show .. ',' .. country.show 	end  	-- handling args table 	show = mu.getShow( vp.ParMap.show, frameArgs, vp.show )  	-- copying frameArgs parameters to args = vp.ParMap parameters 	local args = copyParameters( frameArgs, show )  	-- alternate local language 	if mu.isSet( args.localLang ) then 		lg = lg or require( 'Module:Languages' ) 		args.localLang = args.localLang:lower() 		if lg.lngProps[ args.localLang ] then 			cm.setLanguageParams( args.localLang, page.lang, country ) 		else 			args.localLang = '' 			mu.addMaintenance( 'unknownLanguage' ) 		end 	end  	mu.checkStatus( args ) 	mu.checkStyles( args ) 	-- checking coordinates and converting DMS to decimal coordinates if necessary 	mu.checkCoordinates( args ) 	mu.checkZoom( args ) 	-- remove namespace from category 	mu.checkCommonsCategory( args ) 	mu.checkId( args ) 	for i, param in ipairs( mi.options.parameters ) do 		if mu.isSet( args[ param ] ) then 			mu.addMaintenance( 'parameterUsed', param ) 		end 	end  	args.subtypeAdd = mu.isSet( args.wikidata ) and vp.ParMap.auto 	-- getting features manually entered 	if mu.isSet( args.subtype ) then 		vs = require( 'Module:VCard/Subtypes' ) 		vc.subtypes = checkSubtypes( args, vs.f ) 		if mu.isSet( args.wikidata ) then 			-- y = fetch additional features from Wikidata 			if vc.subtypes.y then 				args.subtypeAdd = true 			-- n = do not show subtypes fetched from Wikidata 			elseif vc.subtypes.n then 				args.subtypeAdd = false 			end 		end 	end  	if type( args.lastedit ) == 'string' and args.lastedit ~= '' 		and not args.lastedit:match( mi.dates.yyyymmdd.p ) then 		mu.addMaintenance( 'wrongDate' ) 		args.lastedit = '' 	end  	-- check Google Maps customer id 	if type( args.googlemaps ) == 'string' and mu.isSet( args.googlemaps ) and 		not args.googlemaps:match( '^[1-9]%d+$' ) then 		mu.addMaintenance( 'wrongGoogleCid' ) 		args.googlemaps = '' 	end 	if type( args.googlemaps ) == 'string' and mu.isSet( args.googlemaps ) then 		mu.addMaintenance( 'parameterUsed', 'google-maps' ) 		if mu.isSet( args.wikidata ) then 			mu.addMaintenance( 'wdWithGoogleCid' ) 		end 	end  	return args, entity, show, country end  local function getQuantity( value, formatter, page ) 	local a, f, u, unit, unitId 	if type( value ) == 'number' then 		return tostring( value ) 	elseif value.amount == '0' then 		return '0' 	else 		a = mu.formatNumber( value.amount ) 		u = '' 		unitId = value.unit 		unit = cm.getCurrency( unitId ) 		if mu.isSet( unit ) then 			if unit.mul then 				a = mu.formatNumber( string.format( '%.2f', -- 2 decimal places 					tonumber( value.amount ) * unit.mul ) ) 			end 			if mi.noCurrencyConversion.all or mi.noCurrencyConversion[ unit.iso ] then 				unit = mu.makeSpan( cm.getCurrencyFormatter( unitId ), 					'voy-currency voy-currency-' .. unit.iso:lower() ) 			else 				er = er or require( 'Module:Exchange rate' ) 				unit = er.getWrapper( a, unit.iso, '', 2, cm.getCurrencyFormatter ) 				mu.addMaintenance( 'currencyTooltip' ) 			end 		else 			unit = vq.labels[ unitId ] 		end 		if unit and unit:find( '%s', 1, true ) then 			a = mw.ustring.format( unit, a ) 		elseif unit then 			u = unit 		elseif mw.wikibase.isValidEntityId( unitId ) then 			-- currency code 			u = wu.getValue( unitId, mi.properties.iso4217 ) 			if u == '' then 				-- unit symbol 				u = wu.getValuesByLang( unitId, mi.properties.unitSymbol, 1, 					page.lang ) 				u = u[ 1 ] or '' 			end 			if u ~= '' then 				mu.addMaintenance( 'unitFromWD' ) 			else 				u = unitId 				mu.addMaintenance( 'unknownUnit' ) 			end 		end 		if a ~= '' and u ~= '' and formatter ~= '' and 			formatter:find( '$1', 1, true ) and formatter:find( '$2', 1, true ) then 			a = mw.ustring.gsub( f, '($1)', a ) 			a = mw.ustring.gsub( a, '($2)', u ) 		else 			a = ( u ~= '' ) and a .. '&nbsp;' .. u or a 		end 	end 	return a end  local function getHourModules() 	if not hr then 		hi = require( 'Module:Hours/i18n' ) 		hr = require( 'Module:Hours' ) 	end end  local function getLabel( id ) 	local label = id 	local tables = { vq.labels } 	if hi then 		table.insert( tables, hi.dateIds ) 	end 	if type( id ) == 'string' and id:match( '^Q%d+$' ) then 		for i, tab in ipairs( tables ) do 			if type( tab[ id ] ) == 'string' then 				label = tab[ id ] 				break 			end 		end 		if label == '' then 			return label 		elseif label == id then 			label = mu.getTypeLabel( id ) 		end 		if label == '' or label == id then 			label = wu.getLabel( id ) or '' 			if label == '' then 				mu.addMaintenance( 'unknownLabel' ) 			else 				mu.addMaintenance( 'labelFromWD' ) 			end 		end 	end 	return label end  local function removeStringDuplicates( ar ) 	local hash = {} 	local result = {} 	local val  	for i = 1, #ar do 		val = ar[ i ] 		if not hash[ val ] then 			table.insert( result, val ) 			hash[ val ] = 1 		end 	end 	return result end  -- getting comments for contacts and prizes from Wikidata using tables local function getComments( statement, properties, args, page ) 	local comments = {} 	local isMobilephone = false 	local minAge, maxAge 	for i, property in ipairs( properties ) do 		local pType = property .. '-type' 		if statement[ property ] then 			if property == mi.properties.minimumAge then 				minAge = getQuantity( statement[ property ][ 1 ], '', page ) 			elseif property == mi.properties.maximumAge then 				maxAge = getQuantity( statement[ property ][ 1 ], '', page ) 			end  			for j, id in ipairs( statement[ property ] ) do 				if statement[ pType ] == 'monolingualtext' then 					id = id.text 				elseif statement[ pType ] == 'time' then 					-- getting last date in case of price/fees 					id = wu.getDateFromTime( id ) 					if not mu.isSet( args.asOf ) or args.asOf < id then 						args.asOf = id 					end 					id = '' 				elseif type( id ) == 'table' then 					id = '' 				end 				if id == mi.qualifiers.mobilePhone then 					isMobilephone = true 				else 					mu.tableInsert( comments, getLabel( id ) ) 				end 			end 		end 	end 	comments = removeStringDuplicates( comments )  	if minAge and maxAge then 		mu.tableInsert( comments, mw.ustring.format( mi.texts.fromTo, 			minAge:gsub( '(%d+).*', '%1' ), maxAge ) ) 	elseif minAge then 		mu.tableInsert( comments, mw.ustring.format( mi.texts.from, minAge ) ) 	elseif maxAge then 		mu.tableInsert( comments, mw.ustring.format( mi.texts.to, maxAge ) ) 	end  	if #comments > 0 then 		return table.concat( comments, mu.commaSeparator ), isMobilephone 	else 		return '', isMobilephone 	end end  local function hasValue( tab, val ) 	for i = 1, #tab do 		if tab[ i ] == val then 			return true 		end 	end 	return false end  local function getLngProperty( lng, p ) 	if not mu.isSet( lng ) then 		return '' 	end  	lg = lg or require( 'Module:Languages' ) 	local item = lg.lngProps[ lng ] 	if not item then 		local hyphen = lng:find( '-', 1, true ) 		if hyphen and hyphen > 1 then 			item = lg.lngProps[ lng:sub( 1, hyphen - 1 ) ] 		end 	end 	if item then 		item = item[ p ] 	end  	return item or ( p == 'c' and 0 or '' ) end  local function removeTableDuplicates( ar ) 	local hash = {} 	local result = {} 	local hashVal  	for i, tab in ipairs( ar ) do 		hashVal = tab.value .. '#' .. tab.comment 		if not hash[ hashVal ] then 			table.insert( result, tab ) 			hash[ hashVal ] = 1 		end 	end 	return result end  local function mergeComments( ar ) 	if #ar > 1 then 		for i = #ar, 2, -1 do 			for j = 1, i - 1, 1 do 				if ar[ i ].value == ar[ j ].value and ar[ i ].comment ~= '' 					and ar[ j ].comment ~= '' then 					ar[ j ].comment = ar[ j ].comment .. '; ' .. ar[ i ].comment 					table.remove( ar, i ) 					break 				end 			end 		end 	end end  local function convertTableWithComment( ar ) 	for i = 1, #ar, 1 do 		if ar[ i ].comment == '' then 			ar[ i ] = ar[ i ].value 		else 			ar[ i ] = ar[ i ].value .. mu.parentheses( ar[ i ].comment ) 		end 	end end  --[[ properties are defined in Module:vCard/Params  p property or set of properties f formatter string c maximum count of results, default = 1 m concat mode (if c > 1), default concat with ', ' v value type, 	empty: string value (i.e. default type), 	id:    string value of an id like Q1234567   	idl:   string value of the label of an id like Q1234567 	il:    language-dependent string value 	iq:    string value with qualifier ids 	au:    quantity consisting of amount and unit 	pau:   quantity consisting of amount (for P8733) 	vq:    string or table value with qualifiers ids and references l = lang: language dependent     wiki / local: monolingual text by wiki or local language le = true: use date for lastedit parameter --]]  -- function returns an array in any case local function getWikidataValues( args, propDef, entity, page, country ) 	local r = '' 	local ar = {} 	local a, i, isMobilephone, item, id, langs, q, t, u, w  	-- setting defaults 	propDef.v = propDef.v or '' 	propDef.f = propDef.f or '' 	propDef.c = propDef.c or 1  	-- getting value arrays 	if propDef.l == 'wiki' then 		ar = wu.getValuesByLang( entity, propDef.p, propDef.c, page.lang ) 	elseif propDef.l == 'local' then 		ar = wu.getValuesByLang( entity, propDef.p, propDef.c, country.lang ) 	elseif propDef.l == 'lang' and propDef.c == 1 then 		id = getLngProperty( country.lang, 'q' ) 		if id == '' then 			country.unknownLanguage = true 		else 			-- using language of work or name ( page.lang, mi.langs, country.lang ) 			a = wu.getValuesByQualifier( entity, propDef.p, mi.properties.languageOfName, id ) 			if next( a ) then 				langs = mu.getLangTable( page.lang, country.lang ) 				for i, lang in ipairs( langs ) do 					item = a[ getLngProperty( lang, 'q' ) ] 					if item then 						break 					end 				end 				ar = { item or a[ next( a, nil ) ] } -- fallback: first item 			end 		end 	elseif propDef.v == 'iq' or propDef.v == 'iqp' then 		q = propDef.v == 'iq' and mi.propTable.quantity or 			mi.propTable.policyComments 		ar = wu.getValuesWithQualifiers( entity, propDef.p, propDef.q, q, 			{ mi.properties.retrieved }, propDef.c ) 		if propDef.le then 			args.lastedit = wu.getLastedit( args.lastedit, ar ) 		end 	elseif propDef.v == 'au' or propDef.v == 'vq' then 		q = propDef.v == 'au' and mi.propTable.feeComments or 			mi.propTable.contactComments 		ar = wu.getValuesWithQualifiers( entity, propDef.p, nil, q, 			{ mi.properties.retrieved }, propDef.c ) 		-- maybe a change of nil to a properties table is useful 		if propDef.le then 			args.lastedit = wu.getLastedit( args.lastedit, ar ) 		end 	else 		ar = wu.getValues( entity, propDef.p, propDef.c ) 	end 	if #ar == 0 and propDef.p ~= mi.properties.instanceOf then 		return ar 	end  	for i = #ar, 1, -1 do 		-- amount with unit (for fees) 		if propDef.v == 'au' then 			a = getQuantity( ar[ i ].value, propDef.f, page ) 			if a == '0' then 				a = vq.labels.gratis 			end 			u, isMobilephone = getComments( ar[ i ], mi.propTable.feeComments, args, page ) 			ar[ i ] = { value = a, comment = u }  		-- for number of rooms P8733 		elseif propDef.v == 'pau' then 			if ar[ i ].unit == '1' then 				a = tonumber( ar[ i ].amount ) or 0 			else 				a = 0 			end 			ar[ i ] = {} 			ar[ i ][ mi.properties.quantity ] = { a } 			ar[ i ][ mi.properties.quantity .. '-type' ] = 'quantity' 			ar[ i ].value = mi.qualifiers.roomNumber 			ar[ i ]['value-type'] = 'wikibase-entityid'  		-- qualifier ids (for subtypes) 		elseif propDef.v == 'iq' or propDef.v == 'iqp' then 			if ar[ i ][ 'value-type' ] ~= 'wikibase-entityid' then 				table.remove( ar, i ) 			end 			if propDef.v == 'iqp' then 				ar[ i ].policyComment, isMobilephone = 					getComments( ar[ i ], mi.propTable.policyComments, args, page ) 			end  		-- strings with qualifiers (for contacts) 		elseif propDef.v == 'vq' then 			if ar[ i ][ 'value-type' ] ~= 'string' then 				table.remove( ar, i ) 			else 				u, isMobilephone = 					getComments( ar[ i ], mi.propTable.contactComments, args, page ) 				if vi.options.useMobile and propDef.t then 					if ( isMobilephone and propDef.t == 'mobile' ) or 						( not isMobilephone and propDef.t == 'landline' ) then 						ar[ i ] = { value = ar[ i ].value, comment = u } 					else 						table.remove( ar, i ) 					end 				else 					ar[ i ] = { value = ar[ i ].value, comment = u } 				end 			end  		-- value, monolingual text, identifier 		else 			if propDef.v == 'id' then 				ar[ i ] = ar[ i ].id 			elseif propDef.v == 'idl' then 				getHourModules() 				ar[ i ] = hr.formatTime( getLabel( ar[ i ].id ) ) 			end 			if ar[ i ] ~= '' and propDef.f ~= '' then 				ar[ i ] = mw.ustring.format( propDef.f, ar[ i ] ) 			end 		end 		if propDef.v == 'au' or propDef.v == 'vq' then 			if ar[ i ] and ar[ i ].value == '' then 				table.remove( ar, i ) 			end 		else 			if ar[ i ] == '' then 				table.remove( ar, i ) 			end 		end 	end  	-- cleanup 	if propDef.v == 'au' or propDef.v == 'vq' then 		ar = removeTableDuplicates( ar ) 		mergeComments( ar ) 		convertTableWithComment( ar ) 	else 		ar = removeStringDuplicates( ar ) 	end  	return ar end  local function getWikidataItem( args, parWDitem, entity, page, country ) 	local arr = {}  	local function singleProperty( propDef ) 		if #arr == 0 then 			arr = getWikidataValues( args, propDef, entity, page, country ) 		else 			for i, value in ipairs( getWikidataValues( args, propDef, entity, page, country ) ) do 				table.insert( arr, value ) -- copy to arr 			end 		end 	end  	local p = parWDitem 	if not p then 		return '' 	end  	p.c = p.c or 1 -- count 	local tp = type( p.p ) 	if tp == 'string' then 		singleProperty( p ) 	elseif tp == 'table' then 		for i, sngl in ipairs( p.p ) do 			if type( sngl ) == 'table' then 				singleProperty( sngl ) 				if p.c == 1 and #arr > 0 then 					break 				end 			end 		end 	end  	if #arr > p.c then 		for i = #arr, p.c + 1, -1 do -- delete supernumerary values 			table.remove( arr, i ) 		end 	end 	if p.m == 'no' then 		return arr 	else 		return table.concat( arr, p.m or mu.commaSeparator ) 	end end  local function getAddressesFromWikidata( args, page, country, entity ) 	local addresses = {} 	local t, u, w, weight  	-- getting addresses from Wikidata but only if necessary 	if args.address == true or type( args.addressLocal ) == 'boolean' then 		-- P6375: address 		addresses = wu.getMonolingualValues( entity, mi.properties.streetAddress ) 		if next( addresses ) then -- sometimes addresses contain <br> tag(s) 			for key, value in pairs( addresses ) do 				addresses[ key ] = value:gsub( '</*br%s*/*>', mi.texts.space ) 			end 		else 			return 		end 	else 		return 	end  	if args.address == true then 		args.address = addresses[ page.lang ] 		-- select address if the same writing system is used 		if not args.address then 			weight = -1 			u = getLngProperty( page.lang, 'w' ) -- writing entity id 			for key, value in pairs( addresses ) do 				-- same writing entity id as page.lang 				w = getLngProperty( key, 'w' ) 				if w == '' then 					country.unknownPropertyLanguage = true 				else 					if key and w == u then -- same writing entity id 						w = getLngProperty( key, 'c' ) -- getting language weight 						if w > weight then -- compare language weight 							args.address = value 							args.addressLang = key 							weight = w 						end 					end 				end 			end 		end 		if not args.address then 			for i, lng in ipairs( mi.langs ) do 				if addresses[ lng ] then 					args.address = addresses[ lng ] 					args.addressLang = lng 					break 				end 			end 		end 		if not args.address then 			args.address = '' 			args.addressLang = '' 		end 		vc.fromWD.address = args.address ~= '' 	end  	-- removing county name from the end of address 	-- same with county name in county language and English 	if type( args.address ) == 'string' then 		args.address = mw.ustring.gsub( args.address, 			'[.,;]*%s*' .. country.country .. '$', '' ) 	end  	t = true 	for i, lng in ipairs( mi.langs ) do 		if country.lang == lng then 			t = false 		end 	end 	-- keeping local address in any case for html data 	args.addAddressLocal = addresses[ country.lang ] or '' 	if t and args.addressLocal == true 		and country.lang ~= page.lang then 		if country.lang ~= '' then 			args.addressLocal = addresses[ country.lang ] or '' 		else 			-- unknown language, maybe missing in Module:Languages 			args.addressLocal = addresses.unknown or '' 		end 		vc.fromWD.addressLocal = args.addressLocal ~= '' 	end end  local function getDataFromWikidata( args, page, country, entity ) 	if args.wikidata == '' then 		return 	end  	mu.getTypeFromWikidata( args, entity )  	-- prevent local data if wiki language == country language 	if page.lang == country.lang then 		for i, value in ipairs( vp.localData ) do 			if type( args[ value ] ) == 'boolean' then 				args[ value ] = '' 			end 		end 	end  	mu.getNamesFromWikidata( args, vc.fromWD, page, country, entity )  	getAddressesFromWikidata( args, page, country, entity ) 	if args.hours == true then 		local lastEdit 		getHourModules() 		args.hours, lastEdit = hr.getHoursFromWikidata( entity, page.lang, 			mi.langs[ 1 ] or '', mi.maintenance.properties, nil, args.lastedit, 			vq.labels, { typeTable = mu.types, idTable = mu.typeIds } ) 		vc.fromWD.hours = args.hours ~= '' 		if vi.options.lasteditHours then 			args.lastedit = lastEdit 		end 	end  	for key, value in pairs( vp.ParWD ) do 		if args[ key ] == true then 			args[ key ] = 				getWikidataItem( args, vp.ParWD[ key ], entity, page, country ) 			vc.fromWD[ key ] = args[ key ] ~= '' 		end 	end  	mu.getArticleLink( args, entity, page ) 	mu.getCommonsCategory( args, entity ) 	mu.getCoordinatesFromWikidata( args, vc.fromWD, entity ) end  local function finalParametersCheck( args, show, page, country, defaultType, entity ) 	-- remove boolean values from parameters to have only strings 	for key, value in pairs( args ) do 		if type( args[ key ] ) == 'boolean' then 			args[ key ] = '' 		end 	end  	-- create givenName, displayName tables 	mu.prepareNames( args )  	-- analysing addressLocal vs address 	if args.addressLang and args.addressLang == country.lang then 		args.addressLocal = '' 		args.addAddressLocal = '' 	end 	if args.addressLocal ~= '' and args.address == '' then 		args.address = 			mu.languageSpan( args.addressLocal, mi.texts.hintAddress, page, country ) 		args.addressLocal = '' 		args.addAddressLocal = '' 		vc.fromWD.address = vc.fromWD.addressLocal 	end  	show.noCoord = args.lat == '' or args.long == '' 	if show.noCoord then 		show.coord = nil 		show.poi   = nil 		mu.addMaintenance( 'missingCoordVc' ) 	end  	-- getting Marker type, group, and color 	if not mu.isSet( args.type ) and mu.isSet( defaultType ) then 		args.type = defaultType 	end 	mu.checkTypeAndGroup( args )  	-- image check 	if not vc.fromWD.image or mi.options.WDmediaCheck then  		mu.checkImage( args, entity ) 	end  	mu.checkUrl( args )  	args.commonscat = args.commonscat:gsub( ' ', '_' )  	-- add final period if not yet exists 	if mu.isSet( args.description ) then 		if mw.ustring.match( args.description, '[%w_€$]$' ) then 			args.description = args.description .. mi.texts.period 		end 		if mw.ustring.len( args.description ) > mi.options.contentLimit and 			mi.options.groupsWithLimit[ args.group ] then 			args.description = mw.ustring.sub( args.description, 1, mi.options.contentLimit ) .. '…' 			mu.addMaintenance( 'contentTooLong' ) 		end 	end end  local function formatText( args, results, key, class ) 	if not mu.isSet( args[ key ] ) then 		return 	end  	local r 	local textKey = key 	local period = mi.texts.period 	if key == 'hours' then 		args[ key ], r = mw.ustring.gsub( args[ key ], 			mi.texts.closedPattern, '' ) 		textKey = ( r > 0 ) and 'closed' or key 	end 	r = mw.ustring.format( mi.texts[ textKey ], args[ key ] ) 	r =	mw.ustring.gsub( r, '^%a', mw.ustring.upper ) 	-- add period if not yet exists 	r = r .. ( mw.ustring.sub( r, -1 ) == period and '' or period )  	table.insert( results, mu.makeSpan( r, class .. addWdClass( key ) ) ) end  local function formatPhone( args, key, country ) 	if not mu.isSet( args[ key ] ) then 		return '' 	end 	 	local class 	local pArgs = { 		phone = args[ key ], 		cc = country.cc, 		isFax = false, 		isTollfree = false, 		format = false 	} 	if vc.fromWD[ key ] then 		pArgs.format = true 		pArgs.size = country.phoneDigits or 2 	end  	if key == 'fax' then 		class = 'p-tel-fax fax listing-fax' .. addWdClass( key ) 		pArgs.isFax = true 	else 		class = 'p-tel tel listing-phone' .. addWdClass( key ) 		if key == 'phone' then 			class = class .. ' listing-landline' 		elseif key == 'tollfree' then 			class = class .. ' listing-tollfree' 			pArgs.isTollfree = true 		elseif key == 'mobile' then 			class = class .. ' listing-mobile' 		end 	end 	return mw.ustring.format( mi.texts[ key ], 		mu.makeSpan( lp.linkPhoneNumbers( pArgs ), class ) ) end  local function formatDate( aDate, aFormat ) 	return mw.getContentLanguage():formatDate( aFormat, aDate, true ) end  local function removePeriods( s ) 	local period = mi.texts.period 	local _period = '%' .. period 	-- closing (span) tags between full stops 	return s:gsub( _period .. '+(</[%l<>/]+>)' .. _period .. '+', '%1' .. period ) 		:gsub( _period .. _period .. '+', period ) end  local function makeMarkerAndName( args, show, page, country, frame ) 	local result = {}  	-- adding status icons 	mu.tableInsert( result, mu.makeStatusIcons( args ) )  	-- adding POI marker 	if show.poi or mu.isSet( args.copyMarker ) then 		table.insert( result, mu.makeMarkerSymbol( args, show, frame ) ) 	end 	 	mu.makeName( result, args, show, page, country, addWdClass( 'name' ), 		addWdClass( 'nameLocal' ) ) 	if #result > 0 then 		result = { table.concat( result, mi.texts.space ) } 	end  	if args.before ~= '' then 		table.insert( result, 1, mu.makeSpan( args.before, 'listing-before' ) ) 	end  	return table.concat( result, mi.texts.space ) end  local function makeEvent( args, page ) 	local isEvent = false 	local s = {} 	local count = 0 -- counts from-to statements 	local startMonth -- month of start date 	local today = page.langObj:formatDate( 'Y-m-d', 'now', true ) 	local todayYear = today:sub( 1, 4 )  -- yyyy 	local todayMonth = today:sub( 6, 7 ) -- mm 	local lastDate = '' 	local lastYear = '' 	local useYMD -- both dates are yyyy-mm-dd  	local function makePeriod( beginP, endP ) 		if beginP == endP then 			endP = '' 		end 		if mu.isSet( beginP ) and mu.isSet( endP ) then 			count = count + 1 			return mw.ustring.format( mi.texts.fromTo2, beginP, endP ) 		elseif mu.isSet( beginP ) then 			return beginP 		else 			return endP 		end 	end  	local function analyseDate( d, m, y ) 		local success, c, t  		if useYMD then 			success, t = pcall( formatDate, d, mi.dates.yyyymmdd.f ) 			success, c = pcall( formatDate, d, 'Y-m-d' ) 			if success then 				lastDate = c > lastDate and c or lastDate 				d = t 			end 			return d, nil 		end  		if d:match( mi.dates.yyyymmdd.p ) then 			y = d:sub( 1, 4 ) 			d = d:sub( 6 ) 		end 		if mu.isSet( y ) then 			if y:match( mi.dates.yy.p ) then 				y = ( '2000' ):sub( -#y ) .. y 			elseif not y:match( mi.dates.yyyy.p ) then 				y = nil 			end 			lastYear = y > lastYear and y or lastYear 		end 		if mu.isSet( d ) and mu.isSet( m ) and d:match( mi.dates.dd.p ) and 			not m:match( mi.dates.mm.p ) then 			-- try to convert month to number string 			success, t = pcall( formatDate, m, 'm' ) 			if success then 				m = t 			else 				for i = 1, 12, 1 do 					if m == mi.months[ i ] or mw.ustring.match( m, mi.monthAbbr[ i ] ) then 						m = '' .. i 						break 					end 				end 			end 		end 		if mu.isSet( d ) and mu.isSet( m ) and d:match( mi.dates.dd.p ) and 			m:match( mi.dates.mm.p ) then 			d = m:gsub( '%.+$', '' ) .. '-' .. d:gsub( '%.+$', '' ) 			m = nil 		elseif mu.isSet( d ) and not mu.isSet( m ) and d:match( mi.dates.dd.p ) then 			d = ( startMonth or todayMonth ) .. '-' .. d:gsub( '%.+$', '' ) 		end 		if mu.isSet( d ) then 			if d:match( mi.dates.mmdd.p ) then 				startMonth = d:gsub( '%-%d+', '' ) 				m = nil 				c = ( y or todayYear ) .. '-' .. d 				success, t = pcall( formatDate, c, mi.dates.mmdd.f ) 				if success then 					d = t 				end 			elseif d:match( mi.dates.dd.p ) and not mu.isSet( m ) and startMonth then 				c = ( y or todayYear ) .. '-' .. startMonth .. '-' .. d 				success, t = pcall( formatDate, c, mi.dates.mmdd.f ) 				if success then 					d = t 				end 			end 		end 		if mu.isSet( m ) then 			d = ( mu.isSet( d ) and ( d .. mi.texts.space ) or '' ) .. m 		end 		return d, y 	end  	if not mu.groupWithEvents( args.group ) then 		return '' 	end 	-- check if vCard is an event 	for i, param in ipairs( vp.checkEvent ) do 		if mu.isSet( args[ param ] ) then 			isEvent = true 			break 		end 	end 	if not isEvent then 		return '' 	end  	if mu.isSet( args.frequency ) then 		table.insert( s, mu.makeSpan( args.frequency, 'listing-frequency' ) ) 	else 		if args.date:match( mi.dates.yyyymmdd.p ) and 			args.endDate:match( mi.dates.yyyymmdd.p ) then 			useYMD = true 			if args.date > args.endDate then 				args.date, args.endDate = args.endDate, args.date 			end 		end 		args.date, args.year 			= analyseDate( args.date, args.month, args.year ) 		args.endDate, args.endYear 			= analyseDate( args.endDate, args.endMonth, args.endYear )  		local d = {} 		mu.tableInsert( d, makePeriod( args.date, args.endDate ) ) 		mu.tableInsert( d, makePeriod( args.year, args.endYear ) ) 		mu.tableInsert( s, mu.makeSpan( table.concat( d, count > 1 and 			mu.commaSeparator or mi.texts.space ), 'listing-date' ) )  		if ( lastYear ~= '' and lastYear < todayYear ) or  			( lastDate ~= '' and lastDate < today ) then 			mu.addMaintenance( 'outdated' ) 		end 	end 	 	if mu.isSet( args.location ) then 		local locations = mu.textSplit( args.location, ',' ) 		for i, location in ipairs( locations ) do 			if location ~= page.subpageText and location ~= page.text 				and mw.title.new( location, '' ).exists then 				location = mu.makeSpan( '[[' .. location .. ']]', 'listing-location' ) 			end 			table.insert( s, location )	 		end 	end  	s = table.concat( s, mu.commaSeparator ) 	return ( s ~= '' and ': ' or '' ) .. s end  local function makeAddressAndDirections( args, page, country ) 	local r = '' 	local t  	if mu.isSet( args.address ) then 		if mu.isSet( args.addressLang ) then 			t = mw.language.fetchLanguageName( args.addressLang, page.lang ) 			if mu.isSet( t ) then 				t = mw.ustring.format( mi.texts.hintAddress2, t ) 			else 				country.unknownPropertyLanguage = true 				t = nil 			end 		end  		r = mu.commaSeparator .. mu.makeSpan( args.address, 			'p-adr adr listing-address' .. addWdClass( 'address' ), true, 			{ title = t, lang = args.addressLang } ) 	end 	if mi.options.showLocalData and mu.isSet( args.addressLocal ) then 		r = r .. mu.comma .. mu.languageSpan( args.addressLocal, mi.texts.hintAddress, 			page, country, 'listing-address-local' .. addWdClass( 'addressLocal' )	) 	end  	t = {} 	if mu.isSet( args.directions ) then 		table.insert( t, mu.makeSpan( args.directions, 			'listing-directions' .. addWdClass( 'directions' ) ) ) 	end 	if mi.options.showLocalData and mu.isSet( args.directionsLocal ) then 		table.insert( t, mu.languageSpan( args.directionsLocal, 			mi.texts.hintDirections, page, country, 			'listing-directions-local' .. addWdClass( 'directionsLocal' ) ) ) 	end 	if #t == 0 then 		return r 	end 	return r .. mi.texts.space .. mu.makeSpan( mu.parentheses( table.concat( t, mu.comma ), true ), 		'listing-add-address' ) end  local function makeContacts( args, country ) 	local t = {} 	local s = ''  	mu.tableInsert( t, formatPhone( args, 'phone', country ) ) 	mu.tableInsert( t, formatPhone( args, 'tollfree', country ) ) 	mu.tableInsert( t, formatPhone( args, 'mobile', country ) ) 	mu.tableInsert( t, formatPhone( args, 'fax', country ) ) 	if args.email ~= '' then 		local lm = require( 'Module:LinkMail' ) 		s = mu.makeSpan( lm.linkMailSet( { email = args.email, ignoreUnicode = 1 } ), 			'u-email email listing-email' .. addWdClass( 'email' ) ) 		mu.tableInsert( t, mw.ustring.format( mi.texts.email, s ) ) 	end 	if args.skype ~= '' then 		local ls = require( 'Module:LinkSkype' ) 		s = mu.makeSpan( ls.linkSkypeSet( { skype = args.skype } ), 			'listing-skype' .. addWdClass( 'skype' ) ) 		mu.tableInsert( t, mw.ustring.format( mi.texts.skype, s ) ) 	end  	s = table.concat( t, mu.commaSeparator ) 	if s ~= '' then 		-- mi.texts.periodSeparator = '. ' 		s = mi.texts.periodSeparator .. mw.ustring.gsub( s, '^%a', mw.ustring.upper ) 	end 	return s end  -- making subtypes string local function makeFeatures( args, tab, show ) 	vs = vs or require( 'Module:VCard/Subtypes' ) 	local function getSubtypeParams( subtype ) 		local r = vs.f[ subtype ] or mu.getTypeParams( subtype ) 		if not r.n then 			r.n = r.label or subtype 		end 		r.g = r.g or vs.fromTypesGroupNumber 		return r 	end  	vc.fromWD.subtypeAdd = type( args.subtypeAdd ) == 'table' and #args.subtypeAdd > 0  	-- merging subtypeAdd (from Wikidata) to manually entered subtypes 	local unknowWDfeatures = false 	local count, label, p, t 	if vc.fromWD.subtypeAdd then 		-- making translation table from Wikidata ids to feature types 		if not vc.subtypeIds then 			vc.subtypeIds = mu.getAliases( vs.f, 'wd' ) 		end  		-- adding type if Wikidata id (wd.value) is known 		-- indexed array prevents multiple identical types 		for i, wd in ipairs( args.subtypeAdd ) do 			t = vc.subtypeIds[ wd.value ] or mu.idToType( wd.value ) 			if not t then 				-- maybe instance or subclass of wd.value are known 				local p31ids = wu.getIds( wd.value, mi.properties.instanceOf ) 				local p279ids = wu.getIds( wd.value, mi.properties.subclassOf ) 				-- merging both arrays 				for i = 1, #p279ids, 1 do 				    table.insert( p31ids, p279ids[ i ] ) 				end 				for i = 1, #p31ids, 1 do 					t = vc.subtypeIds[ p31ids[ i ] ] or mu.idToType( p31ids[ i ] ) 					if t then 						break 					end 				end 			end 			-- subtype from WD is not known 			if not t and not vs.exclude[ wd.value ] then 				unknowWDfeatures = true  				-- try to add a new subtype Q... to vs.f subtypes table 				label = wu.getLabel( wd.value ) 				if label then 					vs.f[ wd.value ] = { 						n = label, 						wd = wd.value, 						g = vs.fromWDGroupNumber 					} 					t = wd.value 				end 			end 			-- add known subtype 			if t then 				vc.subtypes[ t ] = { 					c = ( wd[ mi.properties.quantity ] 						and wd[ mi.properties.quantity ][ 1 ] ) 						or ( wd[ mi.properties.capacity ] and 						wd[ mi.properties.capacity ][ 1 ] ) or '', 					p = wd.policyComment 				} 			end 		end 	end 	if unknowWDfeatures then 		mu.addMaintenance( 'unknowWDfeatures' ) 	end     if next( vc.subtypes ) == nil and #args.subtypeTable == 0 then         return     end  	-- replace selected subtypes 	for subtype, count in pairs( vc.subtypes ) do 		if vs.convert[ subtype ] then 			if type( count ) == 'table' then 				p = count.p 				count = count.c 			end 			t = vs.convert[ subtype ][ count ] or vs.convert[ subtype ][ 1 ] 			vc.subtypes[ t ] = { p = p } 			vc.subtypes[ subtype ] = nil 		end 	end  	-- make subtypes table sortable 	local s = {}; 	for subtype, count in pairs( vc.subtypes ) do 		if type( count ) == 'table' then 			table.insert( s, { t = subtype, c = count.c, p = count.p } ) 		else 			table.insert( s, { t = subtype, c = count } ) 		end 	end 	-- add subtypes from types table 	if args.subtypeTable then 		for i, subtype in ipairs( args.subtypeTable ) do 			table.insert( s, { t = subtype, c = 1 } ) 		end 	end  	-- sorting subtypes 	-- by subtype group and then alphabetically by name 	table.sort( s, 		function( a, b ) 			local at = getSubtypeParams( a.t ) 			local bt = getSubtypeParams( b.t ) 			local na = mu.convertForSort( at.n ) 			local nb = mu.convertForSort( bt.n ) 			return ( at.g < bt.g ) or ( at.g == bt.g and na < nb ) 		end 	)  	-- make text and data output 	local data = {} -- for data-subtype attribute in wrapper tag 	if #s > 0 then 		local r = {}; 		local subtype, f, u, u_n, v 		for i = 1, #s do 			subtype = s[ i ]  			-- for data-subtype="..." in wrapper tag 			u = subtype.t .. ',' .. tostring( i ) 			if type( subtype.c ) == 'number' and subtype.c > 1 then 				u = u .. ',' .. subtype.c 			end 			table.insert( data, u )  			u = getSubtypeParams( subtype.t ) 			if u.g >= vs.firstGroup then 				u_n = u.n 				if not mu.isSet( u_n ) then 					u_n = subtype.t  				end 				u_n = u_n:gsub( '[,;/].*$', '' ) 				count = ( type( subtype.c ) == 'number' ) and subtype.c or 1 				if count > 1 and u_n:find( '%[[^%[%]]*%]' ) then 					v = mw.ustring.format( mi.texts.subtypeWithCount, subtype.c, 						u_n:gsub( '%[([^%[%]]*)|([^%[%]]*)%]', '%1' ) 							:gsub( '%[([^%[%]]*)%]', '%1' ) ) 				else 					v = u_n:gsub( '%[([^%[%]]*)|([^%[%]]*)%]', '%2' ) 						:gsub( '%[([^%[%]]*)%]', '' ) 				end 				if mu.isSet( u.t ) then -- string tooltip 					v = mw.ustring.format( mi.texts.subtypeSpan, u.t, v ) 				elseif mu.isSet( u.f ) then -- icons 					f = mw.ustring.format( mi.texts.subtypeFile, u.f, v ) 					if u.c then 						f = mw.ustring.rep( f, u.c ) 					end 					v = mw.ustring.format( mi.texts.subtypeAbbr, v, f ) 				end 				-- adding policy comment 				if subtype.p and subtype.p ~= '' then 					v = v .. mu.parentheses( subtype.p ) 				end 			end 			table.insert( r, v ) 		end 		if #r > 0 then 			r = #r == 1 and mw.ustring.format( mi.texts.subtype, r[ 1 ] ) 				or mw.ustring.format( mi.texts.subtypes, table.concat( r, mu.commaSeparator ) ) 			if r ~= '' then 				table.insert( tab, mu.makeSpan( r,  					'listing-subtype' .. addWdClass( 'subtypeAdd' ) ) ) 			end 		end 	end  	-- subtype contains now the value for wrapper tag 	args.subtype = table.concat( data, ';' ) end  local function makePayment( args, results ) 	if not mu.isSet( args.payment ) then 		return 	end  	local t 	local class = 'p-note note listing-payment' 	if type( args.payment ) == 'table' then 		local vr = mw.loadData( 'Module:VCard/Cards') 		for i = #args.payment, 1, -1 do -- remove unknown items 			t = args.payment[ i ] 			if vr.cards[ t ] then 				args.payment[ i ] = vr.cards[ t ] 			else 				table.remove( args.payment, i ) 			end 		end 		class = class .. mu.addWdClass( #args.payment > 0 ) 		args.payment = table.concat( args.payment, mu.commaSeparator ) 	else 		mu.addMaintenance( 'paymentUsed' ) 	end 	formatText( args, results, 'payment', class ) end  local function wrapDescription( args, tab, isInline, addText ) 	if args.description ~= '' then 		table.insert( tab, tostring( mw.html.create( isInline and 'span' or 'div' ) 			:addClass( 'p-note note listing-content' ) 			:wikitext( args.description .. ( addText or '' ) ) ) 		) 	end end  local function makeMetadata( args, page ) 	local outdated = false 	local s, success, u 	local t = args.lastedit 	if t ~='' then 		success, t = pcall( formatDate, t, mi.dates.lastedit.f ) 		if not success then 			mu.addMaintenance( 'wrongDate' ) 			t = '' 		else 			success, s = pcall( formatDate, args.lastedit, 'U' ) -- UNIX seconds 			success, u = pcall( formatDate, mi.texts.expirationPeriod, 'U' ) 			if s < u then 				t = t .. mi.texts.space .. mi.texts.maybeOutdated 				outdated = true 			end 		end 	end  	local tag = mw.html.create( 'span' ) 		:attr( 'class', 'listing-metadata listing-metadata-items' ) 		-- add node to save the parent tag 		:node( mw.html.create( 'span' ) 			:addClass( 'listing-metadata-item listing-lastedit' ) 			:addClass( outdated and 'listing-outdated' or nil ) 			:addClass( t == '' and 'listing-item-dummy' or nil ) 			:wikitext( mw.ustring.format( mi.texts.lastedit, 				t == '' and mi.texts.lasteditNone or t ) ) 		) 	if mu.isSet( args.sectionFrom ) then 		local from = args.sectionFrom:gsub( '_', ' ' ) 		if from ~= page.subpageText and from ~= page.text then 			if mu.isSet( args.wikidata ) then 				from = mw.ustring.format( '%s#' .. mi.texts.anchor, from, args.wikidata ) 			end 			tag:node( mw.html.create( 'span' ) 				:addClass( 'listing-metadata-item listing-toSourcePage' ) 				:wikitext( mw.ustring.format( '[[%s|%s]]', from, mi.texts.editInSource ) ) 			) 		end 	end  	return tostring( tag ) end  -- making description, coordinates, and meta data local function makeDescription( args, show, page, country, entity ) 	local results = {}  	-- inline description 	if show.inlineDescription then 		wrapDescription( args, results, true ) 	end  	-- adding features 	makeFeatures( args, results, show )  	-- practicalities 	formatText( args, results, 'hours', 'p-note note listing-hours' ) 	formatText( args, results, 'checkin', 'listing-checkin' ) 	formatText( args, results, 'checkout', 'listing-checkout' ) 	if mu.isSet( args.asOf ) and mu.isSet( args.price ) then 		local success 		success, args.asOf = pcall( formatDate, args.asOf, mi.dates.asOf.f ) 		args.price = args.price .. mw.ustring.format( mi.texts.asOf, args.asOf ) 	end 	formatText( args, results, 'price', 'p-note note listing-price' ) 	makePayment( args, results )  	-- adding Unesco symbol 	if args.unesco ~= '' and vi.options.showUnesco then 		local uLink, uTitle = require( 'Module:VCard/Unesco' ).getUnescoInfo( country ) 		table.insert( results, mu.addLinkIcon( 'listing-unesco voy-symbol-unesco', 			uLink, uTitle, 'unesco' ) ) 	end  	local noContent = #results == 0  	-- adding DMS coordinates 	if show.coord then 		table.insert( results, mu.dmsCoordinates( args.lat, args.long, 			args.givenName.name, vc.fromWD.lat, country.extra ) ) 	end 	if mi.options.showSisters == 'atEnd' then 		table.insert( results, mu.makeIcons( args, page, country, entity, show, vc.fromWD ) ) 	end  	local description 	local space = mi.texts.space 	-- adding description in block mode 	if args.description ~= '' and not show.inlineDescription then 		-- last edit will be inserted at the end of the div tag 		wrapDescription( args, results, false, makeMetadata( args, page ) ) 		noContent = false 		description = table.concat( results, space ) 		if description ~= '' then 			description = space .. description 		end 	-- adding description in inline mode 	else 		description = table.concat( results, space ) 		if description ~= '' then 			description = space .. description 		end 	end  	return removePeriods( description ), noContent end  -- vCard main function function vc.vCard( frame ) 	mu.initMaintenance() 	local page = mu.getPageData()  	-- getting location (vCard/listing) entity, show options and country data 	local args, vcEntity, show, country = initialParametersCheck( frame, page )  	-- associated Wikivoyage page of the location in current Wikivoyage branch 	-- possibly modified by mu.getArticleLink() 	args.wikiPage = ''  	-- getting data from Wikidata 	getDataFromWikidata( args, page, country, vcEntity )  	-- final check 	local defaultType = frame.args.type 	finalParametersCheck( args, show, page, country, defaultType, vcEntity )  	-- making output 	-- leading part for marker mode: only location names and comment  	-- saving address 	args.addressOrig = args.address  	-- creating text parts 	-- leading part (marker and names) 	local leading = makeMarkerAndName( args, show, page, country, frame ) 		.. makeEvent( args, page )  	-- additional parts for vCard mode 	local contacts = '' -- all contacts  	-- get address and directions 	local address = makeAddressAndDirections( args, page, country )  	-- get all contact information 	local contacts = makeContacts( args, country ) 	contacts = removePeriods( address .. contacts ) 	 	-- making description, coordinates, and meta data 	local description, noContent = 		makeDescription( args, show, page, country, vcEntity )  	local r = leading 	local icons = '' 	if contacts == '' and noContent then 		show.inline = true 		r = r .. mu.makeIcons( args, page, country, vcEntity, show, vc.fromWD ) .. description 	else 		if type( mi.options.showSisters ) == 'boolean' then 			-- could also be 'atEnd', then part of body 			icons = mu.makeIcons( args, page, country, vcEntity, show, vc.fromWD ) 		end 		-- mi.texts.periodSeparator = '. ' 		r = removePeriods( r .. contacts .. icons .. 			( show.noperiod and '' or mi.texts.periodSeparator ) .. description ) 			:gsub( '%)(</span>)%s*(<span [^>]*>)%s*%(', '%1; %2' ) 	end 	-- prevents line break before punctuation mark 	r = r:gsub( '</span>([,;.:!?])', '%1</span>' ) 		-- remove space(s) before punctuation marks 		:gsub( '%s+(<span [^>]*>[,;.:!?])', '%1' ) 	if show.inlineDescription then 		r = r:gsub( '%s+$', '' ) .. makeMetadata( args, page ) 	end  	-- error handling and maintenance, not in template and module namespaces 	if country.unknownLanguage then 		mu.addMaintenance( 'unknownLanguage' ) 	end 	if country.unknownPropertyLanguage then 		mu.addMaintenance( 'unknownPropertyLanguage' ) 	end 	r = r .. mu.makeMaintenance( page, { wu, mu, cm, hr } )  	-- wrapping tag 	args.address = args.addressOrig 	args.template = 'vCard' 	return mu.makeWrapper( r, args, page, country, show, vp.vcardData, frame ) end  return vc