Module:Tincture

local p = {} -- Tincture

-- locals local function draw(color, ss, tc, pf, gt) return mw.getCurrentFrame:expandTemplate{ title = 'Tincture/draw' .. ss, args = { color, gt, tc = tc, pf = pf } } end -- local function draw -- local function sortc ( itab, otab, ccode ) local tinct = ccode; if	tinct == 'A'	then tinct = 'a';	end	-- in categories treat argent-dark like argent (2023-05-10) if tinct == 'l' 	and ss == 'CH'	then tinct = 'b';	end		-- for i, v in ipairs(itab) do		if v == ccode then table.insert(otab, tinct) return true end end return false end -- local function sortc -- local function category(colors, ss, tc, cat, no_error_cat) return mw.getCurrentFrame:expandTemplate{ title = 'Tincture/cat' .. ss, args = { table.concat(colors, '/'), tc = tc, cat = cat, ['no error cat'] = no_error_cat and '1' or nil } } end -- local function category -- local function paletcat(ss, cat) return mw.getCurrentFrame:expandTemplate{ title = 'Tincture/catP', args = { ss, cat = cat } } end -- local function p-category -- local function pnotspec local exp = ''; if	mw.ustring.lower(mw.ustring.sub(mw.title.getCurrentTitle.text,-4)) ~= '.svg' then exp = '_(nonSVG)'; end return mw.getCurrentFrame:expandTemplate{ title = 'Igen/cat', args = {'Tincture palette not specified'..exp } } end -- local function pnotspec -- local function defnonsvg return mw.getCurrentFrame:expandTemplate{ title = 'Igen/cat', args = {'Tinctures (Defined nonSVG)' } } end -- local function defnonsvg -- local function tnotspec local exp = ''; if	mw.ustring.lower(mw.ustring.sub(mw.title.getCurrentTitle.text,-4)) ~= '.svg' then exp = '_(nonSVG)'; end --	return mw.getCurrentFrame:expandTemplate{ title = 'Igen/cat', args = {'Tinctures not specified'..exp } } return mw.getCurrentFrame:expandTemplate{ title = 'Igen/cat', args = {'No tincture specified'..exp } } end -- local function tnotspec -- local function tsuppress local exp = ''; --	if	mw.ustring.lower(mw.ustring.sub(mw.title.getCurrentTitle.text,-4)) ~= '.svg' then --		exp = '_(nonSVG)'; --	end return mw.getCurrentFrame:expandTemplate{ title = 'Igen/cat', args = {'Tincture categorizing suppressed'..exp } } end -- local function t-suppress -- local function tinc_cat(colors) return mw.getCurrentFrame:expandTemplate{ title = 'Tincture/cat0', args = { table.concat(colors, '/') } } end -- local function t-category -- local function unab_cat(colors)			--	colors for catsort ? return mw.getCurrentFrame:expandTemplate{ title = 'Igen/cat', args = {'Tinctures uncategorizeable' } } end -- local function unable to categorize

-- local function: converts 'ddd ddd ddd' to  '#rrggbb' (or '#rgb') local function convdh ( p1, p2, p3 ) local lpar = {}; -- separated by either pipe, slash, minus, comma or space local dect = {} local hext = {} local numb = {} local same = true lpar [1] = mw.text.trim ( p1 ); if p3 == nil then				 -- split-pattern: REGEXP won't work if p2 ~= nil then lpar[1] = lpar[1] .. '-' .. mw.text.trim(p2) end lpar[1] = mw.ustring.gsub (lpar[1], '-', '/', 3) lpar[1] = mw.ustring.gsub (lpar[1], ',', '/', 3) lpar[1] = mw.ustring.gsub (lpar[1], ' ', '/', 3) dect = mw.text.split(lpar[1] or '1/2/3', '/'); else lpar [2] = mw.text.trim ( p2 ); lpar [3] = mw.text.trim ( p3 ); dect = lpar; end for i = 1, 3 do		numb[i] = tonumber( dect[i] ) if numb[i] == nil or numb[i] > 255 then error (i .. 'value "' .. dect[i] or '?' .. '" cannot be converted to hexadecimal') --				..dect[1]..','..dect[2]..','..dect[3]..'.'..i)		end		hext [i] = mw.ustring.format ( "%X", numb[i] )		if numb[i] < 16 then hext[i] = '0' .. hext[i] end;		if mw.ustring.byte ( hext [i], 1 ) ~= mw.ustring.byte ( hext[i], 2 )  then			same = false		end	end	if same then 		hext[1] = mw.ustring.sub (hext[1], 2)		hext[2] = mw.ustring.sub (hext[2], 2)		hext[3] = mw.ustring.sub (hext[3], 2)	end	return '#'..hext[1]..hext[2]..hext[3] end -- local function convdh

