Module:Color contrast: Difference between revisions

From Saintapedia
Jump to navigation Jump to search
>Tomoneill
(copy from https://en.wikipedia.org/w/index.php?title=Module:Color_contrast&oldid=877152614)
(Created page with "-- -- This module implements -- {{Color contrast ratio}} -- {{Greater color contrast ratio}} -- {{ColorToLum}} -- {{RGBColorToLum}} -- local p = {} local HTMLcolor = mw.loadData( 'Module:Color contrast/colors' ) local function sRGB (v) if (v <= 0.03928) then v = v / 12.92 else v = math.pow((v+0.055)/1.055, 2.4) end return v end local function rgbdec2lum(R, G, B) if ( 0 <= R and R < 256 and 0 <= G and G < 256 and 0 <= B and B < 256 ) then...")
Line 1: Line 1:
--
--
-- This module implements
-- This module implements
--  {{Color contrast ratio}}
--  {{Color contrast ratio}}
--  {{Greater color contrast ratio}}
--  {{Greater color contrast ratio}}
--  {{ColorToLum}}
--  {{ColorToLum}}
--  {{RGBColorToLum}}
--  {{RGBColorToLum}}
--
--
local p = {}
local p = {}
local HTMLcolor = mw.loadData( 'Module:Color contrast/colors' )
local HTMLcolor = mw.loadData( 'Module:Color contrast/colors' )


local function sRGB (v)
local function sRGB (v)
if (v <= 0.03928) then
if (v <= 0.03928) then
v = v / 12.92
v = v / 12.92
else
else
v = math.pow((v+0.055)/1.055, 2.4)
v = math.pow((v+0.055)/1.055, 2.4)
end
end
return v
return v
end
end


local function rgbdec2lum(R, G, B)
local function rgbdec2lum(R, G, B)
if ( 0 <= R and R < 256 and 0 <= G and G < 256 and 0 <= B and B < 256 ) then
if ( 0 <= R and R < 256 and 0 <= G and G < 256 and 0 <= B and B < 256 ) then
return 0.2126 * sRGB(R/255) + 0.7152 * sRGB(G/255) + 0.0722 * sRGB(B/255)
return 0.2126 * sRGB(R/255) + 0.7152 * sRGB(G/255) + 0.0722 * sRGB(B/255)
else
else
return ''
return ''
end
end
end
end


local function hsl2lum(h, s, l)
local function hsl2lum(h, s, l)
if ( 0 <= h and h < 360 and 0 <= s and s <= 1 and 0 <= l and l <= 1 ) then
if ( 0 <= h and h < 360 and 0 <= s and s <= 1 and 0 <= l and l <= 1 ) then
local c = (1 - math.abs(2*l - 1))*s
local c = (1 - math.abs(2*l - 1))*s
local x = c*(1 - math.abs( math.fmod(h/60, 2) - 1) )
local x = c*(1 - math.abs( math.fmod(h/60, 2) - 1) )
local m = l - c/2
local m = l - c/2


local r, g, b = m, m, m
local r, g, b = m, m, m
if( 0 <= h and h < 60 ) then
if( 0 <= h and h < 60 ) then
r = r + c
r = r + c
g = g + x
g = g + x
elseif( 60 <= h and h < 120 ) then
elseif( 60 <= h and h < 120 ) then
r = r + x
r = r + x
g = g + c
g = g + c
elseif( 120 <= h and h < 180 ) then
elseif( 120 <= h and h < 180 ) then
g = g + c
g = g + c
b = b + x
b = b + x
elseif( 180 <= h and h < 240 ) then
elseif( 180 <= h and h < 240 ) then
g = g + x
g = g + x
b = b + c
b = b + c
elseif( 240 <= h and h < 300 ) then
elseif( 240 <= h and h < 300 ) then
r = r + x
r = r + x
b = b + c
b = b + c
elseif( 300 <= h and h < 360 ) then
elseif( 300 <= h and h < 360 ) then
r = r + c
r = r + c
b = b + x
b = b + x
end
end
return rgbdec2lum(255*r, 255*g, 255*b)
return rgbdec2lum(255*r, 255*g, 255*b)
else
else
return ''
return ''
end
end
end
end


Line 61: Line 110:


if (c == nil) then
if (c == nil) then
return ''
return ''
end
end


-- html '#' entity
-- html '#' entity
c = c:gsub("&#35;", "#")
c = c:gsub("&#35;", "#")


-- whitespace
-- whitespace
c = c:match( '^%s*(.-)[%s;]*$' )
c = c:match( '^%s*(.-)[%s;]*$' )


-- unstrip nowiki strip markers
-- unstrip nowiki strip markers
c = mw.text.unstripNoWiki(c)
c = mw.text.unstripNoWiki(c)


-- lowercase
-- lowercase
c = c:lower()
c = c:lower()


-- first try to look it up
-- first try to look it up
local L = HTMLcolor[c]
local L = HTMLcolor[c]
if (L ~= nil) then
if (L ~= nil) then
return L
return L
end
end


-- convert from hsl
-- convert from hsl
if mw.ustring.match(c,'^hsl%([%s]*[0-9][0-9%.]*[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*%)$') then
if mw.ustring.match(c,'^hsl%([%s]*[0-9][0-9%.]*[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*%)$') then
local h, s, l = mw.ustring.match(c,'^hsl%([%s]*([0-9][0-9%.]*)[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*%)$')
local h, s, l = mw.ustring.match(c,'^hsl%([%s]*([0-9][0-9%.]*)[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*%)$')
return hsl2lum(tonumber(h), tonumber(s)/100, tonumber(l)/100)
return hsl2lum(tonumber(h), tonumber(s)/100, tonumber(l)/100)
end
end


-- convert from rgb
-- convert from rgb
if mw.ustring.match(c,'^rgb%([%s]*[0-9][0-9]*[%s]*,[%s]*[0-9][0-9]*[%s]*,[%s]*[0-9][0-9]*[%s]*%)$') then
if mw.ustring.match(c,'^rgb%([%s]*[0-9][0-9]*[%s]*,[%s]*[0-9][0-9]*[%s]*,[%s]*[0-9][0-9]*[%s]*%)$') then
local R, G, B = mw.ustring.match(c,'^rgb%([%s]*([0-9][0-9]*)[%s]*,[%s]*([0-9][0-9]*)[%s]*,[%s]*([0-9][0-9]*)[%s]*%)$')
local R, G, B = mw.ustring.match(c,'^rgb%([%s]*([0-9][0-9]*)[%s]*,[%s]*([0-9][0-9]*)[%s]*,[%s]*([0-9][0-9]*)[%s]*%)$')
return rgbdec2lum(tonumber(R), tonumber(G), tonumber(B))
return rgbdec2lum(tonumber(R), tonumber(G), tonumber(B))
end
end


-- convert from rgb percent
-- convert from rgb percent
if mw.ustring.match(c,'^rgb%([%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*%)$') then
if mw.ustring.match(c,'^rgb%([%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*%)$') then
local R, G, B = mw.ustring.match(c,'^rgb%([%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*%)$')
local R, G, B = mw.ustring.match(c,'^rgb%([%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*%)$')
return rgbdec2lum(255*tonumber(R)/100, 255*tonumber(G)/100, 255*tonumber(B)/100)
return rgbdec2lum(255*tonumber(R)/100, 255*tonumber(G)/100, 255*tonumber(B)/100)
end
end


-- remove leading # (if there is one) and whitespace
-- remove leading # (if there is one) and whitespace
c = mw.ustring.match(c, '^[%s#]*([a-f0-9]*)[%s]*$')
c = mw.ustring.match(c, '^[%s#]*([a-f0-9]*)[%s]*$')


-- split into rgb
-- split into rgb
local cs = mw.text.split(c or '', '')
local cs = mw.text.split(c or '', '')
if( #cs == 6 ) then
if( #cs == 6 ) then
local R = 16*tonumber('0x' .. cs[1]) + tonumber('0x' .. cs[2])
local R = 16*tonumber('0x' .. cs[1]) + tonumber('0x' .. cs[2])
local G = 16*tonumber('0x' .. cs[3]) + tonumber('0x' .. cs[4])
local G = 16*tonumber('0x' .. cs[3]) + tonumber('0x' .. cs[4])
local B = 16*tonumber('0x' .. cs[5]) + tonumber('0x' .. cs[6])
local B = 16*tonumber('0x' .. cs[5]) + tonumber('0x' .. cs[6])


return rgbdec2lum(R, G, B)
return rgbdec2lum(R, G, B)
elseif ( #cs == 3 ) then
elseif ( #cs == 3 ) then
local R = 16*tonumber('0x' .. cs[1]) + tonumber('0x' .. cs[1])
local R = 16*tonumber('0x' .. cs[1]) + tonumber('0x' .. cs[1])
local G = 16*tonumber('0x' .. cs[2]) + tonumber('0x' .. cs[2])
local G = 16*tonumber('0x' .. cs[2]) + tonumber('0x' .. cs[2])
local B = 16*tonumber('0x' .. cs[3]) + tonumber('0x' .. cs[3])
local B = 16*tonumber('0x' .. cs[3]) + tonumber('0x' .. cs[3])


return rgbdec2lum(R, G, B)
return rgbdec2lum(R, G, B)
end
end


-- failure, return blank
-- failure, return blank
return ''
return ''
end
end


-- This exports the function for use in other modules.
-- This exports the function for use in other modules.
-- The colour is passed as a string.
-- The colour is passed as a string.
function p._lum(color)
function p._lum(color)
return color2lum(color)
return color2lum(color)
end
end


function p._greatercontrast(args)
function p._greatercontrast(args)
local bias = tonumber(args['bias'] or '0') or 0
local bias = tonumber(args['bias'] or '0') or 0
local css = (args['css'] and args['css'] ~= '') and true or false
local css = (args['css'] and args['css'] ~= '') and true or false
local v1 = color2lum(args[1] or '')
local v1 = color2lum(args[1] or '')
local c2 = args[2] or '#FFFFFF'
local c2 = args[2] or '#FFFFFF'
local v2 = color2lum(c2)
local v2 = color2lum(c2)
local c3 = args[3] or '#000000'
local c3 = args[3] or '#000000'
local v3 = color2lum(c3)
local v3 = color2lum(c3)
local ratio1 = -1;
local ratio1 = -1;
local ratio2 = -1;
local ratio2 = -1;
if (type(v1) == 'number' and type(v2) == 'number') then
if (type(v1) == 'number' and type(v2) == 'number') then
ratio1 = (v2 + 0.05)/(v1 + 0.05)
ratio1 = (v2 + 0.05)/(v1 + 0.05)
ratio1 = (ratio1 < 1) and 1/ratio1 or ratio1
ratio1 = (ratio1 < 1) and 1/ratio1 or ratio1
end
end
if (type(v1) == 'number' and type(v3) == 'number') then
if (type(v1) == 'number' and type(v3) == 'number') then
ratio2 = (v3 + 0.05)/(v1 + 0.05)
ratio2 = (v3 + 0.05)/(v1 + 0.05)
ratio2 = (ratio2 < 1) and 1/ratio2 or ratio2
ratio2 = (ratio2 < 1) and 1/ratio2 or ratio2
end
end


if css then
if css then
local c1 = args[1] or ''
local c1 = args[1] or ''
if mw.ustring.match(c1, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or
if mw.ustring.match(c1, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or
mw.ustring.match(c1, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then
mw.ustring.match(c1, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then
c1 = '#' .. c1
c1 = '#' .. c1
end
end
if mw.ustring.match(c2, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or
if mw.ustring.match(c2, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or
mw.ustring.match(c2, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then
mw.ustring.match(c2, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then
c2 = '#' .. c2
c2 = '#' .. c2
end
end
if mw.ustring.match(v3, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or
if mw.ustring.match(v3, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or
mw.ustring.match(v3, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then
mw.ustring.match(v3, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then
c3 = '#' .. c3
c3 = '#' .. c3
end
end
return 'background-color:' .. c1 .. '; color:' .. ((ratio1 > 0) and (ratio2 > 0) and ((ratio1 + bias > ratio2) and c2 or c3) or '') .. ';'
return 'background-color:' .. c1 .. '; color:' .. ((ratio1 > 0) and (ratio2 > 0) and ((ratio1 + bias > ratio2) and c2 or c3) or '') .. ';'
end
end


return (ratio1 > 0) and (ratio2 > 0) and ((ratio1 + bias > ratio2) and c2 or c3) or ''
return (ratio1 > 0) and (ratio2 > 0) and ((ratio1 + bias > ratio2) and c2 or c3) or ''
end
end


function p._ratio(args)
function p._ratio(args)
local v1 = color2lum(args[1])
local v1 = color2lum(args[1])
local v2 = color2lum(args[2])
local v2 = color2lum(args[2])
if (type(v1) == 'number' and type(v2) == 'number') then
if (type(v1) == 'number' and type(v2) == 'number') then
-- v1 should be the brighter of the two.
-- v1 should be the brighter of the two.
if v2 > v1 then
if v2 > v1 then
v1, v2 = v2, v1
v1, v2 = v2, v1
end
end
return (v1 + 0.05)/(v2 + 0.05)
return (v1 + 0.05)/(v2 + 0.05)
else
else
return args['error'] or '?'
return args['error'] or '?'
end
end
end
end


function p._styleratio(args)
function p._styleratio(args)
local style = (args[1] or ''):lower()
local style = (args[1] or ''):lower()
local bg, fg = 'white', 'black'
local bg, fg = 'white', 'black'
local lum_bg, lum_fg = 1, 0
local lum_bg, lum_fg = 1, 0


if args[2] then
if args[2] then
local lum = color2lum(args[2])
local lum = color2lum(args[2])
if lum ~= '' then bg, lum_bg = args[2], lum end
if lum ~= '' then bg, lum_bg = args[2], lum end
end
end
if args[3] then
if args[3] then
local lum = color2lum(args[3])
local lum = color2lum(args[3])
if lum ~= '' then fg, lum_fg = args[3], lum end
if lum ~= '' then fg, lum_fg = args[3], lum end
end
end


local slist = mw.text.split(mw.ustring.gsub(mw.ustring.gsub(style or '', '&#[Xx]23;', '#'), '&#35;', '#'), ';')
local slist = mw.text.split(mw.ustring.gsub(mw.ustring.gsub(style or '', '&#[Xx]23;', '#'), '&#35;', '#'), ';')
for k = 1,#slist do
for k = 1,#slist do
local s = slist[k]
local s = slist[k]
local k,v = s:match( '^[%s]*([^:]-):([^:]-)[%s;]*$' )
local k,v = s:match( '^[%s]*([^:]-):([^:]-)[%s;]*$' )
k = k or ''
k = k or ''
v = v or ''
v = v or ''
if (k:match('^[%s]*(background)[%s]*$') or k:match('^[%s]*(background%-color)[%s]*$')) then
if (k:match('^[%s]*(background)[%s]*$') or k:match('^[%s]*(background%-color)[%s]*$')) then
local lum = color2lum(v)
local lum = color2lum(v)
if( lum ~= '' ) then bg, lum_bg = v, lum end
if( lum ~= '' ) then bg, lum_bg = v, lum end
elseif (k:match('^[%s]*(color)[%s]*$')) then
elseif (k:match('^[%s]*(color)[%s]*$')) then
local lum = color2lum(v)
local lum = color2lum(v)
if( lum ~= '' ) then bg, lum_fg = v, lum end
if( lum ~= '' ) then bg, lum_fg = v, lum end
end
end
end
end
if lum_bg > lum_fg then
if lum_bg > lum_fg then
return (lum_bg + 0.05)/(lum_fg + 0.05)
return (lum_bg + 0.05)/(lum_fg + 0.05)
else
else
return (lum_fg + 0.05)/(lum_bg + 0.05)
return (lum_fg + 0.05)/(lum_bg + 0.05)
end
end
end
end


--[[
--[[
Use {{#invoke:Color contrast|somecolor}} directly or
Use {{#invoke:Color contrast|somecolor}} directly or
{{#invoke:Color contrast}} from a wrapper template.
{{#invoke:Color contrast}} from a wrapper template.


Parameters:
Parameters:
-- |1= — required; A color to check.
-- |1= — required; A color to check.
--]]
--]]
function p.lum(frame)
function p.lum(frame)
local color = frame.args[1] or frame:getParent().args[1]
local color = frame.args[1] or frame:getParent().args[1]
return p._lum(color)
return p._lum(color)
end
end


function p.ratio(frame)
function p.ratio(frame)
local args = frame.args[1] and frame.args or frame:getParent().args
local args = frame.args[1] and frame.args or frame:getParent().args
return p._ratio(args)
return p._ratio(args)
end
end


function p.styleratio(frame)
function p.styleratio(frame)
local args = frame.args[1] and frame.args or frame:getParent().args
local args = frame.args[1] and frame.args or frame:getParent().args
return p._styleratio(args)
return p._styleratio(args)
end
end


function p.greatercontrast(frame)
function p.greatercontrast(frame)
local args = frame.args[1] and frame.args or frame:getParent().args
local args = frame.args[1] and frame.args or frame:getParent().args
return p._greatercontrast(args)
return p._greatercontrast(args)
end
end


return p
return p

Revision as of 14:33, 5 September 2024

This module is used primarily by

{{Color contrast ratio}}
{{ColorToLum}} / {{RGBColorToLum}}
{{Color contrast conformance}}
{{Ensure AAA contrast ratio}}
{{Ensure AA contrast ratio}}
{{Greater color contrast ratio}}

It is also used for tracking within

Module:Navbox
Module:Userbox
Module:Episode list

and for documentation in

Module:College color

Usage

To use this module, you may use one of the above listed templates or invoke the module directly

To compute relative luminescence

{{ColorToLum|color}} or {{#invoke:Color contrast|lum|color}}

To compute a contrast ratio between two colors

{{Color contrast ratio|color1|color2|error=?}} or {{#invoke:Color contrast|ratio|color1|color2|error=?}}

To determine which of two colors (color2a and color2b) has the greater contrast ratio with a particular color (color1)

{{Greater color contrast ratio|color1|color2a|color2b}} or {{#invoke:Color contrast|greatercontrast|color1|color2a|color2b}}

To compute the contrast ratio between the background and text colors specified in a css style string

{{#invoke:Color contrast|styleratio|css style statement string|default background color|default text color}}



--

-- This module implements

--  {{Color contrast ratio}}

--  {{Greater color contrast ratio}}

--  {{ColorToLum}}

--  {{RGBColorToLum}}

--

local p = {}

local HTMLcolor = mw.loadData( 'Module:Color contrast/colors' )

local function sRGB (v)

	if (v <= 0.03928) then

		v = v / 12.92

	else

		v = math.pow((v+0.055)/1.055, 2.4)

	end

	return v

end

local function rgbdec2lum(R, G, B)

	if ( 0 <= R and R < 256 and 0 <= G and G < 256 and 0 <= B and B < 256 ) then

		return 0.2126 * sRGB(R/255) + 0.7152 * sRGB(G/255) + 0.0722 * sRGB(B/255)

	else

		return ''

	end

end

local function hsl2lum(h, s, l)

	if ( 0 <= h and h < 360 and 0 <= s and s <= 1 and 0 <= l and l <= 1 ) then

		local c = (1 - math.abs(2*l - 1))*s

		local x = c*(1 - math.abs( math.fmod(h/60, 2) - 1) )

		local m = l - c/2

		local r, g, b = m, m, m

		if( 0 <= h and h < 60 ) then

			r = r + c

			g = g + x

		elseif( 60 <= h and h < 120 ) then

			r = r + x

			g = g + c

		elseif( 120 <= h and h < 180 ) then

			g = g + c

			b = b + x

		elseif( 180 <= h and h < 240 ) then

			g = g + x

			b = b + c

		elseif( 240 <= h and h < 300 ) then

			r = r + x

			b = b + c

		elseif( 300 <= h and h < 360 ) then

			r = r + c

			b = b + x

		end

		return rgbdec2lum(255*r, 255*g, 255*b)

	else

		return ''

	end

end

local function color2lum(c)

	if (c == nil) then

		return ''

	end

	-- html '#' entity

	c = c:gsub("&#35;", "#")

	-- whitespace

	c = c:match( '^%s*(.-)[%s;]*$' )

	-- unstrip nowiki strip markers

	c = mw.text.unstripNoWiki(c)

	-- lowercase

	c = c:lower()

	-- first try to look it up

	local L = HTMLcolor[c]

	if (L ~= nil) then

		return L

	end

	-- convert from hsl

	if mw.ustring.match(c,'^hsl%([%s]*[0-9][0-9%.]*[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*%)$') then

		local h, s, l = mw.ustring.match(c,'^hsl%([%s]*([0-9][0-9%.]*)[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*%)$')

		return hsl2lum(tonumber(h), tonumber(s)/100, tonumber(l)/100)

	end

	-- convert from rgb

	if mw.ustring.match(c,'^rgb%([%s]*[0-9][0-9]*[%s]*,[%s]*[0-9][0-9]*[%s]*,[%s]*[0-9][0-9]*[%s]*%)$') then

		local R, G, B = mw.ustring.match(c,'^rgb%([%s]*([0-9][0-9]*)[%s]*,[%s]*([0-9][0-9]*)[%s]*,[%s]*([0-9][0-9]*)[%s]*%)$')

		return rgbdec2lum(tonumber(R), tonumber(G), tonumber(B))

	end

	-- convert from rgb percent

	if mw.ustring.match(c,'^rgb%([%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*,[%s]*[0-9][0-9%.]*%%[%s]*%)$') then

		local R, G, B = mw.ustring.match(c,'^rgb%([%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*,[%s]*([0-9][0-9%.]*)%%[%s]*%)$')

		return rgbdec2lum(255*tonumber(R)/100, 255*tonumber(G)/100, 255*tonumber(B)/100)

	end

	-- remove leading # (if there is one) and whitespace

	c = mw.ustring.match(c, '^[%s#]*([a-f0-9]*)[%s]*$')

	-- split into rgb

	local cs = mw.text.split(c or '', '')

	if( #cs == 6 ) then

		local R = 16*tonumber('0x' .. cs[1]) + tonumber('0x' .. cs[2])

		local G = 16*tonumber('0x' .. cs[3]) + tonumber('0x' .. cs[4])

		local B = 16*tonumber('0x' .. cs[5]) + tonumber('0x' .. cs[6])

		return rgbdec2lum(R, G, B)

	elseif ( #cs == 3 ) then

		local R = 16*tonumber('0x' .. cs[1]) + tonumber('0x' .. cs[1])

		local G = 16*tonumber('0x' .. cs[2]) + tonumber('0x' .. cs[2])

		local B = 16*tonumber('0x' .. cs[3]) + tonumber('0x' .. cs[3])

		return rgbdec2lum(R, G, B)

	end

	-- failure, return blank

	return ''

end

-- This exports the function for use in other modules.

-- The colour is passed as a string.

function p._lum(color)

	return color2lum(color)

end

function p._greatercontrast(args)

	local bias = tonumber(args['bias'] or '0') or 0

	local css = (args['css'] and args['css'] ~= '') and true or false

	local v1 = color2lum(args[1] or '')

	local c2 = args[2] or '#FFFFFF'

	local v2 = color2lum(c2)

	local c3 = args[3] or '#000000'

	local v3 = color2lum(c3)

	local ratio1 = -1;

	local ratio2 = -1;

	if (type(v1) == 'number' and type(v2) == 'number') then

		ratio1 = (v2 + 0.05)/(v1 + 0.05)

		ratio1 = (ratio1 < 1) and 1/ratio1 or ratio1

	end

	if (type(v1) == 'number' and type(v3) == 'number') then

		ratio2 = (v3 + 0.05)/(v1 + 0.05)

		ratio2 = (ratio2 < 1) and 1/ratio2 or ratio2

	end

	if css then

		local c1 = args[1] or ''

		if mw.ustring.match(c1, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or

			mw.ustring.match(c1, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then

				c1 = '#' .. c1

		end

		if mw.ustring.match(c2, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or

			mw.ustring.match(c2, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then

				c2 = '#' .. c2

		end

		if mw.ustring.match(v3, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') or

			mw.ustring.match(v3, '^[A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9][A-Fa-f0-9]$') then

				c3 = '#' .. c3

		end

		return 'background-color:' .. c1 .. '; color:' .. ((ratio1 > 0) and (ratio2 > 0) and ((ratio1 + bias > ratio2) and c2 or c3) or '') .. ';'

	end

	return (ratio1 > 0) and (ratio2 > 0) and ((ratio1 + bias > ratio2) and c2 or c3) or ''

end

function p._ratio(args)

	local v1 = color2lum(args[1])

	local v2 = color2lum(args[2])

	if (type(v1) == 'number' and type(v2) == 'number') then

		-- v1 should be the brighter of the two.

		if v2 > v1 then

			v1, v2 = v2, v1

		end

		return (v1 + 0.05)/(v2 + 0.05)

	else

		return args['error'] or '?'

	end

end

function p._styleratio(args)

	local style = (args[1] or ''):lower()

	local bg, fg = 'white', 'black'

	local lum_bg, lum_fg = 1, 0

	if args[2] then

		local lum = color2lum(args[2])

		if lum ~= '' then bg, lum_bg = args[2], lum end

	end

	if args[3] then

		local lum = color2lum(args[3])

		if lum ~= '' then fg, lum_fg = args[3], lum end

	end

	local slist = mw.text.split(mw.ustring.gsub(mw.ustring.gsub(style or '', '&#[Xx]23;', '#'), '&#35;', '#'), ';')

	for k = 1,#slist do

		local s = slist[k]

		local k,v = s:match( '^[%s]*([^:]-):([^:]-)[%s;]*$' )

		k = k or ''

		v = v or ''

		if (k:match('^[%s]*(background)[%s]*$') or k:match('^[%s]*(background%-color)[%s]*$')) then

			local lum = color2lum(v)

			if( lum ~= '' ) then bg, lum_bg = v, lum end

		elseif (k:match('^[%s]*(color)[%s]*$')) then

			local lum = color2lum(v)

			if( lum ~= '' ) then bg, lum_fg = v, lum end

		end

	end

	if lum_bg > lum_fg then

		return (lum_bg + 0.05)/(lum_fg + 0.05)

	else

		return (lum_fg + 0.05)/(lum_bg + 0.05)

	end

end

--[[

Use {{#invoke:Color contrast|somecolor}} directly or

{{#invoke:Color contrast}} from a wrapper template.

Parameters:

	-- |1=	— required; A color to check.

--]]

function p.lum(frame)

	local color = frame.args[1] or frame:getParent().args[1]

	return p._lum(color)

end

function p.ratio(frame)

	local args = frame.args[1] and frame.args or frame:getParent().args

	return p._ratio(args)

end

function p.styleratio(frame)

	local args = frame.args[1] and frame.args or frame:getParent().args

	return p._styleratio(args)

end

function p.greatercontrast(frame)

	local args = frame.args[1] and frame.args or frame:getParent().args

	return p._greatercontrast(args)

end

return p