Module:College color

--

-- This module implements {{CollegePrimaryHex}}, {{CollegePrimaryStyle}},

-- {{CollegePrimaryColorLink}}, {{CollegeSecondaryHex}},

-- {{CollegeSecondaryStyle}}, {{CollegeSecondaryColorLink}}, and {{NCAA color}}

--

local p = {}

local data_module = "Module:College color/data"

local function stripwhitespace(text)

return text:match("^%s*(.-)%s*$")

end

local function ucfirst(s)

local first = s:sub(1, 1)

local others = s:sub(2, -1)

return first:upper() .. others

end

local function bordercss(c, w)

local s = 'inset ' .. w .. 'px ' .. w .. 'px 0 ' .. c

.. ', inset -' .. w .. 'px -' .. w .. 'px 0 ' .. c

return 'box-shadow: ' .. s .. ';'

end

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 color2lum( origc )

local c = stripwhitespace(origc or ''):lower()

-- remove leading # (if there is one)

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

-- split into rgb

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

if( #cs == 6 ) then

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

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

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

return 0.2126 * R + 0.7152 * G + 0.0722 * B

elseif ( #cs == 3 ) then

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

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

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

return 0.2126 * R + 0.7152 * G + 0.0722 * B

end

-- failure

error('Invalid hex color ' .. origc, 2)

end

local function remove_sport(team)

team = mw.ustring.gsub(team, "%s*<[Bb][Rr][^<>]*>%s*", ' ');

team = mw.ustring.gsub(team, " [Tt]eam$", '')

team = mw.ustring.gsub(team, " [Bb]asketball$", '')

team = mw.ustring.gsub(team, " [Bb]aseball$", '')

team = mw.ustring.gsub(team, " [Cc]ross [Cc]ountry$", '')

team = mw.ustring.gsub(team, " [Ff]ield [Hh]ockey$", '')

team = mw.ustring.gsub(team, " [Ff]ootball$", '')

team = mw.ustring.gsub(team, " [Gg]olf$", '')

team = mw.ustring.gsub(team, " [Gg]ymnastics$", '')

team = mw.ustring.gsub(team, " [Ii]ce [Hh]ockey$", '')

team = mw.ustring.gsub(team, " [Ll]acrosse$", '')

team = mw.ustring.gsub(team, " [Rr]owing$", '')

team = mw.ustring.gsub(team, " [Ss]ki$", '')

team = mw.ustring.gsub(team, " [Ss]occer$", '')

team = mw.ustring.gsub(team, " [Ss]oftball$", '')

team = mw.ustring.gsub(team, " [Ss]wim$", '')

team = mw.ustring.gsub(team, " [Tt]ennis$", '')

team = mw.ustring.gsub(team, " [Tt]rack [Aa]nd [Ff]ield$", '')

team = mw.ustring.gsub(team, " [Vv]olleyball$", '')

team = mw.ustring.gsub(team, " [Ww]restling$", '')

team = mw.ustring.gsub(team, " [Ww]omen's$", '')

team = mw.ustring.gsub(team, " [Mm]en's$", '')

return team

end

local function get_colors(team, unknown)

team = stripwhitespace(team or '')

unknown = unknown or {"DCDCDC", "000000"}

local use_default = {

[""] = 1,

["retired"] = 1,

["free agent"] = 1,

}

local colors = nil

if ( team and use_default[team:lower()] ) then

colors = {"DCDCDC", "000000"}

else

local all_colors = mw.loadData(data_module)

colors = all_colors[team]

if ( colors and type(colors) == 'string' ) then

colors = all_colors[colors]

end

end

return colors or unknown

end

local function team_color(team, num, num2)

local colors = get_colors(team, nil)

num = tonumber(num:match('[1-3]') or '0')

num2 = tonumber(num2:match('[1-3]') or '0')

if ( num ) then

return colors[num] or colors[num2] or ''

else

return ''

end

end

local function team_style1(team, borderwidth, fontcolor)

local colors = get_colors(team, nil)

local color = '#' .. (colors[3] or colors[2] or '')

if fontcolor and fontcolor == 'auto' then

-- compute the luminosity of the background

local lum = color2lum(colors[1] or '')

-- compute the contrast with white and black

local wcontrast = (1 + 0.05)/(lum + 0.05)

local bcontrast = (lum + 0.05)/(0 + 0.05)

-- select the text color with the best contrast

if( bcontrast > wcontrast + 1.25 ) then

fontcolor = '#000000'

else

fontcolor = '#FFFFFF'

end

end

local style = 'background-color:#' .. (colors[1] or '') .. ';color:' .. (fontcolor or color) .. ';'

-- remove the border if it's nearly white

if ((1 + 0.05)/(color2lum(color) + 0.05) < 1.25) then

borderwidth = '0'

end

borderwidth = tonumber(borderwidth or '2') or 0

if (borderwidth > 0 and color ~= '#FFFFFF') then

style = style .. bordercss(color, borderwidth)

end

return style

end

local function team_style2(team, borderwidth, fontcolor)

local colors = get_colors(team, nil)

local color = '#' .. (colors[1] or '')

if fontcolor and fontcolor == 'auto' then

-- compute the luminosity of the background

local lum = color2lum(colors[3] or colors[2] or '')

-- compute the contrast with white and black

local wcontrast = (1 + 0.05)/(lum + 0.05)

local bcontrast = (lum + 0.05)/(0 + 0.05)

-- select the text color with the best contrast

if( bcontrast > wcontrast + 1.25 ) then

fontcolor = '#000000'

else

fontcolor = '#FFFFFF'

end

end

local style = 'background-color:#' .. (colors[3] or colors[2] or '') .. ';color:' .. (fontcolor or color) .. ';'

-- remove the border if it's nearly white

if ((1 + 0.05)/(color2lum(color) + 0.05) < 1.25) then

borderwidth = '0'

end

borderwidth = tonumber(borderwidth or '2') or 0

if (borderwidth > 0 and color ~= '#FFFFFF') then

style = style .. bordercss(color, borderwidth)

end

return style

end

local function team_header1(team, borderwidth)

local colors = get_colors(team, nil)

-- set the default background

local background = (colors[1] or 'FFFFFF'):upper()

-- set background to white if it's nearly white

if ((1 + 0.05)/(color2lum(background) + 0.05) < 1.25) then

background = 'FFFFFF'

end

-- now pick a font color

local fontcolor = '000000'

-- compute the luminosity of the background

local lum = color2lum(background)

-- compute the contrast with white and black

local wcontrast = (1 + 0.05)/(lum + 0.05)

local bcontrast = (lum + 0.05)/(0 + 0.05)

-- select the text color with the best contrast

if( bcontrast > wcontrast + 1.25 ) then

fontcolor = '000000'

else

fontcolor = 'FFFFFF'

end

local style

if( background == 'FFFFFF' ) then

style = 'background-color:none;color:#' .. fontcolor .. ';'

else

style = 'background-color:#' .. background .. ';color:#' .. fontcolor .. ';'

end

if borderwidth then

borderwidth = tonumber(borderwidth or '2') or 0

local bordercolor = (colors[3] or colors[2] or 'FFFFFF'):upper()

if (borderwidth > 0 and bordercolor ~= 'FFFFFF') then

-- do not add a border if it's nearly white

if ((1 + 0.05)/(color2lum(bordercolor) + 0.05) >= 1.25) then

style = style .. bordercss('#' .. bordercolor, borderwidth)

end

end

end

return style

end

local function team_header2(team)

local colors = get_colors(team, nil)

-- set the default background

local background = (colors[3] or colors[2] or 'FFFFFF'):upper()

-- set background to white if it's nearly white

if ((1 + 0.05)/(color2lum(background) + 0.05) < 1.25) then

background = 'FFFFFF'

end

-- if the background is white, then use the primary background instead

if( background == 'FFFFFF' ) then

background = (colors[1] or 'FFFFFF'):upper()

end

-- now pick a font color

local fontcolor = '000000'

-- compute the luminosity of the background

local lum = color2lum(background)

-- compute the contrast with white and black

local wcontrast = (1 + 0.05)/(lum + 0.05)

local bcontrast = (lum + 0.05)/(0 + 0.05)

-- select the text color with the best contrast

if( bcontrast > wcontrast + 1.25 ) then

fontcolor = '000000'

else

fontcolor = 'FFFFFF'

end

if( background == 'FFFFFF' ) then

return 'background-color:none;color:#' .. fontcolor .. ';'

else

return 'background-color:#' .. background .. ';color:#' .. fontcolor .. ';'

end

end

local function team_table_head(args, team, ctype)

local colors = get_colors(team, nil)

local borderwidth = tonumber(args['border']) or 0

-- set the default background

local background = (ctype == 'p') and

(colors[1] or 'FFFFFF'):upper() or

(colors[3] or colors[2] or 'FFFFFF'):upper()

-- now pick a font color

local fontcolor = ''

-- compute the luminosity of the background

local lum = color2lum(background)

-- compute the contrast with white and black

local wcontrast = (1 + 0.05)/(lum + 0.05)

local bcontrast = (lum + 0.05)/(0 + 0.05)

-- select the text color with the best contrast

if( bcontrast > wcontrast + 1.25 ) then

fontcolor = '#000000'

else

fontcolor = '#FFFFFF'

end

local s = 'background-color:#' .. background .. ';color:' .. (args['color'] or fontcolor) .. ';'

if borderwidth > 0 then

local bc = (ctype == 'p') and

(colors[3] or colors[2] or ) or (colors[1] or )

if bc ~= 'FFFFFF' then

s = s .. bordercss('#' .. bc, borderwidth)

end

end

local res = '|-\n'

for i=1,50 do

if( args[i] ~= nil ) then

local cstyle = 'scope="col" style="' .. s .. '"'

if args['col' .. i .. 'span'] ~= nil then

cstyle = cstyle .. ' colspan=' .. args['col' .. i .. 'span']

end

if args['class' .. i ] ~= nil then

cstyle = cstyle .. ' class="' .. args['class' .. i] .. '"'

end

res = res .. '! ' .. cstyle .. ' |' .. args[i] .. '\n'

else

return res .. '|-\n'

end

end

return res .. 'Error!\n|-\n'

end

local function team_stripe1(team, borderwidth)

local colors = get_colors(team, nil)

-- set the default scheme

local background = colors[1] or ''

local fontcolor = colors[2] or ''

local bordercolor = (colors[3] or colors[2] or ''):upper()

borderwidth = tonumber(borderwidth or '3') or 0

-- if there is no tertiary color, then pick a font color

if (colors[3] == nil) then

-- compute the luminosity of the background

local lum = color2lum(colors[1])

-- compute the contrast with white and black

local wcontrast = (1 + 0.05)/(lum + 0.05)

local bcontrast = (lum + 0.05)/(0 + 0.05)

-- select the text color with the best contrast

if( bcontrast > wcontrast + 1.25 ) then

fontcolor = '000000'

else

fontcolor = 'FFFFFF'

end

end

-- finally build the style string

local style = ''

if (borderwidth > 0) then

-- use the primary as the border if the border is white or close to white

local bordercontrast = (1 + 0.05)/(color2lum(bordercolor) + 0.05)

if (bordercontrast < 1.25) then

bordercolor = background

local fontcontrast = (1 + 0.05)/(color2lum(colors[2] or 'FFFFFF') + 0.05)

if (fontcontrast < 1.25) then

fontcolor = colors[2] or 'FFFFFF'

end

end

style = style .. ' border:' .. borderwidth .. 'px solid #' .. bordercolor .. ';'

style = style .. ' border-left: none; border-right: none;'

style = style .. ' box-shadow: inset 0 2px 0 #FEFEFE, inset 0 -2px 0 #FEFEFE;'

end

style = 'background-color:#' .. background .. ';color:#' .. fontcolor .. ';' .. style

return style

end

local function team_boxes(frame, team, order, sep)

local function colorbox( h )

local r = mw.html.create('')

r:tag('span')

:addClass('legend-color')

:css('color: black;background-color', '#' .. (h or '')) -- specify color here also; work around Phab:T393538

:wikitext(' ')

return tostring(r)

end

local colors = get_colors(team, 'unknown')

if type(colors) ~= 'table' then

return ''

end

local colorboxes = {}

local colororder = {'1','2','3','4','5'}

local namecheck = 0

if order == '' then

order = colors['order'] or ''

namecheck = 1

end

if order ~= '' then

colororder = mw.text.split(order, '')

end

for k,v in pairs(colororder) do

local i = tonumber(v) or 0

if( namecheck == 0 or colors['name' .. i]) then

if colors[i] then

table.insert(colorboxes,colorbox(colors[i]))

end

end

end

if (#colorboxes > 0) then

return frame:extensionTag{ name = 'templatestyles', args = { src = 'Legend/styles.css'} } .. table.concat(colorboxes, sep)

end

return ''

end

local function team_list(frame, team, num1, num2, num3, num4, num5, sep)

local function colorbox( h )

local r = mw.html.create('')

r:tag('span')

:addClass('legend-color')

:css('background-color', '#' .. (h or ''))

:wikitext(' ')

return tostring(r)

end

local colors = get_colors(team, 'unknown')

if type(colors) ~= 'table' then

return ''

end

local nums = {

tonumber(num1:match('[1-5]') or '0') or 0,

tonumber(num2:match('[1-5]') or '0') or 0,

tonumber(num3:match('[1-5]') or '0') or 0,

tonumber(num4:match('[1-5]') or '0') or 0,

tonumber(num5:match('[1-5]') or '0') or 0}

local colorboxes = {}

local colornames = {}

local colororder = {'1','2','3','4','5'}

local order = colors['order'] or ''

if(order ~= '') then

colororder = mw.text.split(order, '')

end

for k,v in pairs(colororder) do

local i = tonumber(v) or 0

if ( nums[i] > 0 ) then

if( colors['name' .. nums[i]]) then

table.insert(colornames,colors['name' .. nums[i]])

table.insert(colorboxes,colorbox(colors[nums[i]] or ''))

end

end

end

local res = ''

if (#colornames > 0) then

colornames[1] = ucfirst(colornames[1])

end

res = mw.text.listToText(

colornames,

', ',

#colornames == 2 and ' and ' or ', and '

)

if (colors['cite']) then

res = res .. frame:preprocess('' .. colors['cite'] .. '')

end

if (colors['ref']) then

res = res .. '[' .. colors['ref'] .. ']'

end

if (colors['ref2']) then

res = res .. '[' .. colors['ref2'] .. ']'

end

if (#colornames > 0) then

res = res .. sep

end

if (#colorboxes > 0) then

res = res .. frame:extensionTag{ name = 'templatestyles', args = { src = 'Legend/styles.css'} }

res = res .. table.concat(colorboxes, ' ')

end

return res

end

local function team_check(team, unknown)

local colors = get_colors(team, unknown)

if type(colors) == 'table' then

return 'known'

else

return unknown

end

end

function p.color(frame)

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

return team_color(remove_sport(args[1] or ), args[2] or , args[3] or '')

end

function p.color1(frame)

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

return team_color(remove_sport(args[1] or ), '1', )

end

function p.color32(frame)

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

return team_color(remove_sport(args[1] or ''), '3', '2')

end

function p.style1(frame)

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

return team_style1(remove_sport(args[1] or ''), args['border'], args['color'])

end

function p.style2(frame)

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

return team_style2(remove_sport(args[1] or ''), args['border'], args['color'])

end

function p.header1(frame)

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

return team_header1(remove_sport(args[1] or ''), args['border'])

end

function p.header2(frame)

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

return team_header2(remove_sport(args[1] or ''))

end

function p.tablehead1(frame)

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

return team_table_head(args, remove_sport(args['team'] or ''), 'p')

end

function p.tablehead2(frame)

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

return team_table_head(args, remove_sport(args['team'] or ''), 's')

end

function p.stripe1(frame)

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

return team_stripe1(remove_sport(args[1] or ''), args['border'])

end

function p.boxes(frame)

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

return team_boxes(frame, remove_sport(args[1] or ''),

args['order'] or '', args['sep'] or ' ')

end

function p.list(frame)

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

return team_list(frame, remove_sport(args[1] or ''),

args[2] or '1', args[3] or '2', args[4] or '3', args[5] or '4', args[6] or '5', args['sep'] or '')

end

function p.check(frame)

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

return team_check(remove_sport(args[1] or ), args[2] or )

end

function p.check_data()

-- In a sandbox, preview {{#invoke:college color|check_data}}

local results = {'Problems in Module:College color/data:'}

local function problems(msg)

if msg then

table.insert(results, msg)

elseif results[2] then

return table.concat(results, '\n*')

else

return 'No problems detected.'

end

end

local data = require(data_module)

local keys = {}

for k, _ in pairs(data) do

table.insert(keys, k)

end

table.sort(keys)

for _, key in ipairs(keys) do

local val = data[key]

if not (type(key) == 'string' and (type(val) == 'table' or type(val) == 'string')) then

problems('Invalid type for "' .. tostring(key) .. '"')

end

if type(val) == 'table' then

if not (2 <= #val and #val <= 4) then

problems('Invalid number of numbered parameters for "' .. tostring(key) .. '"')

end

for i, v in ipairs(val) do

if not tostring(v):match('^%x%x%x%x%x%x$') then

problems('Parameter [' .. i .. '] should be a 6-hex-digit color but is "' .. tostring(v) .. '" for "' .. tostring(key) .. '"')

end

end

for k, v in pairs(val) do

if type(k) == 'number' then

if not (1 <= k and k <= 4) then

problems('Invalid numbered parameter for "' .. tostring(key) .. '"')

end

elseif type(k) == 'string' then

if not (

k:match('^name[1-4]$') or

k:match('^cite2?$') or

k:match('^order$')

) then

problems('Unexpected key in table for "' .. tostring(key) .. '"')

end

else

problems('Invalid key type in table for "' .. tostring(key) .. '"')

end

end

elseif data[val] == nil then

problems('Undefined alias for "' .. tostring(key) .. '"')

elseif type(data[val]) ~= 'table' then

problems('Alias is not a table for "' .. tostring(key) .. '"')

end

end

return problems()

end

function p.testtable(frame)

local contrasttable_mod = require("Module:College color/contrast")

return contrasttable_mod._testtable(frame.args)

end

return p