Difference between revisions of "Module:Userbox/sandbox"

From blackwiki
Jump to navigation Jump to search
blackwiki>Mr. Stradivarius
(fix function name)
m (65 revisions imported)
 
(45 intermediate revisions by 7 users not shown)
Line 1: Line 1:
 
-- This module implements {{userbox}}.
 
-- This module implements {{userbox}}.
  
local calculateContrastRatio = require('Module:Color contrast')._ratio
+
local categoryHandler = require('Module:Category handler').main
local yesno = require('Module:Yesno')
+
 
 +
local p = {}
  
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
Line 43: Line 44:
 
end
 
end
  
local function hasLegibleContrast(data)
+
--------------------------------------------------------------------------------
local function trim(s)
+
-- Argument processing
return s:match('^%s*(.-)%s*$')
+
--------------------------------------------------------------------------------
end
 
 
 
local function hasText(wikitext)
 
if not wikitext then
 
return false
 
end
 
  
local function getAlt(text)
+
local function makeInvokeFunc(funcName)
return text:match('|alt=([^|]*)') or ''
+
return function (frame)
 +
local origArgs = require('Module:Arguments').getArgs(frame)
 +
local args = {}
 +
for k, v in pairs(origArgs) do
 +
args[k] = v
 
end
 
end
+
return p.main(funcName, args)
wikitext = wikitext:gsub(']]', '|]]')
 
wikitext = wikitext:gsub('%[%[%s*[Mm][Ee][Dd][Ii][Aa]%s*:[^|]-(|.-)]]', getAlt)
 
wikitext = wikitext:gsub('%[%[%s*[Ii][Mm][Aa][Gg][Ee]%s*:[^|]-(|.-)]]', getAlt)
 
wikitext = wikitext:gsub('%[%[%s*[Ff][Ii][Ll][Ee]%s*:[^|]-(|.-)]]', getAlt)
 
return trim(wikitext) ~= ''
 
 
end
 
end
 +
end
  
local function isLegible(color1, color2)
+
p.userbox = makeInvokeFunc('_userbox')
return calculateContrastRatio{color1, color2, error = 0} >= 4.5
+
p['userbox-2'] = makeInvokeFunc('_userbox-2')
end
+
p['userbox-r'] = makeInvokeFunc('_userbox-r')
 
 
local function idIsLegible(isShown, id, color1, color2)
 
return not isShown or not hasText(id) or isLegible(color1, color2)
 
end
 
 
 
return isLegible(data.infoColor, data.backgroundColor) and
 
idIsLegible(data.showId, data.id, data.idColor, data.idBackgroundColor) and
 
idIsLegible(data.showId2, data.id2, data.id2Color, data.id2BackgroundColor)
 
end
 
  
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
-- Data processing functions
+
-- Main functions
 
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
  
local dataFuncs = {}
+
function p.main(funcName, args)
 +
local userboxData = p[funcName](args)
 +
local userbox = p.render(userboxData)
 +
local cats = p.categories(args)
 +
return userbox .. (cats or '')
 +
end
  
function dataFuncs.userbox(args)
+
function p._userbox(args)
 
-- Does argument processing for {{userbox}}.
 
-- Does argument processing for {{userbox}}.
 
local data = {}
 
local data = {}
Line 91: Line 82:
 
local borderWidthNum = checkNum(args['border-width'] or args['border-s'], 1) -- Used to calculate width.
 
local borderWidthNum = checkNum(args['border-width'] or args['border-s'], 1) -- Used to calculate width.
 
data.borderWidth = addSuffix(borderWidthNum, 'px')
 
data.borderWidth = addSuffix(borderWidthNum, 'px')
data.borderColor = args['border-color'] or args[1] or args['border-c'] or args['id-c'] or '#999'
+
data.borderColor = args['border-color'] or args['border-c'] or args[1] or args['id-c'] or '#999'
 
data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
 
data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
 
data.bodyClass = args.bodyclass
 
data.bodyClass = args.bodyclass
Line 127: Line 118:
 
end
 
end
  
dataFuncs['userbox-2'] = function (args)
+
p['_userbox-2'] = function (args)
 
