
![]() | Dieses Modul wird auf vielen Seiten benutzt, und Änderungen werden projektweit sofort wahrgenommen. Bitte teste Änderungen vorher im /Sandkasten oder in deinem Benutzernamensraum. Die getestete Änderung sollte dann in einem einzigen Edit auf dieser Seite eingefügt werden. Bitte diskutiere Änderungen zuerst auf der Diskussionsseite bevor du sie implementierst. |
![]() | Dieses Modul ist halbgeschützt. Es wird sehr häufig verwendet oder substituiert. Änderungen können zu einer großen Serverlast führen, und Vandalismus kann sich auf eine Vielzahl von Artikel auswirken. |
![]() | Dieses Modul benutzt eine oder mehrere Wikidata-Eigenschaften. |
Anwendung
Das Modul wird direkt von der Vorlage {{vCard}} aufgerufen. Parameterbeschreibung siehe dort. Im Projektnamensraum befindet sich die technische Dokumentation Wikivoyage:VCard.
Versionsbezeichnung auf Wikidata: 2025-04-23
Benötigte weitere Module
Dieses Modul benötigt folgende weitere Module: CountryData • CountryData/Currencies • Exchange rate • Hours • Hours/i18n • Languages • LinkMail • LinkPhone • LinkSkype • Marker utilities • Marker utilities/i18n • VCard/Cards • VCard/i18n • VCard/Params • VCard/Qualifiers • VCard/Subtypes • VCard/Unesco • Wikidata utilities
Anmerkungen
- Das ist mein Geschenk zum 10. Geburtstag von Wikivoyage. -- RolandUnger (Diskussion) 22:18, 12. Nov. 2016 (CET)
- Siehe auch die Diskussionen unter Wikivoyage:Expedition 'vCard'.
- Zusätzlich wird MediaWiki:Gadget-MarkerUtilities.js eingesetzt, um Skype-Namen zu verlinken.
Experimentalbeispiele
* {{vCard | name= Ein Hotel | type = hostel | url= http://hotel.de | lat = 52.5144 | long = 13.389722 | show = all | address = Hauptstraße 1 | directions = Abzweig von der Nebenstraße | description = Das in der Innenstadt gelegene Hotel besitzt großzügige Räume, bietet aber nur ÜF. | phone = +49 (0)30 2345 1234 | fax = +49 (0)30 2345 9876, +49 0176 345 1234 | before = [[File:Flag of Germany.svg|border|20px|class=noviewer|Flag of Germany]] | image = Berlin Friedrichstraße Galeries Lafayette.jpg | email = [email protected] | comment = j | lastedit = 2020-09-18 | hours = 7/24. | checkin = ab 14 Uhr | checkout = bis 12 Uhr | payment = Visa, Master, AmEx, Maestro | subtype = wlan, bar, pool, room:89 | skype = nutzer.name; nutzer2.name }} * {{vCard | wikidata = Q201219 | name = Ägyptisches Museum Kairo | comment = auch Nationalmuseum | alt = Egyptian Museum | name-latin = al-Matḥaf al-Miṣrī | address = Mīdān et-Taḥrīr | address-local = ميدان التحرير | directions = im Stadtzentrum | directions-local = بوسط البلد | status = top-sight }} * {{vCard | wikidata = Q201219 | name = Ägyptisches Museum Kairo | auto = n | facebook = j }} <!-- Marker-Modus --> * {{vCard | wikidata = Q19675 | name = Musée du Louvre | description = Noch weiterer Text. }} * {{vCard | wikidata = Q19675 | price = n | hours = n | payment = n }} * {{vCard | wikidata = Q257342 }} * {{vCard | wikidata = Q28934 | description = Test Durchhangeln. }} * {{vCard | wikidata = Q4872 }} * {{vCard | wikidata = Q10697 }} * {{vCard | wikidata = Q12508 | description = Test Durchhangeln. }} * {{vCard | wikidata = Q46033 | auto = n | show = symbol | description = Test Flughafen mit Ikone. }} * {{vCard | wikidata = Q13218762 | description = Anzeige englische Wikipedia. }} * {{vCard | wikidata = Q56506795 | description = Ausgabe Anschriften. }} * {{vCard | wikidata = Q47429618 | auto = n | address = | address-local = | directions = | phone = | subtype = n | description = Test eingeschränkte WD-Ausgabe. }} * {{vCard | wikidata = Q637739 | description = Rollstuhl aus WD. }} * {{vCard | name = Landesmuseum für Vorgeschichte | type = museum | wikidata = Q1332407 | description = Uhrzeiten, Kommentare. }} * {{vCard | name = Museo Nacional de Antropología | type = museum | wikidata = Q2917041 | description = Zusammenfassung Kommentare. }}
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: [email protected], 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)
- 1 Ägyptisches Museum Kairo (المتحف المصري, al-Matḥaf al-Miṣrī, Egyptian Museum, auch Nationalmuseum), Mīdān et-Taḥrīr, ميدان التحرير (im Stadtzentrum, بوسط البلد). Tel.: +20 (0)2 2579 6948, E-Mail: [email protected] Geöffnet: täglich 9:00–17:00, 9:00–16:00 (Ramadan). Preis: LE 30 (Araber*innen), LE 10 (arabische Studierende), kostenlos (bis 5 Jahre; Araber*innen, ab 61 Jahre), LE 550 (Ausländer*innen), LE 275 (ausländische Studierende); Stand 11/2024. (30° 2′ 52″ N 31° 14′ 0″ O)
- Ägyptisches Museum Kairo (30° 2′ 52″ N 31° 14′ 0″ O)
- 2 Musée du Louvre Tel.: +33 (0)1 40 20 53 17, +33 (0)1 40 20 50 50, Fax: +33 (0)1 40 20 54 52, E-Mail: [email protected] Noch weiterer Text. Merkmale: freies WLAN, , Garderobe, Fotografieren erlaubt, Berühren verboten, lautes Sprechen verboten, Rauchverbot, Stöckelschuhe verboten, Trinkverbot.Geöffnet: Mi–Mo 9:00–18:00; geschlossen: Di. Preis: 15 €, kostenlos (bis 17 Jahre; bis 25 Jahre). Akzeptierte Zahlungsarten: Bargeld, Debitkarte.
- Louvre Museum (Musée du Louvre). Tel.: +33 (0)1 40 20 53 17, +33 (0)1 40 20 50 50, Fax: +33 (0)1 40 20 54 52, E-Mail: [email protected] Merkmale: freies WLAN, , Garderobe, Fotografieren erlaubt, Berühren verboten, lautes Sprechen verboten, Rauchverbot, Stöckelschuhe verboten, Trinkverbot.
- 1 Carnegie-Bibliothek (bibliothèque Carnegie), 2 Place Carnegie, 51100 Reims. Tel.: +33 (0)3 26 77 81 41, E-Mail: [email protected]
- 1 Flughafen Brüssel-Zaventem (Brussels Airport, IATA: BRU). Tel.: +32 (0)900 70 000 (Belgien), +32 (0)2 753 77 53 Test Durchhangeln.
- 3 Puschkin-Museum (Государственный музей изобразительных искусств имени А. С. Пушкина), улица Волхонка, 12
- 2 Stadtbibliothek Köln, Josef-Haubrich-Hof 1
Flughafen Frankfurt Main (IATA: FRA) Test Flughafen mit Ikone.
- 5 Freilichtmuseum Karnak (المتحف المفتوح بالكرنك) Anzeige englische Wikipedia. (25° 43′ 10″ N 32° 39′ 27″ O)
- 2 Nile Ritz-Carlton (نايل ريتز كارلتون), 1113 Corniche El Nil St., 11221 Cairo, ١١١٣ كورنيش النيل ، ١١٢٢١ القاهرة. Tel.: +20 (0)2 2577 8899, Fax: +20 (0)2 2578 0475, +20 (0)2 2739 4637, E-Mail: [email protected] (Direktion), [email protected] (Concierge), [email protected] (Restaurant, Bar) Ausgabe Anschriften. Merkmale: ★★★★★, freies WLAN, 329 Zimmer. Check-in: 14:00. Check-out: 12:00. Akzeptierte Zahlungsarten: 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, ٢ شارع قصر النيل ، ميدان التحرير ، القاهرة (östlich vom Ägyptischen Museum). Tel.: +20 (0)2 2575 0777, +20 (0)2 3854 2020, +20 16416 Test eingeschränkte WD-Ausgabe. (30° 2′ 50″ N 31° 14′ 9″ O)
- 2 U-Bahnhof Berliner Straße Rollstuhl aus WD. Merkmal: .
- 6 Landesmuseum für Vorgeschichte, Richard-Wagner-Straße 9, 06114 Halle (Saale). Tel.: +49 (0)345 52 47 30, Fax: +49 (0)345 524 73 51, E-Mail: [email protected] 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 € (Erwachsene), 5,00 € (ermäßigt), kostenlos (Kinder, 0–18 Jahre), 3,00 € (Audioguide).
- 7 Museo Nacional de Antropología, calle de Alfonso XII, 68, 28014 Madrid. Tel.: +34 915 30 64 18, +34 915 39 59 95, Fax: +34 914 67 70 98, E-Mail: [email protected] 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 €, kostenlos (Minderjährige, Behinderte; Senioren, ab 66 Jahre).
Parameter show
- Mögliche Werte:
none
,poi
,coord
,all
,inline
,noairport
,outdent
,symbol
. Eine kommaseparierte Liste mehrerer Werte ist möglich. - Man kann die Standardwerte überschreiben.
- Darstellung von Marker und Koordinaten.
none
ist stärker alspoi
,coord
undall
.all
ist stärker alspoi
undcoord
.
- 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
Hinweise
- Die obige Dokumentation wurde aus der Seite Modul:VCard/Doku eingefügt. (bearbeiten | Versionsgeschichte) Die Kategorien für dieses Modul sollten in der Dokumentation eingetragen werden. Die Interwiki-Links sollten auf Wikidata eingepflegt werden.
- Liste der Unterseiten
-- module variable and administration local vc = { moduleInterface = { suite = 'vCard', serial = '2025-04-23', item = 58187507 }, -- table containing parameters fetched from Wikidata fromWD = {}, -- Wikidata to subtype table subtypeIds = nil, -- complete subtype table including Wikidata subtypes subtypes = {} } -- module import -- require( 'strict' ) local mi = require( 'Module:Marker utilities/i18n' ) local mu = require( 'Module:Marker utilities' ) local vp = require( 'Module:VCard/Params' ) -- parameter lists local vi = require( 'Module:VCard/i18n' ) -- parameter translations local vq = mw.loadData( 'Module:VCard/Qualifiers' ) -- comment tables local cm = require( 'Module:CountryData' ) local er -- modules will be loaded later if needed local hi local hr local lg local lp = require( 'Module:LinkPhone' ) local vs local wu = require( 'Module:Wikidata utilities' ) local function addWdClass( key ) return mu.addWdClass( vc.fromWD[ key ] ) end local function forceFetchFromWikidata( tab ) for key, value in pairs( tab ) do vp.ParMap[ key ] = true end end -- copying frameArgs parameters to args = vp.ParMap parameters local function copyParameters( args, show ) local t, value local exclude = { auto = 1, show = 1, subtype = 1, wikidata = 1 } local copy = { subtype = 1 } vp.ParMap.wikidata = args.wikidata -- force getting data from Wikidata for missing parameters show.inlineDescription = true -- description with div or span tag if vp.ParMap.auto == true then forceFetchFromWikidata( vp.ParWD ) forceFetchFromWikidata( vp.ParWDAdd ) end -- copying args parameters to vp.ParMap parameters for key, v in pairs( vi.p ) do value = args[ key ] if value then value, t = mu.removeCtrls( value, show.inline or key ~= 'description' ) if t then show.inlineDescription = false end if not exclude[ key ] then if value == '' and key ~= 'type' then value = 'y' end t = mu.yesno( value ) if t then if vp.ParMap.wikidata ~= '' then vp.ParMap[ key ] = t == 'y' else vp.ParMap[ key ] = '' end else vp.ParMap[ key ] = value end end if copy[ key ] then vp.ParMap[ key ] = value end end end return vp.ParMap end -- checking subtypes local function checkSubtypes( args, subtypesTable ) if not mu.isSet( args.subtype ) then return {} end local function aliasToSubtype( alias ) if not vc.subtypeAliases then -- alias to subtype table vc.subtypeAliases = mu.getAliases( subtypesTable, 'alias' ) end return vc.subtypeAliases[ alias ] end local function subtypeExists( subtype ) return subtypesTable[ subtype ] and subtype or aliasToSubtype( subtype ) end local subtypes = {} local invalidSubtypes = {} local at, count, invalidCount, item for subtype, v in pairs( mu.split( args.subtype ) ) do invalidCount = false count = '' item = subtype -- split item from count at = item:find( ':', 1, true ) if at then count = tonumber( item:sub( at + 1, #item ) ) or '' item = mw.text.trim( item:sub( 1, at - 1 ) ) if count == '' then invalidCount = true -- ':' without count or not a number else count = math.floor( count ) if count < 2 then count = '' end end end item = subtypeExists( item ) or mu.typeExists( item ) if item then subtypes[ item ] = count end if invalidCount or not item then table.insert( invalidSubtypes, subtype ) end end if #invalidSubtypes > 0 then mu.addMaintenance( 'unknownSubtype', table.concat( invalidSubtypes, mu.commaSeparator ) ) end return subtypes end local function initialParametersCheck( frame, page ) local country, email, entity, param, show, t, v, web, wrongQualifier local frameArgs = mu.checkArguments( frame:getParent().args, vi.p ) frameArgs.wikidata, entity, wrongQualifier = wu.getEntity( frameArgs.wikidata or '' ) if wrongQualifier then mu.addMaintenance( 'wrongQualifier' ) end if mu.isSet( frameArgs.wikidata ) then mu.addMaintenance( 'wikidata' ) v = mu.yesno( frameArgs.auto or '' ) if v then vp.ParMap.auto = v == 'y' else vp.ParMap.auto = vi.options.defaultAuto end else vp.ParMap.auto = false end -- making phone number table t = {} for i, key in ipairs( vp.phones ) do mu.tableInsert( t, frameArgs[ key ] ) end -- making web addresses table web = {} mu.tableInsert( web, frameArgs.url ) email = frameArgs.email or '' email = email:gsub( ',.*$', '' ) -- first email mu.tableInsert( web, email ) -- getting country-specific technical parameters country = cm.getCountryData( entity, t, frameArgs.country, frameArgs.wikidata, web ) if country.fromWD then mu.addMaintenance( 'countryFromWD' ) end if country.unknownCountry then mu.addMaintenance( 'unknownCountry' ) end if country.cc ~= '' then country.trunkPrefix = lp.getTrunkPrefix( country.cc ) end -- for map support country.extra = mi.map.defaultSiteType if mu.isSet( country.iso_3166 ) then country.extra = country.extra .. '_region:' .. country.iso_3166 -- country-specific default show end if mu.isSet( country.show ) then vp.ParMap.show = vp.ParMap.show .. ',' .. country.show end -- handling args table show = mu.getShow( vp.ParMap.show, frameArgs, vp.show ) -- copying frameArgs parameters to args = vp.ParMap parameters local args = copyParameters( frameArgs, show ) -- alternate local language if mu.isSet( args.localLang ) then lg = lg or require( 'Module:Languages' ) args.localLang = args.localLang:lower() if lg.lngProps[ args.localLang ] then cm.setLanguageParams( args.localLang, page.lang, country ) else args.localLang = '' mu.addMaintenance( 'unknownLanguage' ) end end mu.checkStatus( args ) mu.checkStyles( args ) -- checking coordinates and converting DMS to decimal coordinates if necessary mu.checkCoordinates( args ) mu.checkZoom( args ) -- remove namespace from category mu.checkCommonsCategory( args ) mu.checkId( args ) for i, param in ipairs( mi.options.parameters ) do if mu.isSet( args[ param ] ) then mu.addMaintenance( 'parameterUsed', param ) end end args.subtypeAdd = mu.isSet( args.wikidata ) and vp.ParMap.auto -- getting features manually entered if mu.isSet( args.subtype ) then vs = require( 'Module:VCard/Subtypes' ) vc.subtypes = checkSubtypes( args, vs.f ) if mu.isSet( args.wikidata ) then -- y = fetch additional features from Wikidata if vc.subtypes.y then args.subtypeAdd = true -- n = do not show subtypes fetched from Wikidata elseif vc.subtypes.n then args.subtypeAdd = false end end end if type( args.lastedit ) == 'string' and args.lastedit ~= '' and not args.lastedit:match( mi.dates.yyyymmdd.p ) then mu.addMaintenance( 'wrongDate' ) args.lastedit = '' end -- check Google Maps customer id if type( args.googlemaps ) == 'string' and mu.isSet( args.googlemaps ) and not args.googlemaps:match( '^[1-9]%d+$' ) then mu.addMaintenance( 'wrongGoogleCid' ) args.googlemaps = '' end if type( args.googlemaps ) == 'string' and mu.isSet( args.googlemaps ) then mu.addMaintenance( 'parameterUsed', 'google-maps' ) if mu.isSet( args.wikidata ) then mu.addMaintenance( 'wdWithGoogleCid' ) end end return args, entity, show, country end local function getQuantity( value, formatter, page ) local a, f, u, unit, unitId if type( value ) == 'number' then return tostring( value ) elseif value.amount == '0' then return '0' else a = mu.formatNumber( value.amount ) u = '' unitId = value.unit unit = cm.getCurrency( unitId ) if mu.isSet( unit ) then if unit.mul then a = mu.formatNumber( string.format( '%.2f', -- 2 decimal places tonumber( value.amount ) * unit.mul ) ) end if mi.noCurrencyConversion.all or mi.noCurrencyConversion[ unit.iso ] then unit = mu.makeSpan( cm.getCurrencyFormatter( unitId ), 'voy-currency voy-currency-' .. unit.iso:lower() ) else er = er or require( 'Module:Exchange rate' ) unit = er.getWrapper( a, unit.iso, '', 2, cm.getCurrencyFormatter ) mu.addMaintenance( 'currencyTooltip' ) end else unit = vq.labels[ unitId ] end if unit and unit:find( '%s', 1, true ) then a = mw.ustring.format( unit, a ) elseif unit then u = unit elseif mw.wikibase.isValidEntityId( unitId ) then -- currency code u = wu.getValue( unitId, mi.properties.iso4217 ) if u == '' then -- unit symbol u = wu.getValuesByLang( unitId, mi.properties.unitSymbol, 1, page.lang ) u = u[ 1 ] or '' end if u ~= '' then mu.addMaintenance( 'unitFromWD' ) else u = unitId mu.addMaintenance( 'unknownUnit' ) end end if a ~= '' and u ~= '' and formatter ~= '' and formatter:find( '$1', 1, true ) and formatter:find( '$2', 1, true ) then a = mw.ustring.gsub( f, '($1)', a ) a = mw.ustring.gsub( a, '($2)', u ) else a = ( u ~= '' ) and a .. ' ' .. u or a end end return a end local function getHourModules() if not hr then hi = require( 'Module:Hours/i18n' ) hr = require( 'Module:Hours' ) end end local function getLabel( id ) local label = id local tables = { vq.labels } if hi then table.insert( tables, hi.dateIds ) end if type( id ) == 'string' and id:match( '^Q%d+$' ) then for i, tab in ipairs( tables ) do if type( tab[ id ] ) == 'string' then label = tab[ id ] break end end if label == '' then return label elseif label == id then label = mu.getTypeLabel( id ) end if label == '' or label == id then label = wu.getLabel( id ) or '' if label == '' then mu.addMaintenance( 'unknownLabel' ) else mu.addMaintenance( 'labelFromWD' ) end end end return label end local function removeStringDuplicates( ar ) local hash = {} local result = {} local val for i = 1, #ar do val = ar[ i ] if not hash[ val ] then table.insert( result, val ) hash[ val ] = 1 end end return result end -- getting comments for contacts and prizes from Wikidata using tables local function getComments( statement, properties, args, page ) local comments = {} local isMobilephone = false local minAge, maxAge for i, property in ipairs( properties ) do local pType = property .. '-type' if statement[ property ] then if property == mi.properties.minimumAge then minAge = getQuantity( statement[ property ][ 1 ], '', page ) elseif property == mi.properties.maximumAge then maxAge = getQuantity( statement[ property ][ 1 ], '', page ) end for j, id in ipairs( statement[ property ] ) do if statement[ pType ] == 'monolingualtext' then id = id.text elseif statement[ pType ] == 'time' then -- getting last date in case of price/fees id = wu.getDateFromTime( id ) if not mu.isSet( args.asOf ) or args.asOf < id then args.asOf = id end id = '' elseif type( id ) == 'table' then id = '' end if id == mi.qualifiers.mobilePhone then isMobilephone = true else mu.tableInsert( comments, getLabel( id ) ) end end end end comments = removeStringDuplicates( comments ) if minAge and maxAge then mu.tableInsert( comments, mw.ustring.format( mi.texts.fromTo, minAge:gsub( '(%d+).*', '%1' ), maxAge ) ) elseif minAge then mu.tableInsert( comments, mw.ustring.format( mi.texts.from, minAge ) ) elseif maxAge then mu.tableInsert( comments, mw.ustring.format( mi.texts.to, maxAge ) ) end if #comments > 0 then return table.concat( comments, mu.commaSeparator ), isMobilephone else return '', isMobilephone end end local function hasValue( tab, val ) for i = 1, #tab do if tab[ i ] == val then return true end end return false end local function getLngProperty( lng, p ) if not mu.isSet( lng ) then return '' end lg = lg or require( 'Module:Languages' ) local item = lg.lngProps[ lng ] if not item then local hyphen = lng:find( '-', 1, true ) if hyphen and hyphen > 1 then item = lg.lngProps[ lng:sub( 1, hyphen - 1 ) ] end end if item then item = item[ p ] end return item or ( p == 'c' and 0 or '' ) end local function removeTableDuplicates( ar ) local hash = {} local result = {} local hashVal for i, tab in ipairs( ar ) do hashVal = tab.value .. '#' .. tab.comment if not hash[ hashVal ] then table.insert( result, tab ) hash[ hashVal ] = 1 end end return result end local function mergeComments( ar ) if #ar > 1 then for i = #ar, 2, -1 do for j = 1, i - 1, 1 do if ar[ i ].value == ar[ j ].value and ar[ i ].comment ~= '' and ar[ j ].comment ~= '' then ar[ j ].comment = ar[ j ].comment .. '; ' .. ar[ i ].comment table.remove( ar, i ) break end end end end end local function convertTableWithComment( ar ) for i = 1, #ar, 1 do if ar[ i ].comment == '' then ar[ i ] = ar[ i ].value else ar[ i ] = ar[ i ].value .. mu.parentheses( ar[ i ].comment ) end end end --[[ properties are defined in Module:vCard/Params p property or set of properties f formatter string c maximum count of results, default = 1 m concat mode (if c > 1), default concat with ', ' v value type, empty: string value (i.e. default type), id: string value of an id like Q1234567 idl: string value of the label of an id like Q1234567 il: language-dependent string value iq: string value with qualifier ids au: quantity consisting of amount and unit pau: quantity consisting of amount (for P8733) vq: string or table value with qualifiers ids and references l = lang: language dependent wiki / local: monolingual text by wiki or local language le = true: use date for lastedit parameter --]] -- function returns an array in any case local function getWikidataValues( args, propDef, entity, page, country ) local r = '' local ar = {} local a, i, isMobilephone, item, id, langs, q, t, u, w -- setting defaults propDef.v = propDef.v or '' propDef.f = propDef.f or '' propDef.c = propDef.c or 1 -- getting value arrays if propDef.l == 'wiki' then ar = wu.getValuesByLang( entity, propDef.p, propDef.c, page.lang ) elseif propDef.l == 'local' then ar = wu.getValuesByLang( entity, propDef.p, propDef.c, country.lang ) elseif propDef.l == 'lang' and propDef.c == 1 then id = getLngProperty( country.lang, 'q' ) if id == '' then country.unknownLanguage = true else -- using language of work or name ( page.lang, mi.langs, country.lang ) a = wu.getValuesByQualifier( entity, propDef.p, mi.properties.languageOfName, id ) if next( a ) then langs = mu.getLangTable( page.lang, country.lang ) for i, lang in ipairs( langs ) do item = a[ getLngProperty( lang, 'q' ) ] if item then break end end ar = { item or a[ next( a, nil ) ] } -- fallback: first item end end elseif propDef.v == 'iq' or propDef.v == 'iqp' then q = propDef.v == 'iq' and mi.propTable.quantity or mi.propTable.policyComments ar = wu.getValuesWithQualifiers( entity, propDef.p, propDef.q, q, { mi.properties.retrieved }, propDef.c ) if propDef.le then args.lastedit = wu.getLastedit( args.lastedit, ar ) end elseif propDef.v == 'au' or propDef.v == 'vq' then q = propDef.v == 'au' and mi.propTable.feeComments or mi.propTable.contactComments ar = wu.getValuesWithQualifiers( entity, propDef.p, nil, q, { mi.properties.retrieved }, propDef.c ) -- maybe a change of nil to a properties table is useful if propDef.le then args.lastedit = wu.getLastedit( args.lastedit, ar ) end else ar = wu.getValues( entity, propDef.p, propDef.c ) end if #ar == 0 and propDef.p ~= mi.properties.instanceOf then return ar end for i = #ar, 1, -1 do -- amount with unit (for fees) if propDef.v == 'au' then a = getQuantity( ar[ i ].value, propDef.f, page ) if a == '0' then a = vq.labels.gratis end u, isMobilephone = getComments( ar[ i ], mi.propTable.feeComments, args, page ) ar[ i ] = { value = a, comment = u } -- for number of rooms P8733 elseif propDef.v == 'pau' then if ar[ i ].unit == '1' then a = tonumber( ar[ i ].amount ) or 0 else a = 0 end ar[ i ] = {} ar[ i ][ mi.properties.quantity ] = { a } ar[ i ][ mi.properties.quantity .. '-type' ] = 'quantity' ar[ i ].value = mi.qualifiers.roomNumber ar[ i ]['value-type'] = 'wikibase-entityid' -- qualifier ids (for subtypes) elseif propDef.v == 'iq' or propDef.v == 'iqp' then if ar[ i ][ 'value-type' ] ~= 'wikibase-entityid' then table.remove( ar, i ) end if propDef.v == 'iqp' then ar[ i ].policyComment, isMobilephone = getComments( ar[ i ], mi.propTable.policyComments, args, page ) end -- strings with qualifiers (for contacts) elseif propDef.v == 'vq' then if ar[ i ][ 'value-type' ] ~= 'string' then table.remove( ar, i ) else u, isMobilephone = getComments( ar[ i ], mi.propTable.contactComments, args, page ) if vi.options.useMobile and propDef.t then if ( isMobilephone and propDef.t == 'mobile' ) or ( not isMobilephone and propDef.t == 'landline' ) then ar[ i ] = { value = ar[ i ].value, comment = u } else table.remove( ar, i ) end else ar[ i ] = { value = ar[ i ].value, comment = u } end end -- value, monolingual text, identifier else if propDef.v == 'id' then ar[ i ] = ar[ i ].id elseif propDef.v == 'idl' then getHourModules() ar[ i ] = hr.formatTime( getLabel( ar[ i ].id ) ) end if ar[ i ] ~= '' and propDef.f ~= '' then ar[ i ] = mw.ustring.format( propDef.f, ar[ i ] ) end end if propDef.v == 'au' or propDef.v == 'vq' then if ar[ i ] and ar[ i ].value == '' then table.remove( ar, i ) end else if ar[ i ] == '' then table.remove( ar, i ) end end end -- cleanup if propDef.v == 'au' or propDef.v == 'vq' then ar = removeTableDuplicates( ar ) mergeComments( ar ) convertTableWithComment( ar ) else ar = removeStringDuplicates( ar ) end return ar end local function getWikidataItem( args, parWDitem, entity, page, country ) local arr = {} local function singleProperty( propDef ) if #arr == 0 then arr = getWikidataValues( args, propDef, entity, page, country ) else for i, value in ipairs( getWikidataValues( args, propDef, entity, page, country ) ) do table.insert( arr, value ) -- copy to arr end end end local p = parWDitem if not p then return '' end p.c = p.c or 1 -- count local tp = type( p.p ) if tp == 'string' then singleProperty( p ) elseif tp == 'table' then for i, sngl in ipairs( p.p ) do if type( sngl ) == 'table' then singleProperty( sngl ) if p.c == 1 and #arr > 0 then break end end end end if #arr > p.c then for i = #arr, p.c + 1, -1 do -- delete supernumerary values table.remove( arr, i ) end end if p.m == 'no' then return arr else return table.concat( arr, p.m or mu.commaSeparator ) end end local function getAddressesFromWikidata( args, page, country, entity ) local addresses = {} local t, u, w, weight -- getting addresses from Wikidata but only if necessary if args.address == true or type( args.addressLocal ) == 'boolean' then -- P6375: address addresses = wu.getMonolingualValues( entity, mi.properties.streetAddress ) if next( addresses ) then -- sometimes addresses contain <br> tag(s) for key, value in pairs( addresses ) do addresses[ key ] = value:gsub( '</*br%s*/*>', mi.texts.space ) end else return end else return end if args.address == true then args.address = addresses[ page.lang ] -- select address if the same writing system is used if not args.address then weight = -1 u = getLngProperty( page.lang, 'w' ) -- writing entity id for key, value in pairs( addresses ) do -- same writing entity id as page.lang w = getLngProperty( key, 'w' ) if w == '' then country.unknownPropertyLanguage = true else if key and w == u then -- same writing entity id w = getLngProperty( key, 'c' ) -- getting language weight if w > weight then -- compare language weight args.address = value args.addressLang = key weight = w end end end end end if not args.address then for i, lng in ipairs( mi.langs ) do if addresses[ lng ] then args.address = addresses[ lng ] args.addressLang = lng break end end end if not args.address then args.address = '' args.addressLang = '' end vc.fromWD.address = args.address ~= '' end -- removing county name from the end of address -- same with county name in county language and English if type( args.address ) == 'string' then args.address = mw.ustring.gsub( args.address, '[.,;]*%s*' .. country.country .. '$', '' ) end t = true for i, lng in ipairs( mi.langs ) do if country.lang == lng then t = false end end -- keeping local address in any case for html data args.addAddressLocal = addresses[ country.lang ] or '' if t and args.addressLocal == true and country.lang ~= page.lang then if country.lang ~= '' then args.addressLocal = addresses[ country.lang ] or '' else -- unknown language, maybe missing in Module:Languages args.addressLocal = addresses.unknown or '' end vc.fromWD.addressLocal = args.addressLocal ~= '' end end local function getDataFromWikidata( args, page, country, entity ) if args.wikidata == '' then return end mu.getTypeFromWikidata( args, entity ) -- prevent local data if wiki language == country language if page.lang == country.lang then for i, value in ipairs( vp.localData ) do if type( args[ value ] ) == 'boolean' then args[ value ] = '' end end end mu.getNamesFromWikidata( args, vc.fromWD, page, country, entity ) getAddressesFromWikidata( args, page, country, entity ) if args.hours == true then local lastEdit getHourModules() args.hours, lastEdit = hr.getHoursFromWikidata( entity, page.lang, mi.langs[ 1 ] or '', mi.maintenance.properties, nil, args.lastedit, vq.labels, { typeTable = mu.types, idTable = mu.typeIds } ) vc.fromWD.hours = args.hours ~= '' if vi.options.lasteditHours then args.lastedit = lastEdit end end for key, value in pairs( vp.ParWD ) do if args[ key ] == true then args[ key ] = getWikidataItem( args, vp.ParWD[ key ], entity, page, country ) vc.fromWD[ key ] = args[ key ] ~= '' end end mu.getArticleLink( args, entity, page ) mu.getCommonsCategory( args, entity ) mu.getCoordinatesFromWikidata( args, vc.fromWD, entity ) end local function finalParametersCheck( args, show, page, country, defaultType, entity ) -- remove boolean values from parameters to have only strings for key, value in pairs( args ) do if type( args[ key ] ) == 'boolean' then args[ key ] = '' end end -- create givenName, displayName tables mu.prepareNames( args ) -- analysing addressLocal vs address if args.addressLang and args.addressLang == country.lang then args.addressLocal = '' args.addAddressLocal = '' end if args.addressLocal ~= '' and args.address == '' then args.address = mu.languageSpan( args.addressLocal, mi.texts.hintAddress, page, country ) args.addressLocal = '' args.addAddressLocal = '' vc.fromWD.address = vc.fromWD.addressLocal end show.noCoord = args.lat == '' or args.long == '' if show.noCoord then show.coord = nil show.poi = nil mu.addMaintenance( 'missingCoordVc' ) end -- getting Marker type, group, and color if not mu.isSet( args.type ) and mu.isSet( defaultType ) then args.type = defaultType end mu.checkTypeAndGroup( args ) -- image check if not vc.fromWD.image or mi.options.WDmediaCheck then mu.checkImage( args, entity ) end mu.checkUrl( args ) args.commonscat = args.commonscat:gsub( ' ', '_' ) -- add final period if not yet exists if mu.isSet( args.description ) then if mw.ustring.match( args.description, '[%w_€$]$' ) then args.description = args.description .. mi.texts.period end if mw.ustring.len( args.description ) > mi.options.contentLimit and mi.options.groupsWithLimit[ args.group ] then args.description = mw.ustring.sub( args.description, 1, mi.options.contentLimit ) .. '…' mu.addMaintenance( 'contentTooLong' ) end end end local function formatText( args, results, key, class ) if not mu.isSet( args[ key ] ) then return end local r local textKey = key local period = mi.texts.period if key == 'hours' then args[ key ], r = mw.ustring.gsub( args[ key ], mi.texts.closedPattern, '' ) textKey = ( r > 0 ) and 'closed' or key end r = mw.ustring.format( mi.texts[ textKey ], args[ key ] ) r = mw.ustring.gsub( r, '^%a', mw.ustring.upper ) -- add period if not yet exists r = r .. ( mw.ustring.sub( r, -1 ) == period and '' or period ) table.insert( results, mu.makeSpan( r, class .. addWdClass( key ) ) ) end local function formatPhone( args, key, country ) if not mu.isSet( args[ key ] ) then return '' end local class local pArgs = { phone = args[ key ], cc = country.cc, isFax = false, isTollfree = false, format = false } if vc.fromWD[ key ] then pArgs.format = true pArgs.size = country.phoneDigits or 2 end if key == 'fax' then class = 'p-tel-fax fax listing-fax' .. addWdClass( key ) pArgs.isFax = true else class = 'p-tel tel listing-phone' .. addWdClass( key ) if key == 'phone' then class = class .. ' listing-landline' elseif key == 'tollfree' then class = class .. ' listing-tollfree' pArgs.isTollfree = true elseif key == 'mobile' then class = class .. ' listing-mobile' end end return mw.ustring.format( mi.texts[ key ], mu.makeSpan( lp.linkPhoneNumbers( pArgs ), class ) ) end local function formatDate( aDate, aFormat ) return mw.getContentLanguage():formatDate( aFormat, aDate, true ) end local function removePeriods( s ) local period = mi.texts.period local _period = '%' .. period -- closing (span) tags between full stops return s:gsub( _period .. '+(</[%l<>/]+>)' .. _period .. '+', '%1' .. period ) :gsub( _period .. _period .. '+', period ) end local function makeMarkerAndName( args, show, page, country, frame ) local result = {} -- adding status icons mu.tableInsert( result, mu.makeStatusIcons( args ) ) -- adding POI marker if show.poi or mu.isSet( args.copyMarker ) then table.insert( result, mu.makeMarkerSymbol( args, show, frame ) ) end mu.makeName( result, args, show, page, country, addWdClass( 'name' ), addWdClass( 'nameLocal' ) ) if #result > 0 then result = { table.concat( result, mi.texts.space ) } end if args.before ~= '' then table.insert( result, 1, mu.makeSpan( args.before, 'listing-before' ) ) end return table.concat( result, mi.texts.space ) end local function makeEvent( args, page ) local isEvent = false local s = {} local count = 0 -- counts from-to statements local startMonth -- month of start date local today = page.langObj:formatDate( 'Y-m-d', 'now', true ) local todayYear = today:sub( 1, 4 ) -- yyyy local todayMonth = today:sub( 6, 7 ) -- mm local lastDate = '' local lastYear = '' local useYMD -- both dates are yyyy-mm-dd local function makePeriod( beginP, endP ) if beginP == endP then endP = '' end if mu.isSet( beginP ) and mu.isSet( endP ) then count = count + 1 return mw.ustring.format( mi.texts.fromTo2, beginP, endP ) elseif mu.isSet( beginP ) then return beginP else return endP end end local function analyseDate( d, m, y ) local success, c, t if useYMD then success, t = pcall( formatDate, d, mi.dates.yyyymmdd.f ) success, c = pcall( formatDate, d, 'Y-m-d' ) if success then lastDate = c > lastDate and c or lastDate d = t end return d, nil end if d:match( mi.dates.yyyymmdd.p ) then y = d:sub( 1, 4 ) d = d:sub( 6 ) end if mu.isSet( y ) then if y:match( mi.dates.yy.p ) then y = ( '2000' ):sub( -#y ) .. y elseif not y:match( mi.dates.yyyy.p ) then y = nil end lastYear = y > lastYear and y or lastYear end if mu.isSet( d ) and mu.isSet( m ) and d:match( mi.dates.dd.p ) and not m:match( mi.dates.mm.p ) then -- try to convert month to number string success, t = pcall( formatDate, m, 'm' ) if success then m = t else for i = 1, 12, 1 do if m == mi.months[ i ] or mw.ustring.match( m, mi.monthAbbr[ i ] ) then m = '' .. i break end end end end if mu.isSet( d ) and mu.isSet( m ) and d:match( mi.dates.dd.p ) and m:match( mi.dates.mm.p ) then d = m:gsub( '%.+$', '' ) .. '-' .. d:gsub( '%.+$', '' ) m = nil elseif mu.isSet( d ) and not mu.isSet( m ) and d:match( mi.dates.dd.p ) then d = ( startMonth or todayMonth ) .. '-' .. d:gsub( '%.+$', '' ) end if mu.isSet( d ) then if d:match( mi.dates.mmdd.p ) then startMonth = d:gsub( '%-%d+', '' ) m = nil c = ( y or todayYear ) .. '-' .. d success, t = pcall( formatDate, c, mi.dates.mmdd.f ) if success then d = t end elseif d:match( mi.dates.dd.p ) and not mu.isSet( m ) and startMonth then c = ( y or todayYear ) .. '-' .. startMonth .. '-' .. d success, t = pcall( formatDate, c, mi.dates.mmdd.f ) if success then d = t end end end if mu.isSet( m ) then d = ( mu.isSet( d ) and ( d .. mi.texts.space ) or '' ) .. m end return d, y end if not mu.groupWithEvents( args.group ) then return '' end -- check if vCard is an event for i, param in ipairs( vp.checkEvent ) do if mu.isSet( args[ param ] ) then isEvent = true break end end if not isEvent then return '' end if mu.isSet( args.frequency ) then table.insert( s, mu.makeSpan( args.frequency, 'listing-frequency' ) ) else if args.date:match( mi.dates.yyyymmdd.p ) and args.endDate:match( mi.dates.yyyymmdd.p ) then useYMD = true if args.date > args.endDate then args.date, args.endDate = args.endDate, args.date end end args.date, args.year = analyseDate( args.date, args.month, args.year ) args.endDate, args.endYear = analyseDate( args.endDate, args.endMonth, args.endYear ) local d = {} mu.tableInsert( d, makePeriod( args.date, args.endDate ) ) mu.tableInsert( d, makePeriod( args.year, args.endYear ) ) mu.tableInsert( s, mu.makeSpan( table.concat( d, count > 1 and mu.commaSeparator or mi.texts.space ), 'listing-date' ) ) if ( lastYear ~= '' and lastYear < todayYear ) or ( lastDate ~= '' and lastDate < today ) then mu.addMaintenance( 'outdated' ) end end if mu.isSet( args.location ) then local locations = mu.textSplit( args.location, ',' ) for i, location in ipairs( locations ) do if location ~= page.subpageText and location ~= page.text and mw.title.new( location, '' ).exists then location = mu.makeSpan( '[[' .. location .. ']]', 'listing-location' ) end table.insert( s, location ) end end s = table.concat( s, mu.commaSeparator ) return ( s ~= '' and ': ' or '' ) .. s end local function makeAddressAndDirections( args, page, country ) local r = '' local t if mu.isSet( args.address ) then if mu.isSet( args.addressLang ) then t = mw.language.fetchLanguageName( args.addressLang, page.lang ) if mu.isSet( t ) then t = mw.ustring.format( mi.texts.hintAddress2, t ) else country.unknownPropertyLanguage = true t = nil end end r = mu.commaSeparator .. mu.makeSpan( args.address, 'p-adr adr listing-address' .. addWdClass( 'address' ), true, { title = t, lang = args.addressLang } ) end if mi.options.showLocalData and mu.isSet( args.addressLocal ) then r = r .. mu.comma .. mu.languageSpan( args.addressLocal, mi.texts.hintAddress, page, country, 'listing-address-local' .. addWdClass( 'addressLocal' ) ) end t = {} if mu.isSet( args.directions ) then table.insert( t, mu.makeSpan( args.directions, 'listing-directions' .. addWdClass( 'directions' ) ) ) end if mi.options.showLocalData and mu.isSet( args.directionsLocal ) then table.insert( t, mu.languageSpan( args.directionsLocal, mi.texts.hintDirections, page, country, 'listing-directions-local' .. addWdClass( 'directionsLocal' ) ) ) end if #t == 0 then return r end return r .. mi.texts.space .. mu.makeSpan( mu.parentheses( table.concat( t, mu.comma ), true ), 'listing-add-address' ) end local function makeContacts( args, country ) local t = {} local s = '' mu.tableInsert( t, formatPhone( args, 'phone', country ) ) mu.tableInsert( t, formatPhone( args, 'tollfree', country ) ) mu.tableInsert( t, formatPhone( args, 'mobile', country ) ) mu.tableInsert( t, formatPhone( args, 'fax', country ) ) if args.email ~= '' then local lm = require( 'Module:LinkMail' ) s = mu.makeSpan( lm.linkMailSet( { email = args.email, ignoreUnicode = 1 } ), 'u-email email listing-email' .. addWdClass( 'email' ) ) mu.tableInsert( t, mw.ustring.format( mi.texts.email, s ) ) end if args.skype ~= '' then local ls = require( 'Module:LinkSkype' ) s = mu.makeSpan( ls.linkSkypeSet( { skype = args.skype } ), 'listing-skype' .. addWdClass( 'skype' ) ) mu.tableInsert( t, mw.ustring.format( mi.texts.skype, s ) ) end s = table.concat( t, mu.commaSeparator ) if s ~= '' then -- mi.texts.periodSeparator = '. ' s = mi.texts.periodSeparator .. mw.ustring.gsub( s, '^%a', mw.ustring.upper ) end return s end -- making subtypes string local function makeFeatures( args, tab, show ) vs = vs or require( 'Module:VCard/Subtypes' ) local function getSubtypeParams( subtype ) local r = vs.f[ subtype ] or mu.getTypeParams( subtype ) if not r.n then r.n = r.label or subtype end r.g = r.g or vs.fromTypesGroupNumber return r end vc.fromWD.subtypeAdd = type( args.subtypeAdd ) == 'table' and #args.subtypeAdd > 0 -- merging subtypeAdd (from Wikidata) to manually entered subtypes local unknowWDfeatures = false local count, label, p, t if vc.fromWD.subtypeAdd then -- making translation table from Wikidata ids to feature types if not vc.subtypeIds then vc.subtypeIds = mu.getAliases( vs.f, 'wd' ) end -- adding type if Wikidata id (wd.value) is known -- indexed array prevents multiple identical types for i, wd in ipairs( args.subtypeAdd ) do t = vc.subtypeIds[ wd.value ] or mu.idToType( wd.value ) if not t then -- maybe instance or subclass of wd.value are known local p31ids = wu.getIds( wd.value, mi.properties.instanceOf ) local p279ids = wu.getIds( wd.value, mi.properties.subclassOf ) -- merging both arrays for i = 1, #p279ids, 1 do table.insert( p31ids, p279ids[ i ] ) end for i = 1, #p31ids, 1 do t = vc.subtypeIds[ p31ids[ i ] ] or mu.idToType( p31ids[ i ] ) if t then break end end end -- subtype from WD is not known if not t and not vs.exclude[ wd.value ] then unknowWDfeatures = true -- try to add a new subtype Q... to vs.f subtypes table label = wu.getLabel( wd.value ) if label then vs.f[ wd.value ] = { n = label, wd = wd.value, g = vs.fromWDGroupNumber } t = wd.value end end -- add known subtype if t then vc.subtypes[ t ] = { c = ( wd[ mi.properties.quantity ] and wd[ mi.properties.quantity ][ 1 ] ) or ( wd[ mi.properties.capacity ] and wd[ mi.properties.capacity ][ 1 ] ) or '', p = wd.policyComment } end end end if unknowWDfeatures then mu.addMaintenance( 'unknowWDfeatures' ) end if next( vc.subtypes ) == nil and #args.subtypeTable == 0 then return end -- replace selected subtypes for subtype, count in pairs( vc.subtypes ) do if vs.convert[ subtype ] then if type( count ) == 'table' then p = count.p count = count.c end t = vs.convert[ subtype ][ count ] or vs.convert[ subtype ][ 1 ] vc.subtypes[ t ] = { p = p } vc.subtypes[ subtype ] = nil end end -- make subtypes table sortable local s = {}; for subtype, count in pairs( vc.subtypes ) do if type( count ) == 'table' then table.insert( s, { t = subtype, c = count.c, p = count.p } ) else table.insert( s, { t = subtype, c = count } ) end end -- add subtypes from types table if args.subtypeTable then for i, subtype in ipairs( args.subtypeTable ) do table.insert( s, { t = subtype, c = 1 } ) end end -- sorting subtypes -- by subtype group and then alphabetically by name table.sort( s, function( a, b ) local at = getSubtypeParams( a.t ) local bt = getSubtypeParams( b.t ) local na = mu.convertForSort( at.n ) local nb = mu.convertForSort( bt.n ) return ( at.g < bt.g ) or ( at.g == bt.g and na < nb ) end ) -- make text and data output local data = {} -- for data-subtype attribute in wrapper tag if #s > 0 then local r = {}; local subtype, f, u, u_n, v for i = 1, #s do subtype = s[ i ] -- for data-subtype="..." in wrapper tag u = subtype.t .. ',' .. tostring( i ) if type( subtype.c ) == 'number' and subtype.c > 1 then u = u .. ',' .. subtype.c end table.insert( data, u ) u = getSubtypeParams( subtype.t ) if u.g >= vs.firstGroup then u_n = u.n if not mu.isSet( u_n ) then u_n = subtype.t end u_n = u_n:gsub( '[,;/].*$', '' ) count = ( type( subtype.c ) == 'number' ) and subtype.c or 1 if count > 1 and u_n:find( '%[[^%[%]]*%]' ) then v = mw.ustring.format( mi.texts.subtypeWithCount, subtype.c, u_n:gsub( '%[([^%[%]]*)|([^%[%]]*)%]', '%1' ) :gsub( '%[([^%[%]]*)%]', '%1' ) ) else v = u_n:gsub( '%[([^%[%]]*)|([^%[%]]*)%]', '%2' ) :gsub( '%[([^%[%]]*)%]', '' ) end if mu.isSet( u.t ) then -- string tooltip v = mw.ustring.format( mi.texts.subtypeSpan, u.t, v ) elseif mu.isSet( u.f ) then -- icons f = mw.ustring.format( mi.texts.subtypeFile, u.f, v ) if u.c then f = mw.ustring.rep( f, u.c ) end v = mw.ustring.format( mi.texts.subtypeAbbr, v, f ) end -- adding policy comment if subtype.p and subtype.p ~= '' then v = v .. mu.parentheses( subtype.p ) end end table.insert( r, v ) end if #r > 0 then r = #r == 1 and mw.ustring.format( mi.texts.subtype, r[ 1 ] ) or mw.ustring.format( mi.texts.subtypes, table.concat( r, mu.commaSeparator ) ) if r ~= '' then table.insert( tab, mu.makeSpan( r, 'listing-subtype' .. addWdClass( 'subtypeAdd' ) ) ) end end end -- subtype contains now the value for wrapper tag args.subtype = table.concat( data, ';' ) end local function makePayment( args, results ) if not mu.isSet( args.payment ) then return end local t local class = 'p-note note listing-payment' if type( args.payment ) == 'table' then local vr = mw.loadData( 'Module:VCard/Cards') for i = #args.payment, 1, -1 do -- remove unknown items t = args.payment[ i ] if vr.cards[ t ] then args.payment[ i ] = vr.cards[ t ] else table.remove( args.payment, i ) end end class = class .. mu.addWdClass( #args.payment > 0 ) args.payment = table.concat( args.payment, mu.commaSeparator ) else mu.addMaintenance( 'paymentUsed' ) end formatText( args, results, 'payment', class ) end local function wrapDescription( args, tab, isInline, addText ) if args.description ~= '' then table.insert( tab, tostring( mw.html.create( isInline and 'span' or 'div' ) :addClass( 'p-note note listing-content' ) :wikitext( args.description .. ( addText or '' ) ) ) ) end end local function makeMetadata( args, page ) local outdated = false local s, success, u local t = args.lastedit if t ~='' then success, t = pcall( formatDate, t, mi.dates.lastedit.f ) if not success then mu.addMaintenance( 'wrongDate' ) t = '' else success, s = pcall( formatDate, args.lastedit, 'U' ) -- UNIX seconds success, u = pcall( formatDate, mi.texts.expirationPeriod, 'U' ) if s < u then t = t .. mi.texts.space .. mi.texts.maybeOutdated outdated = true end end end local tag = mw.html.create( 'span' ) :attr( 'class', 'listing-metadata listing-metadata-items' ) -- add node to save the parent tag :node( mw.html.create( 'span' ) :addClass( 'listing-metadata-item listing-lastedit' ) :addClass( outdated and 'listing-outdated' or nil ) :addClass( t == '' and 'listing-item-dummy' or nil ) :wikitext( mw.ustring.format( mi.texts.lastedit, t == '' and mi.texts.lasteditNone or t ) ) ) if mu.isSet( args.sectionFrom ) then local from = args.sectionFrom:gsub( '_', ' ' ) if from ~= page.subpageText and from ~= page.text then if mu.isSet( args.wikidata ) then from = mw.ustring.format( '%s#' .. mi.texts.anchor, from, args.wikidata ) end tag:node( mw.html.create( 'span' ) :addClass( 'listing-metadata-item listing-toSourcePage' ) :wikitext( mw.ustring.format( '[[%s|%s]]', from, mi.texts.editInSource ) ) ) end end return tostring( tag ) end -- making description, coordinates, and meta data local function makeDescription( args, show, page, country, entity ) local results = {} -- inline description if show.inlineDescription then wrapDescription( args, results, true ) end -- adding features makeFeatures( args, results, show ) -- practicalities formatText( args, results, 'hours', 'p-note note listing-hours' ) formatText( args, results, 'checkin', 'listing-checkin' ) formatText( args, results, 'checkout', 'listing-checkout' ) if mu.isSet( args.asOf ) and mu.isSet( args.price ) then local success success, args.asOf = pcall( formatDate, args.asOf, mi.dates.asOf.f ) args.price = args.price .. mw.ustring.format( mi.texts.asOf, args.asOf ) end formatText( args, results, 'price', 'p-note note listing-price' ) makePayment( args, results ) -- adding Unesco symbol if args.unesco ~= '' and vi.options.showUnesco then local uLink, uTitle = require( 'Module:VCard/Unesco' ).getUnescoInfo( country ) table.insert( results, mu.addLinkIcon( 'listing-unesco voy-symbol-unesco', uLink, uTitle, 'unesco' ) ) end local noContent = #results == 0 -- adding DMS coordinates if show.coord then table.insert( results, mu.dmsCoordinates( args.lat, args.long, args.givenName.name, vc.fromWD.lat, country.extra ) ) end if mi.options.showSisters == 'atEnd' then table.insert( results, mu.makeIcons( args, page, country, entity, show, vc.fromWD ) ) end local description local space = mi.texts.space -- adding description in block mode if args.description ~= '' and not show.inlineDescription then -- last edit will be inserted at the end of the div tag wrapDescription( args, results, false, makeMetadata( args, page ) ) noContent = false description = table.concat( results, space ) if description ~= '' then description = space .. description end -- adding description in inline mode else description = table.concat( results, space ) if description ~= '' then description = space .. description end end return removePeriods( description ), noContent end -- vCard main function function vc.vCard( frame ) mu.initMaintenance() local page = mu.getPageData() -- getting location (vCard/listing) entity, show options and country data local args, vcEntity, show, country = initialParametersCheck( frame, page ) -- associated Wikivoyage page of the location in current Wikivoyage branch -- possibly modified by mu.getArticleLink() args.wikiPage = '' -- getting data from Wikidata getDataFromWikidata( args, page, country, vcEntity ) -- final check local defaultType = frame.args.type finalParametersCheck( args, show, page, country, defaultType, vcEntity ) -- making output -- leading part for marker mode: only location names and comment -- saving address args.addressOrig = args.address -- creating text parts -- leading part (marker and names) local leading = makeMarkerAndName( args, show, page, country, frame ) .. makeEvent( args, page ) -- additional parts for vCard mode local contacts = '' -- all contacts -- get address and directions local address = makeAddressAndDirections( args, page, country ) -- get all contact information local contacts = makeContacts( args, country ) contacts = removePeriods( address .. contacts ) -- making description, coordinates, and meta data local description, noContent = makeDescription( args, show, page, country, vcEntity ) local r = leading local icons = '' if contacts == '' and noContent then show.inline = true r = r .. mu.makeIcons( args, page, country, vcEntity, show, vc.fromWD ) .. description else if type( mi.options.showSisters ) == 'boolean' then -- could also be 'atEnd', then part of body icons = mu.makeIcons( args, page, country, vcEntity, show, vc.fromWD ) end -- mi.texts.periodSeparator = '. ' r = removePeriods( r .. contacts .. icons .. ( show.noperiod and '' or mi.texts.periodSeparator ) .. description ) :gsub( '%)(</span>)%s*(<span [^>]*>)%s*%(', '%1; %2' ) end -- prevents line break before punctuation mark r = r:gsub( '</span>([,;.:!?])', '%1</span>' ) -- remove space(s) before punctuation marks :gsub( '%s+(<span [^>]*>[,;.:!?])', '%1' ) if show.inlineDescription then r = r:gsub( '%s+$', '' ) .. makeMetadata( args, page ) end -- error handling and maintenance, not in template and module namespaces if country.unknownLanguage then mu.addMaintenance( 'unknownLanguage' ) end if country.unknownPropertyLanguage then mu.addMaintenance( 'unknownPropertyLanguage' ) end r = r .. mu.makeMaintenance( page, { wu, mu, cm, hr } ) -- wrapping tag args.address = args.addressOrig args.template = 'vCard' return mu.makeWrapper( r, args, page, country, show, vp.vcardData, frame ) end return vc