Módulo:XCols

De ProleWiki, la enciclopedia proletaria
Revisión del 05:56 9 oct 2022 de Miguel (discusión | contribs.) (Creación de «Módulo:XCols»)
(difs.) ← Revisión anterior | Revisión actual (difs.) | Revisión siguiente → (difs.)

Módulo para colocar texto en columnas:

  • Reparto automático o manual de las líneas. En el reparto se prioriza que la cumplimentación sea según el orden de la columna (así, por ejemplo, se descarta un reparto en dos columnas, más ajustadas por contenido, en que la segunda columna estuviera más llena que la primera).
  • Para mejorar la lectura el reparto automático en columnas tiene en cuenta no romper:
  • Las líneas que contienen "sublíneas". Así, las "sublíneas" siempre estarán debajo de la línea principal y, por tanto, en la misma columna. Un ejemplo de línea con "sublíneas" sería:
línea 1
  • sublínea 1.1
  • sublínea 1.2
  • Para el grupo de columnas: se puede definir anchura, encabezado, pie, color de fondo, alineación. La alineación a derecha o izquierda permite que el texto del artículo fluya por el otro lado.
  • Para las columnas, se puede definir:
    • Que la anchura de cada una sea igual o ajustada al contenido.
    • La separación entre columnas.


Para más información véase la plantilla {{XCols}}.


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