Module:Box-header: Difference between revisions

Jump to navigation Jump to search
Created page with "local p = {} ---------- Config data ---------- local namedColours = mw.loadData( 'Module:Box-header/colours' ) local modes = { lightest = { sat=0.10, val=1.00 }, light = { sat=0.15, val=0.95 }, normal = { sat=0.40, val=0.85 }, dark = { sat=0.90, val=0.70 }, darkest = { sat=1.00, val=0.45 }, content = { sat=0.04, val=1.00 }, grey = { sat=0.00 } } local min_contrast_ratio_normal_text = 7 -- i.e 7:1 local min_contrast_ratio_large_text = 4.5 -- i.e...."
>Tomoneill
(change to VE edit)
 
(Created page with "local p = {} ---------- Config data ---------- local namedColours = mw.loadData( 'Module:Box-header/colours' ) local modes = { lightest = { sat=0.10, val=1.00 }, light = { sat=0.15, val=0.95 }, normal = { sat=0.40, val=0.85 }, dark = { sat=0.90, val=0.70 }, darkest = { sat=1.00, val=0.45 }, content = { sat=0.04, val=1.00 }, grey = { sat=0.00 } } local min_contrast_ratio_normal_text = 7 -- i.e 7:1 local min_contrast_ratio_large_text = 4.5 -- i.e....")
Line 13: Line 13:
local min_contrast_ratio_normal_text = 7  -- i.e 7:1
local min_contrast_ratio_normal_text = 7  -- i.e 7:1
local min_contrast_ratio_large_text  = 4.5  -- i.e. 4.5:1
local min_contrast_ratio_large_text  = 4.5  -- i.e. 4.5:1
-- Template parameter aliases
-- Template parameter aliases
--  Specify each as either a single value, or a table of values
--  Specify each as either a single value, or a table of values
Line 23: Line 22:
}
}


---------- Dependecies ----------
---------- Dependencies ----------
local colourContrastModule = require('Module:Color contrast')
local colourContrastModule = require('Module:Color contrast')
local hex = require( 'luabit.hex' )
local hex = require( 'luabit.hex' )
Line 33: Line 32:
end
end
local aliases = parameterAliases[parameter]
local aliases = parameterAliases[parameter]
if not aliases then
if not aliases then
return nil
return nil
end
end
if type(aliases) ~= 'table' then
if type(aliases) ~= 'table' then
return args[aliases]
return args[aliases]
end
end
for _, alias in ipairs(aliases) do
for _, alias in ipairs(aliases) do
if args[alias] then
if args[alias] then
return args[alias]
return args[alias]
end
end
end
end
return nil
return nil
end
end


local function setCleanArgs(argsTable)
local function setCleanArgs(argsTable)
local cleanArgs = {}
local cleanArgs = {}
for key, val in pairs(argsTable) do
for key, val in pairs(argsTable) do
if type(val) == 'string' then
if type(val) == 'string' then
val = val:match('^%s*(.-)%s*$')
val = val:match('^%s*(.-)%s*$')
if val ~= '' then
if val ~= '' then
cleanArgs[key] = val
cleanArgs[key] = val
end
end
else
else
cleanArgs[key] = val
cleanArgs[key] = val
end
end
end
end
return cleanArgs
return cleanArgs
end
end


-- Merge two tables into a new table. If the are any duplicate keys, the values from the second overwrite the values from the first.
-- Merge two tables into a new table. If the are any duplicate keys, the values from the second overwrite the values from the first.
local function mergeTables(first, second)
local function mergeTables(first, second)
local merged = {}
local merged = {}
for key, val in pairs(first) do
for key, val in pairs(first) do
merged[key] = val
merged[key] = val
end
end
for key, val in pairs(second) do
for key, val in pairs(second) do
merged[key] = val
merged[key] = val
end
end
return merged
return merged
end
end


local function toOpenTagString(selfClosedHtmlObject)
local function toOpenTagString(selfClosedHtmlObject)
local closedTagString = tostring(selfClosedHtmlObject)
local closedTagString = tostring(selfClosedHtmlObject)
local openTagString = mw.ustring.gsub(closedTagString, ' />$', '>')
local openTagString = mw.ustring.gsub(closedTagString, ' />$', '>')
return openTagString
return openTagString
end
end


