- 欢迎来到THBWiki!如果您是第一次来到这里,请点击右上角注册一个帐户
- 有任何意见、建议、求助、反馈都可以在 讨论板 提出
- THBWiki以专业性和准确性为目标,如果你发现了任何确定的错误或疏漏,可在登录后直接进行改正
模块:并列图像
跳到导航
跳到搜索
模块文档[创建]
| 本模块依赖以下其他模块: |
local list = require('Module:list')
local ArgsTracker = require('Module:checkargs').ArgsTracker
local template_name = '模板:并列图像'
-- 收集错误消息
local errors = list()
local function add_error(message)
errors:append(require('Module:error')._main{ message, template_name })
end
local p = {}
-- 转换数字并检查是否合法
local function to_number(text)
local num = nil
if text and text ~= '' then
num = tonumber(text)
if not num or num <= 0 then
add_error('错误:宽度必须是正数')
return 200
end
end
return num
end
-- 获取图片尺寸
local function get_dimension(file_name)
-- 重定向以保证能获取正确的尺寸
file_name = mw.getCurrentFrame()
:callParserFunction('#redirect', 'File:' .. file_name)
local title = mw.title.new(file_name)
local file = title and title.file or {}
return {
width = tonumber(file.width) or 200,
height = tonumber(file.height) or 200
}
end
-- 生成每行图片数的列表
local function expand_perrow(perrow, image_count)
local perrow_list = list(perrow:gmatch('[1-9]%d*')):map(tonumber)
local len = perrow_list:len()
if len > 1 then
if perrow_list:sum() ~= image_count then
add_error('警告:每行图片数之和与总图片数不相等')
end
return perrow_list
elseif len == 0 then
add_error('错误:perrow 必须是正整数')
end
perrow = perrow_list[1] or image_count
if perrow > image_count then
add_error('警告:第一行图片数大于总图片数')
else
perrow_list = math.floor(image_count / perrow) * list{perrow}
if image_count % perrow > 0 then
perrow_list:append(image_count % perrow)
end
end
return perrow_list
end
-- title.exists 会增加高开销解析函数计数, 但 #ifexist 不会
local function title_exist(title)
local result = mw.getCurrentFrame()
:callParserFunction('#ifexist', title, '1', '0')
return (result == '1') and true or false
end
-- 生成带分类的上传链接
local function generate_upload_link(file_name, category)
local url = mw.uri.localUrl('Special:Upload', {
wpDestFile = file_name,
wpUploadDescription = category
})
local link = string.format('[%s 上传图片]', url.relativePath)
local cell = mw.html.create('div')
:addClass('multi-image-upload')
cell:tag('span')
:wikitext(file_name)
cell:tag('span')
:addClass('new')
:wikitext(link)
return tostring(cell)
end
-- 生成单个图片格子 (包含 caption)
local function render_image_cell(data)
-- 里面的 thumbimage 有 1px 边框
local cell_width = data.width + 2
local cell = mw.html.create('div')
:addClass('multi-image-item')
:css('width', cell_width .. 'px')
:css('max-width', cell_width .. 'px')
local image = data.image
local category = data.category or ''
local content
if category ~= '' and not title_exist('File:' .. image) then
-- 提供分类且文件不存在时, 生成上传链接
content = generate_upload_link(image, category)
else
local width = '|' .. data.width .. 'px'
local class = data.class and ('|class=' .. data.class) or ''
local link = data.link and ('|link=' .. data.link) or ''
local alt = data.alt or ''
if alt ~= '' then
alt = '|alt=' .. alt
end
content = '[[File:' .. image .. width .. class .. link .. alt .. ']]'
end
local image_border = data.image_border or ''
local image_background = data.image_background or ''
local height = data.height
cell:tag('div')
:addClass('thumbimage')
:wikitext(content)
:css('border', (image_border ~= '') and image_border or nil)
:css('background-color', (image_background ~= '') and image_background or nil)
:css('height', height and (height .. 'px') or nil)
:css('overflow', height and 'hidden' or nil)
local caption = data.caption or ''
local caption_align = data.caption_align or ''
if caption ~= '' then
cell:tag('div')
:addClass('thumbcaption')
:css('text-align', (caption_align ~= '') and caption_align or nil)
:wikitext(caption)
end
return tostring(cell)
end
function p._main(args)
-- 遍历参数得到最大的非空 image[n], 作为总图片数
local image_count = list(pairs(args)):map(function(kv)
return kv[2] ~= '' and tonumber(tostring(kv[1]):match('^image([1-9]%d*)$')) or 0
end):max() or 0
-- 图片不允许跳过编号
for i = 1, image_count do
local v = args['image' .. i] or ''
if v == '' then
add_error('警告:您跳过了图片 ' .. i)
end
end
-- 处理 perrow 与 direction
local perrow = args.perrow or ''
if perrow == '' then
local dir = args.direction or ''
if dir == '' then
dir = 'horizontal'
end
-- 未给定 perrow 时由 direction 决定每行图片数
local dir_to_perrow = {
horizontal = tostring(image_count),
vertical = '1'
}
perrow = dir_to_perrow[dir]
if not perrow then
add_error('错误:direction 的值必须是 horizontal 或 vertical')
perrow = tostring(image_count)
end
end
local perrow_list = expand_perrow(perrow, image_count)
-- 保证 image_gap ≥ 0
local image_gap_raw = args.image_gap or ''
local image_gap = tonumber(image_gap_raw)
if not image_gap or image_gap < 0 then
if image_gap_raw ~= '' then
add_error('错误:image_gap 必须是非负数字')
end
image_gap = 0
end
-- 给定 total_width 时获取图片尺寸, 供后续计算使用
local total_width = to_number(args.total_width)
local i = 0
local dims = perrow_list:map(function(row_size)
return list.range(row_size):map(function()
i = i + 1
if total_width then
-- 此行只有一张图片时不需要查询尺寸, 减小开销
if row_size == 1 then
return {}
end
return get_dimension(args['image' .. i] or '')
else
return {
width = to_number(args['width' .. i]) or to_number(args.width) or 200
}
end
end)
end)
-- 给定 total_width 时, 计算缩放到相同高度时的尺寸
if total_width then
dims = dims:map(function(row)
local row_size = row:len()
-- 计算纯图片部分的宽度. 图片默认间距 4px, 每行两侧各 6px 边距
local content_width = total_width - (4 + image_gap) * (row_size - 1) - 12
-- 单张图片直接返回可用宽度
if row_size == 1 then
return list{ { width = content_width } }
end
-- 宽高比列表
local aspect_ratios = row:map(function(dim)
return dim.width / dim.height
end)
-- (宽高比 1 + ··· + 宽高比 n) * 高度 = 总宽度
local height = content_width / aspect_ratios:sum()
-- 宽度四舍五入, 高度向下取整防止出现空白
local row_new = aspect_ratios:map(function(aspect_ratio)
return {
width = math.floor(aspect_ratio * height + 0.5),
height = math.floor(height)
}
end)
return row_new
end)
end
-- 无图片时直接返回空字符串
if image_count == 0 then
add_error('警告:未提供图片')
return '', errors
end
-- 用最大行宽作为容器宽度
local body_width = dims:map(function(row)
local row_size = row:len()
local width_sum = row:map(function(dim) return dim.width end):sum()
return width_sum + (4 + image_gap) * (row_size - 1) + 12
end):max()
if body_width < 100 then
add_error('警告:总宽度过小,至少应大于 100')
end
-- 减去 padding 与 border
body_width = body_width - 8
local align = args.align or ''
if align == '' then
align = 'right'
end
local thumb_class = {
left = 'tleft',
none = 'tnone',
center = 'tnone center',
right = 'tright'
}
local root_class = thumb_class[align]
if not root_class then
add_error('错误:align 的值必须是 left, center, right, none 之一')
end
-- 外层容器
local root = mw.html.create('div')
:addClass('thumb')
:addClass('multi-image')
:addClass(root_class or 'tright')
-- 纵排时添加 noclear. 由于副作用比较大, 允许手动关闭
if perrow == '1' then
local h2_newline = args.h2_newline or ''
if h2_newline == '' then
root:addClass('noclear')
end
end
local border = args.border or ''
local background = args.background or ''
-- 内层容器
local inner = root:tag('div')
:addClass('thumbinner')
:addClass('multi-image-inner')
:css('width', body_width .. 'px')
:css('max-width', body_width .. 'px')
:css('border', (border ~= '') and border or nil)
:css('background-color', (background ~= '') and background or nil)
:css('gap', (image_gap > 0) and (image_gap .. 'px') or nil)
local header = args.header or ''
if header ~= '' then
local header_align = args.header_align or ''
local header_background = args.header_background or ''
inner:tag('div')
:addClass('multi-image-row')
:tag('div')
:addClass('multi-image-header')
:css('text-align', (header_align ~= '') and header_align or nil)
:css('background-color', (header_background ~= '') and header_background or nil)
:wikitext(header)
end
-- 生成图片行
i = 0
for _, row in ipairs(dims) do
local images = row:map(function(dim)
i = i + 1
local category = args['category' .. i] or ''
if category == '' then
category = args.category
end
return render_image_cell{
image = args['image' .. i] or '',
width = dim.width,
height = dim.height,
class = args['class' .. i],
link = args['link' .. i],
alt = args['alt' .. i],
category = category,
image_border = args.image_border,
image_background = args.image_background,
caption = args['caption' .. i],
caption_align = args.caption_align,
}
end)
inner:tag('div')
:addClass('multi-image-row')
:wikitext(images:join(''))
:css('gap', (image_gap > 0) and (image_gap .. 'px') or nil)
end
local footer = args.footer or ''
if footer ~= '' then
local footer_align = args.footer_align or ''
local footer_background = args.footer_background or ''
inner:tag('div')
:addClass('multi-image-row')
:tag('div')
:addClass('thumbcaption')
:addClass('multi-image-footer')
:css('text-align', (footer_align ~= '') and footer_align or nil)
:css('background-color', (footer_background ~= '') and footer_background or nil)
:wikitext(footer)
end
return tostring(root), errors
end
function p.main(frame)
-- 追踪参数使用情况
local args = ArgsTracker(frame:getParent().args)
local text, messages = p._main(args)
local message = args:make_message{ template_name, ignore_blank = 'all' }
if message then
messages:append(message)
end
return text .. messages:join('<br />')
end
return p