Tài liệu mô đun[tạo]
-- module import
local vt = require( 'Modul:VCard/Types' ) -- types to groups like drink, eat, go, see, sleep, ...
local vs = require( 'Modul:VCard/Subtypes' )
local vr = require( 'Modul:VCard/Cards' ) -- credit-card types, not yet used
local vd = require( 'Modul:VCard/Defaults' ) -- country-specific show defaults

local yn = require( 'Modul:Yesno' )
local cd = require( 'Modul:Coordinates' )
local cm = require( 'Modul:CountryData' )
local pt = require( 'Modul:Wikidata2/POITypes' ) -- WD types to vCard/Poi types
local lp = require( 'Modul:LinkPhone' )
local lm = require( 'Modul:LinkMail' )
local ls = require( 'Modul:LinkSkype' )
local ma = require( 'Modul:Map' )
local mc = require( 'Modul:Marker/Colors' )
local un = require( 'Modul:Unesco' )

local vc = {}

local coordURL = 'https://de.wikivoyage.org/w/index.php?title=Special%3AMapsources&params='
local decimalPoint = ','

local texts = {
	['latitude']  = 'Breitengrad',
	['longitude'] = 'Längengrad',
	['phone']     = '<abbr title="Telefon">Tel.</abbr>:',
	['tollfree']  = '<abbr title="Telefon">Tel.</abbr> gebührenfrei:',
	['mobile']    = 'Mobil:',
	['fax']       = 'Fax:',
	['skype']     = 'Skype:',
	['email']     = 'E-Mail:',
	['hours']     = 'Geöffnet:',
	['checkin']   = 'Check-in:',
	['checkout']  = 'Check-out:',
	['price']     = 'Preis:',
	['credit-cards'] = 'Akzeptierte Kreditkarten:',
	['lastedit']  = 'letzte Änderung:',
	['nocredit']  = 'Keine Kreditkarte',
	['subtype']   = 'Merkmale:',
	['markerTip'] = 'Nummer in der Karte&#10;KLICK: direkt zur Karte',
	['iataLink']  = '[[International Air Transport Association|IATA]]',
	['icaoLink']  = '[[Internationale Zivilluftfahrt-Organisation|ICAO]]',
	['unesco']    = 'Unesco-Welterbe',
	['hintName']       = 'Name in der Landessprache',
	['hintAddress']    = 'Anschrift in der Landessprache',
	['hintDirections'] = 'Lage in der Landessprache'
}

local vcIcons = {
	['commons']   = '[[File:Commons-logo.svg|x16px|text-bottom|link=|Wikimedia Commons – Sammlung von Bildern, Videos und Audiodateien zu dieser Einrichtung]]',
	['facebook']  = '[[File:F_icon.svg|16px|text-bottom|link=|Facebook]]',
	['flickr']    = '[[File:Flickr wordmark.svg|x16px|text-bottom|link=|Flickr]]',
	['google']    = '[[File:Google plus.svg|16px|text-bottom|link=|Google Plus]]',
	['twitter']   = '[[File:Twitter Logo Mini.svg|16px|text-bottom|link=|Twitter]]',
	['wikidata']  = '[[File:Wikidata-logo.svg|16px|middle|link=|Wikidata – Daten zu dieser Einrichtung]]',
	['wikipedia'] = '[[File:Antu wikipedia.svg|16px|text-bottom|link=|Wikipedia-Artikel zu dieser Einrichtung]]',
	['youtube']   = '[[File:Logoyoutube2011favicon.svg|x16px|text-bottom|link=|YouTube]]',
}

local errorMsg = {
	['unknownParam']   = ' Unbekannter Parameter: ',
	['unknownParams']  = ' Unbekannte Parameter: ',
	['maintenance']    = '[[Category:VCard: Unbekannte Parameter]]',
	['missingImg']     = '[[Category:Marker: Datei existiert nicht]] Nicht vorhandenes Bild:',
	['typeError']      = '[[Category:VCard: Unbekannter Typ]] <span class="error" title="Unbekannter Typ">Unbekannter Typ</span>',
	['missingType']    = '[[Category:VCard: Typ fehlt]] <span class="listing-vcardInfo" title="Fehlender Typ">Fehlender Typ</span>',
	['wikidata']       = '[[Category:VCard: Einsatz von Wikidata]]',
	['wrongQualifier'] = '[[Category:VCard: Fehlerhafter Wikidata-Qualifikator]] Fehlerhafter Wikidata-Qualifikator',
	['illegalCtrls']   = '[[Category:VCard: Parameter mit unerlaubten Steuerzeichen]] Parameter mit unerlaubten Steuerzeichen',
	['wrongLat']       = '[[Category:Seiten mit fehlerhaften Auszeichnungen zu Koordinaten]] <span class="error" title="Unbekannter Typ">Fehlerhafte Breite</span>',
	['wrongLong']      = '[[Category:Seiten mit fehlerhaften Auszeichnungen zu Koordinaten]] <span class="error" title="Unbekannter Typ">Fehlerhafte Länge</span>',
	['creditUsed']     = '[[Category:VCard: Kreditkarten spezifiziert]]',
	['groupUsed']      = '[[Category:VCard: Parameter group benutzt]]',
	['contentUsed']    = '[[Category:VCard: Parameter content benutzt]]',
	['unknownCredit']  = '[[Category:VCard: Unbekannte Kreditkarte]]',
	['unknownSubtype'] = '[[Category:VCard: Unbekannter Wert für subtype]]',
	['unknown']        = ' Unbekannt: ',
	['unknownGroup']   = '[[Category:VCard: Unbekannte Gruppe]]',
	['wrongDate']      = '[[Category:VCard: Fehlerhaftes Datum]] <span class="error" title="Fehlerhaftes Datum">Fehlerhaftes Datum</span>',
	['dmsCoordinate']  = '[[Category:VCard: DMS-Koordinate]] <span class="listing-vcardInfo" title="DMS-Koordinate">DMS-Koordinate</span>'
}

