Doesn't exist
Module:Colors/class/doc
local Color = {}
Color.__index = Color
Color.mods = require('Module:Colors/mods')
Color.mods.Color = Color
-- Utility functions --
function dec2hex(number, len)
local i, chars, result, d = 0, '0123456789ABCDEF', ''
while number > 0 do
i = i + 1
number, d = math.floor(number / 16), (number % 16) + 1
result = string.sub(chars, d, d) .. result
end
return ('0'):rep((len or 1) - result:len()) .. result
end
function round(number, precision)
precision = precision or 0
local m = 10 ^ precision
return math.floor(number * m + 0.5) / m
end
function escape(value)
return value:gsub('([%%%^%$%(%)%.%[%]%*%+%-%?])', '%%%1')
end
function parameters(value, name)
value = value:lower()
if name ~= nil then
name = name:lower()
local pattern = '^%s*' .. escape(name) .. '(%b())%s*$'
local _, _, res = value:find(pattern)
if res == nil then return false end
res = res:sub(2, -2)
return true, unpack(parameters(res))
else
local groups, i, VAL = {}, 0, value
for all, func, values in string.gmatch(value, "(([0-9a-z-]+)(%b()))") do
i = i + 1
local n = '$'.. func .. i
groups[n] = {func, values}
VAL = VAL:gsub(escape(all), n, 1)
end
local res, list = {}, mw.text.split(VAL, '%s*,%s*')
for i,v in ipairs(list) do
v = mw.text.trim(v)
if groups[v] ~= nil then
list[i] = groups[v]
elseif v:find('%$') then
error('Syntax error', 2)
else
list[i] = v
end
end
return list
end
end
Color.parameters = parameters
function val(value)
if value == nil or type(value) == 'table' then return nil end
value = mw.text.trim(tostring(value))
if value == '' then return nil end
return value
end
local messages = mw.loadData('Module:Colors/i18n')
local lang = mw.language.getContentLanguage():getCode()
function Color.msg(code, ...)
local message
if messages[lang] then
message = tostring(messages[lang][code] or messages['en'][code] or '')
else
message = tostring(messages['en'][code] or '')
end
return message:format(...)
end
Color.types = {}
function Color.types.color(value)
value = val(value)
if value == nil then return nil end
local c = Color.new(value)
if c == nil then error('Format not supported: ' .. value, 2) end
return c
end
function Color.types.rgb(value)
value = val(value)
if value == nil then return 0 end
if value:sub(-1,-1) == '%' then
value = tonumber(value:sub(1, -2)) / 100
else
value = tonumber(value) / 255
end
if value == nil then return 0 end
return value
end
function Color.types.number(value, fallback)
if fallback == 'nil' then fallback = nil
elseif fallback then fallback = 1
else fallback = 0 end
value = val(value)
if value == nil then return fallback end
if value:sub(-1,-1) == '%' then
value = tonumber(value:sub(1, -2)) / 100
else
value = tonumber(value)
end
if value == nil then return fallback end
return value
end
function Color.types.string(value)
return val(value)
end
function Color.types.degrees(value)
value = val(value)
if value == nil then return 0 end
value = value:gsub('%s*deg%s*$', '')
if value:sub(-1,-1) == '%' then
value = tonumber(value:sub(1, -2)) * 3.6
else
value = tonumber(value)
end
local sign = 1
if value == nil then return 0 end
if value < 0 then sign = -1 end
return (math.abs(value) % 360) * sign
end
function Color.types.s_rgb(value)
return {Color.types.rgb(value), not not tostring(value):find('^%s*[%-%+]')}
end
function Color.types.s_number(value, fallback)
return {Color.types.number(value, fallback), not not tostring(value):find('^%s*[%-%+]')}
end
function Color.types.s_degrees(value)
return {Color.types.degrees(value), not not tostring(value):find('^%s*[%-%+]')}
end
-- Creators
function Color.new(text)
text = text:gsub('#', '#'):lower()
-- #ffffffff
local F, _, rgb = string.find(text or '', '^%s*#([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])%s*$')
if F then
return Color.newHEX(rgb)
end
-- #ffffff
local F, _, rgb = string.find(text or '', '^%s*#([0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])%s*$')
if F then
return Color.newHEX(rgb)
end
-- #ffff
local F, _, rgb = string.find(text or '', '^%s*#([0-9a-f][0-9a-f][0-9a-f][0-9a-f])%s*$')
if F then
return Color.newHEX(rgb)
end
-- #fff
local F, _, rgb = string.find(text or '', '^%s*#([0-9a-f][0-9a-f][0-9a-f])%s*$')
if F then
return Color.newHEX(rgb)
end
-- rgb(255, 255, 255)
local F, r, g, b = parameters(text, 'rgb')
if F then
return Color.newRGB(
Color.types.rgb(r),
Color.types.rgb(g),
Color.types.rgb(b)
)
end
-- rgba(255, 255, 255, 1)
local F, r, g, b, a = parameters(text, 'rgba')
if F then
return Color.newRGB(
Color.types.rgb(r),
Color.types.rgb(g),
Color.types.rgb(b),
Color.types.number(a, true)
)
end
-- hsl(0, 0%, 100%)
local F, h, s, l = parameters(text, 'hsl')
if F then
return Color.newHSL(
Color.types.degrees(h),
Color.types.number(s),
Color.types.number(l)
)
end
-- hsla(0, 0%, 100%, 1)
local F, h, s, l, a = parameters(text, 'hsla')
if F then
return Color.newHSL(
Color.types.degrees(h),
Color.types.number(s),
Color.types.number(l),
Color.types.number(a, true)
)
end
return nil
end
function Color.newRGB(r, g, b, a)
local obj = {}
setmetatable(obj, Color)
obj.lastupdate = 0
obj:setRGB(r, g, b)
obj:setA(a or 1)
return obj
end
function Color.newHSL(h, s, l, a)
local obj = {}
setmetatable(obj, Color)
obj.lastupdate = 1
obj:setHSL(h, s, l)
obj:setA(a or 1)
return obj
end
function Color.newHEX(rgb)
if type(rgb) ~= 'string' then error(string.format('String expected, %s given', type(rgb)), 2) end
local o = val(rgb) or ''
rgb = (o:lower():gsub('^#', ''))
local l,r,g,b,a = rgb:len()
if l == 8 then
_,_,r,g,b,a = string.find(rgb or '', '^([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$')
if r ~= nil then
r = tonumber(r or '', 16)
g = tonumber(g or '', 16)
b = tonumber(b or '', 16)
a = tonumber(a or '', 16)
end
elseif l == 6 then
_,_,r,g,b = string.find(rgb or '', '^([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])([0-9a-f][0-9a-f])$')
if r ~= nil then
r = tonumber(r or '', 16)
g = tonumber(g or '', 16)
b = tonumber(b or '', 16)
a = 255
end
elseif l == 4 then
_,_,r,g,b,a = string.find(rgb or '', '^([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])$')
if r ~= nil then
r = tonumber((r or ''):rep(2), 16)
g = tonumber((g or ''):rep(2), 16)
b = tonumber((b or ''):rep(2), 16)
a = tonumber((a or ''):rep(2), 16)
end
elseif l == 3 then
_,_,r,g,b = string.find(rgb or '', '^([0-9a-f])([0-9a-f])([0-9a-f])$')
if r ~= nil then
r = tonumber((r or ''):rep(2), 16)
g = tonumber((g or ''):rep(2), 16)
b = tonumber((b or ''):rep(2), 16)
a = 255
end
end
mw.log(r,g,b,a)
if r == nil or g == nil or b == nil or a == nil then
error('Invalid RGB value: ' .. o, 2)
end
r = r / 255
g = g / 255
b = b / 255
a = a / 255
return Color.newRGB(r, g, b, a)
end
function Color:copy()
local obj = {}
setmetatable(obj, Color)
obj.RED = self.RED
obj.GREEN = self.GREEN
obj.BLUE = self.BLUE
obj.HUE = self.HUE
obj.SATURATION = self.SATURATION
obj.LIGHTNESS = self.LIGHTNESS
obj.ALPHA = self.ALPHA
obj.lastupdate = self.lastupdate
return obj
end
function Color:apply(color)
if color == nil then return self end
if type(color) ~= 'table' or color.lastupdate == nil then error(string.format('Color or nil expected, %s given', type(color)), 2) end
self.RED = color.RED
self.GREEN = color.GREEN
self.BLUE = color.BLUE
self.HUE = color.HUE
self.SATURATION = color.SATURATION
self.LIGHTNESS = color.LIGHTNESS
self.ALPHA = color.ALPHA
self.lastupdate = color.lastupdate
return self
end
-- To string
function Color.tostring(c)
if c.lastupdate == 0 then
if c:a() == 1 then
return c:toHEX()
else
return c:toRGBA()
end
else
if c:a() == 1 then
return c:toHSL()
else
return c:toHSLA()
end
end
end
Color.__tostring = Color.tostring
-- Methods
function Color:calcRGB()
local H, S, L = self.HUE, self.SATURATION, self.LIGHTNESS
local C = (1 - math.abs(2 * L - 1)) * S
local X = C * (1 - math.abs(((H / 60) % 2) - 1))
local M = L - C / 2
local R, G, B
if 0 <= H and H < 60 then
R, G, B = C, X, 0
elseif 60 <= H and H < 120 then
R, G, B = X, C, 0
elseif 120 <= H and H < 180 then
R, G, B = 0, C, X
elseif 180 <= H and H < 240 then
R, G, B = 0, X, C
elseif 240 <= H and H < 300 then
R, G, B = X, 0, C
elseif 300 <= H and H < 360 then
R, G, B = C, 0, X
end
self.lastupdate = 0
return self:setRGB(R + M, G + M, B + M)
end
function Color:calcHSL()
local R, G, B = self.RED, self.GREEN, self.BLUE
local Cmax = math.max(R, G, B)
local Cmin = math.min(R, G, B)
local D = Cmax - Cmin
local L = (Cmin + Cmax) / 2
local S = D / (1 - math.abs(2 * L - 1))
local H
if D == 0 then
H = 0
elseif Cmax == R then
H = 60 * (((G - B) / D) % 6)
elseif Cmax == G then
H = 60 * (((B - R) / D) + 2)
elseif Cmax == B then
H = 60 * (((R - G) / D) + 4)
end
self.lastupdate = 1
return self:setHSL(H, S, L)
end
function Color:r()
if self.lastupdate == 1 then self:calcRGB() end
return self.RED
end
function Color:g()
if self.lastupdate == 1 then self:calcRGB() end
return self.GREEN
end
function Color:b()
if self.lastupdate == 1 then self:calcRGB() end
return self.BLUE
end
function Color:rgb()
if self.lastupdate == 1 then self:calcRGB() end
return self.RED, self.GREEN, self.BLUE
end
function Color:h()
if self.lastupdate == 0 then self:calcHSL() end
return self.HUE
end
function Color:s()
if self.lastupdate == 0 then self:calcHSL() end
return self.SATURATION
end
function Color:l()
if self.lastupdate == 0 then self:calcHSL() end
return self.LIGHTNESS
end
function Color:hsl()
if self.lastupdate == 0 then self:calcHSL() end
return self.HUE, self.SATURATION, self.LIGHTNESS
end
local function adjustValue(v)
if v <= 0.03928 then
return v / 12.92
end
return math.pow(((v + 0.055) / 1.055), 2.4)
end
function Color:luminance()
if self.lastupdate == 1 then self:calcRGB() end
return adjustValue(self.RED) * 0.2126
+ adjustValue(self.GREEN) * 0.7152
+ adjustValue(self.BLUE) * 0.0722
end
function Color:brightness(cr, cg, cb)
if self.lastupdate == 1 then self:calcRGB() end
return math.sqrt(self.RED * self.RED * (cr or 0.241)
+ self.GREEN * self.GREEN * (cg or 0.691)
+ self.BLUE * self.BLUE * (cb or 0.068))
end
function Color:w3brightness()
if self.lastupdate == 1 then self:calcRGB() end
return self.RED * 0.299
+ self.GREEN * 0.587
+ self.BLUE * 0.114
end
function Color:setR(val)
if type(val) ~= 'number' then error(string.format('Number expected, %s given', type(val)), 2) end
if self.lastupdate == 1 then self:calcRGB() end
self.lastupdate = 0
self.RED = math.max(0, math.min(1, val))
return self
end
function Color:setG(val)
if type(val) ~= 'number' then error(string.format('Number expected, %s given', type(val)), 2) end
if self.lastupdate == 1 then self:calcRGB() end
self.lastupdate = 0
self.GREEN = math.max(0, math.min(1, val))
return self
end
function Color:setB(val)
if type(val) ~= 'number' then error(string.format('Number expected, %s given', type(val)), 2) end
if self.lastupdate == 1 then self:calcRGB() end
self.lastupdate = 0
self.BLUE = math.max(0, math.min(1, val))
return self
end
function Color:setRGB(r, g, b)
if self.lastupdate == 1 then self:calcRGB() end
self.lastupdate = 0
if r ~= nil then self:setR(r) end
if g ~= nil then self:setG(g) end
if b ~= nil then self:setB(b) end
return self
end
function Color:setH(val)
if type(val) ~= 'number' then error(string.format('Number expected, %s given', type(val)), 2) end
if self.lastupdate == 0 then self:calcHSL() end
self.lastupdate = 1
self.HUE = val % 360
return self
end
function Color:setS(val)
if type(val) ~= 'number' then error(string.format('Number expected, %s given', type(val)), 2) end
if self.lastupdate == 0 then self:calcHSL() end
self.lastupdate = 1
self.SATURATION = math.max(0, math.min(1, val))
return self
end
function Color:setL(val)
if type(val) ~= 'number' then error(string.format('Number expected, %s given', type(val)), 2) end
if self.lastupdate == 0 then self:calcHSL() end
self.lastupdate = 1
self.LIGHTNESS = math.max(0, math.min(1, val))
return self
end
function Color:setHSL(h, s, l)
if self.lastupdate == 0 then self:calcHSL() end
self.lastupdate = 1
if h ~= nil then self:setH(h) end
if s ~= nil then self:setS(s) end
if l ~= nil then self:setL(l) end
return self
end
function Color:a()
return self.ALPHA
end
function Color:setA(val)
if type(val) ~= 'number' then error(string.format('Number expected, %s given', type(val)), 2) end
self.ALPHA = math.max(0, math.min(1, val))
return self
end
function Color:applyMod(name, arguments)
local res
if Color.mods[name].args then
local ready = {}
for i,v in ipairs(Color.mods[name].args) do
local func, r = v:gsub('color%s+or%s+', '')
if not (r > 0 and pcall(function()
ready[i] = Color.types.color(arguments[i])
end)) then
ready[i] = Color.types[func](arguments[i], 'nil')
end
end
res = Color.mods[name].func(self, unpack(ready))
else
res = Color.mods[name].func(self)
end
if res == nil then return self end
if type(res) ~= 'table' or res.lastupdate == nil then error(string.format('mods[\'%s\']: Color or nil expected in return, got %s', name, type(res)), 2) end
return self:apply(res)
end
function Color:modify(value)
value = val(value)
if value == nil then return self, 0 end
local list = parameters(value)
local count = 0
for i,v in ipairs(list) do
local name, params = unpack(v)
if type(Color.mods[name]) == 'table' then
count = count + 1
self:applyMod(name, parameters(params:sub(2, -2)))
end
end
return self, count
end
function Color:toHEX(full)
local r, g, b = self:rgb()
r = dec2hex(r * 255, 2)
g = dec2hex(g * 255, 2)
b = dec2hex(b * 255, 2)
local result = '#' .. r .. g .. b
if not full then
_, _, r, g, b = result:find('^#([0-9A-F])%1([0-9A-F])%2([0-9A-F])%3$')
if r then return '#' .. r .. g .. b end
end
return result
end
function Color:toRGB()
local r, g, b = self:rgb()
return string.format(
'rgb(%d, %d, %d)',
round(r * 255),
round(g * 255),
round(b * 255)
)
end
function Color:toRGBA(prec)
local r, g, b = self:rgb()
local a = self:a()
local prec = (prec or 1) + 2
return string.format(
'rgba(%d, %d, %d, %g)',
round(r * 255),
round(g * 255),
round(b * 255),
round(a, prec)
)
end
function Color:toRGB2(prec)
local r, g, b = self:rgb()
local prec = prec or 1
return string.format(
'rgb(%g%%, %g%%, %g%%)',
round(r * 100, prec),
round(g * 100, prec),
round(b * 100, prec)
)
end
function Color:toRGBA2(prec, precA)
local r, g, b = self:rgb()
local a = self:a()
local prec = prec or 1
local precA = precA or (prec+2)
return string.format(
'rgba(%g%%, %g%%, %g%%, %g)',
round(r * 100, prec),
round(g * 100, prec),
round(b * 100, prec),
round(a, precA)
)
end
function Color:toHSL(prec)
local h, s, l = self:hsl()
local prec = prec or 1
return string.format(
'hsl(%d, %g%%, %g%%)',
round(h % 360, prec),
round(s * 100, prec),
round(l * 100, prec)
)
end
function Color:toHSLA(prec, precA)
local h, s, l = self:hsl()
local a = self:a()
local prec = prec or 1
local precA = precA or (prec+2)
return string.format(
'hsla(%d, %g%%, %g%%, %g)',
round(h % 360, prec),
round(s * 100, prec),
round(l * 100, prec),
round(a, precA)
)
end
return Color