မေႃႇၵျူး:multiple images
Appearance
Documentation for this module may be created at မေႃႇၵျူး:multiple images/doc
local export = {}
local M = require("Module:module loader").init({
require = {
parameters = "Module:parameters",
pages = "Module:pages",
},
})
local LAYOUT = {
DEFAULT_IMAGE_WIDTH = 250,
ROW_GAP_BASE = 3,
ROW_PADDING = 12,
BODY_WIDTH_OFFSET = 8,
BODY_WIDTH_MIN = 100,
CELL_WIDTH_PADDING = 2,
FALLBACK_ASPECT_HEIGHT = 100,
}
local THUMB_CLASS_BY_ALIGN = {
["left"] = "tleft",
["none"] = "tnone",
["center"] = "tnone",
["right"] = "tright",
}
local PARAMS_SCHEMA = {
image = {
list = true,
allow_holes = true,
required = true,
},
width = { list = true, allow_holes = true, type = "number" },
height = { list = true, allow_holes = true, type = "number" },
caption = { list = true, allow_holes = true },
link = { list = true, allow_holes = true },
alt = { list = true, allow_holes = true },
thumbtime = { list = true, allow_holes = true },
direction = { set = { "", "vertical", "horizontal" } },
border = { set = { "", "none", "infobox" } },
align = { set = { "", "left", "none", "center", "right" } },
caption_align = true,
total_width = { type = "number" },
image_style = true,
header = true,
title = { alias_of = "header" },
footer = true,
image_gap = { type = "number", default = 1 },
perrow = true,
["background color"] = true,
header_align = { set = { "", "left", "right", "center" } },
header_background = true,
footer_align = { set = { "", "left", "right", "center" } },
footer_background = true,
}
local function is_not_empty(val)
if val == nil then return false end
if type(val) == "number" then return true end
return tostring(val):match("^%s*(.-)%s*$") ~= ""
end
local function image_spec_to_base(image_spec)
if not is_not_empty(image_spec) then return nil end
local base = mw.uri.decode(mw.ustring.gsub(image_spec, "%|.*$", ""), "WIKI")
return base ~= "" and base or nil
end
local function get_width(default_width, override_width)
local width
if is_not_empty(default_width) then
width = tonumber(default_width)
elseif is_not_empty(override_width) then
width = tonumber(override_width)
end
return width or LAYOUT.DEFAULT_IMAGE_WIDTH
end
local function get_per_row(per_row_string, image_count)
local parts = mw.text.split(per_row_string or "", "[^%d][^%d]*")
if #parts < 1 then
parts = { tostring(image_count) }
end
local row_index = 1
local images_in_row = tonumber(parts[1] or image_count) or image_count
local images_per_row = {}
while image_count > 0 do
images_per_row[row_index] = images_in_row
image_count = image_count - images_in_row
row_index = row_index + 1
images_in_row = math.min(tonumber(parts[row_index] or images_in_row) or image_count, image_count)
end
return images_per_row
end
local function render_image_cell(image, width, height, link, alt, thumbtime, caption, caption_align, image_style)
local cell_root = mw.html.create("")
local alt_param = "|alt=" .. (alt or "")
local link_param = link and ("|link=" .. link) or ""
local width_param = "|" .. tostring(width) .. "px"
local thumbtime_param = ""
if width_param == "|-nanpx" or (width and width ~= width) then
width_param = ""
end
if is_not_empty(thumbtime) then
thumbtime_param = "|thumbtime=" .. thumbtime
end
local image_div = cell_root:tag("div")
image_div:addClass("thumbimage")
image_div:cssText(image_style)
if height then
image_div:css("height", tostring(height) .. "px")
image_div:css("overflow", "hidden")
end
image_div:wikitext("[[file:" .. image .. width_param .. link_param .. alt_param .. thumbtime_param .. "]]")
if is_not_empty(caption) then
local caption_div = cell_root:tag("div")
caption_div:addClass("thumbcaption")
if is_not_empty(caption_align) then
caption_div:addClass("text-align-" .. caption_align)
end
caption_div:wikitext(caption)
end
return tostring(cell_root)
end
local function get_dimensions(image_spec, width_arg, height_arg)
if tonumber(width_arg) and tonumber(height_arg) then
return tonumber(width_arg), tonumber(height_arg), "manual"
end
local base = image_spec_to_base(image_spec)
local title = base and mw.title.new("File:" .. base)
local file = title and title.file or { width = 0, height = 0 }
local width = tonumber(file.width) or 0
local height = tonumber(file.height) or 0
return width, height, "auto"
end
local function render_multiple_images(args)
local autoscaled = false
local nonautoscaled = false
local default_width = args.width and args.width[1]
local layout_direction = args.direction or "vertical"
local border = args.border or ""
local align = args.align or (border == "infobox" and "center" or "")
local caption_align = args.caption_align or ""
local total_width = args.total_width
local image_style = args.image_style
local header = args.header or ""
local footer = args.footer or ""
local gap_px = math.max(0, args.image_gap or 0)
local image_param_indices = {}
local image_count = 0
local image_list = args.image
local max_image_index = image_list and image_list.maxindex or 0
for param_index = 1, max_image_index do
if is_not_empty(image_list and image_list[param_index]) then
table.insert(image_param_indices, param_index)
image_count = image_count + 1
end
end
table.sort(image_param_indices)
if (args.perrow or args.total_width) and not args.direction then
layout_direction = "horizontal"
end
local images_per_row = get_per_row(layout_direction == "vertical" and "1" or args.perrow, image_count)
local row_count = #images_per_row
local heights = {}
local widths = {}
local max_row_width = 0
local row_width_sums = {}
local image_index = 0
for row_index = 1, row_count do
row_width_sums[row_index] = 0
for _ = 1, images_per_row[row_index] do
image_index = image_index + 1
if image_index <= image_count then
local param_index = image_param_indices[image_index]
local image_spec = args.image and args.image[param_index]
if total_width then
local w, h, scale = get_dimensions(image_spec, args.width and args.width[param_index], args.height and args.height[param_index])
widths[image_index], heights[image_index] = w, h
if scale == "auto" then autoscaled = true elseif scale == "manual" then nonautoscaled = true end
else
widths[image_index] = get_width(default_width, args.width and args.width[param_index])
end
row_width_sums[row_index] = row_width_sums[row_index] + widths[image_index]
end
end
max_row_width = math.max(max_row_width, row_width_sums[row_index])
end
if total_width then
max_row_width = 0
image_index = 0
for row_index = 1, row_count do
local image_index_at_row_start = image_index
local available_row_width = total_width - (LAYOUT.ROW_GAP_BASE + gap_px) * (images_per_row[row_index] - 1) - LAYOUT.ROW_PADDING
local aspect_ratios = {}
local sum_aspect_ratios = 0
for cell_index = 1, images_per_row[row_index] do
image_index = image_index + 1
if image_index <= image_count then
local height_val = heights[image_index] or 0
if height_val > 0 then
aspect_ratios[cell_index] = widths[image_index] / height_val
heights[image_index] = height_val
else
aspect_ratios[cell_index] = widths[image_index] / LAYOUT.FALLBACK_ASPECT_HEIGHT
end
sum_aspect_ratios = sum_aspect_ratios + aspect_ratios[cell_index]
end
end
local row_width_sum = 0
if sum_aspect_ratios > 0 then
local uniform_row_height = available_row_width / sum_aspect_ratios
image_index = image_index_at_row_start
for cell_index = 1, images_per_row[row_index] do
image_index = image_index + 1
if image_index <= image_count then
widths[image_index] = math.floor(aspect_ratios[cell_index] * uniform_row_height + 0.5)
row_width_sum = row_width_sum + widths[image_index]
if heights[image_index] then
heights[image_index] = math.floor(uniform_row_height)
end
end
end
end
row_width_sums[row_index] = row_width_sum
max_row_width = math.max(max_row_width, row_width_sums[row_index])
end
end
if image_count == 0 then
return "", false, false
end
local body_width = LAYOUT.BODY_WIDTH_MIN
for row_index = 1, row_count do
if max_row_width == row_width_sums[row_index] then
body_width = math.max(LAYOUT.BODY_WIDTH_MIN, max_row_width + (LAYOUT.ROW_GAP_BASE + gap_px) * (images_per_row[row_index] - 1) + LAYOUT.ROW_PADDING - LAYOUT.BODY_WIDTH_OFFSET)
break
end
end
local background_color = args["background color"] or ""
local thumb_root = mw.html.create("div")
thumb_root:addClass("thumb")
thumb_root:addClass("tmulti")
thumb_root:addClass(THUMB_CLASS_BY_ALIGN[align] or "tright")
if align == "center" then
thumb_root:addClass("center")
end
if background_color ~= "" then
thumb_root:css("background-color", background_color)
end
local thumb_inner = thumb_root:tag("div")
thumb_inner:addClass("thumbinner multiimageinner")
thumb_inner:css("width", tostring(body_width) .. "px"):css("max-width", tostring(body_width) .. "px")
if background_color ~= "" then
thumb_inner:css("background-color", background_color)
end
if border == "infobox" or border == "none" then
thumb_inner:css("border", "none")
end
if is_not_empty(header) then
thumb_inner:tag("div")
:addClass("trow")
:tag("div")
:addClass("theader")
:css("text-align", is_not_empty(args.header_align) and args.header_align or nil)
:css("background-color", is_not_empty(args.header_background) and args.header_background or nil)
:wikitext(header)
end
image_index = 0
for row_index = 1, row_count do
local row_div = thumb_inner:tag("div"):addClass("trow")
for cell_index = 1, images_per_row[row_index] do
image_index = image_index + 1
if image_index <= image_count then
local cell_div = row_div:tag("div")
cell_div:addClass("tsingle")
if background_color ~= "" then
cell_div:css("background-color", background_color)
end
if (gap_px > 1) and (cell_index < images_per_row[row_index]) then
cell_div:css("margin-right", tostring(gap_px) .. "px")
end
local param_index = image_param_indices[image_index]
local image_spec = args.image and args.image[param_index]
local cell_width = widths[image_index]
cell_div:css("width", tostring(LAYOUT.CELL_WIDTH_PADDING + cell_width) .. "px"):css("max-width", tostring(LAYOUT.CELL_WIDTH_PADDING + cell_width) .. "px")
cell_div:wikitext(
render_image_cell(
image_spec,
cell_width,
heights[image_index],
args.link and args.link[param_index],
args.alt and args.alt[param_index],
args.thumbtime and args.thumbtime[param_index],
args.caption and args.caption[param_index],
caption_align,
image_style
)
)
end
end
end
if is_not_empty(footer) then
local footer_align = string.lower(args.footer_align or "left")
thumb_inner:tag("div")
:addClass("trow")
:css("display", (footer_align ~= "left") and "flow-root" or "flex")
:tag("div")
:addClass("thumbcaption")
:css("text-align", (footer_align ~= "left") and footer_align or nil)
:css("background-color", is_not_empty(args.footer_background) and args.footer_background or nil)
:wikitext(footer)
end
return tostring(thumb_root), autoscaled, nonautoscaled
end
function export.render(frame)
local args = M.parameters.process(frame:getParent().args, PARAMS_SCHEMA)
local html, autoscaled, nonautoscaled = render_multiple_images(args)
local categories = {}
if M.pages.is_content_page(mw.title.getCurrentTitle()) then
if autoscaled then table.insert(categories, "Pages using multiple image with auto scaled images") end
if nonautoscaled then table.insert(categories, "Pages using multiple image with manual scaled images") end
end
local output = {
frame:extensionTag("templatestyles", nil, { src = "multiple images/styles.css", wrapper = ".tmulti" }),
html,
}
if #categories > 0 then
for _, cat in ipairs(categories) do
table.insert(output, "[[Category:" .. cat .. "]]")
end
end
return table.concat(output)
end
return export