Modul:Marker utilities
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: 2024-11-11
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.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 = '2024-11-11',
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( ' ', '_' )
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 normalizeValues( args )
for i, param in ipairs( mi.options.normalizeValues ) do
if mu.isSet( args[ param ] ) then
args[ param ] = args[ param ]:ulower():gsub( '[_%s]+', ' ' )
end
end
end
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
normalizeValues( args )
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
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
-- 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( 'groupUsed' )
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 = id:gsub( '[_%s]+', '_' )
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( ',' )
if at then
t = t:sub( 1, at - 1 )
end
else
t = id:gsub( '_', ' ' )
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
local function replaceWithSpace( s, pattern )
s = s:find( pattern ) and s:gsub( pattern, ' ' ) or s
return s
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 ):gsub( '&', '&' ):gsub( ''', "'" )
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 }
if mu.isSet( args.id ) and mu.isSet( args.wikidata ) and
not mu.isSet( args.copyMarker ) then
attr.id = makeAnchorId( args.id )
end
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 = args.sectionFrom:gsub( '[_]+', ' ' )
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.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
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 = tostring( wrapper:wikitext( result ) )
-- 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