Bureaucrats, Moderators (CommentStreams), Interface administrators, Push subscription managers, Suppressors, Administrators
4,066
edits
>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: | ||
} | } | ||
---------- | ---------- 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}) | |||
: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 .. '] ' | return '[' .. linktarget .. ' ' .. linktext .. '] ' | ||
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> [[" .. linktarget .. '|' .. linktext .. ']] ' | return "<b>·</b> [[" .. linktarget .. '|' .. linktext .. ']] ' | ||
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') .. ' ') | table.insert(output, getParam(args, 'top') .. ' ') | ||
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 |