Módulo:XCols

De Hispanopedia

La documentación para este módulo puede ser creada en Módulo:XCols/doc

local p = {}

local SA = require "Module:SimpleArgs"
--local SD = require "Module:SimpleDebug"
--local TNTT = require "Module:TNTTools"
local dir = require "Module:Dir"

local RS = {
	Lines = 'Lines',
	Header = 'Header',
	FreeHeader = 'FreeHeader',
	Footer = 'Footer',
	NumColumns = 'NumColumns',
	NumDisplay = 'NumDisplay',
	Width = 'Width',
	SameWidth = 'SameWidth',
	HAlign = 'HAlign',
	VAlign = 'VAlign',
	SepCols = 'SepCols',
	BgColor = 'BgColor',
	HeaderBgColor = 'HeaderBgColor',
	FooterBgColor = 'FooterBgColor',
	NColsBiggerNLabs = 'NColsBiggerNLabs',
}

local i18n = {
	[RS.Lines]				= "lines",
	[RS.Header]				= "header",
	[RS.FreeHeader]			= "free_header",
	[RS.Footer]				= "footer",
	[RS.NumColumns]			= "col_n",
	[RS.NumDisplay]			= "display_n",
	[RS.Width]				= "width",
	[RS.SameWidth]			= "same_width",
	[RS.HAlign]				= "h_align",
	[RS.VAlign]				= "v_align",
	[RS.SepCols] 			= "col_sep",
	[RS.BgColor] 			= "bg_color",
	[RS.HeaderBgColor]		= "header_bg_color",
	[RS.FooterBgColor]		= "footer_bg_color",
	[RS.NColsBiggerNLabs] 	= "The column number ($1) is greater than the label number ($2)",
}
local I18n = 'XCols'
i18n = SA.loadI18n (I18n, i18n)

local function I18nStr (S, ...)
	--return TNTT.GetMsgP (I18n, S, {...})
	return SA.I18nStrParams (i18n[S], ...)
end

local function I18nStrArrOr1 (S)
	--return TNTT.TabTransMT (I18n, S, 2)
	return SA.I18nParamsTab (i18n[S])
end

local lang = mw.language.getContentLanguage().code
local dirh = ''
if dir.isRTL(lang) then
	dirh = 'rtl'
else
	dirh = 'ltr'
end	