-- Does argument processing for {{userbox-2}}.
 
-- Does argument processing for {{userbox-2}}.
 
local data = {}
 
local data = {}
Line 133: Line 124:
 
-- Get div tag values.
 
-- Get div tag values.
 
data.float = args.float or 'left'
 
data.float = args.float or 'left'
local borderWidthNum = checkNum(args[9] or args['border-s'], 1) -- Used to calculate width.
+
local borderWidthNum = checkNum(args['border-s'] or args[9], 1) -- Used to calculate width.
 
data.borderWidth = addSuffix(borderWidthNum, 'px')
 
data.borderWidth = addSuffix(borderWidthNum, 'px')
data.borderColor = args[1] or args['border-c'] or args['id1-c'] or '#999999'
+
data.borderColor = args['border-c'] or args[6] or args['id1-c'] or args[1] or '#999999'
 
data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
 
data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
 
data.bodyClass = args.bodyclass
 
data.bodyClass = args.bodyclass
  
 
-- Get table tag values.
 
-- Get table tag values.
data.backgroundColor = args[2] or args['info-c'] or '#eeeeee'
+
data.backgroundColor = args['info-c'] or args[2] or '#eeeeee'
  
 
-- Get info values.
 
-- Get info values.
data.info = args[4] or args.info or "<code>{{{info}}}</code>"
+
data.info = args.info or args[4] or "<code>{{{info}}}</code>"
 
data.infoTextAlign = args['info-a'] or 'left'
 
data.infoTextAlign = args['info-a'] or 'left'
 
data.infoFontSize = checkNumAndAddSuffix(args['info-s'], 8, 'pt')
 
data.infoFontSize = checkNumAndAddSuffix(args['info-s'], 8, 'pt')
data.infoColor = args[8] or args['info-fc'] or 'black'
+
data.infoColor = args['info-fc'] or args[8] or 'black'
 
data.infoPadding = args['info-p'] or '0 4px 0 4px'
 
data.infoPadding = args['info-p'] or '0 4px 0 4px'
 
data.infoLineHeight = args['info-lh'] or '1.25em'
 
data.infoLineHeight = args['info-lh'] or '1.25em'
Line 156: Line 147:
 
data.idWidth = checkNumAndAddSuffix(args['id1-w'], 45, 'px')
 
data.idWidth = checkNumAndAddSuffix(args['id1-w'], 45, 'px')
 
data.idHeight = checkNumAndAddSuffix(args['id-h'], 45, 'px')
 
data.idHeight = checkNumAndAddSuffix(args['id-h'], 45, 'px')
data.idBackgroundColor = args[1] or args['id1-c'] or '#dddddd'
+
data.idBackgroundColor = args['id1-c'] or args[1] or '#dddddd'
 
data.idTextAlign = 'center'
 
data.idTextAlign = 'center'
 
data.idFontSize = checkNumAndAddSuffix(args['id1-s'], 14, 'pt')
 
data.idFontSize = checkNumAndAddSuffix(args['id1-s'], 14, 'pt')
Line 169: Line 160:
 
data.id2Width = checkNumAndAddSuffix(args['id2-w'], 45, 'px')
 
data.id2Width = checkNumAndAddSuffix(args['id2-w'], 45, 'px')
 
data.id2Height = data.idHeight
 
data.id2Height = data.idHeight
data.id2BackgroundColor = args[7] or args['id2-c'] or args[1] or '#dddddd'
+
data.id2BackgroundColor = args['id2-c'] or args[7] or args[1] or '#dddddd'
 
data.id2TextAlign = 'center'
 
data.id2TextAlign = 'center'
 
data.id2FontSize = checkNumAndAddSuffix(args['id2-s'], 14, 'pt')
 
data.id2FontSize = checkNumAndAddSuffix(args['id2-s'], 14, 'pt')
Line 180: Line 171:
 
end
 
end
  
dataFuncs['userbox-r'] = function (args)
+
p['_userbox-r'] = function (args)
 
-- Does argument processing for {{userbox-r}}.
 
-- Does argument processing for {{userbox-r}}.
 
