Svoboda | Graniru | BBC Russia | Golosameriki | Facebook
Saltu al enhavo

Modulo:VCard

El Vikivojaĝo

Versiokontrolo

[redakti]

Versionomo en Vikidatumoj: 2024-07-08 Contra

Ekzempleroj

[redakti]
Map
Mapo de VCard
* {{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|20px|Deutschland]] | image = Berlin Friedrichstraße Galeries Lafayette.jpg | email = [email protected] | comment = y | lastedit = 2023-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 | auto = y | name = Egipta muzeo en 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 = بوسط البلد }}
* {{vCard | wikidata = Q201219 | name = Egipta muzeo en Kairo | facebook = y | twitter = y }} <​!-- Marker-Modus -->
* {{vCard | wikidata = Q1142142 | auto = y | name = Nordiska museet }} Noch weiterer Text.
* {{vCard | wikidata = Q1142142 | auto = y | price = n }}
* {{vCard | wikidata = Q257342 | auto = y }}
* {{vCard | wikidata = Q28934 | auto = y | description = Test Durchhangeln. }}
* {{vCard | wikidata = Q4872 | auto = y }}
* {{vCard | wikidata = Q10697 | auto = y }}
* {{vCard | wikidata = Q12508 | auto = y | description = Test Durchhangeln. }}
* {{vCard | wikidata = Q46033 | description = Test Flughafen. }}
* {{vCard | wikidata = Q46033 | show = symbol | description = Test Flughafen mit Ikone. }}
* {{vCard | wikidata = Q13218762 | auto = y | description = Anzeige englische Wikipedia. }}
* {{vCard | wikidata = Q56506795 | auto = y | description = Ausgabe Anschriften. }}
* {{vCard | wikidata = Q47429618 | address = | address-local = | directions = | phone = | show = nowdsubtype | description = Test eingeschränkte WD-Ausgabe. }}
* {{vCard | wikidata = Q637739 | auto = y | description = Rollstuhl aus WD. }}
* {{vCard | name = Landesmuseum für Vorgeschichte | type = museum | wikidata = Q1332407 | auto = y | description = Uhrzeiten, Kommentare. }}
* {{vCard | name = Museo Nacional de Antropología | type = museum | wikidata = Q2917041 | auto = y | description = Zusammenfassung Kommentare. }}
  • Deutschland 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)
  • . Geöffnet: täglich 9:00–19:00, 9:00–16:00 (Ramadan). Preis: LE 450 (Eksterlandano), LE 230 (Eksterlanda studento), LE 30 (Araba), LE 10 (Araba studento), senkosta (bis 5 Aĝojaro; Araba, ab 61 Aĝojaro); Stand 12/2023. (30° 2′ 52″ N 31° 14′ 0″ O)
  • . Geöffnet: täglich 9:00–19:00, 9:00–16:00 (Ramadan). Preis: LE 450 (Eksterlandano), LE 230 (Eksterlanda studento), LE 30 (Araba), LE 10 (Araba studento), senkosta (bis 5 Aĝojaro; Araba, ab 61 Aĝojaro); Stand 12/2023. (30° 2′ 52″ N 31° 14′ 0″ O)
  • .
  • Noch weiterer Text.
  • .
  • .
  • . Test Durchhangeln.
  • .
  • .
  • . Test Durchhangeln. (29° 58′ 34″ N 31° 7′ 58″ O)
  • . Test Flughafen.
  • Flughaveno Frankfurta (Flughafen Frankfurt Main, ​IATA: FRA), Hugo-Eckener-Ring, 60549 Frankfurt am Main. Tel.: +49 (0)69 69 00, E-Mail: [email protected] . Test Flughafen mit Ikone.
  • . Anzeige englische Wikipedia. (25° 43′ 10″ N 32° 39′ 27″ O)
  • . Ausgabe Anschriften. Merkmale: ★★★★★, freies WLAN. Check-in: 14:00. Check-out: 12:00. Akzeptierte Zahlungsarten: VISA, Mastercard, American Express, UnionPay, Maestro. (30° 2′ 46″ N 31° 13′ 57″ O)
  • . Test eingeschränkte WD-Ausgabe. (30° 2′ 50″ N 31° 14′ 9″ O)
  • . Rollstuhl aus WD. Merkmal: rollstuhlzugänglich.
  • . Uhrzeiten, Kommentare. Geöffnet: Di–Fr 9:00–17:00; Sa–So, Feiertag 10:00–18:00; geschlossen: 24. Dez., 31. Dez. Preis: 7,00 € (Plenkreskulo), 5,00 € (malpliigita kotizo), senkosta (Infano, 0–18 Aĝojaro), 3,00 € (aŭdogvidilo).
  • . Zusammenfassung Kommentare. Merkmale: ohne Blitz, ohne Stativ, Tiere verboten. Geöffnet: Di–Sa 9:30–20:00; So 10:00–15:00; geschlossen: 1. Jan., 6. Jan., 1. Mai, 24. Dez., 25. Dez., 31. Dez. Preis: 3 €, senkosta (Neplenaĝulo, Malkapablo; Maljunuloj, ab 66 Aĝojaro).
  • Modulaj dependecoj

    [redakti]

    VCard

    Marker utilities
    Coordinates
    Coordinates/i18n
    Marker utilities/Groups
    Marker utilities/i18n
    Marker utilities/Maki icons
    Marker utilities/Types
    UrlCheck
    UrlCheck/i18n
    Wikidata utilities
    Marker utilities/i18n
    VCard/Cards
    VCard/i18n
    VCard/Params
    VCard/Qualifiers
    VCard/Subtypes
    VCard/Unesco
    CountryData
    CountryData/Currencies
    CountryData/Geography
    Wikidata utilities
    Exchange rate
    Hours
    Hours/i18n
    Wikidata utilities
    Hours/i18n
    Languages
    LinkMail
    Link utilities
    Link utilities/i18n
    LinkPhone
    Link utilities
    Link utilities/i18n
    Link utilities/Phone numbers
    Yesno
    LinkSkype
    Link utilities
    Link utilities/i18n
    No globals
    Wikidata utilities
    Yesno

    -- module variable and administration
    local vc = {
    	moduleInterface = {
    		suite  = 'vCard',
    		serial = '2024-05-13',
    		item   = 58187507
    	},
    	
    	-- table containing parameters fetched from Wikidata
    	fromWD = {},
    	-- Wikidata to subtype table
    	subtypeIds = nil
    }
    
    -- 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 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, wikidata = 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
    		end
    	end
    
    	return vp.ParMap
    end
    
    local function initialParametersCheck( frame )
    	local country, entity, param, show, t, v, 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
    
    	-- getting country-specific technical parameters
    	country = cm.getCountryData( entity, t, frameArgs.country )
    	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 )
    	show.name = true
    
    	-- copying frameArgs parameters to args = vp.ParMap parameters
    	local args = copyParameters( frameArgs, show )
    	-- checking coordinates and converting DMS to decimal coordinates if necessary
    
    	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 )
    	for i, param in ipairs( mi.options.parameters ) do
    		if mu.isSet( args[ param ] ) then
    			mu.addMaintenance( 'parameterUsed', param )
    		end
    	end
    
    	args.subtypeAdd = not show.nosubtype and not show.nowdsubtype
    
    	if type( args.lastedit ) == 'string' and args.lastedit ~= ''
    		and not args.lastedit:match( mi.dates.yyyymmdd.p ) then
    		mu.addMaintenance( 'wrongDate' )
    		args.lastedit = ''
    	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
    
    -- 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
    
    	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 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
    
    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 args.addressLocal == true 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
    	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 = ''
    	end
    	if args.addressLocal ~= '' and args.address == '' then
    		args.address =
    			mu.languageSpan( args.addressLocal, mi.texts.hintAddress, page, country )
    		args.addressLocal = ''
    		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 makeIcons( args, page, country, entity, show )
    	local name = args.givenName.name
    	local icons = {}
    	local onlyWikidata = mi.options.showSisters and not show.nositelinks and
    		mu.makeSisterIcons( icons, args, page, country, entity )
    	if not show.nosocialmedia then
    		mu.makeSocial( icons, args, vc.fromWD, name )
    	end
    	if #icons > 0 then
    		return ( onlyWikidata and '' or mi.texts.space ) .. table.concat( icons, '' )
    	else
    		return ''
    	end
    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 makeMarkerProperties( args, show )
    	if show.symbol then
    		args.symbol = mu.getMakiIconId( args.typeTable[ 1 ] )
    		if args.symbol then
    			mu.addIconToMarker( args )
    		end
    		if not mu.isSet( args.text ) then
    			args.symbol = ''
    			mu.addMaintenance( 'unknownMAKI' )
    		end
    	end
    	if not mu.isSet( args.symbol ) then
    		args.symbol = '-number-' .. args.group
    	end
    end
    
    local function makeMarkerAndName( args, show, page, country, frame )
    	local result = {}
    
    	if args.before ~= '' then
    		table.insert( result, mu.makeSpan( args.before, 'listing-before' ) )
    	end
    	-- adding status icons
    	mu.tableInsert( result, mu.makeStatusIcons( args ) )
    
    	-- adding POI marker
    	if show.poi or mu.isSet( args.copyMarker ) then
    		makeMarkerProperties( args, show )
    		table.insert( result, mu.makeMarkerSymbol( args, frame ) )
    	end
    	
    	mu.makeName( result, args, show, page, country, addWdClass( 'name' ),
    		addWdClass( 'nameLocal' ) )
    
    	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
    
    -- 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
    
    -- making subtypes string
    local function makeFeatures( args, tab, show )
    	vc.fromWD.subtypeAdd = not show.nowdsubtype and
    		type( args.subtypeAdd ) == 'table' and #args.subtypeAdd > 0
    	if show.nosubtype or not ( vc.fromWD.subtypeAdd or mu.isSet( args.subtype )
    		or #args.subtypeTable > 0 ) then
    		return
    	end
    
    	local vs = 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
    
    	local subtypes = checkSubtypes( args, vs.f )
    
    	-- 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
    				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( subtypes ) == nil and #args.subtypeTable == 0 then
            return
        end
    
    	-- replace selected subtypes
    	for subtype, count in pairs( 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 ]
    			subtypes[ t ] = { p = p }
    			subtypes[ subtype ] = nil
    		end
    	end
    
    	-- make subtypes table sortable
    	local s = {};
    	for subtype, count in pairs( 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 )
    	local text = args.description .. ( addText or '' )
    	if args.description ~= '' then
    		table.insert( tab, tostring( mw.html.create( isInline and 'span' or 'div' )
    			:addClass( 'p-note note listing-content' )
    			:wikitext( text ) )
    		)
    	end
    end
    
    local function makeMetadata( args )
    	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 ) )
    		)
    	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, makeIcons( args, page, country, entity, show ) )
    	end
    
    	local metaData = makeMetadata( args )
    
    	-- adding description in block mode
    	local description
    	local space = mi.texts.space
    	if args.description ~= '' and not show.inlineDescription then
    		-- last edit will be inserted at the end of the div tag
    		wrapDescription( args, results, false, metaData )
    		noContent = false
    		description = table.concat( results, space )
    		if description ~= '' then
    			description = space .. description
    		end
    	else
    		description = table.concat( results, space )
    		if description ~= '' then
    			description = space .. description
    		end
    		description = description .. metaData
    	end
    
    	return removePeriods( description ), noContent
    end
    
    local function makeMaintenance( page )
    	if mi.nsNoMaintenance[ page.namespace ] then
    		return ''
    	end
    	
    	local r = mu.getMaintenance()
    	if mi.options.usePropertyCateg then
    		local m = mi.maintenance.properties -- format string
    		r = r .. wu.getCategories( m ) .. mu.getCategories( m )
    			.. cm.getCategories( m )
    		if hr then
    			r = r .. hr.getCategories( m )
    		end
    	end
    	return r
    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 )
    
    	-- 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 .. makeIcons( args, page, country, vcEntity, show ) .. description
    	else
    		if type( mi.options.showSisters ) == 'boolean' then
    			-- could also be 'atEnd', then part of body
    			icons = makeIcons( args, page, country, vcEntity, show )
    		end
    		-- mi.texts.periodSeparator = '. '
    		r = removePeriods( r .. contacts .. icons ..
    			( show.noperiod and '' or mi.texts.periodSeparator ) .. description )
    			:gsub( '%)(</span>)%s*(<span [^>]*>)%(', '%1; %2' )
    	end
    	-- prevents line break before punctuation mark
    	r = r:gsub( '</span>([,;.:!?])', '%1</span>' )
    
    	-- 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 .. makeMaintenance( page )
    
    	-- wrapping tag
    	args.address = args.addressOrig
    	return mu.makeWrapper( r, args, page, country, show, vp.vcardData, 'vCard', frame )
    end
    
    return vc
    
    Elŝutita el "https://eo.wikivoyage.org/w/index.php?title=Modulo:VCard&oldid=69489"