-- table of parameters

local ParMap = {
	['address']       = '',
	['address-local'] = '',
	['alt']           = '',
	['auto']          = '',
	['before']        = '',
	['checkin']       = '',
	['checkout']      = '',
	['comment']       = '',
	['content']       = '',
	['credit-cards']  = '',
	['wikidata']      = '',
	['description']   = '',
	['directions']    = '',
	['directions-local'] = '',
	['email']         = '',
	['facebook']      = '',
	['fax']           = '',
	['flickr']        = '',
	['google']        = '',
	['group']         = '',
	['hours']         = '',
	['iata']          = '',
	['icao']          = '',
	['image']         = '',
	['intl-area-code'] = '',
	['lastedit']      = '',
	['lat']           = '',
	['long']          = '',
	['mobile']        = '',
	['name']          = 'Kein Name',
	['name-local']    = '',
	['name-latin']    = '',
	['phone']         = '',
	['price']         = '',
	['show']          = 'poi, inline', -- for now: inline as default
	['skype']         = '',
	['subtype']       = '',
	['tollfree']      = '',
	['twitter']       = '',
	['type']          = '',
	['unesco']        = '',
	['url']           = '',
	['youtube']       = ''
}

-- p property or set of properties
-- f formatter url string
-- c maximum count of results, default = 1
-- m mode (if c > 1), default concat with ', '
-- v value type, default: value, id, au = amount + unit

local ParWD = {
	['address-local'] = { p =  'P969' }, -- qualifiers = P669 / P670 (street)
	['email']    = { p =  'P968', c = 5 },
	['facebook'] = { p = { { p = 'P2013', f = 'https://www.facebook.com/$1' },
					{ p = 'P1997', f = 'https://www.facebook.com/pages/-/$1' } } },
	['fax']      = { p = 'P2900', c = 3 },
	['flickr']   = { p = 'P3267', f = 'https://www.flickr.com/photos/$1' },
	['google']   = { p = 'P2847', f = 'https://plus.google.com/$1' },
	['iata']     = { p =  'P238' },
	['icao']     = { p =  'P239' },
	['image']    = { p =   'P18' },
	['phone']    = { p = 'P1329', c = 5 },
	['price']    = { p = 'P2284', c = 1, v = 'au' }, -- maybe useless
	['skype']    = { p = 'P2893', f = 'skype:$1' },
	['twitter']  = { p = 'P2002', f = 'https://twitter.com/$1' },
	['type']     = { p =   'P31', v = 'id' },
	['unesco']   = { p =  'P757' },
	['url']      = { p =  'P856' },
	['youtube']  = { p = 'P2397', f = 'https://www.youtube.com/channel/$1' }
}

-- Country-specific technical parameters
local country = {
	id = '',
	iso_3166 = '',
	cc = '',
	lang = '',
	langName = '',
	currency = ''
}

local wikiLang = mw.getContentLanguage():getCode()
local wrongQualifier = false
local searchLimit = 4 -- for P31
local wdContent = {}

-- vCard Wikidata entity
local vcEntity = nil -- location entity

-- Splitting comma separated lists
local function split( s, convert )
	local t
	local arr = {}
	if (s == nil) or (s == '') then return arr end

	for t in string.gmatch( s .. ',', '([^,]+)' ) do
		t = mw.text.trim( t )
		if t ~= '' then
			if convert then t = string.gsub( t:lower(), ' ', '_') end
			arr[t] = ''
		end
	end

	return arr
end

local function checkId( id )
	local i = id and id:upper() or ''
	local l
	if i ~= '' then
		if string.match( i, '^[%d]+$') ~= nil then -- only number
			i = 'Q' .. i
		elseif string.match( i, '^Q[%d]+$') == nil then -- Q and number
			wrongQualifier = true
			i = ''
		end
	end
	if i ~= '' then
		vcEntity = mw.wikibase.getEntity( i )
		if vcEntity == nil then
			i = ''
			wrongQualifier = true
		end
	end
	return i
end

local function translateType( aType )
	local t = vt.groups[aType]
	if (t ~= nil) and (t ~= '') then return t else return aType end
end

