One Piece Wiki

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 = '&nbsp;·&nbsp;'

	local vde = mw.html.create('div')
		:cssText('position:absolute; left:10px; font-size:80%;')
		:addClass('vde')
	vde:wikitext('&#91;[[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>]]&#93;') -- ?

	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