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