function vc.getEntWDid( anEntity, p )
	if anEntity == nil then return '' end

	local w = anEntity:getBestStatements( p )
	local i = ''

	if (#w > 0) and (w[1].mainsnak.snaktype == 'value') then
		i = w[1].mainsnak.datavalue.value['id']
	end

	return i
end

function vc.getWDid( id, p )
	return vc.getEntWDid( mw.wikibase.getEntity( id ), p )
end

function vc.getEntWDvalue( anEntity, p )
	if anEntity == nil then return '' end

	local w = anEntity:getBestStatements( p )
	local i = ''

	if (#w > 0) and (w[1].mainsnak.snaktype == 'value') then
		i = w[1].mainsnak.datavalue.value
	end

	return i
end

function vc.getWDvalue( id, p )
	return vc.getEntWDvalue( mw.wikibase.getEntity( id ), p )
end

-- p : property id
-- tp: data value type
-- mx: maximum count
-- f : format
function vc.getWDvalues( p, tp, mx, f )
	local ar = {}
	local r = ''
	local i = 0, j, a, u, q
	local t = vcEntity:getBestStatements( p )

	if #t > 0 then
		repeat
			i = i + 1
			if t[i].mainsnak.snaktype == 'value' then
				table.insert( ar, t[i].mainsnak.datavalue.value) end
		until (i >= #t) or (#ar >= mx)
	end
	
	if #ar > 0 then
		for i = 1, #ar, 1 do
			if tp == '' then
				if (ar[i] ~= '') and (f ~= nil) and (f ~= '') and f:find( '$1' ) then
					ar[i] = mw.ustring.gsub( f, '($1)', ar[i])
				end

			elseif tp == 'id' then
				ar[i] = ar[i].id
				if (ar[i] ~= '') and (f ~= nil) and (f ~= '') and f:find( '$1' ) then
					ar[i] = mw.ustring.gsub( f, '($1)', ar[i])
				end
				if p == 'P31' then
					q = ar[i]
					a = pt[q]
					
					if a == nil then
						j = 0
						repeat
							q = vc.getWDid( q, 'P279' )
							if q ~= '' then a = pt[q] end
							j = j + 1
						until (j >= searchLimit) or (q == '') or (a ~= nil)
					end
					if a == nil then a = 'error' end
					
					ar[i] = a
				end

			elseif tp == 'au' then
				a = ar[i].amount
				a = mw.ustring.gsub( a, '+', '')
				if decimalPoint ~= '.' then
					a = mw.ustring.gsub( a, '%.', decimalPoint) end
				u = ar[i].unit
				u = mw.ustring.gsub( u, 'http://www.wikidata.org/entity/', '' )
				if mw.ustring.match( u, '^Q[%d]+$') ~= nil then
					t = vc.getWDvalue( u, 'P558' ) -- unit
					if t == '' then
						t = vc.getWDvalue( u, 'P498' ) -- curreny code
					end
					if t ~= '' then u = t end
				end
				if (a ~= '') and (u ~= '') then
					if (f ~= nil) and (f ~= '') and f:find( '$1' ) and f:find( '$2' ) then
						a = mw.ustring.gsub( f, '($1)', a)
						a = mw.ustring.gsub( a, '($2)', u)
					else
						a = a .. ' ' .. u
					end
				else
					a = a .. ' ' .. u
				end
				if (a == '') or (a == ' ') then ar[i] = nil else ar[i] = a end
			end
		end
	end
	return ar
end

function vc.getFromWD( parWDitem )
	local r = '', i, p, r
	local arr = {}

	local function singleProperty( pr, f, c, v )
		local a, b
		local cn = c
		if (cn == nil) or (cn == '') then cn = 1 end
		local subArr = vc.getWDvalues( pr, v or '', cn, f )

		if cn > #subArr then cn = #subArr end
		if cn > 0 then
			for i = 1, cn, 1 do table.insert( arr, subArr[i] ) end
		end
	
		return
	end

	p = parWDitem
	if p == nil then return r end
	if p.c == nil then p.c = 1 end -- count

	if type( p.p ) == 'string' then
		singleProperty( p.p, p.f, p.c, p.v )
	elseif type( p.p ) == 'table' then
		for key, value in pairs( p.p ) do
			if type( value ) == 'table' then
				singleProperty( value.p, value.f, value.c, value.v )
			end
		end
	end

	if #arr > p.c then
		for i = p.c + 1, #arr, 1 do arr[i] = nil end
	end
	if #arr > 0 then
		r = table.concat( arr, ', ' )
		-- analyse other concat modes
	else
		r = ''
	end

	return r
end

function vc.formatText( key, class )
	if ParMap[key] == '' then return '' end
	
	local r = ' <span class="' .. class
	if wdContent[key] ~= nil then r = r .. ' wikidata-content' end
	r = r .. '">'
	if texts[key] ~= '' then r = r .. texts[key] .. ' ' end
	r = r .. ParMap[key]
	if not ParMap[key]:find( '%.$' ) then r = r .. '.' end
	return r .. '</span>'
end

function vc.formatPhone( key )
	local function concatCC( tel, cc )
		if cc ~= '' then
			local t = tel
			t = t:gsub( '^00', '+' )
			t = t:gsub( '^%+%+', '+' )
			if t:sub( 1, 1 ) == '+' then return t end
			if (cc ~= '+39') and (cc ~= '+378') then t = t:gsub( '^0', '(0)' ) end
			return cc .. ' ' .. t
		else
			return tel
		end
	end

	local pArgs = {}
	pArgs.cc = ParMap['intl-area-code']
	pArgs.phone = concatCC( ParMap[key], pArgs.cc )
	if pArgs.cc == '' then pArgs.cc = country.cc end
	pArgs.nocc = 'true'
	pArgs.isFax = ''
	local t = texts[key]
	if key == 'fax' then
		t = t .. ' <span class="p-tel-fax fax listing-fax'
		pArgs.isFax = 'true'
	else
		t = t .. ' <span class="p-tel tel listing-phone'
	end
	if wdContent[key] ~= nil then t = t .. ' wikidata-content' end
	return t .. '">' .. lp.linkPhoneNumberSet( pArgs ) .. '</span>'
end

function vc.formatSocial( key, f, url )
	local t = ParMap[key], s
	if t == '' then return '' end
	if not t:find( f ) then t = url .. t end
	s = ' <span class="listing-social-media'
	if wdContent[key] ~= nil then s = s .. ' wikidata-content' end
	return s .. '">[' .. t .. vcIcons[key] .. ']</span>'
end

function vc.languageSpan( s, titleHint )
	local c = country.lang
	if (s == nil) or (s == '') then return '' end
	if (c == '') or (c == wikiLang) then return s end

	-- Right-to-left languages
	local r2l = {
		ar = '',
		dv = '',
		fa = '',
		he = '',
		ms = '',
		ur = '',
	}
	
	local t = '<span lang="' .. c .. '" xml:lang="' .. c .. '"' ..
		' title="' .. titleHint .. ' ' .. country.langName .. '"'
	if r2l[c] ~= nil then t = t .. ' dir="rtl"' end
	t = t .. '>' .. s .. '</span>'
	if r2l[c] ~= nil then t = '&rlm;' .. t .. '&lrm;' end
	return t
end

function vc.formatDate( aDate, aFormat )
	return mw.getContentLanguage():formatDate( aFormat, aDate, true )
end

-- vCard main function

function vc.vCard( frame )
	local r, s, t, u, key, value
	local cntrl = 0
	local args = frame:getParent().args
	args.name = args.name or ''
	args['name-local'] = args['name-local'] or ''
	ParMap['name-local'] = args['name-local']

	-- Array with parameters got from Wikidata. Filled by the script
	local extra = 'type:landmark_globe:earth'
	local wikipedia = ''
	local wmCommons = ''

	ParMap.wikidata = checkId( args.wikidata or '' )
	if args.auto == nil then args.auto = 'n' end
	if args.auto == '' then args.auto = 'y' end
	ParMap.auto = yn( args.auto, false )

	local latDMS  = ''
	local longDMS = ''
	local latMs   = '' -- Map sources parameters
	local longMs  = ''

	-- check if Wikidata

	if ParMap.wikidata == '' then
		ParMap.auto = false
	else
		if args.name == '' then
			args.name = vcEntity:getLabel() or ''
			if args.name == '' then args.name = vcEntity:getLabel( 'en' ) or '' end
			ParMap.name = args.name
			ParMap.linkName = args.name
			wdContent.name = ''
		end
		if (vcEntity.sitelinks ~= nil) and (vcEntity.sitelinks[wikiLang .. 'wiki'] ~= nil) then
			wikipedia = tostring( mw.uri.fullUrl( 'w:'
				.. mw.uri.encode( vcEntity.sitelinks[wikiLang .. 'wiki'].title, 'WIKI' ) ) )
		end
	end

	-- checking for expensive sites and countries and getting country-specific data

	country = cm.getCountryData( vcEntity )
	if country.iso_3166 ~= '' then
		extra = extra .. '_region:' .. country.iso_3166
		t = vd[country.iso_3166]
		if t ~= nil then ParMap.show = t end
	end
	if country.lang ~= '' then
		country.langName =
		frame:preprocess( '{{#LANGUAGE:' .. country.lang .. '|' .. wikiLang .. '}}' )
	end
	
	-- handling show and subtype arrays

	local arrShow = split( ParMap.show, true )
	local addShow = split( args.show, true )
	if (addShow.none ~= nil) or (addShow.coord ~= nil) or (addShow.all ~= nil)
		then arrShow.poi = nil end -- overwriting default
	for key, value in pairs( addShow ) do arrShow[key] = value end
	if (arrShow.none ~= nil) or (arrShow.coord ~= nil) or (arrShow.poi ~= nil)
		then arrShow.all = nil end
	if arrShow.none ~= nil then
		if arrShow.coord ~= nil then arrShow.coord = nil end
		if arrShow.poi ~= nil then arrShow.poi = nil end
	end
	if arrShow.nosubtype ~= nil then
		arrShow.subtype = nil	
	end

	-- force getting data from Wikidata if existent

	if ParMap.auto == true then
		if (args['name-local'] == nil) or (args['name-local'] == '') and
			(wikiLang ~= country.lang) then
			args['name-local'] = true
			ParMap['name-local'] = true
		end
		for key, value in pairs( ParWD ) do
			if (args[key] == nil) or (args[key] == '') then
				args[key] = true
				ParMap[key] = true
			end
		end
	end

	-- getting template parameters

	local wrongP = ''
	-- ParMap['intl-area-code'] is separately handled because country code = '+1'
	-- (USA, Canada) is wrongly detect as true
	ParMap['intl-area-code'] = args['intl-area-code']
	if ParMap['intl-area-code'] == nil then ParMap['intl-area-code'] = '' end
	for key, value in pairs( args ) do
		-- remove breaks and controls
		t = mw.ustring.gsub( value, '<br%s*/*>', ' ' )
		-- don't remove if modified by templates -> look for span and Mediawiki tags
		-- (strip markers)
		if not ( mw.ustring.find( t, '<span' ) or mw.ustring.find( t, '`UNIQ' ) )
			then t = mw.ustring.gsub( t, '%c', ' ' ) end
		if t ~= value then
			cntrl = 1
			value = t
		end

		if (key ~= 'auto') and (key ~= 'show') and (key ~= 'wikidata')
			and (key ~= 'intl-area-code') then
			if ParMap[key] == nil then
				if wrongP == '' then wrongP = '' .. key
				else wrongP = wrongP .. ', ' .. key end
			else
				if value == '' then value = true end
				if value == nil then value = false end
				t = yn( value, nil )
				if t ~= nil then
					if ParMap.wikidata ~= '' then ParMap[key] = t else ParMap[key] = '' end
				else ParMap[key] = value end
				if (type( ParMap[key] ) == 'string') and ParMap[key]:find( '{{' ) then
					ParMap[key] = frame:preprocess( ParMap[key] )
				end
			end
		end
	end
	if wrongP ~= '' then
		if wrongP:find( ',' ) then t = errorMsg.unknownParams
		else t = errorMsg.unknownParam end
		wrongP = '<span class="error">' .. t .. wrongP .. errorMsg.maintenance .. '</span>'
	end

	-- handling linked names like [[article|text]]

	if type(ParMap.name) == 'string' then
		ParMap.linkName = mw.ustring.gsub( ParMap.name, '^(.*)%[%[(.*)%]%](.*)$', '%2' )
		if ParMap.linkName ~= '' then
			local t = mw.ustring.gsub( ParMap.linkName, '^(.*)|(.*)$', '%2' )
			if t ~= '' then ParMap.linkName = t end
			ParMap.linkName = mw.text.trim( ParMap.linkName )
		end
		if ParMap.linkName == '' then ParMap.linkName = ParMap.name end
	end

	-- getting data from Wikidata

	if ParMap.wikidata ~= '' then
		for key, value in pairs( ParMap ) do
			if (key ~= 'auto') and (key ~= 'show') and (key ~= 'wikidata') and (ParMap[key] == true) then
				if key == 'name' then
					ParMap['name'] = vcEntity:getLabel() or ''
					if ParMap['name'] == '' then ParMap['name'] = vcEntity:getLabel( 'en' ) or '' end
					ParMap.linkName = ParMap['name']
					wdContent[key] = ''
				elseif key == 'name-local' then
					ParMap['name-local'] = vcEntity:getLabel( country.lang ) or ''
					if ParMap['name-local'] == ParMap.linkName then ParMap['name-local'] = '' end
					wdContent['name-local'] = ''
				elseif ParMap[key] and (ParWD[key] ~= nil) then
					ParMap[key] = vc.getFromWD( ParWD[key] )
					wdContent[key] = ''
				else ParMap[key] = '' end
			end
			if ParMap[key] == false then ParMap[key] = '' end
		end
		if ParMap['type'] == '' then ParMap['type'] = vc.getFromWD( ParWD['type'] ) end
		if ParMap['type'] == 'airport' then
			if ParMap.iata == '' then ParMap.iata = vc.getFromWD( ParWD.iata ) end
			if ParMap.icao == '' then ParMap.icao = vc.getFromWD( ParWD.icao ) end
		end
		ParMap.unesco = vc.getFromWD( ParWD.unesco )
	end

	-- checking subtypes

	t = ''
	local arrSubtype = split( ParMap.subtype, true )
	for key, value in pairs( arrSubtype ) do
		if vs[key] == nil then
			if t ~= '' then t = t .. ', ' end
			t = t .. key
			value = nil
		end
	end
	if t ~= '' then
		wrongP = wrongP .. errorMsg.unknownSubtype
			.. '<span class="error">' .. errorMsg.unknown .. ' ' .. t .. '</span>'
	end
	
	-- checking and getting coordinates

	local dms = false
	if ParMap.lat ~= '' then
		t = tonumber( ParMap.lat )
		if t == nil then 
			r = cd.toDec( ParMap.lat, 'lat', 6 )
			if r.error > 0 then
				wrongP = wrongP .. errorMsg.wrongLat
				ParMap.lat = ''
			else
				ParMap.lat = r.dec
				dms = true
			end
		else
			if (t < -90) or (t > 90) then
				wrongP = wrongP .. errorMsg.wrongLat
				ParMap.lat = ''
			else ParMap.lat = t end
		end
	end
	if ParMap.long ~= '' then
		t = tonumber( ParMap.long )
		if t == nil then 
			r = cd.toDec( ParMap.long, 'long', 6 )
			if r.error > 0 then
				wrongP = wrongP .. errorMsg.wrongLong
				ParMap.long = ''
			else
				ParMap.long = r.dec
				dms = true
			end
		else
			if (t <= -180) or (t > 180) then
				wrongP = wrongP .. errorMsg.wrongLong
				ParMap.long = ''
			else ParMap.long = t end
		end
	end
	if dms then wrongP = wrongP .. errorMsg.dmsCoordinate end
	if (ParMap.lat == '') or (ParMap.long == '') then
		if ParMap.wikidata ~= '' then
			t = vcEntity:getBestStatements( 'P625' )
			if (#t > 0) and t[1].mainsnak.snaktype == 'value' then
				ParMap.lat = t[1].mainsnak.datavalue.value.latitude
				ParMap.long = t[1].mainsnak.datavalue.value.longitude
				wdContent.lat = ''
			end
		end
	end
	if (ParMap.lat == '') or (ParMap.long == '') then
		arrShow.all = nil
		arrShow.coord = nil
		arrShow.poi = nil
	end

	-- getting vCard type

	t = ParMap.group
	if t ~= '' then
		t = ParMap.group:lower()
		wrongP = wrongP .. errorMsg.groupUsed
		if vt.groups[t] == nil then
			t = ''
			wrongP = wrongP .. errorMsg.unknownGroup
		end
	end
	if t == '' then
		t = ParMap['type']:lower()
		if t == '' then
			t = vt.types.default
			wrongP = wrongP .. errorMsg.missingType
		else
			t = t:gsub( ' ', '_' )
			if vt.groups[t] == nil then t = vt.types[ t ] end
			if t == nil then
				wrongP = wrongP .. errorMsg.typeError
				t = vt.types.default
			end
		end
	end
	ParMap.group = t

	-- generating output

	-- leading part for marker mode: only location names and comment

	ParMap.zoom = 17
	ParMap.imageEx = false
	if ParMap.image ~= '' then
		ParMap.imageEx = frame:callParserFunction( '#ifexist', { 'Media:' .. ParMap.image , 1 , 0 } ) == '1'
		if not ParMap.imageEx then wrongP = wrongP .. ' '
			.. ' <span class="error">' .. errorMsg.missingImg .. ' '
			.. ParMap.image .. '</span>' end
	end

	-- wrapping tag

	r = 'vCard vcard h-card'
	if arrShow.indent ~= nil then r = r .. ' listing-vcard-indent' end
	if arrShow.inline ~= nil then r = '<span class="' .. r .. '">'
		else r = '<div class="' .. r .. '">' end

	-- inserting content

	-- adding invisible metadata

	r = r .. '<span class="listing-metadata listing-techParams">'
		.. '<span class="listing-region">' .. country.iso_3166 .. '</span>'
		.. '<span class="listing-country-code">' .. country.cc .. '</span>'
		.. '<span class="listing-lang">' .. country.lang .. '</span>'
		.. '<span class="listing-wiki-lang">' .. wikiLang .. '</span>'
	if ParMap['type'] ~= '' then
		r = r .. '<span class="listing-type">' .. ParMap['type'] .. '</span>' end
	if ParMap.group ~= '' then
		r = r .. '<span class="listing-group">' .. ParMap.group .. '</span>' end
	if ParMap.wikidata ~= '' then
		r = r .. '<span class="listing-wikidata">' .. ParMap.wikidata .. '</span>'
		t = vc.getEntWDvalue( vcEntity, 'P373' )
		if (t ~= '') then
			t = mw.ustring.gsub( t, ' ', '_' )
			r = r .. '<span class="listing-commonscat">' .. t .. '</span>'
			wmCommons = tostring( mw.uri.fullUrl( 'c:Category:' .. mw.uri.encode( t, 'WIKI' ) ) )
		end
	end
	r = r .. '</span>'
	
	if ParMap.image ~= '' then
		r = r .. '<span class="listing-image">[[File:' .. ParMap.image
			.. '|350x250px|' .. ParMap.name .. ']]</span>'
	end

	if (ParMap.lat ~= '') and (ParMap.long ~= '') then
		r = r .. '<span class="p-geo geo listing-coordinates">'
			.. '<abbr class="p-latitude latitude">' .. ParMap.lat .. '</abbr>'
			.. '<abbr class="p-longitude longitude">' .. ParMap.long .. '</abbr></span>'
			.. frame:callParserFunction{ name = '#coordinates',
			args = { ParMap.lat, ParMap.long, extra, name = ParMap.linkName } }
	end
	if ParMap.before ~= '' then r = r .. ParMap.before .. '&#160;' end

	-- adding POI marker

	if (arrShow.all ~= nil) or (arrShow.poi ~= nil) then
		local mArgs = {}
		local color = mc.getColor( ParMap.group )
		mArgs['type'] = 'maplink'
		mArgs.geotype = 'Point'
		mArgs.title = ParMap.name
		mArgs.text = nil
		mArgs.group = translateType( ParMap.group )
		mArgs['marker-symbol'] = '-number-' .. ParMap.group
		mArgs['marker-color'] = color
		mArgs.latitude = ParMap.lat
		mArgs.longitude = ParMap.long
		mArgs.zoom = ParMap.zoom
		mArgs.show = vt.showAll
		if ParMap.imageEx then mArgs.image = ParMap.image else mArgs.image = nil end

		local tag, geojson, tagArgs = ma._tag( mArgs )
		r = r .. '<span class="plainlinks printNoLink poi listing-map" style="background-color: #' .. color
			.. '; border-color: #' .. color .. ';" title="' .. texts.markerTip .. '">'
			.. frame:extensionTag( tag, geojson, tagArgs ) .. '</span> '
	end
	
	-- adding name and url
	
	if wdContent.name ~= nil then r = r .. '<span class="wikidata-content">' end
	r = r .. '<span id="vCard_' .. mw.uri.anchorEncode( ParMap.linkName )
		.. '" class="p-name p-org fn org listing-name">'
	if ParMap.url ~= '' then
		if ParMap.linkName == ParMap.name then
			r = r .. '[' .. ParMap.url .. ' ' .. ParMap.name .. ']</span>'
		else
			-- article and web links
			r = r .. ParMap.name .. '</span> [' .. ParMap.url .. ']'
		end
	else
		r = r .. ParMap.name .. '</span>'
	end
	if wdContent.name ~= nil then r = r .. '</span>' end

	t = ''
	if ParMap['name-local'] ~= '' then
		t = vc.languageSpan( ParMap['name-local'], texts.hintName )
		if wdContent['name-local'] ~= nil then
			t = '<span class="wikidata-content">' .. t .. '</span>'
		end
	end
	if ParMap['name-latin'] ~= '' then
		if t ~= '' then t = t .. ', ' end
		t = t .. '<span class="listing-name-latin listing-emphasized">' .. ParMap['name-latin'] .. '</span>'
	end
	if ParMap.alt ~= '' then
		if t ~= '' then t = t .. ', ' end
		t = t .. ParMap.alt
	end
	if t ~= '' then
		t = '<span class="p-nickname nickname listing-alt">' .. t .. '</span>'
	end
	if ParMap.comment ~= '' then
		if t ~= '' then t = t .. ', ' end
		t = t .. '<span class="listing-emphasized">' .. ParMap.comment .. '</span>'
	end
	if ParMap.iata ~= '' then
		if t ~= '' then t = t .. ', ' end
		t = t .. texts.iataLink .. ': ' .. ParMap.iata
	end
	if (ParMap.iata == '') and (ParMap.icao ~= '') then
		if t ~= '' then t = t .. ', ' end
		t = t .. texts.icaoLink .. ': ' .. ParMap.icao
	end
	if t ~= '' then r = r .. ' (' .. t .. ')' end
	
	local leading = r
	r = ''

	-- additional part for vCard mode
	
	local period = ''

	if ParMap['address-local'] ~= '' then
		ParMap['address-local'] =
			vc.languageSpan( ParMap['address-local'], texts.hintAddress )
		if wdContent['address-local'] ~= nil then
			ParMap['address-local'] = '<span class="wikidata-content">'
				.. ParMap['address-local'] .. '</span>'
		end
		if ParMap.address == '' then
			ParMap.address = ParMap['address-local']
			ParMap['address-local'] = ''
		end
	end
	if ParMap.address ~= '' then
		r = r .. ', <span class="p-adr adr listing-address">'
			.. '<span class="p-street-address street-address">'
			.. ParMap.address .. '</span></span>'
	end
	if ParMap['address-local'] ~='' then
		r = r .. '<span class="listing-add-info">, <span class="listing-address-local">'
			.. ParMap['address-local'] .. '</span></span>'
	end
	
	t = ''
	if ParMap.directions ~= '' then
		t = '<span class="listing-directions listing-emphasized">'
			.. ParMap.directions .. "</span>"
	end
	s = ''
	if ParMap['directions-local'] ~= '' then
		s = '<span class="listing-add-info">, <span class="listing-directions-local">'
			.. vc.languageSpan( ParMap['directions-local'], texts.hintDirections )
			.. '</span></span>'
	end
	if t ~='' then r = r .. ' (' .. t .. s .. ')'
		else r = r .. s end
	if r ~= '' then r = r .. '. '
		else period = '. ' end

	t = ''
	if ParMap.phone ~= '' then
		t = vc.formatPhone( 'phone' )
	end
	if ParMap.tollfree ~= '' then
		if t ~= '' then t = t .. ', ' end
		t = t .. vc.formatPhone( 'tollfree' )
	end
	if ParMap.mobile ~= '' then
		if t ~= '' then t = t .. ', ' end
		t = t .. vc.formatPhone( 'mobile' )
	end
	if ParMap.fax ~= '' then
		if t ~= '' then t = t .. ', ' end
		t = t .. vc.formatPhone( 'fax' )
	end
	if ParMap.email ~='' then
		if t ~= '' then t = t .. ', ' end
		t = t .. texts.email .. ' <span class="u-email email listing-email'
		if wdContent.email ~= nil then t = t .. ' wikidata-content' end
		t = t .. '">' .. lm.linkMailSet( { email = ParMap.email } ) .. '</span>'
	end
	if ParMap.skype ~= '' then
		if t ~= '' then t = t .. ', ' end
		t = t .. texts.skype .. ' <span class="listing-skype'
		if wdContent.skype ~= nil then t = t .. ' wikidata-content' end
		t = t .. '">' .. ls.linkSkypeSet( { skype = ParMap.skype } ) .. '</span>'
	end
	if t ~= '' then
		t = t .. '.'
		r = r .. t
	end
	
	t = ''
	if wikipedia ~= '' then
		t = ' <span class="listing-sister-icons">['
			.. wikipedia .. ' ' .. vcIcons['wikipedia'] .. ']</span>'
	end
	if wmCommons ~= '' then
		t = t .. ' <span class="listing-sister-icons">['
			.. wmCommons .. ' ' .. vcIcons['commons'] .. ']</span>'
	end
	s = ''
	if ParMap.wikidata ~= '' then
		s = '<span class="listing-sister-icons listing-sister-wikidata">['
			.. tostring( mw.uri.fullUrl( 'd:' .. ParMap.wikidata ) )
			.. ' ' .. vcIcons['wikidata'] .. ']</span>'
		t = t .. s
	end
	
	t = t .. vc.formatSocial( 'facebook', 'facebook.com', 'https://www.facebook.com/' )
	t = t .. vc.formatSocial( 'flickr', 'flickr.com', 'https://www.flickr.com/photos/' )
	t = t .. vc.formatSocial( 'google', 'google.com', 'https://plus.google.com/' )
	t = t .. vc.formatSocial( 'twitter', 'twitter.com', 'https://twitter.com/' )
	t = t .. vc.formatSocial( 'youtube', 'youtube.com', 'https://www.youtube.com/channel/' )

	if t ~='' then
		if t == s then
			-- only Wikidata icon. This is not visible for readers who are not logged in.
			r = r .. mw.ustring.gsub( t, '</span>', '.</span>' )
		else
			r = r .. t .. '.'
		end
	end

	if (ParMap.description ~='') or (ParMap.content ~='') then
		t = ParMap.description
		if ParMap.content ~='' then
			t = t .. ' ' .. ParMap.content
			wrongP = wrongP .. errorMsg.contentUsed
		end
		r = r .. ' <span class="p-note note listing-content">' .. t .. '</span>'
	end

	r = r .. vc.formatText( 'hours', 'p-note note listing-hours' )
	r = r .. vc.formatText( 'checkin', 'listing-checkin' )
	r = r .. vc.formatText( 'checkout', 'listing-checkout' )
	r = r .. vc.formatText( 'price', 'p-note note listing-price' )
	r = r .. vc.formatText( 'credit-cards', 'p-note note listing-credit' )
	if ParMap['credit-cards'] ~= '' then wrongP = wrongP .. errorMsg.creditUsed end


	if arrShow.subtype ~= nil then
		t = ''
		for key, value in pairs( arrSubtype ) do
			s = vs[key]
			if (s ~= nil) and (type( s ) == 'string') then
				t = t .. ' [' .. key .. ']'
			end
		end
		if t ~= '' then r = r .. ' <span class="listing-subtype">'
			.. texts.subtype .. ' ' .. t .. '.</span>' end
	end

	if ParMap.unesco ~= '' then
		r = r .. ' [[File:WV-Unesco-icon-small.svg|18px|link=' .. un.getArticleFromWD()
			.. '|' .. texts.unesco .. ']]'
	end

	if (arrShow.all ~= nil) or (arrShow.coord ~= nil) then
		latDMS, _, latMs = cd.getDMSString( ParMap.lat, 4, 'lat', '', '', 'f1' )
		longDMS, _, longMs = cd.getDMSString( ParMap.long, 4, 'long', '', '', 'f1' )

		r = r .. ' <span class="listing-dms-coordinates printNoLink plainlinks'
		if wdContent.lat ~= nil then r = r .. ' wikidata-content' end
		r = r .. '">([' .. coordURL .. latMs .. '_' .. longMs .. '_'
			.. mw.uri.encode( extra )
			.. '&locname=' .. mw.uri.encode( ParMap.linkName )
			.. ' <span class="coordStyle" title="' .. texts.latitude .. '">'
			.. latDMS .. '</span>'
			.. ' <span class="coordStyle" title="' .. texts.longitude .. '">'
			.. longDMS .. '</span>])</span>'
	end
	if ParMap.lastedit ~='' then
		local success;
		success, t = pcall( vc.formatDate, ParMap.lastedit, 'M Y' )
		if not success then
        	t = errorMsg.wrongDate
        end
		r = r .. ' <span class="listing-metadata">(<span class="listing-metadata-items listing-lastedit">'
			.. texts.lastedit .. ' ' .. t
			.. '</span>)</span>'
	else
		r = r .. '<span class="listing-metadata listing-metadata-items>&nbsp;</span>'
	end

	r = mw.ustring.gsub( r, '(%.%.)', '.' )
	r = mw.ustring.gsub( r, '(%.</span>%.)', '</span>.' )
	r = mw.ustring.gsub( r, '(%.</span></span>%.)', '</span></span>.' )
	
	if r == '' then r = leading else r = leading .. period .. r end

	-- wrapping tag

	if arrShow.inline ~= nil then r = r .. '</span>'
		else r = r .. '</div>' end

	if ParMap.wikidata ~= '' then wrongP = wrongP .. errorMsg.wikidata end
	if wrongQualifier then wrongP = wrongP .. '<span class="error">'
		.. errorMsg.wrongQualifier .. '</span>' end
	if cntrl == 1 then wrongP = wrongP .. '<span class="listing-vcardInfo">'
		.. errorMsg.illegalCtrls .. '</span>' end

	local ns = mw.title.getCurrentTitle().namespace
	if (ns == 0) or (ns == 828) then
		return r .. wrongP
	else return r end
end

return vc