--
-- 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
-- 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
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;'
,
'#'
),
'#'
,
'#'
),
';'
)
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
function
p
.
lum
(
frame
)
return
color2lum
(
frame
.
args
[
1
]
or
frame
:
getParent
().
args
[
1
])
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