-- This is an module to implement FreedImg
-- Argument to the functions are as described on [[Template:FI]] and [[Template:FIS]].
--
-- 2021-01-13: Implementation copying original FI and FIS templates, but with
-- added max-width handing to reduce image sizes for very large images
-- 2021-01-16: Removed complex HTML and reduce complexity
-- 2021-01-21: Further reduce complexity of captions and allow captions to contain
-- divs (e.g. centered text)
-- 2021-06-03 Handle blank and missing images
require('strict')
local p = {} --p stands for package
local getArgs = require('Module:Arguments').getArgs
-- this is a limit on the max default image size to avoid multi-MB full-size images
-- being loaded. if the image is smaller than this, it is loaded at full resolution
local max_image_size = 1000
-- this is a nominal "max" screen size used when computing the maximum size of an
-- image with a width in percent. For example, on a 2048px screen, an image at
-- 10% will be 204px at most, so there's no point loading a 1000px image.
local max_screen_size = 2048
local function arg_or_nil(args, name)
if args[name] ~= nil and args[name] ~= "" then
return args[name]
end
return nil
end
-- add to a table if the variable is not nil or empty
local function add_if(t, key, var)
if var ~= nil and var ~= "" then
t[key] = var
end
end
-- append a CSS style to a table so we can use mw.html:css on it
local function add_style_to_table(t, s)
if s == nil or s == "" then
return
end
for rule in string.gmatch(s, "([^;]+)") do
local idx, _
idx, _ = string.find(rule, ":")
if idx then
t[string.sub(rule, 0, idx - 1)] = string.sub(rule, idx + 1)
end
end
end
local function construct_image_markup(img_name, args)
-- get the image info
-- NOTE: we will use the file attribute, this is an expensive function
local img_title = mw.title.makeTitle("File", img_name)
-- Whatever we were given it was not a valid file name
if not img_title then
local error = "The specified image (" .. img_name .. ") is invalid"
return require('Module:Error').error({message = error})
end
-- The filename given does not exist
if not img_title.file.exists then
local error = "The specified image (" .. img_name .. ") does not exist"
return require('Module:Error').error({message = error})
end
local img_width_px
if arg_or_nil(args, "imgwidth") then
-- the user told us what they want
-- This assumes the imgwidth will be specified in pixels, which isn't always the case, Nor does the template documentation mention this.
img_width_px = string.gsub(args['imgwidth'], "px", "")
elseif arg_or_nil(args, "width") then
-- if the width parameter is in px or %, use that to get the image as the
-- image is at most the size of the container
local arg_px_width = string.match(args["width"], '(%d+)px$')
local arg_pc_width = string.match(args["width"], '(%d+)%%$')
if arg_px_width then
-- use what the parameter said
img_width_px = arg_px_width
elseif arg_pc_width then
-- use a nominal huge screen and find the image size upper bound
img_width_px = math.floor((max_screen_size * tonumber(arg_pc_width)) / 100)
-- still limit to the max size as well
img_width_px = math.min(max_image_size, img_width_px)
end
end
if img_width_px == nil then
-- use the default size, or the image size, whichever is smaller
img_width_px = math.min(img_title.file["width"], max_image_size)
end
-- construct the image markup we will use
local img_markup = "[[File:" .. img_name .. "|" .. img_width_px .. "px"
if arg_or_nil(args, "alt") then
img_markup = img_markup .. "|alt=" .. args["alt"]
end
if arg_or_nil(args, "link") then
img_markup = img_markup .. "|link=" .. args["link"]
end
if arg_or_nil(args, "page") then
img_markup = img_markup .. "|page=" .. args["page"]
end
img_markup = img_markup .. "|class=freedImg"
if args["imgclass"] then
img_markup = img_markup .. " " .. args["imgclass"]
end
img_markup = img_markup .. "]]"
return img_markup
end
local function construct_caption(parent, is_div, args, caption, class)
local tag = is_div and "div" or "span"
local pstyle = {}
add_if(pstyle, "text-align", args["talign"])
add_if(pstyle, "margin-right", args["tmright"])
add_if(pstyle, "margin-left", args["tmleft"])
add_if(pstyle, "text-indent", args["indent"])
add_style_to_table(pstyle, arg_or_nil(args, "tstyle"))
local para = parent:tag(tag)
para
:css(pstyle)
:addClass("imgCaption wst-freedimg-caption")
:wikitext(caption)
-- additional classes
if class then
para:addClass(class)
end
return para
end
local function freedImg(is_div, args)
local cats = {}
local img_markup
-- construct the image markup we will use
if args['type'] == "math" or args['type'] == "score" or args['type'] == "user" then
-- math, score and user just place the markup directly
img_markup = args["file"]
elseif args.file == nil then
local error = "The file parameter must be given (use \"missing\" if the image needs to be added later)"
return require('Module:Error').error({message = error})
elseif args.file == "missing" then
img_markup = mw.html.create("span")
:addClass("wst-freedimg-missing")
:wikitext('An image should appear at this position in the text.')
img_markup = tostring(img_markup)
table.insert(cats, "Pages with missing images")
elseif args.file == "removed" then
img_markup = mw.html.create("span")
:addClass("wst-freedimg-missing")
:wikitext('A non free image has been removed.<br>It can be viewed in the original at '.. args["srcdoc"] .. '.' )
img_markup = tostring(img_markup)
table.insert(cats, "Pages with redacted images")
else
img_markup = construct_image_markup(args["file"], args)
end
local outer_tag = is_div and "div" or "span"
local caption_tag = is_div and "p" or "span"
local outer_div_class = {"freedImg", "wst-freedimg"}
if arg_or_nil(args, "cclass") then
table.insert(outer_div_class, args["cclass"])
end
local outer_div_style = {}
add_if(outer_div_style, "width", args["width"])
add_if(outer_div_style, "margin-right", args["margin-right"])
add_if(outer_div_style, "margin-left", args["margin-left"])
add_if(outer_div_style, "float", args["float"])
add_if(outer_div_style, "clear", args["clear"])
if not is_div then
outer_div_style["display"] = "inline-block"
end
add_style_to_table(outer_div_style, args["cstyle"])
local outer = mw.html.create(outer_tag)
outer
:addClass(table.concat(outer_div_class, " "))
:css(outer_div_style)
-- add the top caption, if there is one, to the outer div
if arg_or_nil(args, 'top-caption') then
construct_caption(outer, is_div, args, args['top-caption'], 'wst-freedimg-caption-top')
end
outer:wikitext(img_markup)
-- add the bottom caption, if there is one, to the outer div
if arg_or_nil(args, 'caption') then
construct_caption(outer, is_div, args, args['caption'], 'wst-freedimg-caption-bottom')
end
outer = tostring(outer)
for k, v in pairs(cats) do
outer = outer .. "[[Category:" .. v .. "]]"
end
return outer
end -- function freedimg
--[=[
Construct a "block" FreedImg
]=]
function p.freedImg(frame)
local args = getArgs(frame)
return freedImg(true, args)
end
--[=[
Construct an "inline" FreedImg
]=]
function p.freedImg_span(frame)
local args = getArgs(frame)
return freedImg(false, args)
end
return p