
![]() | 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 getestet und für den projektweiten Gebrauch geeignet. Es kann in Vorlagen benutzt und auf Hilfeseiten erläutert werden. Entwicklungen an dem Modul sollten auf Marker utilities/Test und die Anwendung auf der Spielwiese getestet werden, da wiederholte Trial-and-Error-Edits die Resourcen stark belasten können. |
![]() | Dieses Modul benutzt eine oder mehrere Wikidata-Eigenschaften. |
Anwendung
Das Modul stellt gemeinsame Funktionen für das Modul:Marker und das Modul:vCard zur Verfügung. Im Projektnamensraum befindet sich die technische Dokumentation.
Versionsbezeichnung auf Wikidata: 2025-03-17
Benötigte weitere Module
Dieses Modul benötigt folgende weitere Module: Coordinates • Marker utilities/Groups • Marker utilities/i18n • Marker utilities/Maki icons • Marker utilities/Types • UrlCheck • Wikidata utilities
Verwendung in anderen Modulen
Dieses Modul ist notwendig für die Ausführung folgender Module. Bei Anpassungen sollte die Funktionstüchtigkeit der folgenden Module geprüft werden. Benutze dazu auch diese Tracking-Kategorie um Fehler zu finden, die sich dann auf Artikel auswirken:
- Marker • vCard • VCard/Params
- Modul benötigt das Modul Marker utilities – Wartungskategorie, in der nochmals alle Module gelistet sind, die von diesem Modul abhängig sind.
Wartungsfunktionen
function mu.initMaintenance( name )
Die Funktion initialisiert die Zeichenkettenverwaltung für die Ausgabe von Fehlermeldungen. name
ist der zugehörige Modulname ohne die Namensraumbezeichnung. Diese Funktion setzt auch die Tabellen fehlerhafter Paramter und der Fehlermeldungen/Hinweise zurück.
function mu.addMaintenance( key, value )
Diese Funktion fügt die Fehlermeldung mit dem Schlüssel key
in die Tabelle der Fehlermeldungen und Hinweise ein. value
dient als Ersatz für einen Platzhalter %s
. Ein Teil der oben genannten Funktionen befüllt ebenfalls diese Tabelle.
function mu.makeMaintenance( page, modules )
Diese Funktion liefert eine Zeichenkette mit allen Fehlermeldungen und Hinweisen zurück. modules
ist eine Tabelle mit den Modulvariablen, aus denen weitere Wartungskategorien erhalten werden sollen.
function mu.getCategories( formatStr )
- Liefert eine Zeichenkette mit den Kategorie-Links aller verwendeten Wikidata-Eigenschaften zurück.
Funktionen für allgemeine Nutzung
function mu.isSet( arg )
liefert true
oder false
, je nachdem, ob das Argument arg
gesetzt ist oder nicht. arg
muss existieren und einen Wert ungleich ''
enthalten.
function mu.convertForSort( s )
Wandelt die Zeichenkette s
so um, dass eine korrekte Sortierung ermöglicht wird. Die auszutauschenden Buchstaben sind in der sprachabhängigen Tabelle substitutes
im Modul Marker utilities/i18n enthalten.
function mu.formatNumber( num )
Die Funktion ersetzt in der als Zeichenkette vorliegenden Zahl das Dezimalzeichen und fügt Tausendertrennzeichen ein.
function mu.tableInsert( tab, value )
fügt den Wert value
zur Tabelle tab
hinzu, wenn er existiert und nicht leer ist.
function mu.textSplit( s, sep )
trennt die Zeichenkette s
an Trennzeichen sep
auf und legt sie in einer Tabelle ab, die aber keine leeren Zeichenketten enthält. sep
muss genau die Länge von einem Zeichen (ein Byte) haben und darf kein magisches Pattern-Zeichen sein. Die Funktion ist deutlich schneller als mw.text.split()
.
function mu.split( s )
liefert eine assoziative Tabelle kommaseparierter Werte der Zeichenkette s
. Die Teilzeichenketten werden in Kleinbuchstaben umgewandelt, und Leerräume werden durch den Unterstrich ersetzt.
function mu.makeSpan( s, class, isBdi, attr, css )
liefert eine Zeichenkette, die von einem <span>
-Tag oder <bdi>
-Tag umschlossen ist. class
ist das Klassenattribut des Tags, während die Tabellen attr
und css
weitere Tap-parameter liefern.
function mu.languageSpan( s, titleHint, args, country, addClass )
fügt den Text in ein span-Tag, in dem die Sprache und Textrichtung des des Texts s
angegeben ist. titleHint
ist das title-Attribut des span-Tags, args
die Vorlagenparamtetertabelle und country
eine Tabelle mit landesspezifischen Angaben. addClass
stellt einen zusätzlichen Klassenbezeichner dar.
function mu.addWdClass( isFromWikidata )
liefert den Klassenbezeichner wikidata-content
, wenn isFromWikidata
auf true
gesetzt ist, ansonsten eine leere Zeichenkette.
function mu.getAliases( tab, key )
erstellt aus der Tabelle tab
eine Alias-Tabelle, die in tab
unter dem Schlüssel key
notiert sind.
function mu.yesno( val )
gibt y
oder n
zurück, wenn val
einen entsprechenden Wert besitzt, anderenfalls nil
.
Prüffunktionen
function mu.checkArguments( templateArgs, validKeys )
prüft, ob im Vorlagenaufruf unbekannte oder durch Aliase entstandene doppelte Parameternamen verwendet werden. Diese unbekannten bzw. doppelten Parameter werden in die Tabelle fehlerhafter bzw. doppelter Parameter eingefügt. templateArgs
ist die Tabelle der Vorlagenparamter, validKeys
die Tabelle der erlaubten Parameter (siehe z. B. Modul:VCard/i18n). Die Funktion liefert eine Tabelle der gültigen Argumente zurück.
function mu.checkCommonsCategory( args )
Die Funktion löscht eine evtl. vorhandene Namensraumangabe in der Commons-Kategorie args.commonscat
und fügt eine Wartungskategorie hinzu, wenn die Commons-Kategorie gesetzt wurde.
function mu.checkCoordinates( lat, long )
liefert die überprüften Koordinaten lat, long
. Im Fehlerfall sind lat
und long
leere Zeichenketten. Die Fehlermeldungstabelle enthält zusätzlich einen entsprechenden Eintrag (siehe unten).
function mu.checkZoom( args )
ersetzt args.zoom
mit einem Standardwert, falls args.zoom
nicht im Intervall von [0, 19] liegt.
function mu.checkImage( image, entity )
liefert den überprüften Wert für das image
oder eine leere Zeichenkette zurück. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten). Die Variable mi.options.imageCheck
legt fest, ob überhaupt einen rechenzeitintensive Prüfung vorgenommen wird.
function mu.checkStatus( args )
prüft die übergebenen Werte im Parameter status
und legt die gültigen Werte in args.statusTable
ab. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten).
function mu.checkStyles( args )
ersetzt Stil-Aliase durch CSS-Stile.
function mu.checkId( args )
prüft die Gültigkeit von args.id
und die nicht gleichzeitige Nutzung mit args.wikidata
. Im Fehlerfall wird args.id
gelöscht und eine Fehlermeldung ausgegeben.
function mu.checkTypeAndGroup( args )
liefert die überprüften Werte für den Typ und die Gruppen in args
zurück. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten).
function mu.checkUrl( args )
liefert die überprüfte Internetaddresse url
in der Argumenttabelle args
zurück. Im Fehlerfall enthält die Fehlermeldungstabelle zusätzlich einen entsprechenden Eintrag (siehe unten).
Typ- und Gruppenfunktionen
function mu.getTypeParams( aType )
liefert den Parametersatz aus Module:Marker utilities/Types für den Typ aType
oder nil
zurück.
function mu.getTypeLabel( id )
liefert das erste Label aus der Typenliste zum id
. id
kann ein Marker- oder vCard-Typ bzw. eine Wikidata-Id sein. Im Fehlerfall wird eine leere Zeichenkette oder der id
selbst zurückgegeben.
function mu.typeExists( aType )
liefert den Typ aType
oder nil
zurück, je nachdem, ob der Typ in der Typentabelle enthalten ist. Aliase werden in den zugehörigen Typ umgewandelt.
function mu.groupWithEvents( group )
prüft, ob in der angegebenen Gruppe group
Ereignisse als Typen vorgesehen sind.
function mu.getColor( args )
fügt color
und inverse
zu den Argumenten args
aus der Gruppe group
hinzu. color
ist die zur Gruppe gehörende Farbe und wird aus der Tabelle Modul:Marker utilities/Groups bezogen.
function mu.idToType( id )
liefert zur Wikidata-Id den zugehörigen Typ oder nil
zurück.
Funktionen zur Parameteraufbereitung
function mu.getShow( default, args, validValues )
liefert eine assoziierte Tabelle mit den übergebenen kommaseparierten show
-Attributen, wobei der überschreibbare Standardwert default
berücksichtigt wird. args
ist die Parametertabelle. Die args.show
-Attribute werden auf Gültigkeit hin überprüft.
function mu.removeCtrls( s, onlyInline )
Die Funktion entfernt Steuerzeichen und die HTML-Tags für den Zeilenumbruch und Zeilenwechsel aus der Zeichenkette s
. Wenn onlyInline = false
, dann bleiben Zeilenumbruch und Zeilenwechsel erhalten. Deren Vorkommen wird in der Variablen descrDiv
mitgeteilt. Die Funktion liefert zwei Werte zurück:
s
bereinigte Zeichenkette.descrDiv
Der Container für die Beschreibung muss ein<div>
-Tag sein.
Wikidata-Abfragefunktionen
function mu.getCoordinatesFromWikidata( entity )
liefert die im Wikidata-Datensatz mit der Einität entity
enthaltene Koordinate zurück. Zuerst wird versucht, die Zentrumskoordinate aus der Eigenschaft P5140
zu erhalten, danach die Koordinate aus der Eigenschaft P625
.
function mu.typeSearch( p31 )
Sucht in mehreren P31-P279-Ketten nach Q-ids, deren Werte in der Tabelle Module:Marker utilities/Types enthalten sein könnten. Die Tabelle p31
enthält die vorgefunden P31-Werte. Der erste Treffer wird als Zeichenkette zurückgegeben, im Fehlerfall die Zeichenkette error. Die Maximalanzahl höherer Ebenen für die Suche ist mit mi.searchLimit
vorgegeben, dies sind üblicherweise 4. Es wird nur jeweils die erste P279-Id ausgewertet, also nicht die gesamte Baumstruktur.
function mu.getCommonsCategory( args, entity )
Die Funktion versucht, eine Commons-Kategorie aus Wikidata über die Wikidata-Entität entity
zu beziehen. Zuerst wird der Commons-Site-Link analysiert, dann die Eigenschaft P373 und zuletzt die Eigenschaft P910.
function mu.getLangTable( wikiLang, localLang )
erstellt eine Tabelle mit Sprachbezeichnern unter Nutzung von wikiLang, localLang (country.lang) und den Sprachen langs aus dem Modul Marker utilities/i18n.
function mu.getNamesFromWikidata( args, fromWikidata, country, entity )
Die Funktion befüllt die Tabelle args
mit dem Namen der Einrichtung in der Wiki-Sprache und in der Landessprache mit den Angaben aus Wikidata. Die Tabelle fromWikidata
enthält die Information (fromWikidata.name
, fromWikidata.nameLocal
), ob der Name aus Wikidata stammt.
function mu.getArticleLink( args, entity )
Die Funktion übergibt den Sitelink zum zugehörigen Arikel an args.thisWiki
, außer der Vorlagenaufruf wurde in diesem Arikel vorgenommen.
Marker-Funktionen
function mu.makeMarkerSymbol( args, title, frame, show )
liefert r
: HTML-Quellcode des Marker-Symbols.
Symbolfunktionen
function mu.makeStatusIcons( args )
liefert eine Zeichenkette mit der Bildersyntax gemäß der Tabelle args.statusTable
zurück.
function mu.addLinkIcon( classes, link, title, text )
erstellt den HTML-Code für die Anzeige eines verlinkten Symbolbildes. Die Darstellung erfolgt im Zusammenspiel mit Stilvorlagen. Gefordert werden zu listing-icon
hinzuzufügende CSS-Klassen classes
, ein Link link
(entweder Internet- oder Artikellink), ein Tooltip-Text title
und der meist nicht sichtbare Linktext text
.
function mu.makeIcons( args, page, country, entity, show, fromWikidata )
liefert die verlinkten Schwesterprojekt-Symbole und die verlinkten Symbole von Social-Media-Diensten zurück. Die Angaben stammen meist aus den Sitelinks der Wikidata-Entity entity
. args
ist die Tabelle der übergebenen Vorlagenparameter. Die Tabelle country
enthält länderspezifische Daten wie die Sprachangabe. fromWikidata
die Tabelle der Parameter, die aus Wikidata bezogen wurden.
Ausgabefunktionen
function mu.prepareNames( args )
fügt der Argumente-Tabelle den Tabellen displayName
und givenName
hinzu, die aus den Parametern name
und nameMap
gebildet werden. Hauptaufgabe ist es, den eigentlichen Namen aus einem möglichen Link in Wikiyntax herauszulösen. Beide Tabellen bestehen aus folgenden drei Elementen:
name
:string
, nur der Name.all
:string
, verlinkter Name oder nur der Name, falls kein Link vorliegt.pageTitle
:string
, Artikel, auf den verlinkt wird. Der Titel kann vonname
verschieden sein.
function mu.makeName( result, args, show, page, country, nameClass, localClass )
fügt den Namen und Namensergänzungen zur Tabelle result
hinzu. Zu den Namensergänzungen gehören der alternative Name, der lokale Name, Kommentar und Flughafencodes, die in Klammern geschrieben und mit einem span
-Tag umschlossen werden. nameClass
und localClass
sind zusätzliche Klassenattribute für den Namen und den lokalen Namen.
function mu.parentheses( s, trim )
fügt die Zeichenkette s
in Klammern ein. Wenn trim = true
wird die Formatierungszeichenkette (üblicherweise ' (%s)') getrimmt.
function mu.dmsCoordinates( lat, long, name, fromWD, extraParams, noBrackets )
liefert die Zeichenkette r
, die eine zu den Kartenwerkzeugen verlinkte Dezimalkoordinate enthält, die wahlweise in Klammern gesetzt werden kann. name
ist der Name der Einrichtung, fromWD = true
besagt, dass die Koordinate aus Wikidata stammt und extraParams
enthält zusätzliche Parameter wird Maßstab und Region, die in den Kartenwerkzeugen ausgewertet werden.
function mu.makeWrapper( result, args, country, show, list, aClass, frame )
umgibt zum Inhalt eines Markers oder einer vCard mit einem umschließenden Tag (span, div).
Sonstige Funktionen
function mu.getPageData()
- Die obige Dokumentation wurde aus der Seite Modul:Marker utilities/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
--[[ Functions library for Marker and vCard modules In non-Wikivoyage projects, sister-project links functions have to be adapted. ]]-- -- require( 'strict' ) local cd = require( 'Module:Coordinates' ) local mg = require( 'Module:Marker utilities/Groups' ) local mi = require( 'Module:Marker utilities/i18n' ) local mm -- MAKI icons local mt = require( 'Module:Marker utilities/Types' ) -- types to groups like drink, eat, go, see, sleep, ... local uc -- URL check local wu = require( 'Module:Wikidata utilities' ) -- module variable and administration local mu = { moduleInterface = { serial = '2025-03-17', item = 58187612 }, comma = mi.texts.comma, commaSeparator = mi.texts.commaSeparator } local colorAdjust = { ['-webkit-print-color-adjust'] = 'exact', ['color-adjust'] = 'exact', ['print-color-adjust'] = 'exact' } -- maintenance tools function mu.initMaintenance( name ) mu.invalidParams = {} -- table of unknown parameters mu.duplicateAliases = {} -- table of duplicate parameter aliases mu.maintenance = {} -- table of error strings mu.types = mt.types mu.groups = mg.groups mu.typeAliases = nil -- table for type aliases. Create on demand mu.groupAliases = nil -- table for group aliases end local function contains( new ) for i = 1, #mu.maintenance do if mu.maintenance[ i ] == new then return true end end return false end function mu.addMaintenance( key, value ) local s = key -- fallback local tab = mi.maintenance[ key ] if tab then s = mi.formats.category:format( tab.category ) .. ( tab.err and mi.formats.error:format( tab.err ) or '' ) .. ( tab.hint and mi.formats.hint:format( tab.hint ) or '' ) end s = value and mw.ustring.format( s, value ) or s if not contains( s ) then table.insert( mu.maintenance, s ) end end function mu.getCategories( formatStr ) return wu.getCategories( formatStr ) end local function getMaintenance() if #mu.invalidParams == 1 then mu.addMaintenance( 'unknownParam', mu.invalidParams[ 1 ] ) elseif #mu.invalidParams > 1 then mu.addMaintenance( 'unknownParams', table.concat( mu.invalidParams, mu.commaSeparator ) ) end if #mu.duplicateAliases > 0 then mu.addMaintenance( 'duplicateAliases', table.concat( mu.duplicateAliases, mu.commaSeparator ) ) end return table.concat( mu.maintenance, '' ) end function mu.makeMaintenance( page, modules ) if mi.nsNoMaintenance[ page.namespace ] then return '' end local r = getMaintenance() if mi.options.usePropertyCateg then local m = mi.maintenance.properties -- format string for i, aModule in ipairs( modules ) do if aModule then r = r .. aModule.getCategories( m ) end end end return r end -- general-use functions function mu.isSet( arg ) return arg and arg ~= '' end function mu.convertForSort( s ) s = s:ulower() for i, obj in ipairs( mi.substitutes ) do s = mw.ustring.gsub( s, obj.l, obj.as ) end return s end -- replacing decimal separator and inserting group separators function mu.formatNumber( num ) if mu.isSet( mi.texts.decimalPoint ) and mi.texts.decimalPoint ~= '.' then num = num:gsub( '%.', mi.texts.decimalPoint ) end if mu.isSet( mi.texts.groupSeparator ) then local count repeat num, count = num:gsub( '^([-+]?%d+)(%d%d%d)', '%1%' .. mi.texts.groupSeparator .. '%2' ) until count == 0 end return num end function mu.tableInsert( tab, value ) if mu.isSet( value ) then table.insert( tab, value ) end end -- splitting string s at sep, removing empty substrings -- sep is a single character separator but no magic pattern character function mu.textSplit( s, sep ) local result = {} for str in s:gmatch( '([^' .. sep .. ']+)' ) do mu.tableInsert( result, mw.text.trim( str ) ) end return result end local function encodeSpaces( s ) s = s:gsub( '[_%s]+', '_' ) return s end local function replaceWithSpace( s, pattern ) s = s:find( pattern ) and s:gsub( pattern, ' ' ) or s return s end -- Splitting comma separated lists to a table and converting items function mu.split( s ) local arr = {} if not mu.isSet( s ) then return arr end for i, str in ipairs( mu.textSplit( s, ',' ) ) do arr[ encodeSpaces( str ) ] = 1 end return arr end function mu.makeSpan( s, class, isBdi, attr, css ) return tostring( mw.html.create( isBdi and 'bdi' or 'span' ) :addClass( class ) :attr( attr or {} ) :css( css or {} ) :wikitext( s ) ) end -- bdi and bdo tags are not working properly on all browsers. Adding marks -- (lrm, rlm) is maybe the only way for a correct output function mu.languageSpan( s, titleHint, page, country, addClass ) if not mu.isSet( s ) then return '' end local bdi = mw.html.create( 'bdi' ) :addClass( addClass ) :wikitext( s ) local c = country.lang if c == '' or c == page.lang then return tostring( bdi ) end local dir local fStr = '%s' if country.isRTL and not page.isRTL then dir = 'rtl' fStr = '‏%s‎' elseif not country.isRTL and page.isRTL then dir = 'ltr' fStr = '‎%s‏' end return fStr:format( tostring( bdi :addClass( 'voy-lang voy-lang-' .. c ) :attr( { title = mw.ustring.format( titleHint , country.langName ), lang = c, dir = dir } ) ) ) end function mu.addWdClass( isFromWikidata ) return isFromWikidata and ' voy-wikidata-content' or '' end function mu.getAliases( tab, key ) local result = {} if not tab then return result end local v for k, tb in pairs( tab ) do v = tb[ key ] if v then if type( v ) == 'table' then for i = 1, #v do result[ v[ i ] ] = k end else result[ v ] = k end end end return result end function mu.yesno( val ) return mi.yesno[ val:ulower() ] end -- check functions local function emphasize( s ) return mw.ustring.format( mi.texts.emph, s ) end -- args: template arguments consisting of argument name as key and a value -- validKeys: table with argument name as key used by the script and -- a string or a table of strings for argument names used by the local wiki function mu.checkArguments( templateArgs, validKeys ) local args = {} if not templateArgs or not validKeys or not next( validKeys ) then return args end local keys = {} -- list of wiki-dependent parameter names for key, params in pairs( validKeys ) do if type( params ) == 'string' then keys[ params ] = key else for i = 1, #params do keys[ params[ i ] ] = key end end end local targetKey for key, arg in pairs( templateArgs ) do targetKey = keys[ key ] if targetKey then if args[ targetKey ] then -- prevents duplicates table.insert( mu.duplicateAliases, emphasize( key ) ) else args[ targetKey ] = arg end else table.insert( mu.invalidParams, emphasize( key ) ) end end -- normalize values for i, param in ipairs( mi.options.normalizeValues ) do if mu.isSet( args[ param ] ) then args[ param ] = replaceWithSpace( args[ param ]:ulower(), '[_%s]+' ) end end return args end local function removeNS( s, nsTable ) if not s:find( ':', 1, true ) then return s end local t = s for i = 1, #nsTable do t = mw.ustring.gsub( t, '^' .. nsTable[ i ] .. ':', '' ) if s ~= t then return t end end return t end function mu.checkCommonsCategory( args ) -- remove namespace from category if mu.isSet( args.commonscat ) then args.commonscat = removeNS( args.commonscat, mi.texts.CategoryNS ) end end -- checking coordinates function mu.checkCoordinates( args ) local function clearCoordinates() args.lat, args.long = '', '' end local t if type( args.lat ) == 'boolean' or type( args.long ) == 'boolean' then clearCoordinates() end if args.lat == '' and args.long == '' then return elseif args.lat ~= '' and args.long == '' then t = args.lat:find( ',', 1, true ) if t then args.long = mw.text.trim( args.lat:sub( t + 1, #args.lat ) ) args.lat = mw.text.trim( args.lat:sub( 1, t - 1 ) ) end end if args.lat == '' or args.long == '' then clearCoordinates() mu.addMaintenance( 'wrongCoord' ) return end local dms = false t = tonumber( args.lat ) if t then args.lat = math.abs( t ) <= 90 and t or '' else t = cd.toDec( args.lat, 'lat', 6 ) args.lat = t.error == 0 and t.dec or '' dms = args.lat ~= '' end if args.lat ~= '' then t = tonumber( args.long ) if t then args.long = ( t > -180 and t <= 180 ) and t or '' else t = cd.toDec( args.long, 'long', 6 ) args.long = t.error == 0 and t.dec or '' dms = dms or args.long ~= '' end end if args.lat == '' or args.long == '' then clearCoordinates() mu.addMaintenance( 'wrongCoord' ) elseif dms then mu.addMaintenance( 'dmsCoordinate' ) end end -- check zoom level function mu.checkZoom( args ) args.zoom = math.floor( tonumber( args.zoom ) or mi.map.defaultZoomLevel ) if args.zoom < 0 or args.zoom > mi.map.maxZoomLevel then args.zoom = mi.map.defaultZoomLevel end end -- image check function mu.checkImage( args, entity ) if type( args.image ) == 'boolean' or args.image == '' then return end -- formal checks if args.image:find( '^https?:' ) then args.image = '' else -- remove namespace args.image = removeNS( args.image, mi.texts.FileNS ) local extensionExists = false local im = args.image:lower() for i = 1, #mi.fileExtensions do if im:find( '%.' .. mi.fileExtensions[ i ] .. '$' ) then extensionExists = true break end end if not extensionExists then args.image = '' end end if args.image == '' then mu.addMaintenance( 'wrongImgName' ) return end local alreadyChecked = false if mi.options.mediaCheck and args.image ~= '' then if not mi.options.WDmediaCheck and entity then -- check if image is stored in Wikidata local imgs = wu.getValues( entity, mi.properties.image, nil ) for i = 1, #imgs do if imgs[ i ] == args.image then alreadyChecked = true break end end end if not alreadyChecked then -- expensive function call local title = mw.title.new( 'Media:' .. args.image ) if not title or not title.exists then mu.addMaintenance( 'missingImg', args.image ) args.image = '' end end end end function mu.checkStatus( args ) args.statusTable = {} local hash = {} if mu.isSet( args.status ) then local statusAliases = mu.getAliases( mi.statuses, 'alias' ) for i, t in ipairs( mu.textSplit( args.status, ',' ) ) do t = encodeSpaces( t ) t = statusAliases[ t ] or t if mi.statuses[ t ] then if not hash[ t ] then table.insert( args.statusTable, t ) hash[ t ] = 'x' end else mu.addMaintenance( 'unknownStatus' ) end end if #args.statusTable > 1 then table.sort( args.statusTable, function( a, b ) local at = mi.statuses[ a ] local bt = mi.statuses[ b ] return ( at.g < bt.g ) or ( at.g == bt.g and a < b ) end ) end end end function mu.checkStyles( args ) if mu.isSet( args.styles ) then if mi.nameStyles[ args.styles:lower() ] then args.styles = args.styles:lower() args.styleClass = ' listing-name-style-' .. args.styles args.styles = mi.nameStyles[ args.styles ] end else args.styles = nil end end function mu.checkId( args ) if mu.isSet( args.id ) and mu.isSet( args.wikidata ) then mu.addMaintenance( 'wikidataWithId' ) args.id = '' end if mu.isSet( args.id ) and args.id:find( '[!-,.-/:-?[-^{-\127%z\1-\31]' ) then mu.addMaintenance( 'wrongId' ) args.id = '' end end -- groups translation for map legend into Wiki language local function translateGroup( group ) if not mu.isSet( group ) then group = mt.types.error.group end local t = mg.groups[ group ] if t then t = t.map or t.label or t.alias or group if type( t ) == 'table' then t = t[ 1 ] end return t end return group end local function getTypeFromTypeAliases( aType ) if not mu.typeAliases then mu.typeAliases = mu.getAliases( mt.types, 'alias' ) end return mu.typeAliases[ aType ] end local function getGroupFromGroupAliases( group ) if not mu.groupAliases then mu.groupAliases = mu.getAliases( mg.groups, 'alias' ) end return mu.groupAliases[ group ] end -- getting marker type and group function mu.checkTypeAndGroup( args ) local s, t args.typeTable = {} args.subtypeTable = {} -- check group if mu.isSet( args.group ) then mu.addMaintenance( 'parameterUsed', 'group' ) s = mg.groups[ args.group ] if not s then s = getGroupFromGroupAliases( args.group ) if s then args.group = s s = mg.groups[ args.group ] end end if not s then mu.addMaintenance( 'unknownGroup' ) args.group = '' elseif s.is and s.is == 'color' and mi.options.useTypeCateg then mu.addMaintenance( 'group', args.group ) end end -- check type if not mu.isSet( args.type ) then args.type = mt.types.error.group mu.addMaintenance( 'missingType' ) elseif args.type == mt.types.error.group then mu.addMaintenance( 'unknownType', args.type ) end if args.type == mt.types.error.group then args.group = mt.types.error.group else -- split seperate types and analyse them for i, t in ipairs( mu.textSplit( args.type, ',' ) ) do -- try to find the type t in types or groups t = encodeSpaces( t ) if not mt.types[ t ] then t = getTypeFromTypeAliases( t ) or getGroupFromGroupAliases( t ) or t end s = mg.groups[ t ] if s then -- type is a group itself if s.is and s.is == 'color' then if mi.options.excludeColorTypes then args.group = mt.types.error.group mu.addMaintenance( 'unknownType', t ) else mu.addMaintenance( 'typeIsColor' ) end elseif not mi.options.noTypeMsgs then mu.addMaintenance( 'typeIsGroup' ) end if args.group == '' then args.group = t end else s = mt.types[ t ] if s then if args.group == '' then args.group = s.group end if mu.isSet( s.subtype ) then table.insert( args.subtypeTable, t ) end else args.group = mt.types.error.group mu.addMaintenance( 'unknownType', t ) end end table.insert( args.typeTable, t ) if mi.options.useTypeCateg then mu.addMaintenance( 'type', t ) end end args.type = table.concat( args.typeTable, ',' ) end args.groupTranslated = translateGroup( args.group ) mu.getColor( args ) end local function isUrl( url ) uc = uc or require( 'Module:UrlCheck' ) return uc.isUrl( url, mi.options.skipPathCheck ) end -- url check in args function mu.checkUrl( args ) if not mu.isSet( args.url ) then return end local c = isUrl( args.url ) -- getting result code if c > 2 then mu.addMaintenance( 'wrongUrl' ) args.url = '' elseif c == 2 then -- URL contains IP address mu.addMaintenance( 'urlWithIP' ) else for i = 1, #mi.services do if args.url:find( mi.services[ i ].key .. '.com', 1, true ) then mu.addMaintenance( 'urlIsSocialMedia' ) args.url = '' end end end end -- type and group functions -- getting a set of parameters for a given type function mu.getTypeParams( aType ) return mt.types[ aType ] end function mu.idToType( id ) if not mu.typeIds then mu.typeIds = mu.getAliases( mt.types, 'wd' ) -- Q id to type table end return mu.typeIds[ id ] end function mu.getTypeLabel( id ) if not mu.isSet( id ) then return '' end if id:match( '^Q%d+$' ) then id = mu.idToType( id ) if not id then return '' end else id = encodeSpaces( id ) end local at, t t = mt.types[ id ] if not t then t = getTypeFromTypeAliases( id ) t = t and mt.types[ t ] end if t then t = t.label or id at = t:find( ',', 1, true ) if at then t = t:sub( 1, at - 1 ) end else t = replaceWithSpace( id, '_' ) end return t end function mu.typeExists( aType ) return mt.types[ aType ] and aType or getTypeFromTypeAliases( aType ) end -- check if the specified group can have events function mu.groupWithEvents( group ) return mg.groups[ group ] and mg.groups[ group ].withEvents end -- see: https://www.w3.org/TR/WCAG20/#relativeluminancedef local function hexToLinear( hex ) hex = tonumber( hex, 16 ) / 255.0 if hex <= 0.03928 then return hex / 12.92 else return ( ( hex + 0.055 ) / 1.055 ) ^ 2.4 end end -- relative luminance of a color -- 6 digit hex color local function hexToLuminance( color ) local r = hexToLinear( color:sub( 1, 2 ) ) local g = hexToLinear( color:sub( 3, 4 ) ) local b = hexToLinear( color:sub( 5, 6 ) ) return 0.2126 * r + 0.7152 * g + 0.0722 * b end local function isInverse( backgroundColor ) -- magenta, error: 0.2848 -- the threshold should not be greater than 0.4 local luminanceThreshold = hexToLuminance( 'FF00FF' ) backgroundColor = backgroundColor:sub( 2 ) -- remove # :gsub( '^(%x)(%x)(%x)$', '%1%1%2%2%3%3' ) local luminance = hexToLuminance( backgroundColor ) return hexToLuminance( backgroundColor ) > luminanceThreshold end -- getting color from group in args function mu.getColor( args ) local c = mg.groups[ args.group ] or mg.groups[ 'error' ] args.color = c.color args.inverse = isInverse( c.color ) end -- Splitting comma separated lists to a table of key items -- checking items with allowed key values of validValues table local function splitAndCheck( s, validValues ) local values = {} if not validValues then return values, '' end local errors = {} for item, v in pairs( mu.split( s ) ) do -- value check if validValues[ item ] then values[ item ] = 1 else table.insert( errors, item ) end end return values, table.concat( errors, mu.commaSeparator ) end local function setCopyMarker( args, show ) if show.copy and ( mu.isSet( args.copyMarker ) or not mu.isSet( args.wikidata ) ) then show.copy = nil mu.addMaintenance( 'deleteShowCopy' ) end if show.copy then args.copyMarker = args.wikidata end if mu.isSet( args.copyMarker ) then show.symbol = nil end end -- treatment of social media services if Wikidata is available local function setSocialMedia( args, value ) for i, service in ipairs( mi.services ) do args[ service.key ] = value end end function mu.getShow( default, args, validValues ) local show = mu.split( default ) local add, err = splitAndCheck( args.show, validValues ) if err ~= '' then mu.addMaintenance( 'unknownShow', err ) end -- maintenance if add.inline then show.inlineSelected = true mu.addMaintenance( 'showInlineUsed' ) end if add.poi then mu.addMaintenance( 'showPoiUsed' ) -- is default end -- overwriting defaults if add.none or add.coord or add.poi or add.all then show.all, show.coord, show.poi = nil, nil, nil end -- merge show with add values for key, value in pairs( add ) do show[ key ] = value end if show.none then show.none, show.all, show.coord, show.poi = nil, nil, nil, nil end if show.all then show.all = nil show.coord, show.poi = 1, 1 end if show.noname then show.noname, show.name = nil, nil else show.name = 1 end setCopyMarker( args, show ) if args.wikidata ~= '' then if show.socialmedia then setSocialMedia( args, 'y' ) elseif show.nosocialmedia then setSocialMedia( args, 'n' ) end end if not show.nosocialmedia then show.socialmedia = 1 end return show end -- removing line breaks and controls from parameter strings function mu.removeCtrls( s, onlyInline ) local descrDiv = false -- div tag instead of span tag for description needed? -- remove controls from tags before test s = s:gsub( '(<[^>]+>)', function( t ) return replaceWithSpace( t, '[%z\1-\31]' ) end ) local t = replaceWithSpace( s, '</br%s*>' ) if onlyInline then t = replaceWithSpace( t, '<br[^/>]*/*>' ) t = replaceWithSpace( t, '</*p[^/>]*/*>' ) t = replaceWithSpace( t, '[%z\1-\31]' ) -- not %c because \127 is used for Mediawiki tags (strip markers `UNIQ) else t = replaceWithSpace( t, '[%z\1-\9\11\12\14-\31]' ) descrDiv = t:find( '[\10\13]' ) or t:find( '<br[^/>]*/*>' ) or t:find( '<p[^/>]*>' ) end if t ~= s then mu.addMaintenance( 'illegalCtrls' ) end -- remove LTR and RTL marks t = mw.ustring.gsub( t, '[]+', '' ) if descrDiv then -- unify line breaks to Linux mode t = t:gsub( '\13\10', '\10' ):gsub( '\13', '\10' ) -- replace line breaks by <br> in block mode t = t:gsub( '([^>%]\10])\10+([^<%[\10])', '%1<br class="listing-next-paragraph" />%2' ) end return replaceWithSpace( t, '%s%s+' ), descrDiv end -- fetch data from Wikidata function mu.getCoordinatesFromWikidata( args, fromWikidata, entity ) if not entity or ( args.lat ~= '' and args.long ~= '' ) then return end -- center coordinates from Wikidata local c = wu.getValue( entity, mi.properties.centerCoordinates ) if c == '' then -- coordinates from Wikidata c = wu.getValue( entity, mi.properties.coordinates ) end if c ~= '' then args.lat, args.long = c.latitude, c.longitude fromWikidata.lat = true end end local function typeSearch( p31 ) -- p31: array of Wikidata values if not p31 or #p31 == 0 then return '' end local firstStep = true local function compareLabels( ar ) if not ar then return nil elseif type( ar ) == 'string' then ar = { ar } end for i, value in ipairs( ar ) do if mu.isSet( value ) then value = ( encodeSpaces( value ) ) if mt.types[ value ] then return value end end end return nil end local function compareIds( ar ) local id, t for i = 1, #ar do id = ar[ i ].id -- md: indexed array of q id - types relations t = mu.idToType( id ) if t then return t end -- checking English label and aliases t = compareLabels( mw.wikibase.getLabelByLang( id, 'en' ) ) or compareLabels( wu.getAliases( id, 'en' ) ) if t then if firstStep and not mi.options.noTypeMsgs then firstStep = false mu.addMaintenance( 'typeFromWDchain' ) end return t end end return nil end local aType = compareIds( p31 ) -- check p31 ids first, maybe step 2 is not nessary if aType then return aType end -- now functions becomes expensive because of multiple wu.getValues calls local id, ids firstStep = false for i = 1, #p31 do -- step 2: analyse P279 chains of first ids id = p31[ i ].id -- start id local j = 0 repeat ids = wu.getValues( id, mi.properties.subclassOf, nil ) if #ids > 0 then aType = compareIds( ids ) if aType then if not mi.options.noTypeMsgs then mu.addMaintenance( 'typeFromWDchain' ) end return aType end id = ids[ 1 ].id end j = j + 1 -- limit: maximum levels to analyse until j >= mi.options.searchLimit or #ids == 0 end return '' end function mu.getTypeFromWikidata( args, entity ) if mu.isSet( args.type ) then return end local p31 = wu.getValues( entity, mi.properties.instanceOf ) if #p31 == 0 then p31 = wu.getValues( entity, mi.properties.subclassOf ) end args.type = typeSearch( p31 ) end function mu.getCommonsCategory( args, entity ) -- getting commonscat from commonswiki sitelink before P373 -- because sitelink is checked by Wikidata if type( args.commonscat ) == 'boolean' then args.commonscat = '' end local t = wu.getSitelink( entity, 'commonswiki' ) or '' if t:match( '^Category:.+$' ) then t = t:gsub( '^[Cc]ategory:', '' ) else t = wu.getValue( entity, mi.properties.commonsCategory ) if t == '' then local id = wu.getId( entity, mi.properties.mainCategory ) if id ~= '' then t = wu.getSitelink( id, 'commonswiki' ) or '' t = t:gsub( '^[Cc]ategory:', '' ) end end end if t ~= '' and args.commonscat ~= '' then mu.addMaintenance( 'commonscatWD' ) end if args.commonscat == '' then args.commonscat = t end end function mu.getLangTable( wikiLang, localLang ) local langs = { wikiLang } for i, lang in ipairs( mi.langs ) do table.insert( langs, lang ) end if mu.isSet( localLang ) and localLang ~= wikiLang then table.insert( langs, localLang ) end return langs end -- getting names from Wikidata function mu.getNamesFromWikidata( args, fromWikidata, page, country, entity ) -- getting official names local officialNames = wu.getMonolingualValues( entity, mi.properties.officialName ) if type( args.name ) == 'boolean' or args.name == '' then args.name = '' local langs = mu.getLangTable( page.lang ) for i, lang in ipairs( langs ) do args.name = officialNames[ lang ] if args.name then break end end -- if failed then get labels if not mu.isSet( args.name ) then for i, lang in ipairs( langs ) do args.name = wu.getLabel( entity, lang, true ) -- no fallback if args.name then break end end args.name = args.name or '' end if args.name ~= '' then mu.addMaintenance( 'nameFromWD' ) fromWikidata.name = true end end -- get local name if no name is available if args.name == '' and not ( type( args.nameLocal ) == 'string' and args.nameLocal ~= '' ) then args.nameLocal = true -- no local name if country and wiki language are identical elseif args.nameLocal == true and page.lang == country.lang then args.nameLocal = '' end if type( args.nameLocal ) == 'string' and args.nameLocal ~= '' then -- keeping local name in any case for html data args.addNameLocal = args.nameLocal return end local nameLocal = officialNames[ country.lang ] or '' if nameLocal == '' then nameLocal = wu.getLabel( entity, country.lang, true ) or '' end if args.nameLocal == true then args.nameLocal = nameLocal if args.name == '' and args.nameLocal ~= '' then args.name = args.nameLocal args.nameLocal = '' mu.addMaintenance( 'nameFromWD' ) fromWikidata.name = true end if args.name:ulower() == args.nameLocal:ulower() then args.nameLocal = '' end fromWikidata.nameLocal = args.nameLocal ~= '' elseif page.lang ~= country.lang and args.name ~= nameLocal then args.addNameLocal = nameLocal end end -- getting link to Wikivoyage function mu.getArticleLink( args, entity, page ) local title, isRedirect = wu.getCheckedSitelink( entity, page.lang .. page.globalProject ) if title and title ~= page.text then args.wikiPage = title if isRedirect == true then args.linkIsRedirect = true end end end -- marker functions -- returns a single data set from Module:Marker utilities/Maki icons local function getMaki( key ) mm = mm or require( 'Module:Marker utilities/Maki icons' ) key = key:lower():gsub( '[_%s]+', '-' ) return mm.icons[ key ] end local function getMakiIconId( key ) if not mu.isSet( key ) then return nil end key = key:lower():gsub( '[_%s]+', '-' ) if getMaki( key ) then return key end key = mt.types[ key ] and mt.types[ key ].icon if key and getMaki( key ) then return key end return nil end local function addIconToMarker( args ) args.text = ( '[[File:Maki7-%s.svg|x14px|link=|class=noviewer]]' ):format( args.symbol ) args.isIcon = true end -- distinguishing marker symbols, default: number local function makeMarkerProperties( args, show ) args.symbol = args.symbol or '' local noSymbol = args.symbol == '' if args.symbol == '' and show.poi and show.symbol then args.symbol = getMakiIconId( args.typeTable[ 1 ] ) or '' end local isIcon = getMaki( args.symbol ) if args.symbol == '' or args.symbol == 'number' then args.symbol = '-number-' .. args.group elseif args.symbol == 'letter' then args.symbol = '-letter-' .. args.group elseif args.symbol:len() == 1 and args.symbol:match( '%w' ) then args.text = args.symbol:upper() mu.addMaintenance( 'numberUsed' ) elseif args.symbol ~= '' and args.text == '' and isIcon then addIconToMarker( args ) elseif args.symbol ~= '' and not isIcon then args.text = mi.texts.closeX args.isIcon = true args.group = 'error' mu.getColor( args ) mu.addMaintenance( noSymbol and 'unknownMAKI' or 'unknownIcon' ) end end -- making marker symbol function mu.makeMarkerSymbol( args, show, frame ) local extraClasses = args.inverse and ' listing-map-inverse' or ' listing-map-not-inverse' if args.group == 'error' then extraClasses = extraClasses .. ' listing-map-is-error' end local title = args.givenName.all if mu.isSet( args.copyMarker ) then local copyClass = 'listing-map plainlinks printNoLink voy-copy-marker' .. extraClasses return tostring( mw.html.create( 'span' ) :addClass( copyClass ) :css( colorAdjust ) -- display will be replaced by [[MediaWiki:Gadget-GeneralChanges.js]] script :css( { display = 'none' } ) :attr( { ['data-copy-marker-attribute'] = args.copyMarker:match( 'Q%d+' ) and 'data-wikidata' or 'data-name', title = mi.texts.tooltip, ['data-copy-marker-content'] = args.copyMarker } ) ) end makeMarkerProperties( args, show ) if args.isIcon then extraClasses = extraClasses .. ' listing-map-is-symbol' end local lon = tonumber( args.long ) local lat = tonumber( args.lat ) local tagArgs = { zoom = tonumber( args.zoom ), latitude = lat, longitude = lon, show = mg.showAll, } if mu.isSet( args.text ) then tagArgs.text = args.text tagArgs.class = 'no-icon' end if mu.isSet( args.mapGroup ) then tagArgs.group, tagArgs.show = args.mapGroup, args.mapGroup else tagArgs.group = args.groupTranslated end if mu.isSet( args.image ) then tagArgs.description = '[[File:' .. args.image .. '|100x100px|' .. title .. ']]' end local geoJson = { type = 'Feature', geometry = { type = 'Point', coordinates = { lon, lat } }, properties = { title = title, description = tagArgs.description, ['marker-symbol'] = args.symbol, ['marker-color'] = args.color, ['marker-size'] = 'medium', } } return tostring( mw.html.create( 'span' ) :addClass( 'listing-map plainlinks printNoLink' .. extraClasses ) :attr( 'title', mi.texts.tooltip ) :css( colorAdjust ) :css( { ['background-color'] = args.color, color = args.inverse and '#000' or '#fff' } ) -- frame:extensionTag is expensive :wikitext( frame:extensionTag( 'maplink', mw.text.jsonEncode( geoJson ), tagArgs ) ) ) end -- icon functions function mu.makeStatusIcons( args ) local result = '' for i, v in ipairs( args.statusTable ) do result = result .. mu.makeSpan( ' ', 'listing-status listing-status-' .. v, false, { title = mi.statuses[ v ].label }, colorAdjust ) if mi.statuses[ v ].category then result = result .. ( '[[Category:%s]]' ):format( mi.statuses[ v ].label ) end end return result end function mu.addLinkIcon( classes, link, title, text, addSpace ) local span = mu.makeSpan( ' ', nil, false, { title = title, ['data-icon'] = text }, colorAdjust ) -- space to keep the span tag local lFormat = ( link:find( '^https?://' ) or link:find( '^//' ) ) and '[%s %s]' or '[[%s|%s]]' local iconLink = mw.ustring.format( lFormat, link, span ) if addSpace then iconLink = mu.makeSpan( ' ', 'listing-icon-with-space', true ) .. iconLink end return mu.makeSpan( iconLink, 'listing-icon ' .. classes ) end -- adding linked sister icons local function addSisterIcons( icons, sisters, name, id ) local icon for i, key in ipairs( { 'wikivoyage', 'wikipedia', 'commons', 'wikidata' } ) do if mu.isSet( sisters[ key ] ) then icon = mu.addLinkIcon( 'listing-sister-icon listing-sister-' .. key, sisters[ key ], mw.ustring.format( mi.iconTitles[ key ], name, id ), key, key == 'wikidata' ) -- add leading space table.insert( icons, icon ) end end -- return true if only Wikidata icon return mu.isSet( sisters.wikidata ) and #icons == 1 end -- getting sister project links local function getWikiLink( langArray, wiki, entity, wikilang ) local prefix = wiki == 'wiki' and 'w:' or 'voy:' local link for i, lang in ipairs( langArray ) do if lang ~= '' then link = wu.getFilteredSitelink( entity, lang .. wiki ) if link then prefix = prefix .. ( lang ~= wikilang and ( lang .. ':' ) or '' ) return prefix .. link end end end return '' end -- adding Wikimedia sister project icons local function makeSisterIcons( icons, args, page, country, entity ) local sisters = { commons = '', -- link to Commons category wikidata = '', -- link to Wikidata wikipedia = '', -- link to Wikipedia wikivoyage = '' -- link to another branch, usually en, as a sister link } if mu.isSet( args.wikipedia ) then sisters.wikipedia = 'w:' .. args.wikipedia end if mu.isSet( args.wikidata ) then local langs = mu.getLangTable( page.lang, country.lang ) if sisters.wikipedia == '' then sisters.wikipedia = getWikiLink( langs, 'wiki', entity, page.lang ) end if args.wikiPage == '' then table.remove( langs, 1 ) -- exclude page.lang sisters.wikivoyage = getWikiLink( langs, page.globalProject, entity, page.lang ) if sisters.wikivoyage ~= '' then mu.addMaintenance( 'linkToOtherWV' ) end end sisters.wikidata = 'd:' .. args.wikidata end if args.commonscat ~= '' then sisters.commons = 'c:Category:' .. args.commonscat end return addSisterIcons( icons, sisters, args.givenName.name, args.wikidata ) end -- creating social media icons including value check local function makeSocial( icons, args, fromWikidata, name ) local domain, span, t, which for i, service in ipairs( mi.services ) do -- check values first t = args[ service.key ] or '' domain = type( service.url ) == 'string' and service.url or service.url[ 1 ] domain = domain:gsub( 'com/.*', 'com/' ) if t ~= '' then if t:match( '^http' ) then if not t:find( 'https', 1, true ) then t = t:gsub( '^http', 'https' ) mu.addMaintenance( 'wrongSocialUrl', service.key ) end if isUrl( t ) > 1 or not t:match( '^' .. domain .. '.+$' ) then t = '' mu.addMaintenance( 'wrongSocialUrl', service.key ) end if t ~= '' and not fromWikidata[ service.key ] then mu.addMaintenance( 'socialUrlUsed', service.key ) end else local match = false local sp = service.pattern if type( sp ) == 'string' then sp = { sp } end for i = 1, #sp do if mw.ustring.match( t, sp[ i ] ) then match = true which = i break end end if not match then t = '' mu.addMaintenance( 'wrongSocialId', service.key ) end end end args[ service.key ] = t -- create symbol link if t ~= '' then if not t:find( domain, 1, true ) then -- multiple service urls local formatStr = type( service.url ) == 'string' and service.url or service.url[ which ] t = mw.ustring.format( formatStr, t ) end span = mu.addLinkIcon( 'listing-social-media listing-social-media-' .. service.key .. mu.addWdClass( fromWikidata[ service.key ] ), t, mw.ustring.format( mi.iconTitles[ service.key ], name ), service.key ) table.insert( icons, span ) end end end function mu.makeIcons( args, page, country, entity, show, fromWikidata ) local icons = {} local onlyWikidata = mi.options.showSisters and not show.nositelinks and makeSisterIcons( icons, args, page, country, entity ) if show.socialmedia then makeSocial( icons, args, fromWikidata, args.givenName.name ) end if #icons > 0 then return ( onlyWikidata and '' or mi.texts.space ) .. table.concat( icons, '' ) else return '' end end -- output functions local function removeStars( args ) for i, param in ipairs( mi.options.noStarParams ) do if mu.isSet( args[ param ] ) and args[ param ]:find( '*', 1, true ) then args[ param ] = args[ param ]:gsub( '%*+', '' ) args[ param ] = mw.text.trim( args[ param ] ) mu.addMaintenance( 'nameWithStar' ) end end end local function makeAnchorId( name ) if name and not name:match( '^Q%d+$' ) then name = mw.uri.anchorEncode( name ) end return mw.ustring.format( mi.texts.anchor, name ) end -- getting name table with linked and unlinked names local function getName( name, pageTitle ) local r = { all = '', -- name or linked name name = '', -- only name pageTitle = '', -- if pageTitle ~= '' name is already linked } if type( name ) ~= 'string' or name == '' then return r end local s local t, c = mw.ustring.gsub( name, '^(.*)%[%[(.*)%]%](.*)$', '%2' ) if c > 0 then -- is / contains [[...]] t = mw.text.trim( t ) r.all = '[[' .. t .. ']]' s, c = mw.ustring.gsub( t, '^(.*)|(.*)$', '%2' ) if c > 0 then -- is [[...|...]] r.name = mw.text.trim( s ) r.pageTitle = mw.ustring.gsub( t, '^(.*)|(.*)$', '%1' ) r.pageTitle = mw.text.trim( r.pageTitle ) else r.name = t r.pageTitle = t end else r.name = name r.all = name if mu.isSet( pageTitle ) then r.pageTitle = pageTitle r.all = '[[' .. pageTitle .. '|' .. name .. ']]' r.fromWD = true end end removeStars( r, { 'name', 'all' } ) return r end -- create givenName, displayName tables function mu.prepareNames( args ) local function simplifyString( s, length ) s = mw.ustring.sub( s, 1, length ) s = mw.ustring.gsub( s, "[%.'" .. '"“”„‟«»‘’‚‛‹›]', '' ) s = mw.ustring.gsub( s, '[-‐‑‒–—―]', ' ' ) return s:ulower():gsub( '%s%s+', ' ' ) end local function removeDuplicate( value1, key2 ) if not mu.isSet( value1 ) or not mu.isSet( args[ key2 ] ) then return end local minLen = math.min( mw.ustring.len( value1 ), mw.ustring.len( args[ key2 ] ) ) if simplifyString( value1, minLen ) == simplifyString( args[ key2 ], minLen ) then args[ key2 ] = '' end end -- use local name if name is not given if args.name == '' and args.nameLocal ~= '' then args.name = args.nameLocal args.nameLocal = '' end if args.name == args.nameMap then args.nameMap = '' end -- missing name if args.name == '' then args.name = mi.texts.missingName mu.addMaintenance( 'missingNameMsg' ) end -- names shall not contain tags or template calls if args.name:find( '<', 1, true ) or args.name:find( '{{', 1, true ) or args.nameMap:find( '<', 1, true ) or args.nameMap:find( '{{', 1, true ) or args.nameLocal:find( '<', 1, true ) or args.nameLocal:find( '{{', 1, true ) then mu.addMaintenance( 'malformedName' ) end removeStars( args ) -- handling linked names like [[article|text]] args.displayName = getName( args.name, args.wikiPage ) if mu.isSet( args.nameMap ) then args.givenName = getName( args.nameMap, args.wikiPage ) else -- real copy args.givenName = {} args.givenName.all = args.displayName.all args.givenName.name = args.displayName.name args.givenName.pageTitle = args.displayName.pageTitle end -- reset args.linkIsRedirect if Wikidata link is not used -- args.linkIsRedirect is set by mu.getArticleLink() if not args.displayName.fromWD then args.linkIsRedirect = nil end -- remove identical names removeDuplicate( args.givenName.name, 'nameLocal' ) removeDuplicate( args.givenName.name, 'alt' ) removeDuplicate( args.givenName.name, 'comment' ) removeDuplicate( args.nameLocal, 'alt' ) removeDuplicate( args.alt, 'comment' ) removeDuplicate( args.directions, 'directionsLocal' ) removeDuplicate( args.address, 'addressLocal' ) end local function _makeAirport( code, args ) local span = mu.makeSpan( args[ code ], 'listing-' .. code .. '-code' .. mu.addWdClass( true ) ) return mu.makeSpan( mw.ustring.format( mi.texts[ code ], span ), 'listing-airport listing-' .. code ) end local function makeAirport( args, show ) if show.noairport then return '' else local t = args.type:gsub( ',.*$', '' ) -- only first type if mt.types[ t ] and not mt.types[ t ].useIATA then return '' end end if mu.isSet( args.iata ) then return _makeAirport( 'iata', args ) elseif mu.isSet( args.icao ) then return _makeAirport( 'icao', args ) else return '' end end -- creating a list of nick names etc. local function makeNameAffix( args, page, country, addClass, show ) local result = {} if mi.options.showLocalData then if mu.isSet( args.nameLocal ) then table.insert( result, mu.languageSpan( args.nameLocal, mi.texts.hintName, page, country, 'listing-name-local' .. addClass ) ) end if mu.isSet( args.nameLatin ) then table.insert( result, mu.makeSpan( args.nameLatin, 'listing-name-latin', false, { title = mi.texts.hintLatin, lang = mu.isSet( country.lang ) and ( country.lang .. '-Latn' ) or nil } ) ) end end for i, key in ipairs( { 'alt', 'comment' } ) do if mu.isSet( args[ key ] ) then table.insert( result, mu.makeSpan( args[ key ], 'listing-' .. key ) ) end end mu.tableInsert( result, makeAirport( args, show ) ) if #result == 0 then return '' end return mu.makeSpan( mu.parentheses( table.concat( result, mu.comma ) ), 'p-nickname nickname listing-add-names' ) end -- replace brackets in names local function replaceBrackets( s ) s = s:gsub( '%[', '[' ):gsub( '%]', ']' ) return s end -- creating (linked) name and its affix function mu.makeName( result, args, show, page, country, nameClass, localClass ) local s = '' local r = {} nameClass = nameClass .. ( args.displayName.fromWD and ' listing-link-from-wd' or '' ) -- clear redirect wiki link if required if args.linkIsRedirect and not show.wikilink then args.displayName.pageTitle = '' args.displayName.all = args.displayName.name args.linkIsRedirect = nil nameClass = nameClass .. ' listing-unused-redirect' mu.addMaintenance( 'unusedRedirect' ) end if args.linkIsRedirect then nameClass = nameClass .. ' mw-redirect listing-link-is-redirect' mu.addMaintenance( 'linkIsRedirect' ) end if mu.isSet( args.url ) and args.displayName.pageTitle == '' then s = '[' .. args.url .. ' ' .. replaceBrackets( args.displayName.name ) .. ']' else s = args.displayName.all end if mu.isSet( args.nameExtra ) then s = s .. ' ' .. args.nameExtra end -- insert name if s ~= '' then local attr = { style = args.styles } s = mu.makeSpan( s, 'p-name fn org listing-name' .. nameClass .. ( args.styleClass and args.styleClass or '' ), true, attr ) if mu.isSet( args.metadata ) then s = s .. args.metadata end table.insert( r, s ) end -- insert separate url icon if mu.isSet( args.url ) and args.displayName.pageTitle ~= '' then -- both article and web links table.insert( r, mu.addLinkIcon( 'listing-url', args.url, mi.iconTitles.internet, 'internet' ) ) end -- insert name affix mu.tableInsert( r, makeNameAffix( args, page, country, localClass, show ) ) if #r > 0 then table.insert( result, table.concat( r, mi.texts.space ) ) end end function mu.parentheses( s, trim ) if not mu.isSet( s ) then return '' end local formatter = mi.texts.parentheses if trim then formatter = mw.text.trim( formatter ) end return mw.ustring.format( formatter, s ) end -- getting DMS coordinates function mu.dmsCoordinates( lat, long, name, fromWD, extraParams, noBrackets ) local function coordSpan( title, text ) return mu.makeSpan( text, 'coordStyle', false, { title = title } ) end local dec local latDMS, dec, latMs = cd.getDMSString( lat, 4, 'lat', '', '', mi.map.defaultDmsFormat ) local longDMS, dec, longMs = cd.getDMSString( long, 4, 'long', '', '', mi.map.defaultDmsFormat ) local r = '[' .. mi.map.coordURL .. latMs .. '_' .. longMs .. '_' .. mw.uri.encode( extraParams ) .. '&locname=' .. mw.uri.encode( name ) .. ' ' .. coordSpan( mi.texts.latitude, latDMS ) .. ' ' .. coordSpan( mi.texts.longitude, longDMS ) .. ']' r = noBrackets and r or mu.parentheses( r ) return mu.makeSpan( r, 'listing-dms-coordinates printNoLink plainlinks' .. mu.addWdClass( fromWD ) ) end -- prepare value s for data attribute and replace all entities by characters local function data( s ) return mu.isSet( s ) and mw.text.decode( s, true ) or nil end -- adding wrapper and microformats function mu.makeWrapper( result, args, page, country, show, list, frame ) if not mu.isSet( args.nameLocal ) and mu.isSet( args.addNameLocal ) then args.nameLocal = args.addNameLocal end if not mu.isSet( args.addressLocal ) and mu.isSet( args.addAddressLocal ) then args.addressLocal = args.addAddressLocal end local wrapper = mw.html.create( show.inline and 'span' or 'div' ) :addClass( 'vcard h-card ' .. args.template ) if args.noGpx then wrapper:addClass( 'listing-no-gpx' ) end if show.outdent and not show.inline then wrapper:addClass( 'listing-outdent' ) end if show.inlineSelected or args.template == 'Marker' then wrapper:addClass( 'listing-inline' ) end if mu.isSet( args.copyMarker ) or not show.poi then wrapper:addClass( 'voy-without-marker' ) end if #args.statusTable > 0 then wrapper:addClass( 'listing-with-status' ) end if args.givenName.name ~= mi.texts.missingName then wrapper:attr( 'data-name', data( args.givenName.name ) ) end if not ( mu.isSet( args.copyMarker ) and args.copyMarker == args.wikidata ) then local id = mu.isSet( args.wikidata ) and args.wikidata or args.id if mu.isSet( id ) then wrapper:attr( 'id', makeAnchorId( id ) ) end end local editClass = 'listing-edit' if mu.isSet( args.sectionFrom ) then args.sectionFrom = replaceWithSpace( args.sectionFrom, '[_]+' ) if args.sectionFrom ~= page.text then editClass = 'listing-no-edit' end end wrapper:addClass( editClass ) wrapper:attr( 'data-location', data( page.subpageText ) ) :attr( 'data-location-qid', page.entityId ) :attr( 'data-wikilang', page.lang ) :attr( 'data-country', data( country.iso_3166 ) ) :attr( 'data-country-name', data( country.country ) ) :attr( 'data-lang', data( country.lang ) ) :attr( 'data-lang-name', data( country.langName ) ) :attr( 'data-country-calling-code', data( country.cc ) ) :attr( 'data-trunk-prefix', data( country.trunkPrefix ) ) :attr( 'data-dir', data( country.isRTL and 'rtl' or 'ltr' ) ) :attr( 'data-wiki-dir', data( page.isRTL and 'rtl' or 'ltr' ) ) :attr( 'data-currency', data( country.addCurrency ) ) local arg for key, value in pairs( list ) do if mu.isSet( args[ key ] ) then arg = args[ key ]:gsub( '<[^<>]*>', '' ) -- remove html tags wrapper:attr( value, data( arg ) ) end end if not show.name then wrapper:node( mw.html.create( 'span' ) :addClass( 'p-name fn org listing-name' ) :css( 'display', 'none' ) :wikitext( args.givenName.name ) ) end wrapper:wikitext( result ) if not show.noCoord then wrapper:node( mw.html.create( 'span' ) :addClass( 'p-geo geo listing-coordinates' ) :css( 'display', 'none' ) :node( mw.html.create( 'span' ) :addClass( 'p-latitude latitude' ) :wikitext( args.lat ) ) :node( mw.html.create( 'span' ) :addClass( 'p-longitude longitude' ) :wikitext( args.long ) ) ) end wrapper = tostring( wrapper ) -- adding coordinates to Mediawiki database -- frame:callParserFunction is expensive if not show.noCoord and mi.options.secondaryCoords then wrapper = wrapper .. frame:callParserFunction{ name = '#coordinates', args = { args.lat, args.long, country.extra, name = args.givenName.name } } end return wrapper end function mu.getPageData() local page = mw.title.getCurrentTitle() page.langObj = mw.getContentLanguage() page.lang = page.langObj:getCode() page.langName = mw.language.fetchLanguageName( page.lang, page.lang ) page.isRTL = page.langObj:isRTL() page.entityId = mw.wikibase.getEntityIdForCurrentPage() -- can be nil page.siteName = mw.site.siteName page.globalProject = page.siteName:lower() if page.globalProject == 'wikipedia' then page.globalProject = 'wiki' end return page end return mu