Module:Tincture

From The FunKey Wiki

Documentation for this module may be created at Module:Tincture/doc

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 )
	for i, v in ipairs(itab) do
		if v == ccode then 
			table.insert(otab, v)
			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: 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 / 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 = '&nbsp;'
	for i = 1, 3 do
		if dtab[i] < 100 then
			if dtab[i] < 10 then 
				dtxt = dtxt .. ' '
			end
			dtxt = dtxt .. '&nbsp;'
		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 .. '&nbsp;'
--		end
--		contrast = contrast .. '&nbsp;'
--	end
-- 	contrast = contrast .. tostring ( contval ); 
-- TEST ----------
	dtxt = contrast .. dtxt; 
	return dtxt
end -- function convhdf

-- 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 

-- 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 .. '&nbsp; &nbsp;'
	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 .. '<tt> </tt>'
		else	expl = expl .. '<tt>·</tt>'
	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)
	local ss = args.ss  or '0'
	if ss == '≈' then ss = '0' end
	local tc = args.tc
	local pf = args.pf
	local gt = args.gpltab or ""
	local align = 'tincturebox-left'
	local error_cat = true
	local InFi = (args['+'] == '+')
	local cols = args
	local colors = {}
	local box = {}
	local tab = {}
	local out = {}
	local ordtab = { }
	local insert = false

	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
		error_cat = false
	end

	ss = mw.ustring.upper( ss )
	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
	if cols.ss	then
		ss =  mw.ustring.upper( cols.ss )
	end 	
	for _, v in ipairs(cols) do
		if not v or v == '' then
			break
		elseif v == '-' then
			error_cat = false
		elseif v == '+' then
			InFi = true
		elseif mw.ustring.sub( v, 1, 3 ) == 'ss=' then  -- any case when ss=
			ss = mw.ustring.upper( 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
		else
			table.insert(colors, v)
		end
	end
	
--  0) headline and boxes
	if gt == "2" or gt =="3" then
		table.insert(out, frame:expandTemplate{title='=', args={'<h4>GPLtab '..draw('gpltabnam',ss,'','','tab')..'</h4>'}})
	end
	for i, v in ipairs(colors) do
		box[i] = draw(v, ss, tc, pf, 'box')
	end

-- 1) cat: sorted table
	if error_cat == true then
		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, 'c' )
		insort = sortc (colors, ordtab, 'C' )
		insort = sortc (colors, ordtab, 'g' )
		insort = sortc (colors, ordtab, 'n' )
		insort = sortc (colors, ordtab, 'p' )
		insort = sortc (colors, ordtab, 's' )
		insort = sortc (colors, ordtab, 't' )
		insort = sortc (colors, ordtab, 'v' )
		insort = sortc (colors, ordtab, 'x' )
		table.insert(box, category(ordtab, ss, tc, args.cat, no_error_cat))
	end
	
-- 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' }) ..
			'<div class="tincturebox ' .. align .. '">' .. table.concat(box) .. '</div>'
		if InFi then
			local name = mw.getContentLanguage():ucfirst(frame:expandTemplate{ title = 'I18n/COA', args = { 'tincture' } })
			local link = ' <small>([[Template:Tincture/draw'..ss..'|'..ss..']])</small>'
			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;<br>" } })
		for i, v in ipairs(colors) do
			table.insert(out, draw( v, ss, tc, pf, 'tab') ) 
		end
		table.insert(out, frame:expandTemplate{ title = '=', args = { "<br><br>" } })
	end
	return table.concat(out)
end -- function main / tincture 

return p;