-- local function cats ( titl, outs, tinc ) if mw.ustring.find (titl, tinc .. ',') or mw.ustring.find (titl, tinc .. ' ') then outs = outs .. ', ' .. tinc; end return outs; end -- local function cats

local / global --``--- -- local / global function: converts h → d: #rrggbb or #rgb to table {rr, gg, bb} function p.convht ( frame ) local gpar = {}; local hexv = nil; local hexi = '000'; local hwxt = '0'; local gpar = frame.args; if gpar then hexv = tostring (gpar[1]);	-- global else hexv = tostring ( frame );	-- local end

hexv = mw.text.trim( hexv ) if mw.ustring.sub ( hexv, 1, 1 ) == '#' then hexi = mw.ustring.sub (hexv, 2) hext = '1' elseif mw.ustring.sub ( hexv, 1, 3) == '\\35' then hexi = mw.ustring.sub (hexv, 4) hext = '2' elseif mw.ustring.sub ( hexv, 1, 5) == '&#35;' then hexi = mw.ustring.sub (hexv, 6) hext = '3' elseif mw.ustring.sub ( hexv, 1, 6) == '&#035;' then hexi = mw.ustring.sub (hexv, 7) hext = '4' elseif mw.ustring.sub ( hexv, 1, 6) == '&#x23;' then hexi = mw.ustring.sub (hexv, 7, #hexv) hext = '5' elseif mw.ustring.sub ( hexv, 1, 7) == '&#x023;' then hexi = mw.ustring.sub (hexv, 8, #hexv) hext = '6' else hext = '9' error ('value "' .. hexv .. '" cannot be converted to decimal ' .. #hexv ) end if #hexi ~= 3 and #hexi ~= 6 then error ('value "' .. hexi .. '" with length ' .. #hexi .. ' are invalid' ) end --	error ('value "' .. hexv .. '" type ' .. hext .. ' = ' .. hexi) local dec =  {}; for i = 1, 3 do		if #hexi == 3 then dec [i] = tonumber ( mw.ustring.sub (hexi, i, i)..mw.ustring.sub (hexi, i, i), 16 ) else dec [i] = tonumber ( mw.ustring.sub (hexi, 2*i - 1, 2*i), 16 ) end end return dec; end -- function convht

-- local / global function: converts h ← d '#rrggbb' or '#rgb' and returns 'ddd ddd ddd' function p.convhd ( frame ) local gpar = frame.args; if gpar then decval = p.convht (gpar[1]);	-- global else	 decval = p.convht ( frame );	-- local end return decval[1]..' '..decval[2]..' '..decval[3] end -- function convhd

-- local / global function: converts h → d'#rrggbb' or '#rgb' and returns 'ddd ddd ddd' formatted function p.convhdf ( frame ) local gpar = frame.args; if gpar then dtab = p.convht (gpar[1]);	-- global else dtab = p.convht ( frame );	-- local end local dtxt = ' ' for i = 1, 3 do		if dtab[i] < 100 then if dtab[i] < 10 then dtxt = dtxt .. ' '			end dtxt = dtxt .. ' '		end dtxt = dtxt .. ' ' .. tostring ( dtab [i] ) end local contrast = '0'; if dtab [1] + dtab [2] + dtab [3] < 400 then contrast = 'F'; end -- TEST -- --	local contval = dtab [1] + dtab [2] + dtab [3] -- 	contrast = contrast .. ' - '; --	if contval < 100 then --		if contval < 10 then --			contrast = contrast .. ' ' --		end --		contrast = contrast .. ' ' --	end -- 	contrast = contrast .. tostring ( contval ); -- TEST -- dtxt = contrast .. dtxt; return dtxt end -- function convhdf

-- global function returns contrast color function p.titcolor ( frame ) local gpar = frame.args local decval = p.convhdf ( gpar[1] ); if mw.ustring.sub ( decval, 1, 1 ) == 'F'		then return 'FFF' else return '000' end end -- function titcolor - does not work perfectly

-- global function tbcbox: returns a Tbc box function p.tbcbox ( frame ) local gpar = frame.args local hstr = convdh ( gpar[1], gpar[2], gpar[3] ) return frame:expandTemplate { title = 'colorbox', args = { hstr, title = '"' .. hstr ..'"' } } end -- function tbxbox

-- global function convgpl: gets #rgb, contrast, name; returns line formatted function p.convgpl ( frame ) local gpar = frame.args; local line = p.convhdf ( gpar [1] );						-- convert #rgb local expl = mw.ustring.sub ( line, 2) .. ' ' .. gpar [1];	-- d d d  #rgb if #gpar[1] == 4 then expl = expl .. '  '	end local contrast = gpar [2] or '#001' expl = expl .. ' '				--	.. contrast;								-- contrast		-test --	if mw.ustring.sub ( contrast, 2, 2) == mw.ustring.sub ( line, 1, 1) --		then	expl = expl .. ' '	--	'' --		else	expl = expl .. '·'	--	'·'' --	end expl = expl .. gpar [3];									-- name return expl end -- function convgpl

-- global function convert: gets 3 num, returns hex and dec formatted function p.convert ( frame ) local gpar = frame.args; local hcod = convdh ( gpar[1], gpar[2], gpar[3] ); local fnum = p.convhdf (hcod) return '&#35;' .. mw.ustring.sub ( hcod, 2 ) .. mw.ustring.sub ( fnum, 2); end -- function convert

-- ============================================================================ --  ============================================================================ --  main function tincture function p.main (frame) local getArgs = require( 'Module:Arguments' ).getArgs local args = getArgs(frame) ss = args.ss or '0' if ss == '≈' then ss = '0' end local top = '' if args.s == 'f' then top = 'flag ' end local cm = args.cm or ' '; local tc = args.tc	local pf = args.pf	local gt = args.gpltab or "" local align = 'tincturebox-left' local InFi = (args['+'] == '+') local cols = args local tincunt = 0; local colors = {} local box = {} local tab = {} local out = {} local ordtab = { } --	local ssytab = { ' ', }	--	table of all palettes: catyes --	local ssntab = { '0', }	--	table of all palettes: catnot local gpltab = false local insert = false local gennot = false		--	generell  = don't t-cat local genyes = true			--	generell  = force t-cat local catnot = false		--	indiv. '-' = don't t-cat local catyes = false		--	indiv. '*' = force t-cat local catimp = false		--	indiv. '?' = not t-categorizeable local nopcat = false		--	indiv. '-' = don't p-cat if gt == "" then if mw.title.getCurrentTitle.namespace == 4 then gt = '3' else gt = '1' end end if gt == "2" or gt == "3" then InFi  = true gpltab = true end if cols.ss	then ss = cols.ss 	end

if type(args[1]) == 'string' and args[1]:sub(1, 6) == '<table' then return args[1] end

if args[2] == nil then cols = mw.text.split(args[1] or '', '%s*/%s*') end for _, v in ipairs(cols) do		if not v or v == '' then break			--	empty par elseif v == '-' then v = '0'		--  t-cat suppression catnot = true elseif v == '~' then v = '0'		--  p-cat suppression nopcat = true elseif v == '*' then v = '0'		--  t-cat forcing catyes = true elseif v == '?' then v = '0'		--  not t-catable catimp = true elseif v == '+' then InFi = true elseif mw.ustring.sub( v, 1, 3 ) == 'ss=' then -- any case when ss= ss = mw.ustring.sub( v, 4 ) elseif mw.ustring.len( v ) >= 2 and v == mw.ustring.upper( v ) then --	belongs to character class %u ss = v							--	tincture palette else table.insert(colors, v)				--	it's a tincture tincunt = tincunt + 1 end end

ss = mw.ustring.upper( ss )					--	final formatting general yes/not depending on ss --	for i = 1, #ssytab do			--	force the specified palette ? --		if ss == ssytab [i] then genyes = true; end --	end --	for i = 1, #ssntab do			--	exclude the specified palette ? --		if ss == ssntab [i] then gennot = true; end --	end if	args.el == "y" then gennot = true; end	--	exclude coa elements --				indiv. precedes gener. if	catnot == false	then if	catyes == false	then				--	expl. indiv. "yes" ? catnot = gennot end end if	catyes == false	then catyes = genyes end

-- 0) headline and boxes	if gt == "2" or gt =="3" then		table.insert(out, frame:expandTemplate{title='=', args={' GPLtab '..draw('gpltabnam',ss,,,'tab')..' '}})	end	for i, v in ipairs(colors) do		box[i] = draw(v, ss, tc, pf, 'box')	end