local data = {}
 
local data = {}
Line 188: Line 179:
 
local borderWidthNum = checkNum(args['border-width'] or args['border-s'], 1) -- Used to calculate width.
 
local borderWidthNum = checkNum(args['border-width'] or args['border-s'], 1) -- Used to calculate width.
 
data.borderWidth = addSuffix(borderWidthNum, 'px')
 
data.borderWidth = addSuffix(borderWidthNum, 'px')
data.borderColor = args['border-color'] or args[1] or args['border-c'] or args['id-c'] or '#999'
+
data.borderColor = args['border-color'] or args['border-c'] or args[1] or args['id-c'] or '#999'
 
data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
 
data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
 
data.bodyClass = args.bodyclass
 
data.bodyClass = args.bodyclass
Line 223: Line 214:
 
end
 
end
  
--------------------------------------------------------------------------------
+
function p.render(data)
-- Rendering functions
 
--------------------------------------------------------------------------------
 
 
 
local function renderUserbox(data)
 
 
-- Renders the userbox html using the content of the data table.  
 
-- Renders the userbox html using the content of the data table.  
 
-- Render the div tag html.
 
-- Render the div tag html.
Line 242: Line 229:
 
local tableroot = root:tag('table')
 
local tableroot = root:tag('table')
 
tableroot
 
tableroot
 +
:attr('role', 'presentation')
 
:css('border-collapse', 'collapse')
 
:css('border-collapse', 'collapse')
 
:css('width', data.width)
 
:css('width', data.width)
 
:css('margin-bottom', '0')
 
:css('margin-bottom', '0')
 +
:css('margin-top', '0')
 
:css('background', data.backgroundColor)
 
:css('background', data.backgroundColor)
 
 
Line 250: Line 239:
 
local tablerow = tableroot:tag('tr')
 
local tablerow = tableroot:tag('tr')
 
if data.showId then
 
if data.showId then
tablerow:tag('th')
+
tablerow:tag('td')
 
:css('border', '0')
 
:css('border', '0')
 
:css('width', data.idWidth)
 
:css('width', data.idWidth)
Line 257: Line 246:
 
:css('text-align', data.idTextAlign)
 
:css('text-align', data.idTextAlign)
 
:css('font-size', data.idFontSize)
 
:css('font-size', data.idFontSize)
 +
:css('font-weight', 'bold')
 
:css('color', data.idColor)
 
:css('color', data.idColor)
 
:css('padding', data.idPadding)
 
:css('padding', data.idPadding)
Line 282: Line 272:
 
-- Render the second id html.
 
-- Render the second id html.
 
if data.showId2 then
 
if data.showId2 then
tablerow:tag('th')
+
tablerow:tag('td')
 
:css('border', '0')
 
:css('border', '0')
 
:css('width', data.id2Width)
 
:css('width', data.id2Width)
Line 289: Line 279:
 
:css('text-align', data.id2TextAlign)
 
:css('text-align', data.id2TextAlign)
 
:css('font-size', data.id2FontSize)
 
:css('font-size', data.id2FontSize)
 +
:css('font-weight', 'bold')
 
:css('color', data.id2Color)
 
:css('color', data.id2Color)
 
:css('padding', data.id2Padding)
 
:css('padding', data.id2Padding)
Line 297: Line 288:
 
end
 
end
  
return tostring(root)
+
local title = mw.title.getCurrentTitle()
end
+
if (title.namespace == 2) and not title.text:match("/") then
 +
return tostring(root) -- regular user page
 +
elseif title.namespace == 14 then
 +
return tostring(root) -- category
 +
elseif title.isTalkPage then
 +
return tostring(root) -- talk page
 +
end
 +
 
 +
local legible = true
 +
local contrast = require('Module:Color contrast')._ratio
  
