local getArgs = require('Module:Arguments').getArgs
local p = ---------- Config data ----------local namedColours = mw.loadData('Module:Box-header/colours')local modes = local min_contrast_ratio_normal_text = 7 -- i.e 7:1local min_contrast_ratio_large_text = 4.5 -- i.e. 4.5:1
-- Template parameter aliases-- Specify each as either a single value, or a table of values-- Aliases are checked left-to-right, i.e. `['one'] = ` is equivalent to using `` in a templatelocal parameterAliases =
---------- Dependecies ----------local colourContrastModule = require('Module:Color contrast')local hex = require('luabit.hex')
---------- Utility functions ----------local function getParam(args, parameter) if args[parameter] then return args[parameter] end local aliases = parameterAliases[parameter] if not aliases then return nil end if type(aliases) ~= 'table' then return args[aliases] end for _, alias in ipairs(aliases) do if args[alias] then return args[alias] end end return nilend
local function setCleanArgs(argsTable) local cleanArgs = for key, val in pairs(argsTable) do if type(val)
-- 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 merged = for key, val in pairs(first) do merged[key] = val end for key, val in pairs(second) do merged[key] = val end return mergedend
local function toOpenTagString(selfClosedHtmlObject) local closedTagString = tostring(selfClosedHtmlObject) local openTagString = mw.ustring.gsub(closedTagString, ' />$', '>') return openTagStringend
local function normaliseHexTriplet(hexString) 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)$') if hexComponent and #hexComponent
3 then 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 b = mw.ustring.rep(mw.ustring.sub(hexComponent, 3, 3), 2) return '#' .. mw.ustring.upper(r .. g .. b) end return nilend
---------- Conversions ----------local function decimalToPaddedHex(number) local prefixedHex = hex.to_hex(tonumber(number)) -- prefixed with '0x' local padding = #prefixedHex
local R_255 = math.floor(R*255) local G_255 = math.floor(G*255) local B_255 = math.floor(B*255) return R_255, G_255, B_255endlocal function RGBtoHue(R_255, G_255, B_255) -- per HSL and HSV#Hue and chroma local R = R_255/255 local G = G_255/255 local B = B_255/255
local M = math.max(R, G, B) local m = math.min(R, G, B) local C = M - m local H_prime if C
R then H_prime = math.fmod(((G - B)/C + 6), 6) -- adding six before taking mod ensures positive value elseif M
B then H_prime = (R - G)/C + 4 end local H = 60 * H_prime return Hendlocal function nameToHexTriplet(name) if not name then return nil end local codename = mw.ustring.gsub(mw.ustring.lower(name), ' ', ) return namedColours[codename]end
---------- Choose colours ----------local function calculateColours(H, S, V, minContrast) local bgColour = RGBtoHexTriplet(HSVtoRGB(H, S, V)) local textColour = colourContrastModule._greatercontrast local contrast = colourContrastModule._ratio if contrast >= minContrast then return bgColour, textColour elseif textColour
local function makeColours(hue, modeName) local mode = modes[modeName] local isGrey = not(hue) if isGrey then hue = 0 end
local borderSat = isGrey and modes.grey.sat or 0.15 local border = RGBtoHexTriplet(HSVtoRGB(hue, borderSat, 0.75))
local titleSat = isGrey and modes.grey.sat or mode.sat 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 contentBackground, contentForeground = calculateColours(hue, contentSat, modes.content.val, min_contrast_ratio_normal_text)
return border, titleForeground, titleBackground, contentForeground, contentBackgroundend
local function findHue(colour) local colourAsNumber = tonumber(colour) if colourAsNumber and (-1 < colourAsNumber) and (colourAsNumber < 360) then return colourAsNumber end
local colourAsHexTriplet = normaliseHexTriplet(colour) or nameToHexTriplet(colour) if colourAsHexTriplet then return RGBtoHue(hexTripletToRGB(colourAsHexTriplet)) end
return nullend
local function normaliseMode(mode) if not mode or not modes[mw.ustring.lower(mode)] or mw.ustring.lower(mode)
local tag = mw.html.create('div',) :addClass('box-header-title-container') :addClass('flex-columns-noflex') :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('padding-top', getParam(args, 'padding-top') or '.1em') :css('padding-left', getParam(args, 'padding-left') or '.1em') :css('padding-right', getParam(args, 'padding-right') or '.1em') :css('padding-bottom', getParam(args, 'padding-bottom') or '.1em') :css('moz-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') return toOpenTagString(tag)end
local function boxHeaderTopLinks(args) local style = local tag = mw.html.create('div',) :addClass('plainlinks noprint') :css(style) return toOpenTagString(tag)end
local function boxHeaderEditLink(args) local page = getParam(args, 'editpage') if not page or page
local function boxHeaderViewLink(args) local style = local tag = mw.html.create('span') :css(style) :wikitext('view') local linktext = tostring(tag) local linktarget = ':' .. getParam(args, 'viewpage') return "ยท ' .. linktext .. ' 'end
local function boxHeaderTitle(args) local baseStyle = local tagName = getParam(args, 'SPAN') and 'span' or 'h2' local tag = mw.html.create(tagName) :css(baseStyle) :css('padding-bottom', '.1em') :wikitext(getParam(args, 'title')) if getParam(args, 'extra') then local rules = mw.text.split(getParam(args, 'extra'), ';', true) for _, rule in pairs(rules) do local parts = mw.text.split(rule, ':', true) local prop = parts[1] local val = parts[2] if prop and val then tag:css(prop, val) end end end return tostring(tag)end
local function boxBody(args) local baseStyle = local tag = mw.html.create('div',) :css(baseStyle) :css('border-top-width', (getParam(args, 'border-top') or '1') .. 'px') :css('padding-top', getParam(args, 'padding-top') or '.3em') :css('border-radius', getParam(args, 'border-radius') or '0') return toOpenTagString(tag)end
local function contrastCategories(args) local cats =
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 titleContrast = colourContrastModule._ratio local insufficientTitleContrast = type(titleContrast)
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 bodyContrast = colourContrastModule._ratio local insufficientBodyContrast = type(bodyContrast)
if insufficientTitleContrast and insufficientBodyContrast then return '' elseif insufficientTitleContrast then return '' elseif insufficientBodyContrast then return '' else return endend
---------- Main functions / entry points ----------
-- Entry point for templates (manually-specified colours)function p.boxHeader(frame) local args = getArgs(frame) local page = args.editpage if not args.editpage or args.editpage