Jump to content

Module:Gallery/sandbox: Difference between revisions

From Wikipedia, the free encyclopedia
Content deleted Content added
Removes wikilinks and wikicode when adding the caption to the alt text
Text.getPlain don't strips wikilinks and images. But the Module:Plain text does the desired function
Line 6: Line 6:
-- local templatestyles = 'Module:Gallery/styles.css'
-- local templatestyles = 'Module:Gallery/styles.css'
local yesno = require('Module:Yesno')
local yesno = require('Module:Yesno')
local Text = require('Module:Text')
local plaintextModule = require('Module:Plain text')

Text = Text.Text()
local function plaintext(text)
return plaintextModule.main({ args = { text, encode = "no" } })
end


local function trim(s)
local function trim(s)
Line 19: Line 22:


-- Check if it starts with "File:", "Image:", or "Media:"
-- Check if it starts with "File:", "Image:", or "Media:"
prefix = file:match("^(%a+):")
local prefix = file:match("^(%a+):")
if prefix and (prefix == "file" or prefix == "image" or prefix == "media") then
if prefix and (prefix == "file" or prefix == "image" or prefix == "media") then
return true
return true
Line 49: Line 52:
if k and type(k) == 'string' then
if k and type(k) == 'string' then
if k == 'align' or k == 'state' or k == 'style' or k == 'title' or
if k == 'align' or k == 'state' or k == 'style' or k == 'title' or
k == 'width' or k == 'height' or k == 'lines' or k == 'whitebg' or
k == 'width' or k == 'height' or k == 'whitebg' or
k == 'mode' or k == 'footer' or k == 'perrow' or k == 'noborder' or
k == 'mode' or k == 'footer' or k == 'perrow' or k == 'noborder' or
k:match('^alt%d+$') or k:match('^class%d+$') or k:match('^%d+$') then
k:match('^alt%d+$') or k:match('^class%d+$') or k:match('^%d+$') then
Line 72: Line 75:
-- Otherwise, for testing purposes, assume args are being passed directly in.
-- Otherwise, for testing purposes, assume args are being passed directly in.
local origArgs = (type(frame.getParent) == 'function') and frame:getParent().args or frame
local origArgs = (type(frame.getParent) == 'function') and frame:getParent().args or frame

-- ParserFunctions considers the empty string to be false, so to preserve the previous
-- ParserFunctions considers the empty string to be false, so to preserve the previous
-- behavior of {{gallery}}, change any empty arguments to nil, so Lua will consider
-- behavior of {{gallery}}, change any empty arguments to nil, so Lua will consider
-- them false too.
-- them false too.
Line 162: Line 165:
end
end
local altCount = 0; -- remove after migration
local altCount = 0;
-- Run through virtualgallery and builds gallery
-- Run through virtualgallery and builds gallery
Line 178: Line 181:
-- It is necessary because we add classes codes in the next step,
-- It is necessary because we add classes codes in the next step,
-- we do not want to let the alt empty with just the non-printing characters.
-- we do not want to let the alt empty with just the non-printing characters.
alt = (caption ~= '') and Text.getPlain(caption) or ''
alt = (caption ~= '') and plaintext(caption) or ''
end
end


Line 213: Line 216:
frame:extensionTag{ name = 'gallery', content = '\n' .. table.concat(gallery,'\n'), args = gargs}
frame:extensionTag{ name = 'gallery', content = '\n' .. table.concat(gallery,'\n'), args = gargs}
)
)

if args.footer then
if args.footer then
tbl:tag('div')
tbl:tag('div')

Revision as of 20:02, 7 February 2025

-- This module implements {{gallery}} by wrapping the <gallery> core extension tag.

local p = {}

local templatestyles = 'Module:Gallery/sandbox/styles.css'
-- local templatestyles = 'Module:Gallery/styles.css'
local yesno = require('Module:Yesno')
local plaintextModule = require('Module:Plain text')

local function plaintext(text)
    return plaintextModule.main({ args = { text, encode = "no" } })
end

