# Module:Userbox

-- This module implements .  

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('', cat, sort)  
else  
return mw.ustring.format('', cat)  
end  
end  

---
-- Argument processing  
---

local function makeInvokeFunc(funcName)  
return function  
local origArgs = require('Module:Arguments').getArgs  
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 .  
local data = {}  

-- Get div tag values.  
data.float = args.float or ''  
local borderWidthNum = checkNum(args['-width'] or args['-s'], 1) -- Used to calculate width.  
data.borderWidth = addSuffix(borderWidthNum, 'px')  
data.borderColor = args['-color'] or args['-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>}</code>"  
data.infoTextAlign = args['info-a'] or ''  
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 0 '  
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 ''  
data.idFontSize = checkNum(args['logo-size'] or args[5] or args['id-s'], 14)  
data.idColor = args['logo-color'] or args['id-fc'] or data.infoColor  
data.idPadding = args['logo-padding'] or args['id-p'] or '0 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 .  
local data = {}  

-- Get div tag values.  
data.float = args.float or ''  
local borderWidthNum = checkNum(args['-s'] or args[9], 1) -- Used to calculate width.  
data.borderWidth = addSuffix(borderWidthNum, 'px')  
data.borderColor = args['-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>}</code>"  
data.infoTextAlign = args['info-a'] or ''  
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 0 '  
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.idText  
data.idFontSize = checkNum(args['id1-s'], 14)  
data.idLineHeight = args['id1-lh'] or '1.25em'  
data.idColor = args['id1-fc'] or data.infoColor  
data.idPadding = args['id1-p'] or '0 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.id2Text  
data.id2FontSize = checkNum(args['id2-s'], 14)  
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 '  
data.id2OtherParams = args['id2-op']  

return data  
end  

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

-- Get div tag values.  
data.float = args.float or ''  
local borderWidthNum = checkNum(args['-width'] or args['-s'], 1) -- Used to calculate width.  
data.borderWidth = addSuffix(borderWidthNum, 'px')  
data.borderColor = args['-color'] or args['-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>}</code>"  
data.infoTextAlign = args['info-align'] or args['info-a'] or ''  
data.infoFontSize = checkNumAndAddSuffix(args['info-size'] or args['info-s'], 8, 'pt')  
data.infoPadding = args['info-padding'] or args['info-p'] or '0 0 '  
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 ''  
data.id2FontSize = checkNum(args['logo-size'] or args[5] or args['id-s'], 14)  
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 '  
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(*, (data.borderWidth or *) .. ' solid ' .. (data.borderColor or ''))  
:css('margin', '')  
:css('width', data.width)  
:addClass('wikipediauserbox')  
:addClass(data.bodyClass)  

-- Render the table tag html.  
local tableroot = root:tag('table')  
tableroot  
:attr('role', 'presentation')  
:css('-collapse', 'collapse')  
:css('width', data.width)  
:css('margin-bottom', '0')  
:css('margin-top', '0')  
:css('background', data.backgroundColor)  
:css('color', 'inherit')  

-- Render the id html.  
local tablerow = tableroot:tag('tr')  
if data.showId then  
tablerow:tag('td')  
:css('', '0')  
:css('width', data.idWidth)  
:css('height', data.idHeight)  
:css('background', data.idBackgroundColor)  
:css('text-align', data.idTextAlign)  
:css('font-size', data.idFontSize .. 'pt')  
: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('', '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('', '0')  
:css('width', data.id2Width)  
:css('height', data.id2Height)  
:css('background', data.id2BackgroundColor)  
:css('text-align', data.id2TextAlign)  
:css('font-size', data.id2FontSize .. 'pt')  
: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)  
wikitext = wikitext:gsub("]]", "|]]")  
wikitext = wikitext:gsub("%[%[%s*[Mm][Ee][Dd][Ii][Aa]%s*:[^|]-(|.-)]]", "")  
wikitext = wikitext:gsub("%[%[%s*[Ii][Mm][Aa][Gg][Ee]%s*:[^|]-(|.-)]]", "")  
wikitext = wikitext:gsub("%[%[%s*[Ff][Ii][Ll][Ee]%s*:[^|]-(|.-)]]", "")  
return mw.text.trim(wikitext) ~= ""  
end  

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

-- For bold text >= 14pt, requirement is only 3.  
local idContrastThreshold = 4.5  
local id2ContrastThreshold = 4.5  
if (data.idFontSize or 0) >= 14 then  
idContrastThreshold = 3  
end  
if (data.id2FontSize or 0) >= 14 then  
id2ContrastThreshold = 3  
end  

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

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

if not legible then  
root:wikitext('')  
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 = ''  
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