Module:Userbox

-- 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('%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 "{{{info}}}"

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 = 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 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 "{{{info}}}"

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 = 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 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 = 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 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 "{{{info}}}"

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 = 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 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)

:css('color', 'inherit')

-- 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 .. '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('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 .. '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('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 templates in the wrong namespace'

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