-- 1) categories: p_cat, t_cat	if	gpltab == false					--	either palette or tincture	and args.ns == "6" then					if	nopcat ~= true	then		--	when not suppressed:			if	ss ~= '0'	then		--	Palette def	(^GN)				table.insert(box, paletcat( ss, args.cat ))				if	mw.ustring.lower(mw.ustring.sub(mw.title.getCurrentTitle.text,-4)) ~= '.svg' then						table.insert(box, defnonsvg )				end			else						--	Palette not specified				if	args.el ~= "y" 				and	catimp	== 	false 				and	tincunt ~= 0	then					table.insert(box, pnotspec )				end			end		end										--	Tincture categorizaton		if	catnot == true then  		--	suppressed ?			if args.el ~= "y" then		--	when not an element				table.insert(box, tsuppress )		--	categorization suppressed			end		else			if	catimp == true then 	--	cat impossible ?				table.insert(box, unab_cat )		--	categorizationt unable			elseif tincunt > 0 then		--	sorted table				insort = sortc (colors, ordtab, 'a' ) insort = sortc (colors, ordtab, 'A' ) insort = sortc (colors, ordtab, 'o' ) insort = sortc (colors, ordtab, 'b' ) insort = sortc (colors, ordtab, 'B' ) insort = sortc (colors, ordtab, 'l' )	--	l-azure (CH) insort = sortc (colors, ordtab, 'c' ) insort = sortc (colors, ordtab, 'C' ) insort = sortc (colors, ordtab, 'e' )	--	(h)ermine insort = sortc (colors, ordtab, 'g' ) insort = sortc (colors, ordtab, 'm' )	--	multicolor insort = sortc (colors, ordtab, 'n' ) insort = sortc (colors, ordtab, 'p' ) insort = sortc (colors, ordtab, 's' ) insort = sortc (colors, ordtab, 't' ) insort = sortc (colors, ordtab, 'T' ) insort = sortc (colors, ordtab, 'v' ) insort = sortc (colors, ordtab, 'V' )	--	vair, fur insort = sortc (colors, ordtab, 'x' ) table.insert(box, tinc_cat(ordtab))	--	std tincture cat else						--	tincunt	= zero: table.insert(box, tnotspec ) end							--	catimp false & tincunt > 0 end								--	catnot false end									--	gpltab false

