Módulo:VCard
Version check
Designación de la versión en Wikidata: 2024-07-08
Uso
Module used by the template {{VCard}}
Experimental Examples
* {{vCard | wikidata= Q47476}} * {{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 = info@hotel.de | 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 | auto = y | 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 = بوسط البلد }} * {{vCard | wikidata = Q201219 | name = Ägyptisches Museum Kairo | facebook = j | twitter = j }} <!-- 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. }}
- 1 Alhambra, Calle Real de la Alhambra S/N, 18009 Granada. ☎ +34 958 027 971 🕓 diario 8:30 am–6:00 pm.
1 Ein Hotel (j), Hauptstraße 1 (Abzweig von der Nebenstraße). ☎ +49 (0)30 2345 1234, fax: +49 (0)30 2345 9876, +49 (0)176 345 1234, ✉: info@hotel.de, Skype: nutzer.name, nutzer2.name. Das in der Innenstadt gelegene Hotel besitzt großzügige Räume, bietet aber nur ÜF. Características: Wifi, Bar, 89 Habitación, Piscina. 🕓 7/24. Hora de entrada: ab 14 Uhr. Hora de salida: bis 12 Uhr. Formas de pago aceptadas: Visa, Master, AmEx, Maestro. (52° 30′ 52″ N 13° 23′ 23″ O)
- 2 Ägyptisches Museum Kairo (المتحف المصري, al-Matḥaf al-Miṣrī, Egyptian Museum, auch Nationalmuseum), Mīdān et-Taḥrīr, ميدان التحرير (im Stadtzentrum, بوسط البلد). ☎ +20 (0)2 2578 2452, +2 (0)2 2579 6948, ✉: egyptianmuseum@moantiq.gov.eg 🕓 diario 9:00 am–7:00 pm, 9:00 am–4:00 pm (Ramadán). Precio: LE 450 (extranjero), LE 230 (estudiantes extranjeros), LE 30 (Árabes), LE 10 (estudiantes árabes), libre (hasta 5 años; Árabes, desde 61 años); a partir de 12/2023. (30° 2′ 52″ N 31° 14′ 0″ O)
- 3 Ägyptisches Museum Kairo (المتحف المصري), El Tahrir Sq., Downtown, Cairo, ميدان التحرير ، وسط البلد ، القاهرة. ☎ +20 (0)2 2578 2452, +2 (0)2 2579 6948, ✉: egyptianmuseum@moantiq.gov.eg 🕓 diario 9:00 am–7:00 pm, 9:00 am–4:00 pm (Ramadán). Precio: LE 450 (extranjero), LE 230 (estudiantes extranjeros), LE 30 (Árabes), LE 10 (estudiantes árabes), libre (hasta 5 años; Árabes, desde 61 años); a partir de 12/2023. (30° 2′ 52″ N 31° 14′ 0″ O)
- Noch weiterer Text.
- 1 Biblioteca Carnegie de Reims (bibliothèque Carnegie), 2 Place Carnegie, 51100 Reims. ☎ +33 (0)3 26 77 81 41, ✉: carnegie@bm-reims.fr
- 1 Aeropuerto de Bruselas-National (Brussels Airport, IATA: BRU). ☎ +32 (0)900 70 000 (Belga), +32 (0)2 753 77 53 Test Durchhangeln.
- 6 Museo Pushkin (Государственный музей изобразительных искусств имени А. С. Пушкина), улица Волхонка, 12
- 2 aeropuerto de Frankfurt (Flughafen Frankfurt Main, IATA: FRA), Hugo-Eckener-Ring, 60549 Frankfurt am Main. ☎ +49 (0)69 69 00, ✉: info@fraport.de Test Flughafen.
aeropuerto de Frankfurt (Flughafen Frankfurt Main, IATA: FRA), Hugo-Eckener-Ring, 60549 Frankfurt am Main. ☎ +49 (0)69 69 00, ✉: info@fraport.de Test Flughafen mit Ikone.
- 8 Karnak Open Air Museum (المتحف المفتوح بالكرنك) Anzeige englische Wikipedia. (25° 43′ 10″ N 32° 39′ 27″ O)
- 2 Nile Ritz-Carlton (النيل ريتز كارلتون القاهرة), 1113 Corniche El Nil St., 11221 Cairo, ١١١٣ كورنيش النيل ، ١١٢٢١ القاهرة. ☎ +20 (0)2 2577 8899, fax: +20 (0)2 2578 0475, +20 (0)2 2739 4637, ✉: rc.cairz.leads@ritzcarlton.com (dirección), rc.cairz.concierge@ritzcarlton.com (conserje), rc.cairz.restaurants.reservations@ritzcarlton.com (restaurante, bar) Ausgabe Anschriften. Características: ★★★★★, Wifi gratis. Hora de entrada: 2:00 pm. Hora de salida: 12:00 del mediodía. Formas de pago aceptadas: VISA, Mastercard, American Express, UnionPay, Maestro. (30° 2′ 46″ N 31° 13′ 57″ O)
- 3 Steigenberger Hotel El Tahrir Cairo (فندق شتيجنبرجر التحرير القاهرة), 2 Kasr El Nil St., El Tahrir Sq., Cairo, ٢ شارع قصر النيل ، ميدان التحرير ، القاهرة (شرق المتحف المصري). ☎ +20 (0)2 2575 0777, +20 (0)2 3854 2020, +20 16416, fax: +20 (0)2 3854 2029, ✉: info@steigenbergereltahrir.com, reservation@steigenbergertahrir.com, frontoffice.eltahrir@steigenberger.com Test eingeschränkte WD-Ausgabe. (30° 2′ 50″ N 31° 14′ 9″ O)
- 9 Landesmuseum für Vorgeschichte, Richard-Wagner-Straße 9, 06114 Halle (Saale). ☎ +49 (0)345 52 47 30, fax: +49 (0)345 524 73 51, ✉: info@landesmuseum-vorgeschichte.de Uhrzeiten, Kommentare. 🕓 Mar–Vie 9:00 am–5:00 pm; Sab–Dom, Feriado legal 10:00 am–6:00 pm; Cerrado: 24. Dic., 31. Dic. Precio: 7,00 € (adultos), 5,00 € (reducido), libre (niños, 0–18 años), 3,00 € (guía de audio).
- 10 Museo Nacional de Antropología, calle de Alfonso XII, 68, 28014 Madrid. ☎ +34 915 30 64 18, +34 915 39 59 95, fax: +34 914 67 70 98, ✉: antropologico@cultura.gob.es Zusammenfassung Kommentare. Características: no trípode, Sin flash, Animales prohibidos. 🕓 Mar–Sab 9:30 am–8:00 pm; dom 10:00 am–3:00 pm; Cerrado: 1. Ene., 6. Ene., 1. May, 24. Dic., 25. Dic., 31. Dic. Precio: 3 €, libre (menores, personas discapacitadas; personas mayores, desde 66 años).
Parameter show
- Mögliche Werte:
none
,poi
,coord
,all
,nosubtype
,nowdsubtype
,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 alspoi
,coord
undall
.all
ist stärker alspoi
undcoord
.
- Die Ausgabe der Subtypen ist vorbereitet, wird aber zurzeit nicht standardmäßig durchgeführt (
nosubtype
).nosubtype
ist stärker alssubtype
.
- 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.
- 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
Esta documentación está transcluida desde Módulo:VCard/doc.
Los editores pueden experimentar en la zona de pruebas (crear) y en los casos de prueba (crear) del módulo.
Por favor, añade las categorías en la subpágina de documentación. Subpáginas de este módulo.
Los editores pueden experimentar en la zona de pruebas (crear) y en los casos de prueba (crear) del módulo.
Por favor, añade las categorías en la subpágina de documentación. Subpáginas de este módulo.
-- module variable and administration
local vc = {
moduleInterface = {
suite = 'vCard',
serial = '2024-07-08',
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, 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 )
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 .. ' ' .. 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 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 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 )
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 )
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 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 ) )
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
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>' )
if show.inlineDescription then
r = r:gsub( '%s+$', '' ) .. makeMetadata( args )
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 .. makeMaintenance( page )
-- wrapping tag
args.address = args.addressOrig
return mu.makeWrapper( r, args, page, country, show, vp.vcardData, 'vCard', frame )
end
return vc