local function trim(s)
	return mw.ustring.gsub(mw.ustring.gsub(s or '', '%s', ' '), '^%s*(.-)%s*$', '%1')
end

local tracking, preview

local function isImage(file)
    local file = trim(file):lower() -- Case insensitive check

    -- Check if it starts with "File:", "Image:", or "Media:"
    local prefix = file:match("^(%a+):")
    if prefix and (prefix == "file" or prefix == "image" or prefix == "media") then
        return true
    end
    
    local valid_extensions = {
        "apng", "djvu", "flac", "gif", "jfi", "jfif", "jif", "jpe", "jpeg", "jpg",
        "m1a", "m1v", "m2a", "m2v", "mid", "mp1", "mp2", "mp3", "mpa", "mpe", "mpeg", "mpg",
        "mpv", "oga", "ogg", "ogv", "opus", "pdf", "png", "stl", "svg", "svgz", "tif", "tiff",
        "wav", "wave", "webm", "webp", "xcf"
    }
    
    -- Extract file extension, of 3 or 4 characters only
    local ext = file:match("%.(%w%w%w%w?)$")
    
    -- Check if the extension is in the valid list
    if ext then
        for _, valid_ext in ipairs(valid_extensions) do
            if ext == valid_ext then
            	table.insert(tracking, '[[Category:Pages using gallery without a media namespace prefix]]')
                return true
            end
        end
    end
    return false
end

local function checkarg(k,v)
	if k and type(k) == 'string' then
		if k == 'align' or k == 'state' or k == 'style' or k == 'title' or
			k == 'width' or k == 'height' or k == 'whitebg' or
			k == 'mode' or k == 'footer' or k == 'perrow' or k == 'noborder' or
			k:match('^alt%d+$') or k:match('^class%d+$') or k:match('^%d+$') then
			-- valid
		elseif k == 'captionstyle' then
			if not v:match('^text%-align%s*:%s*center[;%s]*$') then
				table.insert(tracking, '[[Category:Pages using gallery with the captionstyle parameter]]')
			end
		else
			-- invalid
			local vlen = mw.ustring.len(k)
			k = mw.ustring.sub(k, 1, (vlen < 25) and vlen or 25)
			k = mw.ustring.gsub(k, '[^%w%-_ ]', '?')
			table.insert(tracking, '[[Category:Pages using gallery with unknown parameters|' .. k .. ']]')
			table.insert(preview, '"' .. k .. '"')
		end
	end
end

