Difference between revisions of "Module:Userbox/sandbox"

From blackwiki
Jump to navigation Jump to search
blackwiki>Mr. Stradivarius
(try to make the contrast code easier to read)
blackwiki>Mr. Stradivarius
(remove the dependency on Module:Category handler and only add only add tracking categories to the appropriate pages)
Line 1: Line 1:
 
-- This module implements {{userbox}}.
 
-- This module implements {{userbox}}.
  
local categoryHandler = require('Module:Category handler').main
 
 
local calculateContrastRatio = require('Module:Color contrast')._ratio
 
local calculateContrastRatio = require('Module:Color contrast')._ratio
 +
local yesno = require('Module:Yesno')
  
 
local p = {}
 
local p = {}
Line 105: Line 105:
 
local userboxData = p[funcName](args)
 
local userboxData = p[funcName](args)
 
local userbox = p.render(userboxData)
 
local userbox = p.render(userboxData)
local cats = p.categories(args)
+
local cats = p.categories(args, userboxData)
return userbox .. (cats or '')
+
return (userbox or '') .. (cats or '')
 
end
 
end
  
Line 317: Line 317:
 
:cssText(data.id2OtherParams)
 
:cssText(data.id2OtherParams)
 
:wikitext(data.id2)
 
:wikitext(data.id2)
end
 
 
if not hasLegibleContrast(data) then
 
root:wikitext('[[Category:Potentially illegible userboxes]]')
 
 
end
 
end
  
Line 326: Line 322:
 
end
 
end
  
function p.categories(args, page)
+
function p.categories(args, data, page)
-- Gets categories from [[Module:Category handler]].
+
-- Render tracking categories.
-- The page parameter makes the function act as though the module was being called from that page.
+
-- The page parameter makes the function act as though the module was being
-- It is included for testing purposes.
+
-- called from that page. It is included for testing purposes.
local cats = {}
+
local title
cats[#cats + 1] = args.usercategory
+
if page then
cats[#cats + 1] = args.usercategory2
+
title = mw.title.new(page)
cats[#cats + 1] = args.usercategory3
 
if #cats > 0 then
 
-- 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)
 
 
else
 
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 nil
 
return nil
 
end
 
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 then -- User namespace
 +
for i, cat in ipairs(chcats) do
 +
ret[#ret + 1] = makeCat(cat)
 +
end
 +
elseif title.namespace == 10 then -- Template namespace
 +
local basepage = title.baseText
 +
for i, cat in ipairs(chcats) 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
 
end
  
 
return p
 
return p

Revision as of 05:26, 7 September 2015

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

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

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

--------------------------------------------------------------------------------
-- 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, userboxData)
	return (userbox or '') .. (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[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

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[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

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[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

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
		: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

function p.categories(args, data, page)
	-- Render tracking categories.
	-- 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 nil
	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 then -- User namespace
				for i, cat in ipairs(chcats) do
					ret[#ret + 1] = makeCat(cat)
				end
			elseif title.namespace == 10 then -- Template namespace
				local basepage = title.baseText
				for i, cat in ipairs(chcats) 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

return p