Usage[]
This module implements the {{Portrait Gallery}}
template. Please see the template for usage instructions.
Acknowledgements[]
This module recreates the Portrait Gallery's existing behavior, originally written in wikitext, more efficiently and with minor ease-of-use changes.
See contribution history for Template:Portrait Gallery and its subpages for contributors to the original template, with main contributor User:Sff9.
--------------------------------------------------------------------
-- Portrait Gallery Module
-- * Recreates 'Template:Portrait Gallery' functionality
--------------------------------------------------------------------
local p = {}
local args = {} -- Arguments
local last_arg = 0 -- Index of last gallery item argument
local cols -- Number of columns
------------------------------------------------
-- Entries
------------------------------------------------
-- Generates HTML for gallery entries
local function processEntries()
if last_arg == 0 then return '' end
local counter = 1
local tr = mw.html.create('tr')
local td = mw.html.create('td')
:cssText('padding: 0; margin: 0;')
local wrapper = mw.html.create('div')
:addClass('portrait-gallery-section')
:cssText('font-size: 14px; display: flex; flex-wrap: wrap;')
if last_arg <= 2 then wrapper:addClass('portrait-gallery-2') end
td:node(wrapper)
tr:node(td)
-- Iterate over entries, using last arg as end point
while counter <= last_arg + 1 do
-- Since slots can be skipped, check that current index is filled
if args[counter] and not args[counter]:match('^%s*$') then
-- Isolate name, image, and link
args[counter] = string.gsub(args[counter], '\\\\', '\\ \\')
local name = split(args[counter])[1]
local link = split(args[counter])[3] or split(split(name, '<')[1], '(')[1]
local image = split(args[counter])[2]
if link == 'NOLINK' then link = '' end
if image == '' or not image then
if link ~= '' then
image = link..' Portrait.png'
else
image = split(split(name, '<')[1], '(')[1]..' Portrait.png'
end
elseif image == 'NOPIC' then
image = 'NoPicAvailable.png'
end
local entry = mw.html.create('div')
:addClass('portrait-gallery-entry')
:cssText('width: 123px; border: 1px solid; padding: 1px;')
local structure = mw.html.create('div')
:addClass('portrait-gallery-structure')
:cssText('display: flex; flex-flow: column; height: 100%;')
local imageElement = mw.html.create('span')
:addClass('portrait-gallery-image')
:cssText('height: 119px; display: flex; flex-direction: column; justify-content: center;')
imageElement:wikitext(string.format('[[File:%s|119x119px|link=%s]]',
image, link))
local nameElement = mw.html.create('div')
:addClass('portrait-gallery-name')
:cssText('display: flex; align-items: center; justify-content: center; flex: 1 1 auto')
if link == '' then
nameElement:wikitext('<small>'..name..'</small>')
else
nameElement:wikitext(string.format('[[%s|<small>%s</small>]]', link, name))
end
-- Compile HTML
structure:node(imageElement)
structure:node(nameElement)
entry:node(structure)
wrapper:node(entry)
end
counter = counter + 1 -- increment counter
end
-- All names processed. Gallery section is complete.
return tostring(tr)
end
-- String split function
-- From https://stackoverflow.com/a/7615129
--
-- @param inputstr The string to be split
-- @param sep The separator to split on
function split(inputstr, sep)
if sep == nil then
sep = "\\"
end
local t = {}
for str in string.gmatch(inputstr, "([^"..sep.."]+)") do
table.insert(t, trim(str))
end
return t
end
-- String trim function
-- From http://lua-users.org/wiki/StringTrim ("trim1")
--
-- @param s The string to trim
function trim(s)
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
--- Processes the VDE links in the title
--
-- @param parent The VEQ's parent element
local function processVEQ( parent )
if not args.template then return end
local between = ' · '
local vde = mw.html.create('div')
:cssText('position:absolute; left:10px; font-size:80%;')
:addClass('vde')
vde:wikitext('[[[Template:'..args.template..'|<span title="View">v</span>]]') -- V
vde:wikitext(between)
vde:wikitext('[https://onepiece.fandom.com/Template:'.. args.template
:gsub(' ', '_') ..'?action=edit <span title="Edit">e</span>]') -- E
vde:wikitext(between)
vde:wikitext('[[Template:Portrait Gallery|<span title="How does it work?">?</span>]]]') -- ?
parent:node(vde)
end
------------------------------------------------
-- ARGUMENTS PREPROCESSOR
-- Extracts arguments from frame and stores them in args table
------------------------------------------------
--- Preprocessor for the arguments.
-- Will fill up the args table with the parameters from the frame grouped by their type.
--
-- @param frame The frame passed to the Module.
local function preProcessArgs(frame)
local tmp = {}
if frame == mw.getCurrentFrame() then
tmp = frame:getParent().args
else
tmp = frame
end
-- Set default arg values
args['included'] = 'no'
args['icols'] = 5
args['cols'] = 5
-- Loop over all the args
for k, v in pairs(tmp) do
-- DO NOT skip empty args
args[k] = v -- Simple copy
-- Keep track of last image
if type(k) == 'number' and k % 2 == 1 and k > last_arg then
last_arg = k
end
end
-- Support for either "colorscheme" or "cs" keywords
if args['colorscheme'] then args['cs'] = args['colorscheme'] end
-- Define number of columns
if args['included'] == 'yes' then cols = args['icols'] else cols = args['cols'] end
cols = tonumber(cols) -- Ensure col is a number
if not cols then cols = 5 end -- If cols has an invalid value, then default to 5
end
------------------------------------------------
-- MAIN FUNCTIONS
------------------------------------------------
--- Processes the arguments to create the portrait gallery or portrait gallery section
--
-- @return A string with HTML of the portrait gallery or portrait gallery section
local function _gallery()
-- Create the root HTML element
local gallery = ''
local header = ''
-- If not included AND ( "multi" is defined as "header" OR "multi" and "section" are not defined )
-- create a table header
if args['included'] ~= 'yes' and ((args['multi'] and args['multi'] == 'header') or (not args['multi'] and not args['section'])) then
header = string.format('<table border="1" class="portrait-gallery cs %s" style="margin: auto; font-weight: bold; text-align: center; max-width: %spx;',
(args['cs'] or ''), (cols * 123) + 8)
if args['backcolor'] then header = header..'background: #'..args['backcolor']..';' end
if args['textcolor'] then header = header..'color: #'..args['textcolor']..';' end
header = header..'">'
end
local sectionHeader = ''
-- If "multi" is not defined as "footer", and a title ("title" or "section") is defined,
-- create a section header
if (args['multi'] and args['multi'] ~= 'footer') or (args['title'] or args['section']) then
local title = args['title'] or args['section']
sectionHeader = mw.html.create('tr')
local th = mw.html.create('th'):attr('colspan', cols)
local div = mw.html.create('div'):cssText('position: relative;')
processVEQ(div)
local span = mw.html.create('span')
:cssText('display: inline-block; width: 0; min-width: 100%;')
if args.template then span:addClass('portrait-gallery-header') end
if args.multi == 'header' then span:cssText('font-size: larger;') end
span:wikitext(title)
-- Compile section header HTML
div:node(span)
th:node(div)
sectionHeader:node(th)
sectionHeader = tostring(sectionHeader)
end
local section = processEntries()
local footer = ''
-- If not included AND ( "multi" is defined as "footer" OR "multi" and "section" are not defined )
-- create a table footer
if args['included'] ~= 'yes' and ((args['multi'] and args['multi'] == 'footer') or (not args['multi'] and not args['section'])) then
footer = '</table>'
end
gallery = header..sectionHeader..section..footer
return gallery
end
--- Main module entry point.
-- To be called with {{#invoke:Portrait Gallery|main}} or directly from another module.
--
-- @param frame The frame passed to the module via the #invoke. If called from another
-- module directly, this should be a table with the parameter definition.
function p.main(frame)
-- Save the arguments in a local variable so other functions can use them.
preProcessArgs(frame)
return _gallery()
end
return p