function p.gallery(frame)
	-- If called via #invoke, use the args passed into the invoking template.
	-- Otherwise, for testing purposes, assume args are being passed directly in.
	local origArgs = (type(frame.getParent) == 'function') and frame:getParent().args or frame

    -- ParserFunctions considers the empty string to be false, so to preserve the previous
    -- behavior of {{gallery}}, change any empty arguments to nil, so Lua will consider
    -- them false too.
    local args = {}
    tracking, preview = {}, {}
    for k, v in pairs(origArgs) do
    	if v ~= '' then
    		args[k] = v
    		checkarg(k,v)
    	end
	end
	
	if (args.mode or '') == 'packed' and (args.align or '') == '' then
		args.align = 'center'
	end

	if (args.align or '') == 'centre' then
		args.align = 'center'
	end

	local tbl = mw.html.create('div')
	tbl:addClass('mod-gallery-sb')
    --tbl:addClass('mod-gallery')
    
	if args.state then
		tbl
			:addClass('mod-gallery-collapsible')
			:addClass('collapsible')
			:addClass(args.state)
	end
	
	if args.style then
		tbl:cssText(args.style)
	else
		tbl:addClass('mod-gallery-default')
	end
	
	if args.align then
		tbl:addClass('mod-gallery-' .. args.align:lower())
	end

	if args.title then
		tbl:tag('div')
			:addClass('title')
				:tag('div')
					:wikitext(args.title)
	end
	
	local gargs = {}
	gargs['class'] = 'nochecker' .. (args.noborder and '' or ' bordered-images')
	gargs['widths'] = tonumber(args.width) or 180
	gargs['heights'] = tonumber(args.height) or 180
	gargs['style'] = args.captionstyle
	gargs['perrow'] = args.perrow
	gargs['mode'] = args.mode
	if yesno(args.whitebg or 'yes') then
		gargs['class'] = gargs['class'] .. ' whitebg'
	end
	
	local virtualgallery = {}
	local gallery = {}
	
	local imageCount = 0

	local zwsp = string.char(0xE2, 0x80, 0x8B) -- U+200B Zero Width Space
	local zwnj = string.char(0xE2, 0x80, 0x8C) -- U+200C Zero Width Non-Joiner
	
	-- create a coding to identify classes
	-- using unicode non-printing characters
	-- this is a workaround until we get the class arg in the <gallery> tag
	-- https://phabricator.wikimedia.org/T344784
 
	local skininvert    = zwsp .. zwsp .. zwsp;
	local bgtransparent = zwsp .. zwsp .. zwnj;
	
	for i = 1, #args do
	    local currentfield = trim(args[i]) or ''
	
	    if currentfield == '' then
	        -- Skip empty fields
	    elseif isImage(currentfield) then
	        imageCount = imageCount + 1
	        virtualgallery[imageCount] = { currentfield }
	    elseif imageCount > 0 and virtualgallery[imageCount][2] == nil then
	    	-- In case of multiple captions, use the first and ignore the laters
	        virtualgallery[imageCount][2] = currentfield
	    end
	end
	
	local altCount = 0;
	
	-- Run through virtualgallery and builds gallery
	for n = 1, #virtualgallery do
	    local img = virtualgallery[n][1]
	    local caption = virtualgallery[n][2] or ''
	    local alt = trim(args['alt' .. n] or '')
	    local class = trim(args['class' .. n] or '')
	    
	    -- we count alt text only before any modification
	    if alt ~= '' then
	    	altCount = altCount + 1
	    else
	    	-- if alt is empty, we use the caption as a alt text.
	    	-- It is necessary because we add classes codes in the next step,
	    	-- we do not want to let the alt empty with just the non-printing characters.
	    	alt = (caption ~= '') and plaintext(caption) or ''
	    end

	    -- we attach the non-printing code to the end of the alt text
	    if mw.ustring.find(class, 'skin%-invert') then -- this matches both skin-invert and skin-invert-image
	    	alt = alt .. skininvert
	    end
	    
	    -- as it is possible to combine multiple classes, we use find instead of ==
	    if mw.ustring.find(class, 'bg%-transparent') then
	    	alt = alt .. bgtransparent
	    end
	    
	    table.insert(gallery, img .. 
	    	(alt ~= '' and ('|alt=' .. alt) or '') ..
	    	(caption ~= '' and ('|' .. caption) or ''))
	end
	
	-- For tracking and verifying during migration to the new algorimth.
	-- It can be removed once everything is verified
	if math.ceil(#args / 2) > imageCount then
		if altCount > 0 then
			table.insert(tracking, '[[Category:Pages using gallery with potential alt text mismatch]]')
		--else
		--	table.insert(tracking, '[[Category:Pages using gallery with extra empty fields]]')
		end
	end
	
	
	tbl:tag('div')
		:addClass('main')
		:tag('div')
			:wikitext(
				frame:extensionTag{ name = 'gallery', content = '\n' .. table.concat(gallery,'\n'), args = gargs}
				)

	if args.footer then
		tbl:tag('div')
			:addClass('footer')
				:tag('div')
					:wikitext(args.footer)
	end

	local trackstr = (#tracking > 0) and table.concat(tracking, '') or ''
	if #preview > 0 then
		trackstr = require('Module:If preview')._warning({
			'Unknown parameters ' .. table.concat(preview, '; ') .. '.'
		}) .. trackstr
	end
	
	return frame:extensionTag{ name = 'templatestyles', args = { src = templatestyles} } .. tostring(tbl) .. trackstr
end

return p