モジュール:Marker utilities
このテンプレートは、以下のLuaモジュールを使用しています。 |
このモジュールは、モジュール:VCardで使用される関数を提供します。Wikivoyage:Marker utilities (ドイツ語版) も参照。
使用状況
このモジュールを使用しているモジュールは以下の通りです:
関数
メンテナンス関数
function mu.initMaintenance( name )
この関数は、エラーメッセージ出力のための変数を初期化します。name
には、名前空間名を除いた関連モジュールの名前が入ります。また、この関数は不正なパラメータとエラー・注釈のテーブルも初期化します。
function mu.addMaintenance( key, value )
この関数は、key
に与えられたコードから、エラーや注釈などのメッセージを取得してエラーと注釈のテーブルに入れます。value
はメッセージの引数として、%s
を置換します。上で紹介したいくつかの関数もこのテーブルに値を入れます。
function mu.getMaintenance()
この関数は、全てのエラーと注釈のメッセージを返します。
function mu.getCategories( formatStr )
使用されたすべてのウィキデータプロパティについて、そのプロパティのカテゴリへのリンクを返します。
補助関数
function mu.isSet( arg )
引数arg
がセットされているかどうかについて、true
またはfalse
を返します。arg
に空文字列以外の値が代入されている場合のみ、true
が返ります。
function mu.convertForSort( s )
ソート用に、文字列s
に含まれる特殊文字を置換します。置換される文字は、国際化ファイルのsubstitutes
テーブルに入っています。漢字は量が膨大であり列挙が不可能であるため、日本語版ウィキボヤージュではこの関数に意味がありません。
function mu.formatNumber( num )
数値の引数num
について、小数点を文字列として置換し、3桁ごとに桁の区切り文字を挿入します。
function mu.tableInsert( tab, value )
テーブルtab
が定義されており、空文字列でなければ、tab
にvalue
を挿入します。
function mu.textSplit( s, sep )
文字列s
を区切り文字sep
で分割し、空文字列を含まないテーブルに代入します。sep
は正確に1文字(1バイト)の長さを持ち、パターンマッチの構文は使用できません。この関数はmw.text.split()
よりも遥かに速いです。
function mu.split( s )
文字列s
をカンマで区切った連想配列を返します。部分文字列は小文字に変換され、スペースはアンダースコアで置換されます。
function mu.makeSpan( s, class, isBdi, attr, css )
s
を<span>
タグまたは<bdi>
タグで囲んだ文字列を返します。class
はCSSクラス、attr
は属性のテーブル、css
は要素に直接書かれるスタイルシートのテーブルです。
function mu.languageSpan( s, titleHint, args, country, addClass )
言語とテキストの方向が指定された<span>
タグにテキストs
を挿入します。titleHint
は<span>
タグのtitle
属性、args
はテンプレートのパラメータが入ったテーブル、country
は国の情報のテーブルです。
function mu.addWdClass( isFromWikidata )
isFromWikidata
がtrue
の場合、クラス識別子wikidata-content
を返し、そうでなければ空文字列を返します。
function mu.getAliases( tab, key )
テーブルtab
から、別名のキーと正式名称の値を持ったテーブルを作成します。
function mu.yesno( val )
val
が既知の値であればy
またはn
を返し、なければnil
を返します。モジュール:Yesnoと同じ機能です。
評価関数
function mu.checkArguments( templateArgs, validKeys )
テンプレートの呼び出し時に、未知のパラメータや別名によって重複したパラメータが使用されているか確認します。これらの不明または重複したパラメータは、無効なパラメータのテーブル(invalidParams
)または重複した別名のテーブル(duplicateAliases
)に入ります。templateArgs
は確認したいパラメータのテーブルで、validKeys
は既知のパラメータのテーブルです(モジュール:VCard/i18nなどを参照)。返り値は有効なパラメータのテーブルです。
function mu.checkCommonsCategory( args )
この関数は、コモンズのカテゴリargs.commonscat
から名前空間名を除去し、コモンズのカテゴリが設定されている場合はメンテナンス用のカテゴリを追加します。
function mu.checkCoordinates( lat, long )
lat
とlong
の値を確認します。エラーがある場合、latとlongは空文字列となります。エラーメッセージのテーブルにも対応する部分があります(下記参照)。
function mu.checkZoom( args )
args.zoom
の値が0~19の範囲の数字でない場合、args.zoom
を既定値に設定します。
function mu.checkImage( image, entity )
画像の値を確認し、エラーがあった場合には空文字列を代入します。エラーメッセージのテーブルにも対応する部分があります(下記参照)。
function mu.checkStatus( args )
status
パラメータの値を確認し、有効な値をargs.statusTable
に代入します。エラーが発生した場合、エラーメッセージのテーブルにも対応する部分があります(下記参照)。
function mu.checkTypeAndGroup( args )
args
のタイプとグループの値を確認し、正しい値を代入します。エラーが発生した場合、エラーメッセージのテーブルにも対応する部分があります(下記参照)。
function mu.checkUrl( args )
args
のURLの値を確認し、正しい値を代入します。エラーが発生した場合、エラーメッセージのテーブルにも対応する部分があります(下記参照)。
タイプとグループの関数
function mu.getTypeParams( aType )
aType
で指定されたタイプのテーブルを、モジュール:Marker utilities/Typesから取得して返します。該当するものがない場合、nil
が返ります。
function mu.getTypeLabel( id )
id
に対応するタイプの最初のラベルを返します。id
は、vCardのタイプかウィキデータの項目名です。エラーがある場合、空文字列かid
そのものが返ります。
function mu.typeExists( aType )
aType
がモジュール:Marker utilities/TypesにあればaTypeを、なければnil
を返します。別名は対応するタイプに変換されます。
function mu.groupWithEvents( group )
指定されたグループgroup
が、イベントのタイプを使用できるかどうかを返します。
function mu.getColor( args )
グループgroup
から引数args
にcolor
とinverse
を追加します。
function mu.idToType( id )
ウィキデータIDからタイプを検索し、見つかった場合はそのタイプを、そうでなければnil
を返します。
整形関数
function mu.getShow( default, args, validValues )
上書き可能な既定値を踏まえて、カンマ区切りでパラメータに渡されたshow
の値を持つ連想配列を返します。args
はパラメータのテーブルで、この関数内ではargs.show
のみが評価されます。
function mu.removeCtrls( s, onlyInline )
この関数は、文字列s
から制御文字や改行を削除します。onlyInline = false
の場合には改行は削除されません。この関数の返り値は2つあります:
- 整形された
s
<div>
タグのコンテナを持つ説明文descrDiv
クエリ関数
function mu.getCoordinatesFromWikidata( entity )
ウィキデータのデータセットにある座標をエンティティのインスタンスと共にに返します。まず P5140 プロパティから中心座標を取得しようと試み、次に P625 プロパティから座標の取得を試みます。
function mu.typeSearch( p31 )
いくつかの P31~P279 チェーンで、モジュール:Marker utilities/Typesのテーブルに値が含まれているかもしれないQIDを検索します。テーブル P31 には見つかったP31の値が含まれています。はじめにヒットした値が文字列で返されます。エラーの場合は、エラー文字列が返されます。上位レベルを検索する際の深さの最大数はmi.searchLimit
で設定でき、通常は4です。最初の P279 ID だけが評価され、ツリー構造全体は評価されません。
function mu.getCommonsCategory( args, entity )
この関数は、ウィキデータにあるエンティティのインスタンスからコモンズのカテゴリを取得しようと試みます。最初に接続されているコモンズへのリンクが調べられ、次に P373 プロパティ、最後に P910 プロパティを調べます。
function mu.getLangTable( wikiLang, localLang )
wikiLang
、localLang
(country.lang
)、モジュール:Marker utilities/i18nの言語langs
を使って言語コードの配列を作成します。
function mu.getNamesFromWikidata( args, fromWikidata, country, entity )
この関数は、ウィキデータの情報を用いて、ウィキの言語と現地語の名称でテーブルの引数を埋めます。fromWikidata
テーブルには、名称の情報をウィキデータから取得したかどうか(fromWikidata.name
、fromWikidata.nameLocal
)が格納されます。
function mu.getArticleLink( args, entity )
この記事でテンプレート呼び出しが行われない限り、この関数は関連する記事へのサイトリンクをargs.thisWiki
に渡します。
マーカー関数
function mu.getMaki( key )
Die Funktion liefert eine Tabelle für den angegebenen key
aus der Tabelle Module:Marker utilities/Maki icons zurück.
function mu.getMakiIconId( aType )
Die Funktion liefert den Namen eines MAKI-Symbols zum Type aType
oder nil
zurück, wenn es keinen gibt oder eine Abbildung für den Fließtext fehlt.
function mu.addIconToMarker( args )
fügt einen Text, der ein Symbol enthält, zur Parametertabelle args
.
function mu.makeMarkerSymbol( args, title, frame, show )
liefert r
: HTML-Quellcode des Marker-Symbols.
アイコン関数
function mu.makeStatusIcons( args )
args.statusTable
テーブルに従って画像の文字列を返します。
function mu.addLinkIcon( classes, link, title, text )
リンクされたアイコン画像を表示するためのHTMLを生成します。表示にはテンプレートのスタイルシートも必要です。listing-icon
に追加するCSSクラスclasses
と、リンクlink
(外部リンクでも内部リンクでも)、ツールチップtitle
、通常は表示されないリンクテキストtext
が必要です。
function mu.makeSisterIcons( icons, args, country, entity )
リンクされた姉妹プロジェクトのアイコンをテーブルに代入します。通常、情報はウィキデータ・エンティティのサイトリンクから取得します。args
は、テンプレートに渡されたパラメータのテーブルです。テーブルcountry
には言語をはじめとした国のデータが入っています。
function mu.makeSocial( icons, args, fromWikidata, name )
リンクされたソーシャルメディアサービスのアイコンをテーブルに代入します。args
は、テンプレートに渡されたパラメータのテーブルです。fromWikidata
はウィキデータから取得したパラメータのテーブル、name
はマーカーやvCardが紹介している場所の名称です。
出力関数
function mu.prepareNames( args )
name
パラメータとnameMap
から形成されるdisplayName
テーブルとgivenName
テーブルを引数テーブルに追加します。ウィキ構文内の有効なリンクから実際の名前を抽出することが主な目的です。どちらのテーブルも次の3つの要素で構成されます:
name
(string
):表示される名前all
(string
):テンプレートにリンク付きで渡された場合はリンクを含めたウィキ構文、そうでない場合は名前だけpageTitle
(string
):リンクの名前。name
と異なるかもしれません。
function mu.makeName( result, args, show, page, country, nameClass, localClass )
result
テーブルに名前と追加の名前を追加します。追加の名前には、別名、現地語での名前、コメント、空港コードが含まれ、括弧とspanタグに挟まれています。nameClass
とlocalClass
は、名前と現地語名の追加クラスです。
function mu.parentheses( s, trim )
この関数は、括弧内に文字列s
を挿入します。ブール値trim
がtrue
ならば、書式の文字列(通常は (%s)
)が切り抜かれます。
function mu.dmsCoordinates( lat, long, name, fromWD, extraParams, noBrackets )
文字列r
を返します。この文字列は、マップツールへのリンクを含む10進数の座標で、任意で括弧内に入れることもできます。name
は場所の名前で、fromWD = true
は座標がウィキデータ由来であることを意味します。また、extraParams
にはマップツールで使用される縮尺と領域のデータが入ります。
function mu.makeWrapper( result, args, country, show, list, aClass, frame )
マーカーやvCardの内容をタグ(<span>
、<div>
)で囲みます。
その他の関数
function mu.getPageData()
この関数は、ページに関連したデータを返します。
関連項目
この解説は、モジュール:Marker utilities/docから呼び出されています。 (編集 | 履歴) 編集者は、このモジュールをサンドボックス (作成 | 複製)とテストケース (作成)で試すことができます。(解説) このモジュールのサブページ一覧。 |
--[[
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-10-15',
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.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.getCategories( formatStr )
return wu.getCategories( formatStr )
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
end
setCopyMarker( args, show )
if args.wikidata ~= '' then
if show.socialmedia then
setSocialMedia( args, 'y' )
elseif show.nosocialmedia then
setSocialMedia( args, 'n' )
end
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
function mu.getMaki( key )
mm = mm or require( 'Module:Marker utilities/Maki icons' )
key = key:lower():gsub( '[_%s]+', '-' )
return mm.icons[ key ]
end
function mu.getMakiIconId( key )
if not mu.isSet( key ) then
return nil
end
key = key:lower():gsub( '[_%s]+', '-' )
if mu.getMaki( key ) then
return key
end
key = mt.types[ key ] and mt.types[ key ].icon
if key and mu.getMaki( key ) then
return key
end
return nil
end
function mu.addIconToMarker( args )
args.text = ( '[[File:Maki7-%s.svg|x14px|link=|class=noviewer]]' ):format( args.symbol )
args.isIcon = true
end
-- making marker symbol
function mu.makeMarkerSymbol( args, frame )
local extraClasses = args.inverse and ' listing-map-inverse' or
' listing-map-not-inverse'
if args.isIcon then
extraClasses = extraClasses .. ' listing-map-is-symbol'
end
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
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, link
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
function mu.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
function mu.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
-- 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, t
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
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( result, s )
end
if mu.isSet( args.url ) and args.displayName.pageTitle ~= '' then
-- both article and web links
table.insert( result, mu.addLinkIcon( 'listing-url', args.url,
mi.iconTitles.internet, 'internet' ) )
end
mu.tableInsert( result, makeNameAffix( args, page, country, localClass,
show ) )
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
arg = args[ key ]:gsub( '<[^<>]*>', '' ) -- remove html tags
wrapper:attr( value, data( arg ) )
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