Module:Userbox/sandbox
< Module:Userbox
Jump to navigation
Jump to search
Revision as of 06:22, 8 September 2015 by blackwiki>Mr. Stradivarius (various bits of refactoring)
Documentation for this module may be created at Module:Userbox/sandbox/doc
-- This module implements {{userbox}}.
local calculateContrastRatio = require('Module:Color contrast')._ratio
local yesno = require('Module:Yesno')
--------------------------------------------------------------------------------
-- 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
local function hasLegibleContrast(data)
local function trim(s)
return s:match('^%s*(.-)%s*$')
end
local function hasText(wikitext)
if not wikitext then
return false
end
local function getAlt(text)
return text:match('|alt=([^|]*)') or ''
end
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
local function isLegible(color1, color2)
return calculateContrastRatio{color1, color2, error = 0} >= 4.5
end
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
--------------------------------------------------------------------------------
local dataFuncs = {}
function dataFuncs.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[1] or args['border-c'] 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
dataFuncs['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[9] or args['border-s'], 1) -- Used to calculate width.
data.borderWidth = addSuffix(borderWidthNum, 'px')
data.borderColor = args[1] or args['border-c'] or args['id1-c'] 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[2] or args['info-c'] or '#eeeeee'
-- Get info values.
data.info = args[4] or args.info or "<code>{{{info}}}</code>"
data.infoTextAlign = args['info-a'] or 'left'
data.infoFontSize = checkNumAndAddSuffix(args['info-s'], 8, 'pt')
data.infoColor = args[8] or args['info-fc'] 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[1] or args['id1-c'] 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[7] or args['id2-c'] 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
dataFuncs['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[1] or args['border-c'] 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
--------------------------------------------------------------------------------
-- Rendering functions
--------------------------------------------------------------------------------
local function renderUserbox(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
:css('border-collapse', 'collapse')
:css('width', data.width)
:css('margin-bottom', '0')
:css('background', data.backgroundColor)
-- Render the id html.
local tablerow = tableroot:tag('tr')
if data.showId then
tablerow:tag('th')
: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('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('th')
: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('color', data.id2Color)
:css('padding', data.id2Padding)
:css('line-height', data.id2LineHeight)
:css('vertical-align', 'middle')
:cssText(data.id2OtherParams)
:wikitext(data.id2)
end
return tostring(root)
end
local function renderCategories(args, data, page)
-- The page parameter makes the function act as though the module was being
-- called from that page. It is included for testing purposes.
local title
if page then
title = mw.title.new(page)
else
title = mw.title.getCurrentTitle()
end
-- Exit if we are on a blacklisted page or categorization is disabled.
if title.prefixedText:find('/[aA]rchive') or yesno(args.nocat) then
return ''
end
local ret = {}
-- User-specified categories
if not args.notcatsubpages or not title.isSubpage then
local userCats = {}
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
-- Tracking categories
local function maybeUserboxTemplate(title)
-- 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
return table.concat(ret)
end
--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------
local p = {}
function p._testFuncs()
local ret = {}
for name, func in pairs(dataFuncs) do
ret[name] = func
end
ret.renderUserbox = renderUserbox
ret.renderCategories = renderCategories
return ret
end
function p.main(funcName, args)
local data = dataFuncs[funcName](args)
local userbox = renderUserbox(data)
local cats = p.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