Module:External
来自Vocawiki
更多操作
模块文档
用于{{External}},站点数据在Module:External/data。
local acandy = require( 'Module:ACandy' )
local data = require( 'Module:External/data' )
local a, Raw, Fragment = acandy.a, acandy.Raw, acandy.Fragment
local wikis = data.wikis
local byAlias = data.byAlias
local languages = data.languages or {}
local p = {}
-- 参数 ------------------------------------------------------------------
-- 依次取第一个非空参数
local function arg( args, ... )
for _, name in ipairs( { ... } ) do
local value = args[ name ]
if value then
value = mw.text.trim( value )
if value ~= '' then
return value
end
end
end
end
-- 模板调用时frame:getParent().args是惰性表:next()恒为nil,只有pairs()能遍历
local function hasArgs( args )
for _ in pairs( args ) do
return true
end
return false
end
-- 站点 ------------------------------------------------------------------
-- 站点标识或别名→数据表;大小写不敏感
local function resolve( key )
return byAlias[ mw.ustring.lower( mw.text.trim( key or '' ) ) ]
end
-- 这些参数名是设置项,不能当站点名
local reserved = {
wiki = true, site = true, title = true, page = true,
label = true, text = true, note = true, lang = true, name = true,
}
-- 收集卡片
local function collect( args )
local named = {}
local unknown, blank = {}, {}
-- wiki=/site=:整次调用只针对这一个站点
local key = arg( args, 'wiki', 'site' )
if key then
local wiki = resolve( key )
if not wiki then
error( 'External:未知的外部 wiki「' .. key .. '」,见 [[Module:External/data]]。' )
end
return { {
wiki = wiki,
title = arg( args, 'title', 'page', 1, 2 ) or mw.title.getCurrentTitle().text,
} }
end
-- 位置参数:第1位站点、第2位条目,条目省略时用本页页名
local unknownPositional
local site = arg( args, '1' )
if site then
local wiki = resolve( site )
if wiki then
named[ wiki.id ] = {
wiki = wiki,
title = arg( args, '2', 'title', 'page' ) or mw.title.getCurrentTitle().text,
}
else
unknownPositional = site
end
end
-- 具名“站点=条目”
for name, value in pairs( args ) do
if type( name ) == 'string' and not reserved[ name ] and not name:match( '^%d+$' ) then
local wiki = resolve( name )
if wiki then
value = mw.text.trim( value )
if value == '' then
blank[#blank+1] = name
else
named[ wiki.id ] = { wiki = wiki, title = value }
end
else
unknown[#unknown+1] = name
end
end
end
-- 按数据表的声明顺序出卡片
local list = {}
for _, wiki in ipairs( wikis ) do
local item = named[ wiki.id ]
if item then
list[#list+1] = item
end
end
if #list > 0 then
return list
end
if unknownPositional then
error( 'External:未知的外部wiki“' .. unknownPositional .. '”,见[[Module:External/data]]。' )
end
if #unknown > 0 then
table.sort( unknown )
error( 'External:未知的外部wiki“' .. table.concat( unknown, '”“' )
.. '”,见[[Module:External/data]]。' )
end
if #blank > 0 then
table.sort( blank )
error( 'External:外部wiki“' .. table.concat( blank, '”“' )
.. '”没有写条目名。' )
end
error( 'External:未指定外部wiki,请用“站点=条目”或“wiki=站点”。' )
end
-- 条目名与URL -----------------------------------------------------------
-- 拆出#锚点
local function splitFragment( title )
local page, fragment = title:match( '^([^#]*)#(.*)$' )
if page then
return page, fragment
end
return title
end
-- 拆出语言前缀;只有多语言站点(维基百科)需要
local function splitLang( title, wiki )
if wiki.defaultLang then
local code, rest = title:match( '^([%a][%w%-]*):(.+)$' )
if code and mw.language.isKnownLanguageTag( code:lower() ) then
return code:lower(), mw.text.trim( rest )
end
end
return nil, title
end
local function buildUrl( wiki, lang, page, fragment, args )
local encoded = mw.uri.encode( page, wiki.encode or 'WIKI' )
local url
if type( wiki.url ) == 'function' then
url = wiki.url( encoded, lang, args )
if not url then
return nil
end
elseif wiki.defaultLang then
url = string.format( wiki.url, lang or wiki.defaultLang, encoded )
else
url = string.format( wiki.url, encoded )
end
if fragment then
url = url .. '#' .. mw.uri.encode( fragment )
end
return url
end
-- 文字 ------------------------------------------------------------------
local function siteName( wiki, lang )
local name = wiki.name
if lang and wiki.langInName ~= false then
name = ( languages[ lang ] or mw.language.fetchLanguageName( lang, 'zh' ) ) .. name
end
return name
end
-- 非中文站点的条目名不参与中文变体转换。
local function noConvert( text, lang, custom )
if custom or not lang or lang:sub( 1, 2 ) == 'zh'
or text:find( '|', 1, true ) or text:find( '-{', 1, true ) then
return text
end
return Fragment{ Raw( '-{' ), text, Raw( '}-' ) }
end
-- 卡片 ------------------------------------------------------------------
-- 图标:文件不存在就不输出[[File:]],缺图会渲染成上传红链,把卡片拆开
local function logoExists( wiki )
return wiki.logo and mw.title.new( 'File:' .. wiki.logo ).fileExists
end
-- 卡片内容:返回HTML字符串,整段交给{{#externallink}}当显示内容再解析一次。
local function buildContent( wiki, name, label, note, lang )
local children = {}
if logoExists( wiki ) then
local classes = { 'extwiki-logo' }
if wiki.pixelated then
classes[#classes+1] = 'extwiki-logo--pixelated'
end
if wiki.rounded then
classes[#classes+1] = 'extwiki-logo--rounded'
end
-- 低分辨率图标按原始尺寸取,交给CSS做像素化放大。
-- [[File:…]]要留给解析器展开,所以是Raw
local file = wiki.pixelated
and ( '[[File:' .. wiki.logo .. '|link=|alt=]]' )
or ( '[[File:' .. wiki.logo .. '|24px|link=|alt=]]' )
children[#children+1] = a.span[ table.concat( classes, ' ' ) ]( Raw( file ) )
end
-- ACandy的元素只接受一个props参数,多个子节点要写成表;
-- 字符串子节点由ACandy负责转义
children[#children+1] = a.span['extwiki-text']{
a.span['extwiki-label text-xs']( name .. '有' .. note .. ':' ),
a.span{
class = 'extwiki-title text-sm',
lang = lang,
dir = lang and 'auto' or nil,
[1] = label,
},
}
children[#children+1] = a.span['extwiki-go']{ ['aria-hidden'] = 'true' }
return tostring( Fragment( children ) )
end
-- 一张卡片:single为真时label/lang/name才生效(多站点时这些参数会让所有卡片重名)
local function makeCard( frame, item, args, single )
local wiki = item.wiki
local lang, rest = splitLang( item.title, wiki )
local override = single and arg( args, 'lang' )
if override then
lang = override:lower()
end
-- 固定语言站点(zhwp/enwp/jawp)没写语言前缀,但条目名仍属于该语言
local displayLang = lang or wiki.fixedLang
local page, fragment = splitFragment( rest )
local url = page ~= '' and buildUrl( wiki, lang, page, fragment, args )
if not url then
return nil
end
local name = ( single and arg( args, 'name' ) ) or siteName( wiki, lang )
local note = arg( args, 'note' ) or '相关页面'
-- 没写label时用条目名:纯文本,交给ACandy转义;
-- 写了label就当wikitext
local custom = single and arg( args, 'label', 'text' )
local label = custom and Raw( custom ) or page
label = noConvert( label, displayLang, custom )
return tostring( frame:callParserFunction( '#externallink', {
url,
buildContent( wiki, name, label, note, displayLang ),
class = 'extwiki-card',
title = '在' .. name .. '上查看「' .. page .. '」',
} ) )
end
-- 入口 ------------------------------------------------------------------
function p.main( frame )
local args = frame:getParent().args
if not hasArgs( args ) then
args = frame.args
end
local list = collect( args )
local single = #list == 1
-- 一次调用的所有卡片放进同一个块元素,卡片在块里并排
local props = { class = 'extwiki', ['data-nosnippet'] = '' }
local any = false
for _, item in ipairs( list ) do
local card = makeCard( frame, item, args, single )
if card then
any = true
props[#props+1] = Raw( card )
end
end
return any and tostring( a.div( props ) ) or ''
end
return p