local function renderCategories(args, data, page)
+
local function has_text(wikitext)
-- The page parameter makes the function act as though the module was being
+
local function get_alt(text)
-- called from that page. It is included for testing purposes.
+
return text:match("|alt=([^|]*)") or ""
local title
+
end
if page then
+
title = mw.title.new(page)
+
wikitext = wikitext:gsub("]]", "|]]")
else
+
wikitext = wikitext:gsub("%[%[%s*[Mm][Ee][Dd][Ii][Aa]%s*:[^|]-(|.-)]]", get_alt)
title = mw.title.getCurrentTitle()
+
wikitext = wikitext:gsub("%[%[%s*[Ii][Mm][Aa][Gg][Ee]%s*:[^|]-(|.-)]]", get_alt)
 +
wikitext = wikitext:gsub("%[%[%s*[Ff][Ii][Ll][Ee]%s*:[^|]-(|.-)]]", get_alt)
 +
return mw.text.trim(wikitext) ~= ""
 
end
 
end
  
-- Exit if we are on a blacklisted page or categorization is disabled.
+
if contrast { data.infoColor, data.backgroundColor, error = 0 } < 4.5 then
if title.prefixedText:find('/[aA]rchive') or yesno(args.nocat) then
+
legible = false
return ''
 
 
end
 
end
  
local ret = {}
+
if data.showId and contrast { data.idColor, data.idBackgroundColor, error = 0 } < 4.5 then
 +
if has_text(data.id or "") then
 +
legible = false
 +
end
 +
end
  