local function normaliseHexTriplet(hexString)
local function normaliseHexTriplet(hexString)
if not hexString then return nil end
if not hexString then return nil end
local hexComponent = mw.ustring.match(hexString, '^#(%x%x%x)$') or mw.ustring.match(hexString, '^#(%x%x%x%x%x%x)$')
local hexComponent = mw.ustring.match(hexString, '^#(%x%x%x)$') or mw.ustring.match(hexString, '^#(%x%x%x%x%x%x)$')
if hexComponent and #hexComponent == 6 then
if hexComponent and #hexComponent == 6 then
return mw.ustring.upper(hexString)
return mw.ustring.upper(hexString)
end
end
if hexComponent and #hexComponent == 3 then
if hexComponent and #hexComponent == 3 then
local r = mw.ustring.rep(mw.ustring.sub(hexComponent, 1, 1), 2)
local r = mw.ustring.rep(mw.ustring.sub(hexComponent, 1, 1), 2)
local g = mw.ustring.rep(mw.ustring.sub(hexComponent, 2, 2), 2)
local g = mw.ustring.rep(mw.ustring.sub(hexComponent, 2, 2), 2)
local b = mw.ustring.rep(mw.ustring.sub(hexComponent, 3, 3), 2)
local b = mw.ustring.rep(mw.ustring.sub(hexComponent, 3, 3), 2)
return '#' .. mw.ustring.upper(r .. g .. b)
return '#' .. mw.ustring.upper(r .. g .. b)
end
end
return nil
return nil
end
end