-- 2) box	if gt == "1" or gt =="3" then		if args.align == 'right' or args.align == 'center' then			align = 'tincturebox-' .. align		end		local frame = mw.getCurrentFrame		text = frame:extensionTag('templatestyles', '', { src = 'Tincture/styles.css' }) ..			' ' .. table.concat(box) .. cm .. ' '		if InFi then			local name = mw.getContentLanguage:ucfirst(frame:expandTemplate{ title = 'I18n/COA', args = { top .. 'tincture' } })			local link = ' ('..ss..') '			if ss ~= '' and ss ~= '0' then name = name .. link end 			table.insert(out, frame:expandTemplate{ title = 'InFi', args = { name, text } })		else			table.insert(out, text )		end	end -- 3) tab if gt == "2" or gt =="3" then table.insert(out, frame:expandTemplate{ title = '=', args = { "&#35; " } }) for i, v in ipairs(colors) do			table.insert(out, draw( v, ss, tc, pf, 'tab') ) end table.insert(out, frame:expandTemplate{ title = '=', args = { " " } }) end return table.concat(out) end -- function main / tincture

-- main function: create sorted tincture categories function p.cstc (frame) local titl = mw.ustring.lower( mw.title.getCurrentTitle.text ) local outs = '' outs = cats (titl, outs, 'argent' ) outs = cats (titl, outs, 'argent-dark' ) outs = cats (titl, outs, 'or' ) outs = cats (titl, outs, 'azure' ) outs = cats (titl, outs, 'brunâtre' )	-- + outs = cats (titl, outs, 'céleste' ) outs = cats (titl, outs, 'carnation' ) outs = cats (titl, outs, 'cendrée' ) outs = cats (titl, outs, 'ermine' ) outs = cats (titl, outs, 'gules' ) --	outs = cats (titl, outs, 'multiple' ) outs = cats (titl, outs, 'murrey' )		-- + outs = cats (titl, outs, 'naranja' ) outs = cats (titl, outs, 'orange_t' )	-- + + outs = cats (titl, outs, 'purpure' ) outs = cats (titl, outs, 'sable' ) outs = cats (titl, outs, 'sanguine' )	-- + --	outs = cats (titl, outs, 'tawny' )		-- +? outs = cats (titl, outs, 'tenné' ) outs = cats (titl, outs, 'vert' ) outs = cats (titl, outs, 'vair' ) outs = cats (titl, outs, 'invisible' ) outs = mw.ustring.upper( mw.ustring.sub( outs, 3, 3 ) ) .. mw.ustring.sub( outs, 4 ) .. ' in heraldry' if mw.ustring.gsub( outs, ', ', '? ' ) == mw.ustring.gsub( outs, ', ', '? ', 1 ) then outs = mw.ustring.gsub( outs, ', ', ' and ' );	-- "and" when only two colors end return outs end -- cstc

return p;