-- User-specified categories
+
if data.showId2 and contrast { data.id2Color, data.id2BackgroundColor, error = 0 } < 4.5 then
if not args.notcatsubpages or not title.isSubpage then
+
if has_text(data.id2 or "") then
local userCats = {}
+
legible = false
userCats[#userCats + 1] = args.usercategory
 
userCats[#userCats + 1] = args.usercategory2
 
userCats[#userCats + 1] = args.usercategory3
 
if #userCats > 0 then
 
if title.namespace == 2 and title.rootText ~= 'UBX' then
 
-- We are in the user namespace, and this is not the "UBX"
 
-- userbox pseudo-namespace.
 
for i, cat in ipairs(userCats) do
 
ret[#ret + 1] = makeCat(cat)
 
end
 
elseif title.namespace == 10 then
 
-- We are in the template namespace.
 
local basepage = title.baseText
 
for i, cat in ipairs(userCats) do
 
ret[#ret + 1] = makeCat(cat, ' ' .. basepage)
 
end
 
end
 
 
end
 
end
 
end
 
end
  
-- Tracking categories
+
if not legible then
local function maybeUserboxTemplate(title)
+
root:wikitext('[[Category:Userboxes with insufficient color contrast]]')
-- Whether this title could be a userbox template, rather than just a
 
-- transclusion of a userbox.
 
return title.namespace == 2 and title.isSubpage or -- In user space, but not a user page
 
title.namespace == 4 or -- Project namespace
 
title.namespace == 10 -- Template namespace
 
end
 
if title.namespace == 0 then -- Main namespace
 
ret[#ret + 1] = makeCat('Pages with misplaced templates')
 
elseif maybeUserboxTemplate(title) and not hasLegibleContrast(data) then
 
ret[#ret + 1] = makeCat('Potentially illegible userboxes')
 
 
end
 
end
  
return table.concat(ret)
+
return tostring(root)
 
end
 
end
  
--------------------------------------------------------------------------------
+
function p.categories(args, page)
-- Exports
+
-- Gets categories from [[Module:Category handler]].
--------------------------------------------------------------------------------
+
-- The page parameter makes the function act as though the module was being called from that page.
 
+
-- It is included for testing purposes.
local p = {}
+
local cats = {}
 
+
cats[#cats + 1] = args.usercategory
function p._testFuncs()
+
cats[#cats + 1] = args.usercategory2
local ret = {}
+
cats[#cats + 1] = args.usercategory3
for name, func in pairs(dataFuncs) do
+
cats[#cats + 1] = args.usercategory4
ret[name] = func
+
cats[#cats + 1] = args.usercategory5
 +
-- Get the title object
 +
local title
 +
if page then
 +
title = mw.title.new(page)
 +
else
 +
title = mw.title.getCurrentTitle()
 +
end
 +
-- Build category handler arguments.
 +
local chargs = {}
 +
chargs.page = page
 +
chargs.nocat = args.nocat
 +
chargs.main = '[[Category:Pages with misplaced templates]]'
 +
if args.notcatsubpages then
 +
chargs.subpage = 'no'
 +
end
 +
-- User namespace.
 +
local user = ''
 +
for i, cat in ipairs(cats) do
 +
user = user .. makeCat(cat)
 +
end
 +
chargs.user = user
 +
-- Template namespace.
 +
local basepage = title.baseText
 +
local template = ''
 +
for i, cat in ipairs(cats) do
 +
template = template .. makeCat(cat, ' ' .. basepage)
 
end
 
end
ret.renderUserbox = renderUserbox
+
chargs.template = template
ret.renderCategories = renderCategories
+
return categoryHandler(chargs)
return ret
 
 
end
 
end
 
function p.main(funcName, args)
 
local data = dataFuncs[funcName](args)
 
local userbox = renderUserbox(data)
 
local cats = renderCategories(args, data)
 
return (userbox or '') .. (cats or '')
 
end
 
 
setmetatable(p, {
 
__index = function (t, key)
 
return function (frame)
 
local origArgs = require('Module:Arguments').getArgs(frame, {
 
wrappers = {
 
'Template:Userbox',
 
'Template:Userbox-2',
 
'Template:Userbox-r',
 
}
 
})
 
local args = {}
 
for k, v in pairs(origArgs) do
 
args[k] = v
 
end
 
return p.main(key, args)
 
end
 
end
 
})
 
  
 
return p
 
return p

Latest revision as of 05:04, 30 September 2020

Documentation for this module may be created at Module:Userbox/sandbox/doc

-- This module implements {{userbox}}.

local categoryHandler = require('Module:Category handler').main

local p = {}

--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------

local function checkNum(val, default)
	-- Checks whether a value is a number greater than or equal to zero. If so,
	-- returns it as a number. If not, returns a default value.
	val = tonumber(val)
	if val and val >= 0 then
		return val
	else
		return default
	end
end

local function addSuffix(num, suffix)
	-- Turns a number into a string and adds a suffix.
	if num then
		return tostring(num) .. suffix
	else
		return nil
	end
end

local function checkNumAndAddSuffix(num, default, suffix)
	-- Checks a value with checkNum and adds a suffix.
	num = checkNum(num, default)
	return addSuffix(num, suffix)
end

local function makeCat(cat, sort)
	-- Makes a category link.
	if sort then
		return mw.ustring.format('[[Category:%s|%s]]', cat, sort)
	else
		return mw.ustring.format('[[Category:%s]]', cat)
	end
end

--------------------------------------------------------------------------------
-- Argument processing
--------------------------------------------------------------------------------

local function makeInvokeFunc(funcName)
	return function (frame)
		local origArgs = require('Module:Arguments').getArgs(frame)
		local args = {}
		for k, v in pairs(origArgs) do
			args[k] = v
		end
		return p.main(funcName, args)
	end
end

p.userbox = makeInvokeFunc('_userbox')
p['userbox-2'] = makeInvokeFunc('_userbox-2')
p['userbox-r'] = makeInvokeFunc('_userbox-r')

--------------------------------------------------------------------------------
-- Main functions
--------------------------------------------------------------------------------

function p.main(funcName, args)
	local userboxData = p[funcName](args)
	local userbox = p.render(userboxData)
	local cats = p.categories(args)
	return userbox .. (cats or '')
end

function p._userbox(args)
	-- Does argument processing for {{userbox}}.
	local data = {}

	-- Get div tag values.
	data.float = args.float or 'left'
	local borderWidthNum = checkNum(args['border-width'] or args['border-s'], 1) -- Used to calculate width.
	data.borderWidth = addSuffix(borderWidthNum, 'px')
	data.borderColor = args['border-color'] or args['border-c'] or args[1] or args['id-c'] or '#999'
	data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
	data.bodyClass = args.bodyclass

	-- Get table tag values.
	data.backgroundColor = args['info-background'] or args[2] or args['info-c'] or '#eee'

	-- Get info values.
	data.info = args.info or args[4] or "<code>{{{info}}}</code>"
	data.infoTextAlign = args['info-a'] or 'left'
	data.infoFontSize = checkNumAndAddSuffix(args['info-size'] or args['info-s'], 8, 'pt')
	data.infoHeight = checkNumAndAddSuffix(args['logo-height'] or args['id-h'], 45, 'px')
	data.infoPadding = args['info-padding'] or args['info-p'] or '0 4px 0 4px'
	data.infoLineHeight = args['info-line-height'] or args['info-lh'] or '1.25em'
	data.infoColor = args['info-color'] or args['info-fc'] or 'black'
	data.infoOtherParams = args['info-other-param'] or args['info-op']
	data.infoClass = args['info-class']

	-- Get id values.
	local id = args.logo or args[3] or args.id
	data.id = id
	data.showId = id and true or false
	data.idWidth = checkNumAndAddSuffix(args['logo-width'] or args['id-w'], 45, 'px')
	data.idHeight = checkNumAndAddSuffix(args['logo-height'] or args['id-h'], 45, 'px')
	data.idBackgroundColor = args['logo-background'] or args[1] or args['id-c'] or '#ddd'
	data.idTextAlign = args['id-a'] or 'center'
	data.idFontSize = checkNumAndAddSuffix(args['logo-size'] or args[5] or args['id-s'], 14, 'pt')
	data.idColor = args['logo-color'] or args['id-fc'] or data.infoColor
	data.idPadding = args['logo-padding'] or args['id-p'] or '0 1px 0 0'
	data.idLineHeight = args['logo-line-height'] or args['id-lh'] or '1.25em'
	data.idOtherParams = args['logo-other-param'] or args['id-op']
	data.idClass = args['id-class']

	return data
end

p['_userbox-2'] = function (args)
	-- Does argument processing for {{userbox-2}}.
	local data = {}

	-- Get div tag values.
	data.float = args.float or 'left'
	local borderWidthNum = checkNum(args['border-s'] or args[9], 1) -- Used to calculate width.
	data.borderWidth = addSuffix(borderWidthNum, 'px')
	data.borderColor = args['border-c'] or args[6] or args['id1-c'] or args[1] or '#999999'
	data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
	data.bodyClass = args.bodyclass

	-- Get table tag values.
	data.backgroundColor = args['info-c'] or args[2] or '#eeeeee'

	-- Get info values.
	data.info = args.info or args[4] or "<code>{{{info}}}</code>"
	data.infoTextAlign = args['info-a'] or 'left'
	data.infoFontSize = checkNumAndAddSuffix(args['info-s'], 8, 'pt')
	data.infoColor = args['info-fc'] or args[8] or 'black'
	data.infoPadding = args['info-p'] or '0 4px 0 4px'
	data.infoLineHeight = args['info-lh'] or '1.25em'
	data.infoOtherParams = args['info-op']

	-- Get id values.
	data.showId = true
	data.id = args.logo or args[3] or args.id1 or 'id1'
	data.idWidth = checkNumAndAddSuffix(args['id1-w'], 45, 'px')
	data.idHeight = checkNumAndAddSuffix(args['id-h'], 45, 'px')
	data.idBackgroundColor = args['id1-c'] or args[1] or '#dddddd'
	data.idTextAlign = 'center'
	data.idFontSize = checkNumAndAddSuffix(args['id1-s'], 14, 'pt')
	data.idLineHeight = args['id1-lh'] or '1.25em'
	data.idColor = args['id1-fc'] or data.infoColor
	data.idPadding = args['id1-p'] or '0 1px 0 0'
	data.idOtherParams = args['id1-op']

	-- Get id2 values.
	data.showId2 = true
	data.id2 = args.logo or args[5] or args.id2 or 'id2'
	data.id2Width = checkNumAndAddSuffix(args['id2-w'], 45, 'px')
	data.id2Height = data.idHeight
	data.id2BackgroundColor = args['id2-c'] or args[7] or args[1] or '#dddddd'
	data.id2TextAlign = 'center'
	data.id2FontSize = checkNumAndAddSuffix(args['id2-s'], 14, 'pt')
	data.id2LineHeight = args['id2-lh'] or '1.25em'
	data.id2Color = args['id2-fc'] or data.infoColor
	data.id2Padding = args['id2-p'] or '0 0 0 1px'
	data.id2OtherParams = args['id2-op']

	return data
end

p['_userbox-r'] = function (args)
	-- Does argument processing for {{userbox-r}}.
	local data = {}

	-- Get div tag values.
	data.float = args.float or 'left'
	local borderWidthNum = checkNum(args['border-width'] or args['border-s'], 1) -- Used to calculate width.
	data.borderWidth = addSuffix(borderWidthNum, 'px')
	data.borderColor = args['border-color'] or args['border-c'] or args[1] or args['id-c'] or '#999'
	data.width = addSuffix(240 - 2 * borderWidthNum, 'px') -- Also used in the table tag.
	data.bodyClass = args.bodyclass
	
	-- Get table tag values.
	data.backgroundColor = args['info-background'] or args[2] or args['info-c'] or '#eee'

	-- Get id values.
	data.showId = false -- We only show id2 in userbox-r.

	-- Get info values.
	data.info = args.info or args[4] or "<code>{{{info}}}</code>"
	data.infoTextAlign = args['info-align'] or args['info-a'] or 'left'
	data.infoFontSize = checkNumAndAddSuffix(args['info-size'] or args['info-s'], 8, 'pt')
	data.infoPadding = args['info-padding'] or args['info-p'] or '0 4px 0 4px'
	data.infoLineHeight = args['info-line-height'] or args['info-lh'] or '1.25em'
	data.infoColor = args['info-color'] or args['info-fc'] or 'black'
	data.infoOtherParams = args['info-other-param'] or args['info-op']
	
	-- Get id2 values.
	data.showId2 = true
	data.id2 = args.logo or args[3] or args.id or 'id'
	data.id2Width = checkNumAndAddSuffix(args['logo-width'] or args['id-w'], 45, 'px')
	data.id2Height = checkNumAndAddSuffix(args['logo-height'] or args['id-h'], 45, 'px')
	data.id2BackgroundColor = args['logo-background'] or args[1] or args['id-c'] or '#ddd'
	data.id2TextAlign = args['id-a'] or 'center'
	data.id2FontSize = checkNumAndAddSuffix(args['logo-size'] or args[5] or args['id-s'], 14, 'pt')
	data.id2Color = args['logo-color'] or args['id-fc'] or data.infoColor
	data.id2Padding = args['logo-padding'] or args['id-p'] or '0 0 0 1px'
	data.id2LineHeight = args['logo-line-height'] or args['id-lh'] or '1.25em'
	data.id2OtherParams = args['logo-other-param'] or args['id-op']

	return data
end

function p.render(data)
	-- Renders the userbox html using the content of the data table. 
	-- Render the div tag html.
	local root = mw.html.create('div')
	root
		:css('float', data.float)
		:css('border', (data.borderWidth or '') .. ' solid ' .. (data.borderColor or ''))
		:css('margin', '1px')
		:css('width', data.width)
		:addClass('wikipediauserbox')
		:addClass(data.bodyClass)

	-- Render the table tag html.
	local tableroot = root:tag('table')
	tableroot
		:attr('role', 'presentation')
		:css('border-collapse', 'collapse')
		:css('width', data.width)
		:css('margin-bottom', '0')
		:css('margin-top', '0')
		:css('background', data.backgroundColor)
	
	-- Render the id html.
	local tablerow = tableroot:tag('tr')
	if data.showId then
		tablerow:tag('td')
			:css('border', '0')
			:css('width', data.idWidth)
			:css('height', data.idHeight)
			:css('background', data.idBackgroundColor)
			:css('text-align', data.idTextAlign)
			:css('font-size', data.idFontSize)
			:css('font-weight', 'bold')
			:css('color', data.idColor)
			:css('padding', data.idPadding)
			:css('line-height', data.idLineHeight)
			:css('vertical-align', 'middle')
			:cssText(data.idOtherParams)
			:addClass(data.idClass)
			:wikitext(data.id)
	end

	-- Render the info html.
	tablerow:tag('td')
		:css('border', '0')
		:css('text-align', data.infoTextAlign)
		:css('font-size', data.infoFontSize)
		:css('padding', data.infoPadding)
		:css('height', data.infoHeight)
		:css('line-height', data.infoLineHeight)
		:css('color', data.infoColor)
		:css('vertical-align', 'middle')
		:cssText(data.infoOtherParams)
		:addClass(data.infoClass)
		:wikitext(data.info)
	
	-- Render the second id html.
	if data.showId2 then
		tablerow:tag('td')
			:css('border', '0')
			:css('width', data.id2Width)
			:css('height', data.id2Height)
			:css('background', data.id2BackgroundColor)
			:css('text-align', data.id2TextAlign)
			:css('font-size', data.id2FontSize)
			:css('font-weight', 'bold')
			:css('color', data.id2Color)
			:css('padding', data.id2Padding)
			:css('line-height', data.id2LineHeight)
			:css('vertical-align', 'middle')
			:cssText(data.id2OtherParams)
			:wikitext(data.id2)
	end

	local title = mw.title.getCurrentTitle()
	if (title.namespace == 2) and not title.text:match("/") then
		return tostring(root) -- regular user page
	elseif title.namespace == 14 then
		return tostring(root) -- category
	elseif title.isTalkPage then
		return tostring(root) -- talk page
	end

	local legible = true
	local contrast = require('Module:Color contrast')._ratio

	local function has_text(wikitext)
		local function get_alt(text)
			return text:match("|alt=([^|]*)") or ""
		end
	
		wikitext = wikitext:gsub("]]", "|]]")
		wikitext = wikitext:gsub("%[%[%s*[Mm][Ee][Dd][Ii][Aa]%s*:[^|]-(|.-)]]", get_alt)
		wikitext = wikitext:gsub("%[%[%s*[Ii][Mm][Aa][Gg][Ee]%s*:[^|]-(|.-)]]", get_alt)
		wikitext = wikitext:gsub("%[%[%s*[Ff][Ii][Ll][Ee]%s*:[^|]-(|.-)]]", get_alt)
		return mw.text.trim(wikitext) ~= ""
	end

	if contrast { data.infoColor, data.backgroundColor, error = 0 } < 4.5 then
		legible = false
	end

	if data.showId and contrast { data.idColor, data.idBackgroundColor, error = 0 } < 4.5 then
		if has_text(data.id or "") then
			legible = false
		end
	end

	if data.showId2 and contrast { data.id2Color, data.id2BackgroundColor, error = 0 } < 4.5 then
		if has_text(data.id2 or "") then
			legible = false
		end
	end

	if not legible then
		root:wikitext('[[Category:Userboxes with insufficient color contrast]]')
	end

	return tostring(root)
end

function p.categories(args, page)
	-- Gets categories from [[Module:Category handler]].
	-- The page parameter makes the function act as though the module was being called from that page.
	-- It is included for testing purposes.
	local cats = {}
	cats[#cats + 1] = args.usercategory
	cats[#cats + 1] = args.usercategory2
	cats[#cats + 1] = args.usercategory3
	cats[#cats + 1] = args.usercategory4
	cats[#cats + 1] = args.usercategory5
	-- Get the title object
	local title
	if page then
		title = mw.title.new(page)
	else
		title = mw.title.getCurrentTitle()
	end
	-- Build category handler arguments.
	local chargs = {}
	chargs.page = page
	chargs.nocat = args.nocat
	chargs.main = '[[Category:Pages with misplaced templates]]'
	if args.notcatsubpages then
		chargs.subpage = 'no'
	end
	-- User namespace.
	local user = ''
	for i, cat in ipairs(cats) do
		user = user .. makeCat(cat)
	end
	chargs.user = user
	-- Template namespace.
	local basepage = title.baseText
	local template = ''
	for i, cat in ipairs(cats) do
		template = template .. makeCat(cat, ' ' .. basepage)
	end
	chargs.template = template
	return categoryHandler(chargs)
end

return p