---------- Conversions ----------
---------- Conversions ----------
local function decimalToPaddedHex(number)
local function decimalToPaddedHex(number)
local prefixedHex = hex.to_hex(tonumber(number)) -- prefixed with '0x'
local prefixedHex = hex.to_hex(tonumber(number)) -- prefixed with '0x'
local padding =  #prefixedHex == 3 and '0' or ''  
local padding =  #prefixedHex == 3 and '0' or ''  
return mw.ustring.gsub(prefixedHex, '0x', padding)
return mw.ustring.gsub(prefixedHex, '0x', padding)
end
end
local function hexToDecimal(hexNumber)
local function hexToDecimal(hexNumber)
return tonumber(hexNumber, 16)
return tonumber(hexNumber, 16)
end
end
local function RGBtoHexTriplet(R, G, B)
local function RGBtoHexTriplet(R, G, B)
return '#' .. decimalToPaddedHex(R) .. decimalToPaddedHex(G) .. decimalToPaddedHex(B)
return '#' .. decimalToPaddedHex(R) .. decimalToPaddedHex(G) .. decimalToPaddedHex(B)
end
end
local function hexTripletToRGB(hexTriplet)
local function hexTripletToRGB(hexTriplet)
local R_hex, G_hex, B_hex = string.match(hexTriplet, '(%x%x)(%x%x)(%x%x)')
local R_hex, G_hex, B_hex = string.match(hexTriplet, '(%x%x)(%x%x)(%x%x)')
return hexToDecimal(R_hex), hexToDecimal(G_hex), hexToDecimal(B_hex)
return hexToDecimal(R_hex), hexToDecimal(G_hex), hexToDecimal(B_hex)
end
end
local function HSVtoRGB(H, S, V) -- per [[HSL and HSV#Converting_to_RGB]]
local function HSVtoRGB(H, S, V) -- per [[HSL and HSV#Converting_to_RGB]]
local C = V * S
local C = V * S
local H_prime = H / 60
local H_prime = H / 60
local X = C * ( 1 - math.abs(math.fmod(H_prime, 2) - 1) )
local X = C * ( 1 - math.abs(math.fmod(H_prime, 2) - 1) )
local R1, G1, B1
local R1, G1, B1
if H_prime <= 1 then
if H_prime <= 1 then
R1 = C
R1 = C
G1 = X
G1 = X
B1 = 0
B1 = 0
elseif H_prime <= 2 then
elseif H_prime <= 2 then
R1 = X
R1 = X
G1 = C
G1 = C
B1 = 0
B1 = 0
elseif H_prime <= 3 then
elseif H_prime <= 3 then
R1 = 0
R1 = 0
G1 = C
G1 = C
B1 = X
B1 = X
elseif H_prime <= 4 then
elseif H_prime <= 4 then
R1 = 0
R1 = 0
G1 = X
G1 = X
B1 = C
B1 = C
elseif H_prime <= 5 then
elseif H_prime <= 5 then
R1 = X
R1 = X
G1 = 0
G1 = 0
B1 = C
B1 = C
elseif H_prime <= 6 then
elseif H_prime <= 6 then
R1 = C
R1 = C
G1 = 0
G1 = 0
B1 = X
B1 = X
end
end
local m = V - C
local m = V - C
local R = R1 + m
local R = R1 + m
local G = G1 + m
local G = G1 + m
local B = B1 + m
local B = B1 + m


local R_255 = math.floor(R*255)
local R_255 = math.floor(R*255)
local G_255 = math.floor(G*255)
local G_255 = math.floor(G*255)
local B_255 = math.floor(B*255)
local B_255 = math.floor(B*255)
return R_255, G_255, B_255
return R_255, G_255, B_255
end
end
local function RGBtoHue(R_255, G_255, B_255) -- per [[HSL and HSV#Hue and chroma]]
local function RGBtoHue(R_255, G_255, B_255) -- per [[HSL and HSV#Hue and chroma]]
local R = R_255/255
local R = R_255/255
local G = G_255/255
local G = G_255/255
local B = B_255/255
local B = B_255/255


local M = math.max(R, G, B)
local M = math.max(R, G, B)
local m = math.min(R, G, B)
local m = math.min(R, G, B)
local C = M - m
local C = M - m
local H_prime
local H_prime
if C == 0 then
if C == 0 then
return null
return null
elseif M == R then
elseif M == R then
H_prime = math.fmod(((G - B)/C + 6), 6) -- adding six before taking mod ensures positive value
H_prime = math.fmod(((G - B)/C + 6), 6) -- adding six before taking mod ensures positive value
elseif M == G then
elseif M == G then
H_prime = (B - R)/C + 2
H_prime = (B - R)/C + 2
elseif M == B then
elseif M == B then
H_prime = (R - G)/C + 4
H_prime = (R - G)/C + 4
end
end
local H = 60 * H_prime
local H = 60 * H_prime
return H
return H
end
end
local function nameToHexTriplet(name)
local function nameToHexTriplet(name)
if not name then return nil end
if not name then return nil end
local codename = mw.ustring.gsub(mw.ustring.lower(name), ' ', '')
local codename = mw.ustring.gsub(mw.ustring.lower(name), ' ', '')
return namedColours[codename]
return namedColours[codename]
end
end


---------- Choose colours ----------
---------- Choose colours ----------
local function calculateColours(H, S, V, minContrast)
local function calculateColours(H, S, V, minContrast)
local bgColour = RGBtoHexTriplet(HSVtoRGB(H, S, V))
local bgColour = RGBtoHexTriplet(HSVtoRGB(H, S, V))
local textColour = colourContrastModule._greatercontrast({bgColour})
local textColour = colourContrastModule._greatercontrast({bgColour})
local contrast = colourContrastModule._ratio({ bgColour, textColour })
local contrast = colourContrastModule._ratio({ bgColour, textColour })
if contrast >= minContrast then
if contrast >= minContrast then
return bgColour, textColour
return bgColour, textColour
elseif textColour == '#FFFFFF' then
elseif textColour == '#FFFFFF' then
-- make the background darker and slightly increase the saturation
-- make the background darker and slightly increase the saturation
return calculateColours(H, math.min(1, S+0.005), math.max(0, V-0.03), minContrast)
return calculateColours(H, math.min(1, S+0.005), math.max(0, V-0.03), minContrast)
else
else
-- make the background lighter and slightly decrease the saturation
-- make the background lighter and slightly decrease the saturation
return calculateColours(H, math.max(0, S-0.005), math.min(1, V+0.03), minContrast)
return calculateColours(H, math.max(0, S-0.005), math.min(1, V+0.03), minContrast)
end
end
end
end


local function makeColours(hue, modeName)
local function makeColours(hue, modeName)
local mode = modes[modeName]
local mode = modes[modeName]
local isGrey = not(hue)
local isGrey = not(hue)
if isGrey then hue = 0 end
if isGrey then hue = 0 end


local borderSat = isGrey and modes.grey.sat or 0.15
local borderSat = isGrey and modes.grey.sat or 0.15
local border = RGBtoHexTriplet(HSVtoRGB(hue, borderSat, 0.75))
local border = RGBtoHexTriplet(HSVtoRGB(hue, borderSat, 0.75))


local titleSat = isGrey and modes.grey.sat or mode.sat
local titleSat = isGrey and modes.grey.sat or mode.sat
local titleBackground, titleForeground = calculateColours(hue, titleSat, mode.val, min_contrast_ratio_large_text)
local titleBackground, titleForeground = calculateColours(hue, titleSat, mode.val, min_contrast_ratio_large_text)


local contentSat = isGrey and modes.grey.sat or modes.content.sat
local contentSat = isGrey and modes.grey.sat or modes.content.sat
local contentBackground, contentForeground = calculateColours(hue, contentSat, modes.content.val, min_contrast_ratio_normal_text)
local contentBackground, contentForeground = calculateColours(hue, contentSat, modes.content.val, min_contrast_ratio_normal_text)


return border, titleForeground, titleBackground, contentForeground, contentBackground
return border, titleForeground, titleBackground, contentForeground, contentBackground
end
end


local function findHue(colour)
local function findHue(colour)
local colourAsNumber = tonumber(colour)
local colourAsNumber = tonumber(colour)
if colourAsNumber and ( -1 < colourAsNumber ) and ( colourAsNumber < 360) then
if colourAsNumber and ( -1 < colourAsNumber ) and ( colourAsNumber < 360) then
return colourAsNumber
return colourAsNumber
end
end


local colourAsHexTriplet = normaliseHexTriplet(colour) or nameToHexTriplet(colour)
local colourAsHexTriplet = normaliseHexTriplet(colour) or nameToHexTriplet(colour)
if colourAsHexTriplet then
if colourAsHexTriplet then
return RGBtoHue(hexTripletToRGB(colourAsHexTriplet))
return RGBtoHue(hexTripletToRGB(colourAsHexTriplet))
end
end


return null
return null
end
end


local function normaliseMode(mode)
local function normaliseMode(mode)
if not mode or not modes[mw.ustring.lower(mode)] or mw.ustring.lower(mode) == 'grey' then
if not mode or not modes[mw.ustring.lower(mode)] or mw.ustring.lower(mode) == 'grey' then
return 'normal'
return 'normal'
end
end
return mw.ustring.lower(mode)
return mw.ustring.lower(mode)
end
end
---------- Build output ----------
---------- Build output ----------
local function boxHeaderOuter(args)
local function boxHeaderOuter(args)
local baseStyle = {
local baseStyle = {
clear = 'both',
clear = 'both',
['box-sizing'] = 'border-box',
['box-sizing'] = 'border-box',
border = ( getParam(args, 'border-type') or 'solid' ) .. ' ' .. ( getParam(args, 'titleborder') or getParam(args, 'border') or '#ababab' ),
border = ( getParam(args, 'border-type') or 'solid' ) .. ' ' .. ( getParam(args, 'titleborder') or getParam(args, 'border') or '#ababab' ),
background = getParam(args, 'titlebackground') or '#bcbcbc',
background = getParam(args, 'titlebackground') or '#bcbcbc',
color = getParam(args, 'titleforeground') or '#000',
color = getParam(args, 'titleforeground') or '#000',
padding = getParam(args, 'padding') or '.1em',
padding = getParam(args, 'padding') or '.1em',
['text-align'] = getParam(args, 'title-align') or 'center',
['text-align'] = getParam(args, 'title-align') or 'center',
['font-family'] = getParam(args, 'font-family') or 'sans-serif',
['font-family'] = getParam(args, 'font-family') or 'sans-serif',
['font-size'] = getParam(args, 'titlefont-size') or '100%',
['font-size'] = getParam(args, 'titlefont-size') or '100%',
['margin-bottom'] = '0px',
['margin-bottom'] = '0px',
}
}


local tag = mw.html.create('div', {selfClosing = true})


local tag = mw.html.create('div', {selfClosing = true})
:addClass('box-header-title-container')
:addClass('box-header-title-container')
:addClass('flex-columns-noflex')
:addClass('flex-columns-noflex')
:css(baseStyle)
:css(baseStyle)
:css('border-width', ( getParam(args, 'border-top') or getParam(args, 'border-width') or '1' ) .. 'px ' .. ( getParam(args, 'border-width') or '1' ) .. 'px 0')
:css('border-width', ( getParam(args, 'border-top') or getParam(args, 'border-width') or '1' ) .. 'px ' .. ( getParam(args, 'border-width') or '1' ) .. 'px 0')
:css('padding-top', getParam(args, 'padding-top') or '.1em')
:css('padding-top', getParam(args, 'padding-top') or '.1em')
:css('padding-left', getParam(args, 'padding-left') or '.1em')
:css('padding-left', getParam(args, 'padding-left') or '.1em')
:css('padding-right', getParam(args, 'padding-right') or '.1em')
:css('padding-right', getParam(args, 'padding-right') or '.1em')
:css('padding-bottom', getParam(args, 'padding-bottom') or '.1em')
:css('padding-bottom', getParam(args, 'padding-bottom') or '.1em')
:css('moz-border-radius', getParam(args, 'title-border-radius') or '0')
:css('moz-border-radius', getParam(args, 'title-border-radius') or '0')
:css('webkit-border-radius', getParam(args, 'title-border-radius') or '0')
:css('webkit-border-radius', getParam(args, 'title-border-radius') or '0')
:css('border-radius', getParam(args, 'title-border-radius') or '0')
:css('border-radius', getParam(args, 'title-border-radius') or '0')
return toOpenTagString(tag)
return toOpenTagString(tag)
end
end


local function boxHeaderTopLinks(args)
local function boxHeaderTopLinks(args)
local style = {
local style = {
float = 'right',
float = 'right',
['margin-bottom'] = '.1em',
['margin-bottom'] = '.1em',
['font-size'] = getParam(args, 'font-size') or '80%',
['font-size'] = getParam(args, 'font-size') or '80%',
color = getParam(args, 'titleforeground') or '#000'
color = getParam(args, 'titleforeground') or '#000'
}
}
local tag = mw.html.create('div', {selfClosing = true})
local tag = mw.html.create('div', {selfClosing = true})
:addClass('plainlinks noprint' )
:addClass('plainlinks noprint' )
:css(style)
:css(style)
return toOpenTagString(tag)
return toOpenTagString(tag)
end
end


local function boxHeaderEditLink(args)
local function boxHeaderEditLink(args)
local style = {
local style = {
color = getParam(args, 'titleforeground') or '#000'
color = getParam(args, 'titleforeground') or '#000'
}
}
local tag = mw.html.create('span')
local tag = mw.html.create('span')
:css(style)
:css(style)
:wikitext('edit')
:wikitext('edit')
local linktext = tostring(tag)
local linktext = tostring(tag)
local linktarget = tostring(mw.uri.fullUrl(getParam(args, 'editpage'), {veaction='edit', section=getParam(args, 'section')}))
local linktarget = tostring(mw.uri.fullUrl(getParam(args, 'editpage'), {veaction='edit', section=getParam(args, 'section')}))
return '[' .. linktarget  .. ' ' .. linktext .. ']&nbsp;'
return '[' .. linktarget  .. ' ' .. linktext .. ']&nbsp;'
end
end


local function boxHeaderViewLink(args)
local function boxHeaderViewLink(args)
local style = {
local style = {
color = getParam(args, 'titleforeground') or '#000'
color = getParam(args, 'titleforeground') or '#000'
}
}
local tag = mw.html.create('span')
local tag = mw.html.create('span')
:css(style)
:css(style)
:wikitext('view')
:wikitext('view')
local linktext = tostring(tag)
local linktext = tostring(tag)
local linktarget = ':' .. getParam(args, 'viewpage')
local linktarget = ':' .. getParam(args, 'viewpage')
return "<b>·</b>&nbsp;[[" .. linktarget  .. '|' .. linktext .. ']]&nbsp;'
return "<b>·</b>&nbsp;[[" .. linktarget  .. '|' .. linktext .. ']]&nbsp;'
end
end


local function boxHeaderTitle(args)
local function boxHeaderTitle(args)
local baseStyle = {
local baseStyle = {
['font-family'] = getParam(args, 'title-font-family') or 'sans-serif',
['font-family'] = getParam(args, 'title-font-family') or 'sans-serif',
['font-size'] = getParam(args, 'title-font-size') or '100%',
['font-size'] = getParam(args, 'title-font-size') or '100%',
['font-weight'] = getParam(args, 'title-font-weight') or 'bold',
['font-weight'] = getParam(args, 'title-font-weight') or 'bold',
border = 'none',
border = 'none',
margin = '0',
margin = '0',
padding = '0',
padding = '0',
color = getParam(args, 'titleforeground') or '#000';
color = getParam(args, 'titleforeground') or '#000';
}
}
local tagName = getParam(args, 'SPAN') and 'span' or 'h2'
local tagName = getParam(args, 'SPAN') and 'span' or 'h2'
local tag = mw.html.create(tagName)
local tag = mw.html.create(tagName)
:css(baseStyle)
:css(baseStyle)
:css('padding-bottom', '.1em')
:css('padding-bottom', '.1em')
:wikitext(getParam(args, 'title'))
:wikitext(getParam(args, 'title'))
if getParam(args, 'extra') then
if getParam(args, 'extra') then
local rules = mw.text.split(getParam(args, 'extra'), ';', true)
local rules = mw.text.split(getParam(args, 'extra'), ';', true)
for _, rule in pairs(rules) do
for _, rule in pairs(rules) do
local parts = mw.text.split(rule, ':', true)
local parts = mw.text.split(rule, ':', true)
local prop = parts[1]
local prop = parts[1]
local val = parts[2]
local val = parts[2]
if prop and val then
if prop and val then
tag:css(prop, val)
tag:css(prop, val)
end
end
end
end
end
end
return tostring(tag)
return tostring(tag)
end
end


local function boxBody(args)
local function boxBody(args)
local baseStyle = {
local baseStyle = {
['box-sizing'] = 'border-box',
['box-sizing'] = 'border-box',
border = ( getParam(args, 'border-width') or '1' ) .. 'px solid ' .. ( getParam(args, 'border') or '#ababab'),
border = ( getParam(args, 'border-width') or '1' ) .. 'px solid ' .. ( getParam(args, 'border') or '#ababab'),
['vertical-align'] = 'top';
['vertical-align'] = 'top';
background = getParam(args, 'background') or '#fefeef',
background = getParam(args, 'background') or '#fefeef',
opacity = getParam(args, 'background-opacity') or '1',
opacity = getParam(args, 'background-opacity') or '1',
color = getParam(args, 'foreground') or '#000',
color = getParam(args, 'foreground') or '#000',
['text-align'] = getParam(args, 'text-align') or 'left',
['text-align'] = getParam(args, 'text-align') or 'left',
margin = '0 0 10px',
margin = '0 0 10px',
padding = getParam(args, 'padding') or '1em',
padding = getParam(args, 'padding') or '1em',
}
}
local tag = mw.html.create('div', {selfClosing = true})
local tag = mw.html.create('div', {selfClosing = true})
:css(baseStyle)
:css(baseStyle)
:css('border-top-width', ( getParam(args, 'border-top') or '1' ) .. 'px')
:css('border-top-width', ( getParam(args, 'border-top') or '1' ) .. 'px')
:css('padding-top', getParam(args, 'padding-top') or '.3em')
:css('padding-top', getParam(args, 'padding-top') or '.3em')
:css('-moz-border-radius', getParam(args, 'border-radius') or '0')
:css('-moz-border-radius', getParam(args, 'border-radius') or '0')
:css('-webkit-border-radius', getParam(args, 'border-radius') or '0')
:css('-webkit-border-radius', getParam(args, 'border-radius') or '0')
:css('border-radius', getParam(args, 'border-radius') or '0')
:css('border-radius', getParam(args, 'border-radius') or '0')
return toOpenTagString(tag)
return toOpenTagString(tag)
end
end


local function contrastCategories(args)
local function contrastCategories(args)
local cats = ''
local cats = ''


local titleText = nameToHexTriplet(getParam(args, 'titleforeground')) or normaliseHexTriplet(getParam(args, 'titleforeground')) or '#000000'
local titleText = nameToHexTriplet(getParam(args, 'titleforeground')) or normaliseHexTriplet(getParam(args, 'titleforeground')) or '#000000'
local titleBackground = nameToHexTriplet(getParam(args, 'titlebackground')) or normaliseHexTriplet(getParam(args, 'titlebackground')) or '#bcbcbc'
local titleBackground = nameToHexTriplet(getParam(args, 'titlebackground')) or normaliseHexTriplet(getParam(args, 'titlebackground')) or '#bcbcbc'
local titleContrast = colourContrastModule._ratio({titleBackground, titleText})
local titleContrast = colourContrastModule._ratio({titleBackground, titleText})
local insufficientTitleContrast = type(titleContrast) == 'number' and ( titleContrast < min_contrast_ratio_large_text )
local insufficientTitleContrast = type(titleContrast) == 'number' and ( titleContrast < min_contrast_ratio_large_text )


local bodyText = nameToHexTriplet(getParam(args, 'foreground')) or normaliseHexTriplet(getParam(args, 'foreground')) or '#000000'
local bodyText = nameToHexTriplet(getParam(args, 'foreground')) or normaliseHexTriplet(getParam(args, 'foreground')) or '#000000'
local bodyBackground = nameToHexTriplet(getParam(args, 'background')) or normaliseHexTriplet(getParam(args, 'background')) or '#fefeef'
local bodyBackground = nameToHexTriplet(getParam(args, 'background')) or normaliseHexTriplet(getParam(args, 'background')) or '#fefeef'
local bodyContrast =  colourContrastModule._ratio({bodyBackground, bodyText})
local bodyContrast =  colourContrastModule._ratio({bodyBackground, bodyText})
local insufficientBodyContrast = type(bodyContrast) == 'number' and ( bodyContrast < min_contrast_ratio_normal_text )
local insufficientBodyContrast = type(bodyContrast) == 'number' and ( bodyContrast < min_contrast_ratio_normal_text )


if insufficientTitleContrast and insufficientBodyContrast then
if insufficientTitleContrast and insufficientBodyContrast then
return '[[Category:Box-header with insufficient title contrast]][[Category:Box-header with insufficient body contrast]]'
return '[[Category:Box-header with insufficient title contrast]][[Category:Box-header with insufficient body contrast]]'
elseif insufficientTitleContrast then
elseif insufficientTitleContrast then
return '[[Category:Box-header with insufficient title contrast]]'
return '[[Category:Box-header with insufficient title contrast]]'
elseif insufficientBodyContrast then
elseif insufficientBodyContrast then
return '[[Category:Box-header with insufficient body contrast]]'
return '[[Category:Box-header with insufficient body contrast]]'
else
else
return ''
return ''
end
end
end
end


Line 377: Line 660:


-- Entry point for templates (manually-specified colours)
-- Entry point for templates (manually-specified colours)
function p.boxHeader(frame)
function p.boxHeader(frame)
local parent = frame.getParent(frame)
local parent = frame.getParent(frame)
local parentArgs = parent.args
local parentArgs = parent.args
local page = parentArgs.editpage
local page = parentArgs.editpage
if not parentArgs.editpage or parentArgs.editpage == '' then
if not parentArgs.editpage or parentArgs.editpage == '' then
page = parent:preprocess('{{FULLPAGENAME}}')
page = parent:preprocess('{{FULLPAGENAME}}')
end
end
local output = p._boxHeader(parentArgs, page)
local output = p._boxHeader(parentArgs, page)
if mw.ustring.find(output, '{') then
if mw.ustring.find(output, '{') then
return frame:preprocess(output)
return frame:preprocess(output)
end
end
return output
return output
end
end


-- Entry point for modules (manually-specified colours)
-- Entry point for modules (manually-specified colours)
function p._boxHeader(_args, page)
function p._boxHeader(_args, page)
local args = setCleanArgs(_args)
local args = setCleanArgs(_args)
if page and not args.editpage then
if page and not args.editpage then
args.editpage = page
args.editpage = page
end
end
if not args.title then
if not args.title then
args.title = '{{{title}}}'
args.title = '{{{title}}}'
end
end
local output = {}
local output = {}
table.insert(output, boxHeaderOuter(args))
table.insert(output, boxHeaderOuter(args))
if not getParam(args, 'EDITLINK') then
if not getParam(args, 'EDITLINK') then
table.insert(output, boxHeaderTopLinks(args))
table.insert(output, boxHeaderTopLinks(args))
if not getParam(args, 'noedit') then
if not getParam(args, 'noedit') then
table.insert(output, boxHeaderEditLink(args))
table.insert(output, boxHeaderEditLink(args))
end
end
if getParam(args, 'viewpage') then
if getParam(args, 'viewpage') then
table.insert(output, boxHeaderViewLink(args))
table.insert(output, boxHeaderViewLink(args))
end
end
if getParam(args, 'top') then
if getParam(args, 'top') then
table.insert(output, getParam(args, 'top') .. '&nbsp;')
table.insert(output, getParam(args, 'top') .. '&nbsp;')
end
end
table.insert(output, '</div>')
table.insert(output, '</div>')
end
end
table.insert(output, boxHeaderTitle(args))
table.insert(output, boxHeaderTitle(args))
table.insert(output, '</div>')
table.insert(output, '</div>')
table.insert(output, boxBody(args))
table.insert(output, boxBody(args))
if not getParam(args, 'TOC') then
if not getParam(args, 'TOC') then
table.insert(output, '__NOTOC__')
table.insert(output, '__NOTOC__')
end
end
if not getParam(args, 'EDIT') then
if not getParam(args, 'EDIT') then
table.insert(output, '__NOEDITSECTION__')
table.insert(output, '__NOEDITSECTION__')
end
end
table.insert(output, contrastCategories(args))
table.insert(output, contrastCategories(args))


return table.concat(output)
return table.concat(output)
end
end


-- Entry point for templates (automatically calculated colours)
-- Entry point for templates (automatically calculated colours)
function p.autoColour(frame)
function p.autoColour(frame)
local parent = frame.getParent(frame)
local parent = frame.getParent(frame)
local parentArgs = parent.args
local parentArgs = parent.args
local colourParam = getParam(parentArgs, 'colour')
local colourParam = getParam(parentArgs, 'colour')
local generatedColour = nil
local generatedColour = nil
if not colourParam or colourParam == '' then
if not colourParam or colourParam == '' then
-- convert the root page name into a number and use that
-- convert the root page name into a number and use that
local root = parent:preprocess('{{ROOTPAGENAME}}')
local root = parent:preprocess('{{ROOTPAGENAME}}')
local rootStart = mw.ustring.sub(root, 1, 12)
local rootStart = mw.ustring.sub(root, 1, 12)
local digitsFromRootStart = mw.ustring.gsub(rootStart, ".", function(s) return math.fmod(string.byte(s, 2) or string.byte(s, 1), 10) end)
local digitsFromRootStart = mw.ustring.gsub(rootStart, ".", function(s) return math.fmod(string.byte(s, 2) or string.byte(s, 1), 10) end)
local numberFromRoot = tonumber(digitsFromRootStart, 10)
local numberFromRoot = tonumber(digitsFromRootStart, 10)
generatedColour = math.fmod(numberFromRoot, 360)
generatedColour = math.fmod(numberFromRoot, 360)
end
end
local output = p._autoColour(parent.args, generatedColour)
local output = p._autoColour(parent.args, generatedColour)
if mw.ustring.find(output, '{') then
if mw.ustring.find(output, '{') then
return frame:preprocess(output)
return frame:preprocess(output)
end
end
return output
return output
end
end


-- Entry point for modules (automatically calculated colours)
-- Entry point for modules (automatically calculated colours)
function p._autoColour(_args, generatedColour)
function p._autoColour(_args, generatedColour)
local args = setCleanArgs(_args)
local args = setCleanArgs(_args)
local hue = generatedColour or findHue(getParam(args, 'colour'))
local hue = generatedColour or findHue(getParam(args, 'colour'))
local mode = normaliseMode(getParam(args, 'mode'))
local mode = normaliseMode(getParam(args, 'mode'))
local border, titleForeground, titleBackground, contentForeground, contentBackground = makeColours(hue, mode)
local border, titleForeground, titleBackground, contentForeground, contentBackground = makeColours(hue, mode)
local boxTemplateArgs = mergeTables(args, {
local boxTemplateArgs = mergeTables(args, {
title = getParam(args, '1') or '{{{1}}}',
title = getParam(args, '1') or '{{{1}}}',
editpage = getParam(args, '2') or '',
editpage = getParam(args, '2') or '',
noedit = getParam(args, '2') and '' or 'yes',
noedit = getParam(args, '2') and '' or 'yes',
border = border,
border = border,
titleforeground = titleForeground,
titleforeground = titleForeground,
titlebackground = titleBackground,
titlebackground = titleBackground,
foreground = contentForeground,
foreground = contentForeground,
background = contentBackground
background = contentBackground
})
})
return p._boxHeader(boxTemplateArgs)
return p._boxHeader(boxTemplateArgs)
end
end
return p
return p

Navigation menu