function p.MultiCol (splited, NCols, LinesByLin)
	local NLines = 0
	local LinCol = {}
	local WithBreakLine = false
	local HeightByLin = {}
	local ColWidht = {}
	
	function CountChBegin (s, achar)
		local c = 1
		for j = 2, #s do
			if string.sub(s,j,j) == achar then
				c = c + 1
			else
				break
			end	
		end	
		return c
	end --CountChBegin

	function PrepareItems ()
		local IsUl = false
		local IsFirst = true
		local NLinesCol = 0
		for k, v in ipairs(LinesByLin) do
			NLinesCol = NLinesCol + 1
			v = mw.text.trim (v)
			if splited then
				local z = {}
				table.insert (z, v)
				LinesByLin[k] = z	
			else	
				v = mw.text.split(v, "\n")
				LinesByLin[k] = v	
			end
		end
		WithBlankLin = false
		for k, v in ipairs(LinesByLin) do
			if #v > 1 then
				WithBlankLin = true
			end
			local Char = string.sub(v[1],1,1) or ''
			if Char == ';' then
				local Sep = string.find(v[1],':') or 0
				if Sep ~=0 then
					local Temp = {}
					local Begin = mw.text.trim (string.sub (v[1],1,Sep-1))
					local End = mw.text.trim (string.sub (v[1],Sep+1))
					table.insert (Temp, Begin)
					table.insert (Temp, End)
					for kk = 2, #v do
						table.insert (Temp, v[kk])
					end	
					LinesByLin[k] = Temp
				end	
			end	
		end	
		local TempH = {}
		for k, v in ipairs(LinesByLin) do
			if #v == 1 then
				table.insert (TempH, v)
			else
				local vz = {}
				for kk, vv in ipairs(v) do
					local Begin = string.sub(vv, 1, 3)
					if (Begin == '---') then
						if #vz > 0 then
							table.insert (TempH, vz)
							vz = {}
						end	
						table.insert (TempH, {vv})
					else	
						table.insert (vz, vv)
					end	
				end
				if #vz > 0 then
					table.insert (TempH, vz)
				end	
			end	
		end	
		LinesByLin = {}
		for k, v in ipairs(TempH) do
			table.insert (LinesByLin, v)
		end	
		for k, v in ipairs(LinesByLin) do
			local Begin = string.sub(v[1], 1, 3)
			if (#v == 1) and (Begin == '---') then
				WithBreakLine = true
				break
			end	
		end
		if WithBreakLine then
			local LinesByLinTemp = {}
			local CurCol = 1
			for k, v in ipairs(LinesByLin) do
				local Begin = string.sub(v[1], 1, 3)
				if (#v == 1) and (Begin == '---') then
					if k ~= 1 then
						CurCol = CurCol + 1
					end	
					local width = string.sub(v[1], 4)
					if width ~= '' then
						SA.CheckSize (CurCol, width)
					end	
					table.insert (ColWidht, width)
				else
					table.insert (LinesByLinTemp, v)
					table.insert (LinCol, CurCol)			
				end	
			end
			NCols = CurCol
			LinesByLin = {}
			for k, v in ipairs(LinesByLinTemp) do
				table.insert (LinesByLin, v)
			end	
		else
			if not WithBlankLin then
				Levels = {}
				local MaxLevel = 0
				local MinLevel = 3
				for k, v in ipairs(LinesByLin) do
					local Begin = string.sub(v[1], 1, 1)
					local Begin2 = string.sub(v[1], 2, 2)
					function GetLev (achar)
						if Begin2 == achar then
							return 3
						else
							return 2
						end
					end
					if Begin == '=' then
						Lev = 0
					elseif Begin == '*' then
						Lev = GetLev ('*')
					elseif Begin == '#' then
						Lev = GetLev ('#')
					elseif Begin == ':' then
						Lev = GetLev (':')
					else
						Lev = 1
					end	
					MinLevel = math.min (MinLevel, Lev)
					MaxLevel = math.max (MaxLevel, Lev)
					table.insert (Levels, Lev)
				end	
				local c = false
				local various = MinLevel ~= MaxLevel
				if various then			
					local LinesByLinTemp = {}
					local Lines = {}
					for k, v in ipairs(LinesByLin) do
						if Levels[k] == MinLevel then
							if #Lines == 0 then
								table.insert (Lines, v[1])
							else
								table.insert (LinesByLinTemp, Lines)
								Lines = {v[1]}
							end
						else
							table.insert (Lines, v[1])
						end
					end
					table.insert (LinesByLinTemp, Lines)
					LinesByLin = {}
					for k, v in ipairs(LinesByLinTemp) do
						table.insert (LinesByLin, v)
					end
				end	
			end
			for k, v in ipairs(LinesByLin) do
				table.insert (LinCol, 1)
				local h = 0
				for kk, vv in ipairs(v) do
					local HByL = 0
					if kk == 1 then
						local Char = string.sub(vv,1,1) or ''
						if Char == '=' then
							local HH = {2, 1.8, 1.38, 1.28, 1.2}
							local c = CountChBegin (vv, '=')
							HByL = HH[c]
						elseif (Char == ';') or (Char == '*') then
							HByL = 0.8
						elseif #v > 1 then
							local Char = string.sub(v[2],1,1) or ''
							if (Char == '*') or (Char == '#') or (Char == ':') then
								HByL = 1
							else
								HByL = 0.8
							end	
						else	
							HByL = 1
						end	
					elseif kk == #v then
						HByL = 1
					else	
						HByL = 0.8
					end	
					h = h + HByL
				end	
				table.insert (HeightByLin, h)
				NLines = NLines + h
			end	
		end	
	end --PrepareItems
	
	function SetColToLines ()
		local AbsLinesPerCol = 0
		function CalcLinesPerCol (L,C)
			AbsLinesPerCol = L/C
		end
		CalcLinesPerCol (NLines, NCols)
		local DoItN = 0
		local LinesForDo = NLines
		local ColsForDo = NCols
		local CurrCol = 1
		for k, v in ipairs(LinesByLin) do
			local fornext = false
			if (CurrCol < NCols) and (((AbsLinesPerCol - DoItN) + (HeightByLin[k]/2)) < ((DoItN + HeightByLin[k]) - AbsLinesPerCol)) then
				LinesForDo = LinesForDo - DoItN
				CalcLinesPerCol (LinesForDo, ColsForDo-1)
				CurrCol = CurrCol + 1
				DoItN = HeightByLin[k]
				ColsForDo = ColsForDo - 1
			else
				DoItN = DoItN + HeightByLin[k]		
			end
			LinCol[k] = CurrCol
		end
	end --SetColToLines
	
	function TheItems ()
		local IsUl = 0
		local IsUlIntra = false
		local LastWasUl = false
		local IsOl = 0
		local IsOlIntra = false
		local LastWasOl = false
		local IsDl = false
		local IsDlx = false
		local IsDlIntra = false
		local LastWasDl = false

		local Lines = {}
		local vv = ''
		
		local Result = {}
		local CurrCol = 1
		
		function SplitChBegin0 (k, i, achar)
			local s = LinesByLin[k][i]
			local c = 1
			for j = 2, #s do
				if string.sub(s,j,j) == achar then
					c = c + 1
				else
					break
				end	
			end	
			return mw.text.trim (string.sub(s,c+1)), c
		end --SplitChBegin0

		function SplitChBegin (k, i, achar)
			local s, c = SplitChBegin0 (k, i, achar) 
			LinesByLin[k][i] = '<li>'..s..'</li>'
			return c
		end --SplitChBegin

		function WithHeader (k, i)
			local s, c = SplitChBegin0 (k, i, '=')
			local cc = 0
			for j = #s, 1, -1 do
				if string.sub(s,j,j) == '=' then
					cc = cc + 1
				else
					break
				end	
			end	
			s = string.sub (s,1,#s-cc)	
			LinesByLin[k][i] = '<h'..c..'>'..s..'</h'..c..'>'
		end --WithHeader

		function AddClose (num, achar)
			for i = 1, num do
				vv = vv..'</'..achar..'l>'
			end
		end --AddClose
		
		function OnEnd ()
			if IsUl > 0 then
				AddClose (IsUl, 'u')
				IsUl = 0
			elseif IsOl > 0	then
				AddClose (IsOl, 'o')
				IsOl = 0
			elseif IsDlx then
				vv = vv..'</dl>'
				IsDlx = false
			elseif IsDl	then
				vv = vv..'</dl>'
				IsDl = false
			end	
		end --OnEnd
				
		for k, v in ipairs(LinesByLin) do
			if #v ~= 1 then
				vv = vv..'<p>'
			end	
			for i, j in ipairs(LinesByLin[k]) do
			
				function BetweenAny (What)
					LinesByLin[k][i] = '<'..What..'>'..string.sub(LinesByLin[k][i], 2)..'</'..What..'>'
				end	
				function BeginUlOrOlOrDl (What)
					LinesByLin[k][i] = '<'..What..'l>'..LinesByLin[k][i]
				end	
				function EndUlOrOlOrDl (What)
					LinesByLin[k][i] = '</'..What..'l>'..LinesByLin[k][i]
				end	
				
				function BeginOrEndUlOrOl (IniV, NewV, achar)
					local IsBegin = IniV < NewV
					local s = ''
					if IsBegin then
						for k = 1, NewV-IniV do
							s = s..'<'..achar..'l>'
						end
					else
						for k = 1, (IniV-NewV)-1 do
							s = s..'</'..achar..'l>'
						end
						EndUlOrOlOrDl (achar)
					end
					LinesByLin[k][i] = s..LinesByLin[k][i]
				end --BeginOrEndUlOrOl
				
				local Char = string.sub(LinesByLin[k][i],1,1) or ''
				if Char == '*' then
					local c = SplitChBegin (k, i, '*')
					if IsUl ~= c then
						BeginOrEndUlOrOl (IsUl, c, 'u')
						IsUl = c
					end	
					IsUlIntra = i > 1
				elseif Char == '#' then
					local c = SplitChBegin (k, i, '#')
					if IsOl ~= c then
						BeginOrEndUlOrOl (IsUl, c, 'o')
						IsOl = c
					end	
					IsOlIntra = i > 1
				elseif Char == '=' then
					OnEnd ()
					WithHeader (k, i)
				elseif Char == ';' then
					BetweenAny ('dt')
					BeginUlOrOlOrDl ('d')
					IsDlx = true
				elseif Char == ':' then
					BetweenAny ('dd')
					if not IsDl then
						BeginUlOrOlOrDl ('d')
					end	
					IsDl = true
					IsDlIntra = i > 1
				else	
					if #v == 1 then
						LinesByLin[k][i] = '<p>'..LinesByLin[k][i]..'</p>'
					elseif i ~= #LinesByLin[k] then
						LinesByLin[k][i] = LinesByLin[k][i]..'<br>'
					end	
					if IsUl > 0 then
						BeginOrEndUlOrOl (IsUl, 0, 'u')
						IsUl = 0
					end	
					if IsOl > 0 then
						BeginOrEndUlOrOl (IsUl, 0, 'o')
						IsOl = 0
					end	
					if IsDl then
						EndUlOrOlOrDl ('d')
					end	
					if IsDlx then
						LinesByLin[k][i] = '<dd>'..LinesByLin[k][i]..'</dd>'
					end	
					IsUlIntra = false
					IsOlIntra = false
					IsDlIntra = false
				end
				--revised intralin
				if vv ~= '' then
					if IsUl > 0 then
						vv = vv..LinesByLin[k][i]
						if (i < #LinesByLin[k]) and ((LinesByLin[k][i+1] == '') or (string.sub(LinesByLin[k][i+1],1,1) ~= '*')) then
							vv = vv..'</ul>'
							IsUl = IsUl - 1
							IsUlIntra = false
							LastWasUl = true
						end	
					elseif IsOl > 0 then
						vv = vv..LinesByLin[k][i]
						if (i < #LinesByLin[k]) and ((LinesByLin[k][i+1] == '') or (string.sub(LinesByLin[k][i+1],1,1) ~= '#')) then
							vv = vv..'</ol>'
							IsOl = IsOl - 1
							IsOlIntra = false
							LastWasOl = true
						end	
					elseif IsDl then
						vv = vv..LinesByLin[k][i]
						if (i < #LinesByLin[k]) and ((LinesByLin[k][i+1] == '') or (string.sub(LinesByLin[k][i+1],1,1) ~= ':')) then
							vv = vv..'</dl>'
							IsDl = false
							IsDlIntra = false
							LastWasDl = true
						end	
					elseif LastWasUl then
						vv = vv..LinesByLin[k][i]
						LastWasUl = false
					elseif LastWasOl then
						vv = vv..LinesByLin[k][i]
						LastWasOl = false
					elseif LastWasDl then
						vv = vv..LinesByLin[k][i]
						LastWasDl = false
					else	
						vv = vv..LinesByLin[k][i]
					end	
				else
					vv = LinesByLin[k][i]
				end	
			end
			--revised lin with lines
			if IsUlIntra then
				AddClose (IsUl, 'u')
				IsUl = 0
				IsUlIntra = false
			end	
			if IsOlIntra then
				AddClose (IsOl, 'o')
				IsOl = 0
				IsOlIntra = false
			end	
			if IsDlx then
				vv = vv..'</dl>'
				IsDlx = false
			end	
			if IsDlIntra then
				vv = vv..'</dl>'
				IsDl = false
				IsDlIntra = false
			end
			if #v ~= 1 then
				vv = vv..'</p>'
			end	
			local IsLastLine = (k == #LinesByLin)
			if IsLastLine or (CurrCol ~= LinCol[k+1]) then
				if not IsLastLine then
					CurrCol = LinCol[k+1]
				end	
				OnEnd ()
				if vv ~= '' then
					table.insert(Result, vv)
					vv = ''
				end	
			end	
		end
		return Result
	end --TheItems	

	PrepareItems()
	if (not WithBreakLine) then
		SetColToLines ()
	end	
	return TheItems (true), NCols, ColWidht
end --MultiCol

function p.MultiColX (splited, width, same_width, sep_cols, NCols, Lines, h_align, v_align, header, footer, bg_color, free_header, header_bg_color, footer_bg_color)
	Lines, NCols, ColWidht = p.MultiCol (splited, NCols, Lines)
	local col_width = ''
	if same_width then
		col_width = math.floor(100/NCols)..'%'
	end
	local td = {}
	local s = mw.html.create("table")
	
	function AddAny (S, IsHeader, Color)
		if (S ~= nil) and (S ~= '') then
			local tr = s:newline():tag('tr')
				if Color ~= nil then
					tr:	css ('background-color', Color)
				end	
				local td = tr:newline():tag('td')
					:attr('colspan', NCols)
					if Color ~= nil then
						td:	css ('padding-right', '0.3em')
						td:	css ('padding-left', '0.3em')
						td:	css ('padding-top', '0.2em')
						td:	css ('padding-bottom', '0.2em')
					end	
				if IsHeader then
					local d = td:newline():tag('div')
						:addClass ('center')
						:css	('width', 'auto')
						:css	('margin-left', 'auto')
						:css	('margin-right', 'auto')
						:wikitext ( "'''"..S.."'''")
				else		
					S = mw.text.split (S, "\n")
					S = table.concat(S, '<br>')
					td :wikitext (S)
				end	
		end	
	end	
	
		s:	attr ('direction', dirh)
		if h_align ~= nil then
			s:	attr ('align', h_align)
			if h_align ~= 'center' then
				s:	css ('padding-left', '8px')
				s:	css ('padding-right', '8px')
				if h_align == 'right' then
					s:	css ('margin-left', '12px') 
				elseif h_align == 'left' then
					s:	css ('margin-right', '12px') 
				end
			end	
		end	
		if width ~= nil then
			s:	css ('width', width)
		end	
		if bg_color ~= nil then 
			s:	css ('background-color', bg_color)
			s:	css ('padding-right', sep_cols)
			s:	css ('padding-left', sep_cols)
		end	
		AddAny (header, true, header_bg_color)
		AddAny (free_header, false, header_bg_color)
		local tr = s:newline():tag('tr')
			if v_align ~= nil then
				tr:attr ('valign', v_align) 
			end	
			for k = 1, NCols do
				td[k] = tr:newline():tag('td')
				if same_width then
					td[k]:css ('width', col_width) 
				elseif (#ColWidht > 0) and (ColWidht[k] ~= '') then
					td[k]:css ('width', ColWidht[k]) 
				end	
				local AddRight = false
				local AddLeft = false
				if NCols > 1 then
					if k == 1 then
						if dir.isRTL(lang) then
							AddLeft = true
						else
							AddRight = true
						end	
					elseif k == NCols then
						if dir.isRTL(lang) then
							AddRight = true
						else
							AddLeft = true
						end	
					else	
						AddRight = true
						AddLeft = true
					end	
				end	
				if AddRight then
					td[k] :css ('padding-right', sep_cols)
				end
				if AddLeft then
					td[k] :css ('padding-left', sep_cols)
				end
					td[k]:wikitext (Lines[k])
			end	
		AddAny (footer, false, footer_bg_color)
	return tostring (s)
end --MultiColX

function p.MainVals (args, Required)
	local lines = ''
	if Required then
		lines = SA.RStr_Par (args, I18nStrArrOr1(RS.Lines))
	else
		lines = SA.Str_Par (args, I18nStrArrOr1(RS.Lines))
	end	
	local NLines = 0
	local splited = false
	if lines ~= nil then
		lines = mw.text.split(mw.text.trim(lines), "\n\n")
		NLines = #lines
		if NLines == 1 then
			lines = mw.text.split (lines[1], "\n")
			NLines = #lines
			splited = true
		end	
	end	
	local NCols = SA.PosInt_Par (args, I18nStrArrOr1(RS.NumColumns), 1, 1, 10)
	local width = SA.Size_Par (args, I18nStrArrOr1(RS.Width), true, {perc={20,100},em={12,119},px={200,1900}})
	local same_width = SA.Bool_Par (args, I18nStrArrOr1(RS.SameWidth), false)
	local sep_cols = SA.Size_Par (args, I18nStrArrOr1(RS.SepCols), false, {em={0.6,2.2},px={9,36}}, '0.6em')
	local h_align = SA.HAlign_Par (args, I18nStrArrOr1(RS.HAlign))
	local v_align = SA.VAlign_Par (args, I18nStrArrOr1(RS.VAlign), 'top')
	local header = SA.Str_Par (args, I18nStrArrOr1(RS.Header))
	local footer = SA.Str_Par (args, I18nStrArrOr1(RS.Footer))
	return splited, NLines, lines, NCols, width, same_width, sep_cols, h_align, v_align, header, footer
end --MainVals

function p.main (frame)
	local args,NArgs = SA.GetArgs (frame)
	if NArgs == 0 then return end
	local splited, NLines, Lines, NCols, width, same_width, sep_cols, h_align, v_align, header, footer, bg_color, free_header, header_bg_color = p.MainVals (args, true)
	if NCols > NLines then
		error (I18nStr (RS.NColsBiggerNLabs, NCols, NLines))
	else
		local bg_color = SA.Str_Par (args, I18nStrArrOr1(RS.BgColor))
		local free_header = SA.Str_Par (args, I18nStrArrOr1(RS.FreeHeader))
		local header_bg_color = SA.Str_Par (args, I18nStrArrOr1(RS.HeaderBgColor))
		local footer_bg_color = SA.Str_Par (args, I18nStrArrOr1(RS.FooterBgColor))
		return p.MultiColX (splited, width, same_width, sep_cols, NCols, Lines, h_align, v_align, header, footer, bg_color, free_header, header_bg_color, footer_bg_color)
	end	
end --main

return p