Mô đun:Box-header

local getArgs = require('Mô đun:Arguments').getArgs
local p = {}
---------- Config data ----------
local namedColours = mw.loadData( 'Mô đun: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. 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' = { 'two', 'three' }` is equivalent to using `` in a template
local parameterAliases = {
	'1' = 1,
	'2' = 2,
	'colour' = 'color'
}
---------- Dependecies ----------
local colourContrastModule = require('Mô đun:Color contrast')
local hex = require( 'luabit.hex' )
---------- Utility functions ----------
local function getParam(args, parameter)
	if argsparameter then
		return argsparameter
	end
	local aliases = parameterAliasesparameter
	if not aliases then
		return nil
	end
	if type(aliases) ~= 'table' then
		return argsaliases
	end
	for _, alias in ipairs(aliases) do
		if argsalias then
			return argsalias
		end
	end
	return nil
end
local function setCleanArgs(argsTable)
	local cleanArgs = {}
	for key, val in pairs(argsTable) do
		if type(val) == 'string' then
			val = val:match('^%s*(.-)%s*$')
			if val ~=  then
				cleanArgskey = val
			end
		else
			cleanArgskey = val
		end
	end
	return cleanArgs
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.
local function mergeTables(first, second)
	local merged = {}
	for key, val in pairs(first) do
		mergedkey = val
	end
	for key, val in pairs(second) do
		mergedkey = val
	end
	return merged
end
local function toOpenTagString(selfClosedHtmlObject)
	local closedTagString = tostring(selfClosedHtmlObject)
	local openTagString = mw.ustring.gsub(closedTagString, ' />$', '>')
	return openTagString
end
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 == 6 then
		return mw.ustring.upper(hexString)
	end
	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 nil
end
---------- Conversions ----------
local function decimalToPaddedHex(number)
	local prefixedHex = hex.to_hex(tonumber(number)) -- prefixed with '0x'
	local padding =  #prefixedHex == 3 and '0' or  
	return mw.ustring.gsub(prefixedHex, '0x', padding)
end
local function hexToDecimal(hexNumber)
	return tonumber(hexNumber, 16)
end
local function RGBtoHexTriplet(R, G, B)
	return '#' .. decimalToPaddedHex(R) .. decimalToPaddedHex(G) .. decimalToPaddedHex(B)
end
local function hexTripletToRGB(hexTriplet)
	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)
end
local function HSVtoRGB(H, S, V) -- per HSL and HSV#Converting_to_RGB
	local C = V * S
	local H_prime = H / 60
	local X = C * ( 1 - math.abs(math.fmod(H_prime, 2) - 1) )
	local R1, G1, B1
	if H_prime = minContrast then
		return bgColour, textColour
	elseif textColour == '#FFFFFF' then
		-- 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)
	else
		-- 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)
	end
end
local function makeColours(hue, modeName)
	local mode = modesmodeName
	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, contentBackground
end
local function findHue(colour)
	local colourAsNumber = tonumber(colour)
	if colourAsNumber and ( -1 · ' .. linktext .. ' '
end
local function boxHeaderTitle(args)
	local baseStyle = {
		'font-family' = getParam(args, 'title-font-family') or 'sans-serif',
		'font-size' = getParam(args, 'title-font-size') or '100%',
		'font-weight' = getParam(args, 'title-font-weight') or 'bold',
		border = 'none',
		margin = '0',
		padding = '0',
		color = getParam(args, 'titleforeground') or '#000';
	}
	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 = parts1
			local val = parts2
			if prop and val then
				tag:css(prop, val)
			end
		end
	end
	return tostring(tag)
end
local function boxBody(args)
	local baseStyle = {
		'box-sizing' = 'border-box',
		border = ( getParam(args, 'border-width') or '1' ) .. 'px solid ' .. ( getParam(args, 'border') or '#ababab'),
		'vertical-align' = 'top';
		background = getParam(args, 'background') or '#fefeef',
		opacity = getParam(args, 'background-opacity') or '1',
		color = getParam(args, 'foreground') or '#000',
		'text-align' = getParam(args, 'text-align') or 'left',
		margin = '0 0 10px',
		padding = getParam(args, 'padding') or '1em',
	}
	local tag = mw.html.create('div', {selfClosing = true})
		: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({titleBackground, titleText})
	local insufficientTitleContrast = type(titleContrast) == 'number' and ( titleContrast ')
	end
	table.insert(output, boxHeaderTitle(args))
	table.insert(output, )
	table.insert(output, boxBody(args))
	if not getParam(args, 'TOC') then
		table.insert(output, '__NOTOC__')
	end
	if not getParam(args, 'EDIT') then
		table.insert(output, '__NOEDITSECTION__')
	end
	table.insert(output, contrastCategories(args))
	return table.concat(output)
end
-- Entry point for templates (automatically calculated colours)
function p.autoColour(frame)
	local args = getArgs(frame)
	local colourParam = getParam(args, 'colour')
	local generatedColour = nil
	if not colourParam or colourParam ==  then
		-- convert the root page name into a number and use that
		local root = mw.title.getCurrentTitle().rootPageTitle.prefixedText
		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 numberFromRoot = tonumber(digitsFromRootStart, 10)
		generatedColour = math.fmod(numberFromRoot, 360)
	end
	local output = p._autoColour(args, generatedColour)
	if mw.ustring.find(output, '{') then
		return frame:preprocess(output)
	end
	return output
end
-- Entry point for modules (automatically calculated colours)
function p._autoColour(_args, generatedColour)
	local args = setCleanArgs(_args)
	local hue = generatedColour or findHue(getParam(args, 'colour'))
	local mode = normaliseMode(getParam(args, 'mode'))
	local border, titleForeground, titleBackground, contentForeground, contentBackground = makeColours(hue, mode)
	local boxTemplateArgs = mergeTables(args, {
		title = getParam(args, '1') or ,
		editpage = getParam(args, '2') or ,
		noedit = getParam(args, '2') and  or 'yes',
		border = border,
		titleforeground = titleForeground,
		titlebackground = titleBackground,
		foreground = contentForeground,
		background = contentBackground
	})
	return p._boxHeader(boxTemplateArgs)
end
	
return p