Módulo:SimpleArgs

De Enciclopedia de conocimiento de la Iglesia de Dios
Ir a la navegación Ir a la búsqueda

Uso

Utilizado por otros módulos para comprobación de parámetros pasados.

Plantilla:ForMultilingualTrans

Funciones

  • Arg(ument)s son los argumentos del frame actual.
  • Par(armeter)Id(entificator) es el nombre/s o el orden del parámetro.
  • La marca * indica que es un parámetro opcional. Si no se quiere asignar un valor opcional no correlativo se puede asignar el valor de nil.
Ejemplo: función_de_número (Args, paridas, LimInf*, LimSup*) -> función_de_número (args, 1, nil, 12), comprobará que el número no sea mayor de 12.
Obviamente sería correcto -> función_de_número (args, 1), sin ninguna comprobación de los valores de los números.
  • Str(ing): Cadena.
  • Num(ber): Número.
  • Int(teger): Entero.
  • Pos(itive)Num/Int: Número/entero positivo.
  • ZeroOrPosNum/Int: Cero o número/entero positivo.
  • Char(acter): Caracter.
  • Tab(ble): Tabla.

Principal

  • GetArgs (frame, HandleError*, SameSize*) - Devuelve los argumentos y su número.
Si HandleError == true -> Error.handle = true
Si SameSize == true -> Error.samesize = true

Manipulación de los errores

  • Error = {
handle = false,
yes = false,
msg = ,
samesize = false,
}
  • MsgError (S)

Si handle = false, cuando se produce un error se presenta un error de lua. Así: Error Lua: error.

Si handle = true, es el que hace el módulo quien deberá hacer un return con el error, así return MsgError(), devolviendo la fuente de un error de lua. Así: error. Se hace una comprobación parámetro a parámetro si no se detecta ningún error.

  • El error se anota en una variable.
  • Se anota que ha habido un error y así no se comprueba más errores en el resto de parámetros.
  • Al final de la comprobación y entrada de variables el programador debería escribir: if SA.Error.yes then return SA.MsgError() end (donde SA está definida cómo local SA = require "Module:SimpleArgs").

Si handle = true y samesize = true entonces el mensaje devuelto será con letra en negrita y de color rojo. Así: error.

Vea los ejemplos.

Comprobación de los nombres de los parámetros

V/F nombre Parámetros o
Valores
Descripción
F CheckParams args, UsualArgs Comprobación de los nombres de los parámetros (de args) según la tabla de nombres permitidos para los parámetros (UsualArgs). Si un mismo parámetro tiene varios nombres, estos nombres se pueden agrupar en una tabla, como se muestra en ejemplo más abajo (modo sencillo). Se genera un error indicando el nombre de palabra no encontrado.
V HasChild HasChild Utilizado en CheckParamsM
F CheckParamsM args, UsualArgs, OtherArgs*, ArgNamesIn1 Realiza la comprobación de los parámetros como CheckParams pero con tablas más complejas.

UsualArgs La lista de parámetros con diversas estructuras (véase más abajo, en modo sencillo i).

OtherArgs Es una tabla sencilla que contiene nombres de parámetros que no necesitan ninguna traducción. Se trata de parámetros para funciones específicas como pueden ser listados de parámetros, cambio de idioma o una demostración.

Devuelve dos tablas con los dos tipos de errores encontrados. Una lista con los nombres de los parámetros no encontrados y el otro con duplicados. Un parámetro duplicado es un error raro que se puede producir cuando un parámetro puede tener más de una denominación y en la llamada desde una plantilla se hace más de una llamada con el mismo parámetro.

Tablas UsualArgs

1. Ejemplos para CheckParams y CheckParamsM o modo sencillo:

{"name", "surname"}

Modo sencillo con más de un nombre por parámetro:

{{"name","Name"}, {"surname","Surname"}}

2. Ejemplos para CheckParamsM o modo complejo:

Utilizando ArgNamesIn1 = true, se indica que es el primer elemento o tabla lo que contiene el nombre o los nombres de los parámetros:

    ['birth'] = {{"birth_year",1}, "P569"}, 
    ['death'] = {{"death_year",2}, "P570"},
    ['intro'] = {"intro",          "Q305178"},

Utilizando HasChild:

    [SA.HasChild] = true,
    ['year'] = {
        ['birth'] = {"birth_year",1},
        ['death'] = {"death_year",2},
    },	
    ['other'] = {
        ['intro'] = "intro",
    },	

Otras

  • p.ParamsSep = '/' - Variable usada para la siguiente función.
  • ConcatParams (tab) - Se utiliza cuando un parámetro tiene más de un nombre para el mismo parámetro.

Comprobación de números

Devuelven el número N si este número es correcto, de lo contrario devuelven un mensaje de error.

Nombre Parámetros
CheckNum N, ParId, LimInf, LimSup
CheckNumIsInt N, ParId, LimInf*, LimSup*)
CheckNumIsPos N, ParId, LimInf*, LimSup*)
CheckNumIsZeroOrPos N, ParId, LimSup*)

Comprobación de si las cadenas son números

Devuelven un número si la cadena es un número correcto, de lo contrario devuelven un mensaje de error.

Nombre Parámetros
CheckSIsNum S, ParId, LimInf*, LimSup*
CheckSIsInt S, ParId, LimInf*, LimSup*
CheckSIsPosInt S, ParId, LimInf*, LimSup*
CheckSIsZeroOrPosInt S, ParId, LimSup*

Valores de los parámetros específicos del frame

Las funciones con una R inicial indican que el parámetro es necesario. Devuelven el valor del tipo pedido del parámetro si es correcto, de lo contrario devuelven un mensaje de error.

Mire los ejemplos de Módulo:SimpleArgs/Tests/SVals

Cadenas

Nombre Parámetros
Str_Par Args, ParId, Default*
RStr_Par Args, ParId, OKEmpty*
Char_Par Args, ParId, Pattern, Default*
RChar_Par Args, ParId, Pattern
NulOrWhitespace_Par Args, ParId
StrChkTab_Par Args, ParId, Tab, CaseSens*, Default*
RStrChkTab_Par Args, ParId, Tab, CaseSens*
StrIdxChkTab_Par Args, ParId, Tab, CaseSens*, Default*
RStrIdxChkTab_Par Args, ParId, Tab, CaseSens*

Números reales

Nombre Parámetros
Num_Par Args, ParId, Default*, LimInf*, LimSup*
RNum_Par Args, ParId, LimInf*, LimSup*
PosNum_Par Args, ParId, Default*, LimInf*, LimSup*
RPosNum_Par Args, ParId, LimInf*, LimSup*
ZeroOrPosNum_Par Args, ParId, Default*, LimSup*
RZeroOrPosNum_Par Args, ParId, LimSup*

Números enteros

Nombre Parámetros
Int_Par Args, ParId, Default*, LimInf*, LimSup*
RInt_Par Args, ParId, LimInf*, LimSup*
PosInt_Par Args, ParId, Default*, LimInf*, LimSup*
RPosInt_Par Args, ParId, LimInf*, LimSup*
ZeroOrPosInt_Par Args, ParId, Default*, LimSup*
RZeroOrPosInt_Par Args, ParId, LimSup*

Tamaño, html

Donde limits es una tabla con los márgenes inferior y superior de los 3 tipos de tamaño posibles: porcentaje (perc), em y píxel (px):

Por ejemplo: {perc={20,100}, em={12,119}, px={200,1900}}

Nombre Parámetros
Size_Par Args, ParId, WithPerc, limits*, Default*
RSize_Par Args, ParId, WithPerc, limits*

Colores, html

Devuelve, con las siguientes funciones el color pasado como parámetro es devuelto como cadena y sólo la parte "numérica" (NNNNNN). Así si el parámetro es "Green" devuelve '008000', y si es '#00800B' devuelve '00800B'. Admite los nombres de colores web.

Nombre Parámetros
Color_Par Args, ParId, Default*
RColor_Par Args, ParId

También existe la siguiente función ReverseColor que permite encontrar el color "contrario" al introducido para conseguir el máximo contraste. Esto facilita determinar el color del texto para un fondo de color determinado o viceversa. Así, por ejemplo:

  • {{#invoke:SimpleArgs|ReverseColor|Red}} devuelve Aqua
  • {{#invoke:SimpleArgs|ReverseColor|#000000}} devuelve White

Existe la función _ReverseColor (rgb) a llamar desde otro módulo donde rgb es el color en formato NNNNNN. Para convertir el color a formato NNNNNN existe la función CheckSIsColor.

Alineación horizontal

Donde los valores posibles son: left|izquierdo|izquierda, center|centro y right|derecho|derecha.

Nombre Parámetros
HAlign_Par Args, ParId, Default*
RHAlign_Par Args, ParId

Alineación vertical

Donde los valores posibles son: top|arriba, center|centro y bottom|abajo.

Nombre Parámetros
VAlign_Par Args, ParId, Default*
RVAlign_Par Args, ParId

Booleano

Donde los valores posibles son: sí|si|s|yes|y|true|verdad|t|1 y no|n|false|falso|f|0.

Nombre Parámetros
Bool_Par Args, ParId, Default*
RBool_Par Args, ParId

Índice de una lista

Nombre Parámetros
StrIdxChkTab Args, ParId, CaseSens, Default, ...
RStrIdxChkTab Args, ParId, CaseSens, ...
StrIdxChkTabE Args, ParId, CaseSens, Default, ...
RStrIdxChkTabE Args, ParId, CaseSens, ...

Tablas de tablas de cadenas o números

MinItemNum y MaxItemNum, indican el mínimo y el máximo de elementos insertados en la tabla.

Cuando alguno de los valores no esté asignado actuará según OnEmpty: 0: Se incluirá. 1: No se incluirá. 2. Activará un error y su mensaje.

De un parámetro

Para el mismo parámetro con los elementos separados por Sep. Ejemplo, con "sep" = ":" y el parámetro = "12: 1: 1,3" devuelve {12, 1, 1.3}

Nombre Parámetros
StrTab_1Par Args, ParId, Sep, MinItemNum*, MaxItemNum*, OnEmpty*
NumTab_1Par Args, ParId, Sep, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
PosNumTab_1Par Args, ParId, Sep, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
ZeroOrPosNumTab_1Par Args, ParId, Sep, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
IntTab_1Par Args, ParId, Sep, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
PosIntTab_1Par Args, ParId, Sep, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
ZeroOrPosIntTab_1Par Args, ParId, Sep, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
De varios parámetros

Nota diferencial, para Par(armeter)Id(entificator):

  • Si es una posición:
    • Y es un 1: captará todos los parámetros desde el primero hasta el último que no sea un número. Así puede contener parámetros no numéricos (no posicionales a la vez).
    • Y es otro número: captará todos los parámetros desde la posición hasta el último (pasado por NArgs).

Los parámetros pueden ser:

  • Si es una cadena, ésta deberá contener un $d que será sustituido por un entero correlativo (a partir del 1) hasta que no se encuentre ningún parámetro. Ejemplo: 'para $d, buscará 'para 1', 'para 2', etc.
  • Si es una tabla, se hará la misma búsqueda que el anterior punto por cada uno de los valores. Ejemplo: {'para $d, param $d}, buscará 'para 1' y 'param 1', después 'para 2' y 'param 2', etc.

Las funciones:

Nombre Parámetros
StrTab_NPar Args, NArgs, ParId, MinItemNum*, MaxItemNum*, OnEmpty*
NumTab_NPar Args, NArgs, ParId, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
PosNumTab_NPar Args, NArgs, ParId, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
ZeroOrPosNumTab_NPar Args, NArgs, ParId, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
IntTab_NPar Args, NArgs, ParId, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
PosIntTab_NPar Args, NArgs, ParId, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*
ZeroOrPosIntTab_NPar Args, NArgs, ParId, MinItemNum*, MaxItemNum*, LimInf*, LimSup*, OnEmpty*

Mire los ejemplos de Módulo:SimpleArgs/Tests/SVals

Otras

F/V Nombre Parámetros Explicación
F HasValue v Devuelve true si (v ~= nil) and (v ~= '')
F deep_copy_table orig Devuelve una copia de la tabl orig
F tableMerge t1, t2 Sencilla fusión de tablas, utilitzado en loadI18n.
V wiki_langcode El idioma de la Wikipedia
F get_lang langcode Devuelve langcode si tiene un valor, de lo contrario devuelve el idioma del usuario si es que éste no se encuentra en una página de artículo, en este caso devuelve el idioma de la Wikipedia.
V lang_to_use Contiene el valor de get_lang.
F I18nName ModName Devuelve el nombre del módulo i18n, en caso de wiki_langcode ~= lang_to_use devuelve i18n/lang_to_use si existe.
F loadI18n ModName, DefTable Devuelve la tabla fusionada i18n con DefTable.
F TemplateName frame Devuelve el nombre de la plantilla desde la cual es llamada. Así en la Plantilla:LaMía, escribiendo {{#invoke:SimpleArg|TemplateName}}, o invocando desde una función (llamada desde la plantilla) de un módulo con TemplateName(frame) retornarían: LaMía.
F MainTemplateName frame Similar a la anterior, para cuando se utiliza desde plantillas de prueba. Así en la Plantilla:LaMía/prueba devolvería igualmente: LaMía.
F CheckIsStr v, S Comprueba que v sea una cadena con un valor, de lo contrario genera un error.
F CheckIsStrOrTab v, S Comprueba que v sea una cadena con un valor o una tabla de cadenas con valor, de lo contrario genera un error.
F CheckIsAnyStrOrTab v, S Comprueba que v sea una cadena o una tabla de cadenas, también genera un error.

Colores

ColorToHex (frame) convierte el nombre del color en formato hexadecimal.

color_black_contrast (frame) determina la luminosidad de un color, con algunas correcciones para grises. A utilizar para el fondo de un texto de superposición. Proviene en parte de Three algorithms for converting color to grayscale. Existe la función _color_black_contrast (rgb) a llamar desde otro módulo.

txtcolor_for_bg (frame), permite encontrar el color "contrario" al introducido para conseguir el máximo contraste del texto, determinando si debe ser blanco o negro. Existe la función _txtcolor_for_bg (rgb) a llamar desde otro módulo.

ReverseColor (frame), alternativo a color_black_contrast permite encontrar el color "contrario" al introducido para conseguir el máximo contraste. Esto facilita determinar el color del texto para un fondo de color determinado o viceversa. Existe la función _ReverseColor (rgb) a llamar desde otro módulo. Ejemplo:

  • {{#invoke:SimpleArgs|ReverseColor|Red}} devuelve Aqua
  • {{#invoke:SimpleArgs|ReverseColor|#000000}} devuelve White
Ejemplos de algunas funciones de color con algunos colores
Color de fondo
y su nombre
ColorToHex color_black_contrast Con fondo de texto
teniendo en cuenta los
valores de la columna anterior(1)
txtcolor_for_bg (2)
255 --
 LightCyan  E0FFFF 242  LightCyan   LightCyan   
 yellow  FFFF00 235  yellow   yellow   
 Wheat  F5DEB3 213  Wheat    Wheat   
200 0.3
 aqua  00FFFF 184  aqua   aqua   
 lime  00FF00 163  lime   lime   
 silver  C0C0C0 159  silver   silver   
 Violet  EE82EE 143  Violet   Violet   
140 0.5
 fuchsia  FF00FF 92  fuchsia   fuchsia   
 olive  808000 88  olive   olive   
80 0.7
 red  FF0000 71  red   red   
 teal  008080 69  teal   teal   
 gray  808080 63  gray   gray   
 green  008000 61  green   green   
 maroon  800000 27  maroon   maroon   
 blue  0000FF 20  blue   blue   
 navy  000080 8  navy   navy   

1: Utilizando <span style="background-color:rgba(255,255,255,opacity)> {{{1|}}} </span>

opacity = número de 0 a 1, utilizando los valores 0,3, 0,5 y 0,7

2: Utilizando ReverseColor


local p = {}

--[[
Version 2020-08-28
1. Get parameters (whether the parameters are called with #invoke or from a template)
2. Get parameters (of several types with their verification) from frame parameters.
	ParId parameter is identified by a position (1, 2, 3 ... ), single name (|name= |other name=) or table of two names for each parameter.
	This last options is used in translations that retain the original language (or main), usually English, i.e.:
	local keywords = { -- in this case with Spanish
		name =		 {'name',		'nombre'},
		other name = {'other name',	'otro nombre'},
	}
	Then the parameter can be obtained with p.GetStr_Par (Args, keywords[name])
--]]

local RS = {
	NotFoundArgName = 'NotFoundArgName',
	SIsNotNumber = 'SIsNotNumber',
	NIsNotPosNumber = 'NIsNotPosNumber',
	NIsNotZeroOrPosNumber = 'NIsNotZeroOrPosNumber',
	NIsNotInt = 'NIsNotInt',
	NIsNotInRange = 'NIsNotInRange',
	NIsLessThan = 'NIsLessThan',
	NIsGreaterThan = 'NIsGreaterThan',
	InvalColorLength = 'InvalColorLength',
	InvalColorChar = 'InvalColorChar',
	InvalColorName = 'InvalColorName',
	STabIsNotInRange = 'STabIsNotInRange',
	STabFewItems = 'STabFewItems',
	SIsNotAssigned = 'SIsNotAssigned',
	CharNotFoundInPattern = 'CharNotFoundInPattern',
	SNotFoundInTab = 'SNotFoundInTab',
	EmptyValue = 'EmptyValue',
	SizeUnitRequired = 'SizeUnitRequired',
	InvalSizeUnit = 'InvalSizeUnit',
	SizeWithoutNumber = 'SizeWithoutNumber',
	InvalAlign = 'InvalAlign',
	InvalBool = 'InvalBool',
	left = 'left',
	right = 'right',
	top = 'top',
	bottom = 'bottom',
	center = 'center',
	Yes = 'Yes',
	No = 'No',
	PossibleValues = 'PossibleValues',
}

local SD = require('Module:SimpleDebug')
local I18n = 'SimpleArgs'

local i18n = {
	[RS.NotFoundArgName]		= "The parameter name \"$1\" is invalid",
	[RS.SIsNotNumber]			= "\"$1\" is not a number",
	[RS.NIsNotPosNumber] 		= "$1 is not a positive number",
	[RS.NIsNotZeroOrPosNumber]	= "$1 is not zero or a positive number",
	[RS.NIsNotInt]				= "$1 is not an integer.",
	[RS.NIsNotInRange]			= "$1 is not between $2 and $3",
	[RS.NIsLessThan] 			= "$1 is less than $2",
	[RS.NIsGreaterThan]			= "$1 is greater than $2",
	[RS.InvalColorLength]		= 'Invalid color code length, after "#", 6 valid characters (0-F) are required (found "$1")',
	[RS.InvalColorChar]			= 'Invalid character in the code color (from "$1")',
	[RS.InvalColorName]			= 'Color name ($1) is not a valid HTML color name',
	[RS.STabIsNotInRange]		= "The item number ($1) is not between $2 and $3",
	[RS.STabFewItems]			= "There are too few elements ($1), at least $2 are required",
	[RS.SIsNotAssigned]			= "Required parameter is missing",
	[RS.CharNotFoundInPattern]	= "\"$1\" is not a character of \"$2\"",
	[RS.SNotFoundInTab]			= "\"$1\" is not found in \"$2\"",
	[RS.EmptyValue]				= "\"$1\" has not a value",
	[RS.SizeUnitRequired]		= "Size unit required (em, px or %)",
	[RS.InvalSizeUnit]			= "Invalid unit ($1)",
	[RS.SizeWithoutNumber]		= "Without number",
	[RS.InvalAlign]				= "Invalid alignment ($1)",
	[RS.InvalBool]				= "Invalid boolean ($1)",
	[RS.left]					= "left",
	[RS.right]					= "right",
	[RS.top] 					= "top",
	[RS.bottom]					= "bottom",
	[RS.center]					= "center",
	[RS.Yes] 					= "yes|y|true|t|1",
	[RS.No]						= "no|n|false|f|0",
	[RS.PossibleValues]			= "Possible values: $1",
}

function p.HasValue (v)
	return (v ~= nil) and (v ~= '')
end

--http://lua-users.org/wiki/CopyTable:
function p.deep_copy_table (orig)
    local orig_type = type(orig)
    local copy
    if orig_type == 'table' then
        copy = {}
        for orig_key, orig_value in next, orig, nil do
            copy[p.deep_copy_table(orig_key)] = p.deep_copy_table(orig_value)
        end
        setmetatable(copy, p.deep_copy_table(getmetatable(orig)))
    else -- number, string, boolean, etc
        copy = orig
    end
    return copy
end --deep_copy_table

-- Credit to http://stackoverflow.com/a/1283608/2644759
-- cc-by-sa 3.0
function p.tableMerge (t1, t2)
	for k,v in pairs(t2) do
		if type(v) == "table" then
			if type(t1[k] or false) == "table" then
				p.tableMerge(t1[k] or {}, t2[k] or {})
			elseif ((v ~= nil) and (t1[k] == nil)) or p.HasValue(v) then --old version ...else t1[k] = v end
				t1[k] = v
			end
		elseif ((v ~= nil) and (t1[k] == nil)) or p.HasValue(v) then --old version ...else t1[k] = v end
			t1[k] = v
		end
	end
	return t1
end --tableMerge

local wiki_langcode = mw.language.getContentLanguage().code	
function p.get_lang (langcode)
	--form Module:Wikidades
	if mw.language.isKnownLanguageTag(langcode or '') == false then
		if not mw.title.getCurrentTitle().isContentPage then
			local cframe = mw.getCurrentFrame()
			langcode = cframe:preprocess('{{int:lang}}')
		end
		if mw.language.isKnownLanguageTag(langcode or '') == false then
			langcode = wiki_langcode
		end
	end	
	return langcode
end --get_lang
p.lang_to_use = p.get_lang ()

function p.I18nName (ModName)
	local i18n_suffix0 = "/i18n"
	local i18n_suffix = ''
	if p.lang_to_use ~= wiki_langcode then 
		i18n_suffix = i18n_suffix0..'/'..p.lang_to_use 
	else
		i18n_suffix = i18n_suffix0
	end
	local FN = "Module:"..ModName
	local title = mw.title.new (FN..i18n_suffix)
	if title.exists then
		return FN..i18n_suffix
	else
		FN = FN..i18n_suffix0
		title = mw.title.new (FN)
		if title.exists then
			return FN
		else	
			error (string.format('Not found "%s"',FN)) -- Translation not required
		end	
	end
end --I18nName

function p.loadI18n (ModName, DefTable)
	local FN = p.I18nName (ModName)
	return p.tableMerge(DefTable, require(FN).i18n)
end --loadI18n

---

function p.I18nStrParams (S, ...)
	local result = mw.message.newRawMessage (S, ...)
	return result:plain()
end

function p.TrimWdTab (tab)
	for k, i in ipairs(tab) do
		tab[k] = mw.text.trim (i)
	end
	return tab
end	--TrimWdTab

function p.I18nParamsTab (Wds, MaxTrans)
	local tab = mw.text.split (Wds, '|')
	if (MaxTrans ~= nil) and (#tab > MaxTrans) then
		error (string.format('Found %s translations for "%s"',#tab,S)) -- Translation not required
	end
	return p.TrimWdTab (tab)
end --I18nParamsTab

function p.I18nParamsTabCS (Wds, CaseSensitive)
	CaseSensitive = ((CaseSensitive ~= nil) and (CaseSensitive == true)) or true
	if not CaseSensitive then
		Wds = string.lower (Wds)
	end	
    tab = mw.text.split (Wds, '|')
	return p.TrimWdTab (tab)
end --I18nParamsTabCS

function p.SFoundInWdTab (WdTable, val, CaseSensitive, S)
	if (S == nil) or (S == '') then
		error('Not parameters trying to find "'..val..'"') --It doesn't require translation, only for degug
	end	
	local Arr = p.I18nParamsTabCS (WdTable[S], CaseSensitive)
	if not CaseSensitive then
		val = string.lower (val)
	end	
	for _, W in ipairs(Arr) do
		if W == val then
			return true
		end	
	end	
	return false
end --SFoundInWdTab

function p.IdxFromWdTab (WdTable, val, CaseSensitive, ...)
	local Arr = unpack(arg)
	if Arr == nil then
		error('Not parameters trying to find "'..val..'"') --It doesn't require translation, only for degug
	end	
	local Idx = 0
	for I, W in ipairs(Arr) do
		if p.SFoundInWdTab (WdTable, val, CaseSensitive, W) then
			Idx = I
			break
		end	
	end	
	return Idx
end	--IdxFromWdTab

function p.JoinI18nTables (tabs)
	local res = {}
	for _, v in ipairs(tabs) do
		for _, vv in pairs(v) do
			table.insert (res, vv)
		end
	end	
	return res
end	--JoinTables

----------

i18n = p.loadI18n (I18n, i18n)

local function I18nStr (S, ...)
	return p.I18nStrParams (i18n[S], ...)
end

function p.i18n_vals (frame)
    local args = p.GetArgs (frame)
    local res = i18n[p.RStr_Par(args,1)]
    if res == nil then
    	error ('Invalid word')
    else
    	return res
    end	
end --i18n_vals	

----------

p.Error = {
	handle = false,
	yes = false,
	msg = '',
	samesize = false
	}
function p.MsgError (S)
	if S == nil then
		if p.Error.samesize then
			return '<span style="color:red;font-weight:bold">'..p.Error.msg..'</span>'
		else	
			return '<strong class="error">'..p.Error.msg..'</strong>'
		end
	else
		if p.Error.handle then
			p.Error.msg = S
			p.Error.yes = true
		else
			error (S, 0)
		end
	end
end--MsgError

function p.GetAndClearError ()
	local E = p.Error.msg
	p.Error.msg = ''
	p.Error.yes = false
	return E
end	--GetAndClearError

function p.TableSize (tab)
	Count = 0
	for _, v in pairs(tab) do
		Count = Count + 1
	end
	return Count
end --TableSize

function p.TableWithItems (tab)
	if #tab > 0 then
		return true
	else
		for _, v in pairs(tab) do
			return true
		end
		return false
	end	
end	--TableWithItems

function p.GetArgs (frame, HandleError, SameSize)
	local origArgs
	if frame == mw.getCurrentFrame() then
		-- We're being called via #invoke. If the invoking template passed any parameters,
		-- use them. Otherwise, use the parameters that were passed into the template.
		origArgs = frame:getParent().args
		for _, v in pairs( frame.args ) do
			origArgs = frame.args
			break
		end
	else		
		-- We're being called from another module or from the debug console, so assume
		-- the parameters are passed in directly.
		origArgs = frame
	end
	NArgs = p.TableSize (origArgs)
	if (HandleError ~= nil) and (HandleError == true) then
		p.Error.handle = true
		if (SameSize ~= nil) and (SameSize == true) then
			p.Error.samesize = true
		end
	end
	if p.Error.handle and (NArgs == 0) then 
		p.MsgError ('')
	end
	return origArgs, NArgs
end --GetArgs

function p.GetArgsT (frame, HandleError, SameSize)
	--Get paramenters,num from template that call to module (returned 1st,2nd) and from module (returned 3rd,4th) 
	local origArgs, NArgs = p.GetArgs (frame, HandleError, SameSize) 
	pargs = frame:getParent().args
	NPArgs = p.TableSize(pargs)
	if  NPArgs == 0 then
		return pargs, NPArgs, origArgs, NArgs
	else
		return origArgs, NArgs, pargs, NPArgs
	end	
end --GetArgsT

function p.GetArgsTMerge (frame, HandleError, SameSize)
	local arg1, NArg1, arg2, NArg2 = p.GetArgsT (frame, HandleError, SameSize)
	local args = {}
	for k, val in pairs(arg1) do
		args[k] = val
	end	
	for k, val in pairs(arg2) do
		args[k] = val
	end	
	return args
end --GetArgsTMerge	

p.ParaSep = '/'
function p.ConcatParams (tab)
	return '"'..table.concat(tab,p.ParaSep)..'"'
end

local function ParamErrorS (ParId, S)
	if ParId == nil then
		error ('Not assigned ParId') --Not require translation, only debug
	elseif ParId ~= '' then
		local SParaId = ''
		if type(ParId)=='table' then
			SParaId = p.ConcatParams (ParId)
		else
			if type(ParId)=='number' then
				ParId = '#'..ParId
			end
			SParaId = ParId 
		end
		S = SParaId..' = '..S
	end
	p.MsgError (S)
end--ParamErrorS

local function ParamError (ParId, RS, Value)
	if Value == '' then
		Value = '""'
	end
	local S = I18nStr (RS, tostring(Value))
	ParamErrorS (ParId, S)
end--ParamError

----------

local function GetArgsPass (args)
	local ArgsPass = {}
	for k, v in pairs(args) do
		if type(k) == 'string' then
			table.insert (ArgsPass, k)
		end
	end	
	return ArgsPass
end --GetArgsPass

function p.ErrNotFoundArgName (v)
	p.MsgError (I18nStr (RS.NotFoundArgName, v))
end

function p.CheckParams (args, UsualArgs)
	if p.Error.yes then return end
	local ArgsPass = GetArgsPass (args)
	local UsualArgsC = p.deep_copy_table (UsualArgs)
	for _, v in ipairs(ArgsPass) do
		local found = false
		for kk, vv in pairs(UsualArgsC) do
			if type(vv) == 'table' then
				for j, w in ipairs(vv) do
					if v == w then
						found = true
						UsualArgsC[kk][j] = nil
						break
					end	
				end
			else
				if v == vv then
					found = true
					UsualArgsC[kk] = nil
				end	
			end	
			if found then
				break
			end	
		end	
		if not found then
			p.ErrNotFoundArgName (v)
			break
		end	
	end
end --CheckParams

p.HasChild = 'HasChild'
function p.CheckParamsM (args, UsualArgs, OtherArgs, ArgNamesIn1)
--[[ Warning!: UsualArgs is a table of table or parameters, 
	e.g.{args_main_module, args_shared_module1, args_shared_module2} or
		{args_main_module}
	In ArgNamesIn1 mode, each parameter has the name/s and type: e.g.
	["key"] = {"Arg_name", "i+"}   or
	["key"] = {{"Arg_name","arg_name"} "i+"}
	in this case "i+" indicates a positive integer.
--]]
	if p.Error.yes then return end
	if p.Error.handle then
		unknown_param = {}
	end	
	local ArgsPass = GetArgsPass (args)
	local flags_dupli_args = {}
	local flags_dupli_args_s = {}
	local dupli_args = {}
	local key = ''
	local UsualArgsC = p.deep_copy_table (UsualArgs)
	for _, v in ipairs(ArgsPass) do
		local found = false
		for kk, vv in ipairs(UsualArgsC) do
			if vv[p.HasChild] == true then
				for j, w in pairs(vv) do
					if j ~= p.HasChild then
						for jj, ww in pairs(w) do 
							if ArgNamesIn1 then
								ww = ww[1] --skip type
							end	
							if type(ww) == 'table' then
								for jjj, www in ipairs(ww) do
									if v == www then
										found = true
										key = jj
										break
									end	
								end	
							else
								if v == ww then
									key = jj
									found = true
								end	
							end	
							if found then
								break
							end	
						end	
					end
					if found then
						break
					end	
				end	
			else
				for j, w in pairs(vv) do
					if ArgNamesIn1 then
						w = w[1] --skip type
					end	
					if type(w) == 'table' then
						for jj, ww in ipairs(w) do 
							if v == ww then
								key = j
								found = true
								break
							end	
						end	
					else
						if v == w then
							key = j
							found = true
						end	
					end	
					if found then
						break
					end	
				end
			end	
			if found then
				break
			end	
		end
		if found then
			if flags_dupli_args[key] then
				table.insert (dupli_args, key..' ('..flags_dupli_args_s[key]..'-'..v..')')
			else   
		    	flags_dupli_args[key] = true
		    	flags_dupli_args_s[key] = v
	    	end
		end	
		if (not found) and (OtherArgs ~= nil) then
			if type(OtherArgs) == 'table' then
				for _, vv in ipairs(OtherArgs) do
					if v == vv then
						found = true
						break
					end	
				end	
			else
				found = v == vv
			end	
		end	
		if not found then 	
			p.ErrNotFoundArgName (v)
			if p.Error.handle then
				table.insert (unknown_param, p.GetAndClearError())
			else	
				break
			end	
		end	
	end
	if p.Error.handle and ((#unknown_param > 0) or (#dupli_args > 0)) then
		return unknown_param, dupli_args
	end	
end --CheckParamsM

----------

local function prepNum (N)
	return mw.getContentLanguage():formatNum (N)
end

function p.CheckNum (N, ParId, LimInf, LimSup)
	if (p.Error.yes) or (not N) then return end
	if ((LimInf ~= nil) and (N < LimInf)) and ((LimSup ~= nil) and (N > LimSup)) then
		ParamErrorS (ParId, I18nStr (RS.NIsNotInRange, prepNum(N), prepNum(LimInf), prepNum(LimSup)))
	elseif (LimInf ~= nil) and (N < LimInf) then
		ParamErrorS (ParId, I18nStr (RS.NIsLessThan, prepNum(N), prepNum(LimInf)))
	elseif (LimSup ~= nil) and (N > LimSup) then
		ParamErrorS (ParId, I18nStr (RS.NIsGreaterThan, prepNum(N), prepNum(LimSup)))
	end
end --p.CheckNum

function p.CheckNumIsInt (N, ParId, LimInf, LimSup)
	if p.Error.yes then return end
	if N == math.floor(N) then
		p.CheckNum (N, ParId, LimInf, LimSup)
	else
		ParamError (ParId, RS.NIsNotInt, prepNum(N))
	end
end --CheckNumIsInt

function p.CheckNumIsPos (N, ParId, LimInf, LimSup)
	if p.Error.yes then return end
	if N > 0 then
		p.CheckNum (N, ParId, LimInf, LimSup)
	else	
		ParamError (ParId, RS.NIsNotPosNumber, N)
	end
end --CheckNumIsPos

function p.CheckNumIsZeroOrPos (N, ParId, LimSup)
	if p.Error.yes then return end
	if N >= 0 then
		p.CheckNum (N, ParId, 0, LimSup)
	else	
		ParamError (ParId, RS.NIsNotZeroOrPosNumber, N)
	end
end --CheckNumIsZeroOrPos

-----

function p.CheckSIsNum (S, ParId, LimInf, LimSup)
	if p.Error.yes then return end
	local N = tonumber(S)
	if not N then
		ParamError (ParId, RS.SIsNotNumber, S)
	else
		p.CheckNum (N, ParId, LimInf, LimSup)
	end
end --CheckSIsNum

function p.CheckSIsInt (S, Where, LimInf, LimSup)
	if p.Error.yes then return end
   	p.CheckSIsNum (S, Where, LimInf, LimSup) 
	if p.Error.yes then return end
   	p.CheckNumIsInt (tonumber(S), Where)
end

function p.CheckSIsPosInt (S, ParId, LimInf, LimSup)
	if p.Error.yes then return end
   	p.CheckSIsInt (S, ParId, LimInf, LimSup) 
	if p.Error.yes then return end
   	p.CheckNumIsPos (tonumber(S), ParId)
end

function p.CheckSIsZeroOrPosInt (S, ParId, LimSup)
	if p.Error.yes then return end
   	p.CheckSIsInt (S, ParId, 0, LimSup) 
	if p.Error.yes then return end
   	p.CheckNumIsZeroOrPos (tonumber(S), ParId)
end

function p.NotAssignedValue (ParId)
	ParamErrorS (ParId, I18nStr (RS.SIsNotAssigned))
end

----------

p.OKEmptyVal = true
function p.Str_Par (Args, ParId, Default)
	if p.Error.yes then return end
	local S = nil
	if type(ParId) == 'table' then
		for _, W in ipairs(ParId) do
			S = Args[W]
			if S ~= nil then
				break
			end
		end
	else	
		S = Args[ParId]
	end
	if S == nil then
		if Default ~= nil then
			S = Default
		end
	else
		S = mw.text.trim (S)
		if S == '' then
			if Default ~= nil then
				S = Default
			elseif not p.OKEmptyVal then
				S = nil
			end	
		end
	end
	return S
end --Str_Par

function p.RStr_Par (Args, ParId, OKEmpty)
	if p.Error.yes then return end
	local S = p.Str_Par (Args, ParId)
	if (S == nil) or ((S == '') and ((OKEmpty == nil) or (not OKEmpty))) then
		p.NotAssignedValue (ParId)
	else
		return S
	end
end --RStr_Par

function p.Char_Par (Args, ParId, Pattern, Default)
	if p.Error.yes then return end
	local Char = p.Str_Par (Args, ParId, Default)
	if p.Error.yes then return end
	if Char ~= nil then
		local Valid = ((Default ~= nil) and (Char == Default)) or (string.len(Char) == 1) and (string.find(Pattern,Char) ~= nil)
		if not Valid then
			ParamErrorS (ParId, I18nStr (RS.CharNotFoundInPattern, Char, Pattern))
		end
	end
	return Char
end --Char_Par

function p.RChar_Par (Args, ParId, Pattern)
	if p.Error.yes then return end
	local Char = p.Char_Par (Args, ParId, Pattern)
	if p.Error.yes then return end
	if Char == nil then
		p.NotAssignedValue (ParId)
	else
		return Char
	end
end --RChar_Par

function p.NulOrWhitespace_Par (Args, ParId)
	if p.Error.yes then return end
	local S = p.Str_Par (Args, ParId)
	if p.Error.yes then return end
	return (S == nil) or (S == '')
end

local function PerhapsLow (CaseSens, Wd)
	if CaseSens then
		return Wd
	else
		return string.lower(Wd)
	end
end --PerhapsLow

function p.StrChkTab_Par0 (ParId, S, Tab, CaseSens, Default) 
	if (S ~= nil) and (S ~= Default) then
		local SIni = S
		S = PerhapsLow(CaseSens,S)
		local found = false
		for _, W in ipairs(Tab) do
			if S == PerhapsLow(CaseSens,W) then
				S = W
				found = true
				break
			end
		end
		if not found then
			ParamErrorS (ParId, I18nStr (RS.SNotFoundInTab, SIni, table.concat(Tab,', ')))
			S = nil
		end
	end
	return S
end --StrChkTab_Par0

function p.StrChkTab_Par (Args, ParId, Tab, CaseSens, Default)
	if p.Error.yes then return end
	local S = p.Str_Par (Args, ParId, Default)
	if p.Error.yes then return end
	return p.StrChkTab_Par0 (ParId, S, Tab, CaseSens, Default)
end --StrChkTab_Par

function p.RStrChkTab_Par (Args, ParId, Tab, CaseSens)
	if p.Error.yes then return end
	local S = p.StrChkTab_Par (Args, ParId, Tab)
	if p.Error.yes then return end
	if S == nil then
		p.NotAssignedValue (ParId)
	else
		return S
	end
end --RStrChkTab_Par

function p.StrIdxChkTab_Par (Args, ParId, Tab, CaseSens, Default)
	if p.Error.yes then return end
	local S = p.Str_Par (Args, ParId, Default)
	if p.Error.yes then return end
	if (S ~= nil) and (S ~= Default) then
		local SIni = S
		S = PerhapsLow(CaseSens,S)
		local found = false
		for I, W in ipairs(Tab) do
			if S == PerhapsLow(CaseSens,W) then
				S = I
				found = true
				break
			end
		end
		if not found then
			ParamErrorS (ParId, I18nStr (RS.SNotFoundInTab, SIni, table.concat(Tab,', ')))
			S = nil
		end
	end
	return S
end --StrIdxChkTab_Par

function p.RStrIdxChkTab_Par (Args, ParId, Tab, CaseSens)
	if p.Error.yes then return end
	local S = p.StrIdxChkTab_Par (Args, ParId, Tab)
	if p.Error.yes then return end
	if S == nil then
		p.NotAssignedValue (ParId)
	else
		return S
	end
end --RStrIdxChkTab_Par

--Used in parameters to force an integer, number and others to return "NONE" (parameter to non-use)
p.AcceptNone = false 
p.None = "NONE"
local function IsNone (S)
	return p.AcceptNone and (S == p.None)
end	

function p.Num_Par (Args, ParId, Default, LimInf, LimSup)
	if p.Error.yes then return end
	local N = p.Str_Par (Args, ParId)
	if p.Error.yes then return end
	if IsNone(N) then return p.None end
	if N == nil then
		if Default ~= nil then
			N = Default
		end
		return N, true
	else
		p.CheckSIsNum (N, ParId, LimInf, LimSup)
		if p.Error.yes then return end
		return tonumber(N), false
	end
end --Num_Par

function p.RNum_Par (Args, ParId, LimInf, LimSup)
	if p.Error.yes then return end
	local N = p.Num_Par (Args, ParId, nil, LimInf, LimSup)
	if p.Error.yes then return end
	if N == nil then
		p.NotAssignedValue (ParId)
	else
		return N, false
	end
end --RNum_Par

function p.PosNum_Par (Args, ParId, Default, LimInf, LimSup)
	if p.Error.yes then return end
	local N, IsDefault = p.Num_Par (Args, ParId, Default, LimInf, LimSup)
	if IsNone(N) then return p.None end
	if p.Error.yes then return end
	if not IsDefault then
		p.CheckNumIsPos (N, ParId)
		if p.Error.yes then return end
	end
	return N
end --PosNum_Par

function p.RPosNum_Par (Args, ParId, LimInf, LimSup)
	if p.Error.yes then return end
	local N = p.PosNum_Par (Args, ParId, nil, LimInf, LimSup)
	if p.Error.yes then return end
	if N == nil then
		p.NotAssignedValue (ParId)
	else
		return N
	end
end --RNum_Par

function p.ZeroOrPosNum_Par (Args, ParId, Default, LimSup)
	if p.Error.yes then return end
	local N, IsDefault = p.Num_Par (Args, ParId, Default, 0, LimSup)
	if IsNone(N) then return p.None end
	if p.Error.yes then return end
	if not IsDefault then
		p.CheckNumIsZeroOrPos (N, ParId)
		if p.Error.yes then return end
	end
	return N
end --ZeroOrPosNum_Par

function p.RZeroOrPosNum_Par (Args, ParId, LimSup)
	if p.Error.yes then return end
	local N = p.ZeroOrPosNum_Par (Args, ParId, nil, LimSup)
	if p.Error.yes then return end
	if N == nil then
		p.NotAssignedValue (ParId)
	else
		return N
	end
end --RZeroOrPosNum_Par

function p.Int_Par (Args, ParId, Default, LimInf, LimSup)
	N, IsDefault = p.Num_Par (Args, ParId, Default, LimInf, LimSup)
	if IsNone(N) then return p.None end
	if p.Error.yes then return end
	if not IsDefault then
		p.CheckNumIsInt (N, ParId)
		if p.Error.yes then return end
	end
	return N
end --Int_Par

function p.RInt_Par (Args, ParId, LimInf, LimSup)
	if p.Error.yes then return end
	local N = p.Int_Par (Args, ParId, nil, LimInf, LimSup)
	if p.Error.yes then return end
	if N == nil then
		p.NotAssignedValue (ParId)
	else
		return N, true
	end
end --RInt_Par

function p.PosInt_Par (Args, ParId, Default, LimInf, LimSup)
	if p.Error.yes then return end
	local N = p.Int_Par (Args, ParId, Default, LimInf, LimSup)
	if IsNone(N) then return p.None end
	if p.Error.yes then return end
	if N ~= nil then
		p.CheckNumIsPos (N, ParId)
		if p.Error.yes then return end
	end
	return N
end --PosInt_Par

function p.RPosInt_Par (Args, ParId, LimInf, LimSup)
	if p.Error.yes then return end
	local N = p.PosInt_Par (Args, ParId, nil, LimInf, LimSup)
	if p.Error.yes then return end
	if N == nil then
		p.NotAssignedValue (ParId)
	else
		return N, false
	end
end --RPosInt_Par

function p.ZeroOrPosInt_Par (Args, ParId, Default, LimSup)
	if p.Error.yes then return end
	local N = p.Int_Par (Args, ParId, Default, 0, LimSup)
	if IsNone(N) then return p.None end
	if p.Error.yes then return end
	if N ~= nil then
		p.CheckNumIsZeroOrPos (N, ParId)
		if p.Error.yes then return end
	end
	return N
end --ZeroOrPosInt_Par

function p.RZeroOrPosInt_Par (Args, ParId, LimSup)
	if p.Error.yes then return end
	local N = p.ZeroOrPosInt_Par (Args, ParId, nil, LimSup)
	if p.Error.yes then return end
	if N == nil then
		p.NotAssignedValue (ParId)
	else
		return N, true
	end
end --RZeroOrPosInt_Par

---

local function PossibleValues (tab)
	local S = ''
	for _, W in ipairs(tab) do
		if S ~='' then
			S = S..', '
		end	
		local tab = {}
		tab2 = p.I18nParamsTabCS (i18n[W])
		S = S..table.concat (tab2, p.ParaSep)
	end
	return I18nStr (RS.PossibleValues, S)
end --PossibleValues	

---

function p.CheckSize (CurCol, width) --used in XCols
	local val = string.match (width, '%d[%d.,]*')
	if val == nil then
		ParamErrorS (CurCol, I18nStr (RS.SizeWithoutNumber))
	else
		local unit = string.sub (width, #val+1)
		if unit == '' then
			ParamErrorS (CurCol, I18nStr (RS.SizeUnitRequired))
		elseif (unit ~= '%') and (unit ~= 'em') and (unit ~= 'px') then
			ParamErrorS (CurCol, I18nStr (RS.InvalSizeUnit, unit))
		end
	end	
end --CheckSize	

function p.Size_Par (Args, ParId, WithPerc, limits, Default)
	if p.Error.yes then return end
	local width = p.Str_Par (Args, ParId, Default)
	if p.Error.yes then return end
	if p.HasValue (width) then
		local val = string.match (width, '%d[%d.,]*')
		if val == nil then
			ParamErrorS (ParId, I18nStr (RS.SizeWithoutNumber))
		else
			local unit = string.sub (width, #val+1)
			if unit == '' then
				ParamErrorS (ParId, I18nStr (RS.SizeUnitRequired))
			elseif (not WithPerc) and (unit == '%') then	
				ParamErrorS (ParId, I18nStr (RS.InvalSizeUnit, unit))
			elseif (unit ~= '%') and (unit ~= 'em') and (unit ~= 'px') then
				ParamErrorS (ParId, I18nStr (RS.InvalSizeUnit, unit))
			elseif limits ~= nil then
				checked = false
				val = tonumber (val)
				function Check (sel_lim, sel_unit)
					if (not checked) and (sel_lim ~= nil) and (unit == sel_unit) then
						if ((sel_lim[1] ~= nil) and (val < sel_lim[1])) or ((sel_lim[2] ~= nil) and (val > sel_lim[2])) then
							ParamErrorS (ParId, I18nStr (RS.NIsNotInRange, width, sel_lim[1]..sel_unit, sel_lim[2]..sel_unit))
						end	
						checked = true
					end	
				end	
				Check (limits['em'], 'em')
				Check (limits['px'], 'px')
				if WithPerc then
					Check (limits['perc'], '%')
				end	
			end
		end
	end
	return width
end	--Size_Par

function p.RSize_Par (Args, ParId, WithPerc, limits)
	if p.Error.yes then return end
	local width = p.Size_Par (Args, ParId, WithPerc, limits)
	if p.Error.yes then return end
	if width == nil then
		p.NotAssignedValue (ParId)
	else
		return width
	end
end --RSize_Par

p.Colors = {
	-- Pink colors
	["pink"]			= 'FFC0CB', -- Pink
	["lightpink"]		= 'FFB6C1', -- LightPink
	["hotpink"]			= 'FF69B4', -- HotPink
	["deeppink"]		= 'FF1493', -- DeepPink
	["palevioletred"] 	= 'DB7093', -- PaleVioletRed
	["mediumvioletred"] = 'C71585', -- MediumVioletRed
	-- Red colors
	["lightsalmon"]		= 'FFA07A', -- LightSalmon
	["salmon"]			= 'FA8072', -- Salmon
	["darksalmon"]		= 'E9967A', -- DarkSalmon
	["lightcoral"]		= 'F08080', -- LightCoral
	["indianred"]		= 'CD5C5C', -- IndianRed
	["crimson"]			= 'DC143C', -- Crimson
	["firebrick"]		= 'B22222', -- Firebrick
	["darkred"]			= '8B0000', -- DarkRed
	["red"]				= 'FF0000', -- Red
	-- Orange colors
	["orangered"]		= 'FF4500', -- OrangeRed
	["tomato"]			= 'FF6347', -- Tomato
	["coral"]			= 'FF7F50', -- Coral
	["darkorange"]		= 'FF8C00', -- DarkOrange
	["orange"]			= 'FFA500', -- Orange
	-- Yellow colors
	["yellow"]			= 'FFFF00', -- Yellow
	["lightyellow"]		= 'FFFFE0', -- LightYellow
	["lemonchiffon"]	= 'FFFACD', -- LemonChiffon
	["lightgoldenrodyellow"] = 'FAFAD2', -- LightGoldenrodYellow
	["papayawhip"]		= 'FFEFD5', -- PapayaWhip
	["moccasin"]		= 'FFE4B5', -- Moccasin
	["peachpuff"]		= 'FFDAB9', -- PeachPuff
	["palegoldenrod"]	= 'EEE8AA', -- PaleGoldenrod
	["khaki"]			= 'F0E68C', -- Khaki
	["darkkhaki"]		= 'BDB76B', -- DarkKhaki
	["gold"]			= 'FFD700', -- Gold
	-- Brown colors
	["cornsilk"]		= 'FFF8DC', -- Cornsilk
	["blanchedalmond"]	= 'FFEBCD', -- BlanchedAlmond
	["bisque"]			= 'FFE4C4', -- Bisque
	["navajowhite"]		= 'FFDEAD', -- NavajoWhite
	["wheat"]			= 'F5DEB3', -- Wheat
	["burlywood"]		= 'DEB887', -- Burlywood
	["tan"]				= 'D2B48C', -- Tan
	["rosybrown"]		= 'BC8F8F', -- RosyBrown
	["sandybrown"]		= 'F4A460', -- SandyBrown
	["goldenrod"]		= 'DAA520', -- Goldenrod
	["darkgoldenrod"]	= 'B8860B', -- DarkGoldenrod
	["peru"]			= 'CD853F', -- Peru
	["chocolate"]		= 'D2691E', -- Chocolate
	["saddlebrown"]		= '8B4513', -- SaddleBrown
	["sienna"]			= 'A0522D', -- Sienna
	["brown"]			= 'A52A2A', -- Brown
	["maroon"]			= '800000', -- Maroon
	-- Green colors
	["darkolivegreen"]	= '556B2F', -- DarkOliveGreen
	["olive"]			= '808000', -- Olive
	["olivedrab"]		= '6B8E23', -- OliveDrab
	["yellowgreen"]		= '9ACD32', -- YellowGreen
	["limegreen"]		= '32CD32', -- LimeGreen
	["lime"]			= '00FF00', -- Lime
	["lawngreen"]		= '7CFC00', -- LawnGreen
	["chartreuse"]		= '7FFF00', -- Chartreuse
	["greenyellow"]		= 'ADFF2F', -- GreenYellow
	["springgreen"]		= '00FF7F', -- SpringGreen
	["mediumspringgreen"] = '00FA9A', -- MediumSpringGreen
	["lightgreen"]		= '90EE90', -- LightGreen
	["palegreen"]		= '98FB98', -- PaleGreen
	["darkseagreen"]	= '8FBC8F', -- DarkSeaGreen
	["mediumaquamarine"] = '66CDAA', -- MediumAquamarine
	["mediumseagreen"]	= '3CB371', -- MediumSeaGreen
	["seagreen"]		= '2E8B57', -- SeaGreen
	["forestgreen"]		= '228B22', -- ForestGreen
	["green"]			= '008000', -- Green
	["darkgreen"]		= '006400', -- DarkGreen
	-- Cyan colors
	["aqua"]			= '00FFFF', -- Aqua
	["cyan"]			= '00FFFF', -- Cyan
	["lightcyan"]		= 'E0FFFF', -- LightCyan
	["paleturquoise"]	= 'AFEEEE', -- PaleTurquoise
	["aquamarine"]		= '7FFFD4', -- Aquamarine
	["turquoise"]		= '40E0D0', -- Turquoise
	["mediumturquoise"]	= '48D1CC', -- MediumTurquoise
	["darkturquoise"]	= '00CED1', -- DarkTurquoise
	["lightseagreen"]	= '20B2AA', -- LightSeaGreen
	["cadetblue"]		= '5F9EA0', -- CadetBlue
	["darkcyan"]		= '008B8B', -- DarkCyan
	["teal"]			= '008080', -- Teal
	-- Blue colors
	["lightsteelblue"]	= 'B0C4DE', -- LightSteelBlue
	["powderblue"]		= 'B0E0E6', -- PowderBlue
	["lightblue"]		= 'ADD8E6', -- LightBlue
	["skyblue"]			= '87CEEB', -- SkyBlue
	["lightskyblue"]	= '87CEFA', -- LightSkyBlue
	["deepskyblue"]		= '00BFFF', -- DeepSkyBlue
	["dodgerblue"]		= '1E90FF', -- DodgerBlue
	["cornflowerblue"]	= '6495ED', -- CornflowerBlue
	["steelblue"]		= '4682B4', -- SteelBlue
	["royalblue"]		= '4169E1', -- RoyalBlue
	["blue"]			= '0000FF', -- Blue
	["mediumblue"]		= '0000CD', -- MediumBlue
	["darkblue"]		= '00008B', -- DarkBlue
	["navy"]			= '000080', -- Navy
	["midnightblue"]	= '191970', -- MidnightBlue
	-- Purple, violet, and magenta colors
	["lavender"]		= 'E6E6FA', -- Lavender
	["thistle"]			= 'D8BFD8', -- Thistle
	["plum"]			= 'DDA0DD', -- Plum
	["violet"]			= 'EE82EE', -- Violet
	["orchid"]			= 'DA70D6', -- Orchid
	["fuchsia"]			= 'FF00FF', -- Fuchsia
	["magenta"]			= 'FF00FF', -- Magenta
	["mediumorchid"]	= 'BA55D3', -- MediumOrchid
	["mediumpurple"]	= '9370DB', -- MediumPurple
	["blueviolet"]		= '8A2BE2', -- BlueViolet
	["darkviolet"]		= '9400D3', -- DarkViolet
	["darkorchid"]		= '9932CC', -- DarkOrchid
	["darkmagenta"]		= '8B008B', -- DarkMagenta
	["purple"]			= '800080', -- Purple
	["indigo"]			= '4B0082', -- Indigo
	["darkslateblue"]	= '483D8B', -- DarkSlateBlue
	["slateblue"]		= '6A5ACD', -- SlateBlue
	["mediumslateblue"]	= '7B68EE', -- MediumSlateBlue
	-- White colors
	["white"]			= 'FFFFFF', -- White
	["snow"]			= 'FFFAFA', -- Snow
	["honeydew"]		= 'F0FFF0', -- Honeydew
	["mintcream"]		= 'F5FFFA', -- MintCream
	["azure"]			= 'F0FFFF', -- Azure
	["aliceblue"]		= 'F0F8FF', -- AliceBlue
	["ghostwhite"]		= 'F8F8FF', -- GhostWhite
	["whitesmoke"]		= 'F5F5F5', -- WhiteSmoke
	["seashell"]		= 'FFF5EE', -- Seashell
	["beige"]			= 'F5F5DC', -- Beige
	["oldlace"]			= 'FDF5E6', -- OldLace
	["floralwhite"]		= 'FFFAF0', -- FloralWhite
	["ivory"]			= 'FFFFF0', -- Ivory
	["antiquewhite"]	= 'FAEBD7', -- AntiqueWhite
	["linen"]			= 'FAF0E6', -- Linen
	["lavenderblush"]	= 'FFF0F5', -- LavenderBlush
	["mistyrose"]		= 'FFE4E1', -- MistyRose
	-- Gray and black colors
	["gainsboro"]		= 'DCDCDC', -- Gainsboro
	["lightgray"]		= 'D3D3D3', -- LightGray
	["silver"]			= 'C0C0C0', -- Silver
	["darkgray"]		= 'A9A9A9', -- DarkGray
	["gray"]			= '808080', -- Gray
	["dimgray"]			= '696969', -- DimGray
	["lightslategray"]	= '778899', -- LightSlateGray
	["slategray"]		= '708090', -- SlateGray
	["darkslategray"]	= '2F4F4F', -- DarkSlateGray
	["black"]			= '000000', -- Black
}

function p.ColorToHex (frame)
    local args = p.GetArgs (frame)
	return p.RColor_Par (args, 1)
end	

local function GetRValue (rgb)
  -- Red color
  return tonumber (string.sub(rgb,1,2), 16)
end

local function GetGValue(rgb)
  -- Green color	
  return tonumber (string.sub(rgb,3,4), 16)
end

local function GetBValue(rgb)
  -- BLue color	
  return tonumber (string.sub(rgb,5,6), 16)
end

function p.CheckSIsColor (S, ParId, Default)
	-- Returns if is valid and the color in format NNNNNN i.e. 
	--   for S == 'Green' returns true,'008000'
	--   for S == '#008000' returns true,'008000'
	function SixCharsFromCol (Col)
		if Col == nil then
			return nil
		else
			function NumColor (Col)
				if string.len (Col) ~= 6 then
					ParamError (ParId, RS.InvalColorLength, Col)
				end	
				if tonumber ('0x'..Col) == nil then
					ParamError (ParId, RS.InvalColorChar, Col)
				end	
			end	
			if string.sub (Col, 1, 5) == '&#35;' then
				Col = string.sub (Col, 6)
				NumColor (Col)
			elseif string.sub (Col, 1, 1) == '#' then
				Col = string.sub (Col, 2)
				NumColor (Col)
			else
				local ColI = Col
				local rgbn = string.lower (Col)
				Col = p.Colors[rgbn] 
				if Col == nil then
			  		ParamError (ParId, RS.InvalColorName, ColI)
				end
			end	
			if p.Error.yes then
				return nil
			else
				return Col
			end	
		end	
	end	-- SixCharsFromCol
	if p.Error.yes then return end
	if S == nil then
		S = Default
		return SixCharsFromCol (S) 
	end
	if S == nil then
		return nil
	else
		return SixCharsFromCol (S) 
	end	
end --CheckSIsColor

function p.Color_Par (Args, ParId, Default)
	if p.Error.yes then return end
	local rgb = p.Str_Par (Args, ParId)
	if p.Error.yes then return end
	return p.CheckSIsColor (rgb, ParId, Default)
end	--Color_Par

function p.RColor_Par (Args, ParId, Default)
	if p.Error.yes then return end
	local rgb = p.RStr_Par (Args, ParId)
	if p.Error.yes then return end
	return p.CheckSIsColor (rgb, ParId)
end	--Color_Par

---

function p.HAlign_Par (Args, ParId, Default)
	local HAlign = {
		RS.left,
		RS.right,
		RS.center,
	}
	if p.Error.yes then return end
	local align = p.Str_Par (Args, ParId)
	if p.Error.yes then return end
	if (align == nil) or (align == '') then
		return Default
	else	
		local Idx = p.IdxFromWdTab (i18n, align, true, HAlign)
		if Idx == 0 then
			ParamErrorS (ParId, I18nStr (RS.InvalAlign,align)..'. '..PossibleValues(HAlign))
		else
			return HAlign[Idx]
		end
	end
end --HAlign_Par

function p.RHAlign_Par (Args, ParId)
	if p.Error.yes then return end
	local align = p.HAlign_Par (Args, ParId)
	if p.Error.yes then return end
	if align == nil then
		p.NotAssignedValue (ParId)
	else
		return align
	end
end --RHAlign_Par

---

function p.VAlign_Par (Args, ParId, Default)
	local VAlign = {
		RS.top,
		RS.bottom,
		RS.center,
	}
	if p.Error.yes then return end
	local align = p.Str_Par (Args, ParId)
	if p.Error.yes then return end
	if (align == nil) or (align == '') then
		return Default
	else	
		local Idx = p.IdxFromWdTab (i18n, align, true, VAlign)
		if Idx == 0 then
			ParamErrorS (ParId, I18nStr (RS.InvalAlign,align)..'. '..PossibleValues(VAlign))
		else
			return VAlign[Idx]
		end
	end
end --VAlign_Par

function p.RVAlign_Par (Args, ParId)
	if p.Error.yes then return end
	local align = p.VAlign_Par (Args, ParId)
	if p.Error.yes then return end
	if align == nil then
		p.NotAssignedValue (ParId)
	else
		return align
	end
end --RVAlign_Par

---

function p.Bool_Par (Args, ParId, Default)
	local yesno = {
		RS.Yes,
		RS.No,
	}
	if p.Error.yes then return end
	local B = p.Str_Par (Args, ParId)
	if IsNone(N) then return p.None end
	if p.Error.yes then return end
	if (B == nil) or (B == '') then
		return Default
	else
		local Idx = p.IdxFromWdTab (i18n, B, true, yesno)
		if Idx == 0 then
			ParamErrorS (ParId, I18nStr (RS.InvalBool,B)..'. '..PossibleValues(yesno))
		elseif Idx == 1 then
			return true
		elseif Idx == 2 then	
			return false
		end
	end
end --Bool_Par

function p.RBool_Par (Args, ParId)
	if p.Error.yes then return end
	local B = p.Bool_Par (Args, ParId)
	if p.Error.yes then return end
	if B == nil then
		p.NotAssignedValue (ParId)
	else
		return B
	end
end --RBool_Par

---

function SFoundInArr (val, CaseSens, ParId)
	found = false
	if type(ParId) == 'table' then
		for _, W in ipairs(ParId) do
			if val == PerhapsLow(CaseSens, W) then
				found = true
				break
			end
		end
	else
		if val == PerhapsLow(CaseSens, ParId) then
			found = true
		end
	end
	return found
end --SFoundInArr

local function StrIdxChkTab0 (val, CaseSens, Default, ...)
	if p.Error.yes then return end
	if arg == nil then
		error('Not parameters trying to find "'..W..'"') --It doesn't require translation, only for degug
	end
	local Idx = 0
	if not p.Error.yes then 
		if not CaseSens then
			val = string.lower(val)
		end
		local tab = unpack(arg)
		for I, W in ipairs(tab) do
			if SFoundInArr (val, CaseSens, W) then
				Idx = I
				break
			end
		end
		if (Idx == 0) and (Default ~= nil) then
			Idx = Default
		end
	end
	if p.Error.yes then 
		return p.MsgError()
	else
		return Idx
	end
end --StrIdxChkTab0

function p.StrIdxChkTab (Args, ParId, CaseSens, Default, ...)
	if p.Error.yes then return end
	local W = p.Str_Par (Args, ParId, Default)
	local Idx = StrIdxChkTab0 (W, CaseSens, Default, arg)
	if p.Error.yes then 
		return p.MsgError()
	else
		return Idx
	end
end--StrIdxChkTab
	
function p.RStrIdxChkTab (Args, ParId, CaseSens, ...)
	if p.Error.yes then return end
	local W = p.RStr_Par (Args, ParId)
	local Idx = StrIdxChkTab0 (W, CaseSens, nil, arg)
	if p.Error.yes then 
		return p.MsgError()
	else
		return Idx
	end
end --RStrIdxChkTab

local function IdxOrNotFound (ParId, W, Idx, ...)
	local Err = {}
	if Idx == 0 then
		local tab = unpack(arg)
		for _, Wd in ipairs(tab) do
			if type(Wd) == 'table' then
				table.insert (Err, table.concat(Wd,p.ParaSep))
			else
				table.insert (Err, Wd)
			end
		end
		ParamErrorS (ParId, I18nStr (RS.SNotFoundInTab, W, table.concat(Err,', ')))
	else
		return Idx
	end
end --IdxOrNotFound	

function p.StrIdxChkTabE (Args, ParId, CaseSens, Default, ...)
	if p.Error.yes then return end
	local W = p.Str_Par (Args, ParId, Default)
	local Idx = StrIdxChkTab0 (W, CaseSens, Default, arg)
	if p.Error.yes then 
		return p.MsgError()
	else
		return IdxOrNotFound (ParId, W, Idx, arg)
	end
end--StrIdxChkTabE
	
function p.RStrIdxChkTabE (Args, ParId, CaseSens, ...)
	if p.Error.yes then return end
	local W = p.RStr_Par (Args, ParId)
	local Idx = StrIdxChkTab0 (W, CaseSens, nil, arg)
	if p.Error.yes then 
		return p.MsgError()
	else
		return IdxOrNotFound (ParId, W, Idx, arg)
	end
end --RStrIdxChkTabE

-----

local function InRange (Num, ParId, MinItemNum, MaxItemNum)
	out = ((MinItemNum ~= nil) and (Num < MinItemNum)) or ((MaxItemNum ~= nil) and (Num > MaxItemNum))
	if out then
		if MaxItemNum ~= nil then
			ParamErrorS (ParId, I18nStr (RS.STabIsNotInRange, Num, tostring(MinItemNum), tostring(MaxItemNum)))
		else
			ParamErrorS (ParId, I18nStr (RS.STabFewItems, Num, tostring(MinItemNum)))
		end
	end
	return not out
end--InRange

function p.StrTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	if Sep == nil then
		error ('Undefined items separator') -- Don't translate, it ies only for debug
	end
	if OnEmpty == nil then
		OnEmpty = 2  --set an empty value error
	end
	local tab = mw.text.split (p.Str_Par (Args, ParId), Sep)
	if p.Error.yes then return end
	if InRange (#tab, ParId, MinItemNum, MaxItemNum) then
		for I, W in ipairs(tab) do
			tab[I] = mw.text.trim (tab[I])
		end
		if OnEmpty == 0 then
			return tab
		elseif OnEmpty == 1 then
			local tab2 = {} 
			for _, W in ipairs(tab) do
				if W ~= '' then
					table.insert (tab2, W)
				end
			end
			return tab2
		else
			for _, W in ipairs(tab) do
				if W == '' then
					ParamError (ParId, RS.EmptyValue)
				end
			end
			return tab
		end
	end
end --StrTab_1Par

function p.NumTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	tab = p.StrTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, OnEmpty)
	if p.Error.yes then return end
	for I, W in ipairs(tab) do
		if W ~= nil then
			local J = tonumber(W)
			if not J then
				ParamError (ParId, RS.SIsNotNumber, W)
				return tab
			else
				p.CheckNum (J, Pos, LimInf, LimSup)
				if p.Error.yes then return end
				tab[I] = J
			end
		end
	end
	return tab
end --NumTab_1Par	

function p.PosNumTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	tab = p.NumTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsPos (W, ParId)
		end
		if p.Error.yes then return end
	end
	return tab
end --ZeroOrPosNumTab_1Par	

function p.ZeroOrPosNumTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	tab = p.NumTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsZeroOrPos (W, ParId)
		end
		if p.Error.yes then return end
	end
	return tab
end --ZeroOrPosNumTab_1Par	

function p.IntTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	tab = p.NumTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsInt (W, ParId)
		end
		if p.Error.yes then return end
	end
	return tab
end--IntTab_1Par

function p.PosIntTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	tab = p.IntTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsPos (W, ParId)
		end
		if p.Error.yes then return end
	end
	return tab
end --PosIntTab_1Par

function p.ZeroOrPosIntTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	tab = p.IntTab_1Par (Args, ParId, Sep, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsZeroOrPos (W, ParId)
		end
		if p.Error.yes then return end
	end
	return tab
end --ZeroOrPosIntTab_1Par

--------

function p.StrTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, OnEmpty)
	local tab = {}
	if OnEmpty == nil then
		OnEmpty = 2  --set an empty value error
	end
	function IncInTab (val)
		val = mw.text.trim(val)
		if OnEmpty == 0 then
	 		if val ~= '' then
				table.insert(tab,val)
			end
		elseif OnEmpty == 1 then
	 		table.insert(tab,val)
		else
			if val == '' then
				ParamError (ParId, RS.EmptyValue)
			else	
	 			table.insert(tab,val)
			end
	 	end
	end
	if p.Error.yes then return end
	if type(ParId) == 'number' then
		if ParId == 1 then
			for _, v in ipairs(Args) do
				IncInTab (v)
				if p.Error.yes then return end
			end
		else	
			for k = ParId, NArgs do
				local S = Args[k]
				IncInTab (S)
				if p.Error.yes then return end
			end
		end	
	elseif type(ParId) == 'string' then
		found = false
		local Pos = 1
		while not found do
			local key = string.format (ParId, Pos)
			for k, v in pairs(Args) do
				if k == key then
					IncInTab (v)
					if p.Error.yes then return end
					found = true
					break
				end
			end
			if not found then 
				break
			end
			found = false
			Pos = Pos + 1
		end
	elseif type(ParId) == 'table' then
		found = false
		local Pos = 1
		while not found do
			for _, b in ipairs(ParId) do
				local key = string.format (b, Pos)
				for k, v in pairs(Args) do
					if k == key then
						IncInTab (v)
						if p.Error.yes then return end
						found = true
						break
					end
				end
				if found then
					break
				end
			end
			if not found then 
				break
			end
			found = false
			Pos = Pos + 1
		end
	end
	InRange (#tab, ParId, MinItemNum, MaxItemNum)
	return tab, #tab
end --StrTab_NPar

function p.NumTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local tab = p.StrTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, OnEmpty)
	if p.Error.yes then return end
	local Pos = ParId
	for I, W in ipairs(tab) do
		if W ~= nil then
			local J = tonumber(W)
			if not J then
				ParamError (Pos, RS.SIsNotNumber, W)
			else
				p.CheckNum (J, Pos, LimInf, LimSup)
				if p.Error.yes then return end
				tab[I] = J
			end
		end
		Pos = Pos + 1
	end
	return tab
end --NumTab_NPar

function p.PosNumTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local tab = p.NumTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local Pos = ParId
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsPos (W, Pos) 
		end
		if p.Error.yes then return end
		Pos = Pos + 1
	end
	return tab
end --PosNumTab_NPar

function p.ZeroOrPosNumTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local tab = p.NumTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local Pos = ParId
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsZeroOrPos (W, Pos) 
		end
		if p.Error.yes then return end
		Pos = Pos + 1
	end
	return tab
end --ZeroOrPosNumTab_NPar

function p.IntTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local tab = p.NumTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local Pos = ParId
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckSIsInt (W, Pos) 
		end
		if p.Error.yes then return end
		Pos = Pos + 1
	end
	return tab
end --IntTab_NPar

function p.PosIntTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local tab = p.IntTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local Pos = ParId
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsPos (W, Pos) 
		end
		if p.Error.yes then return end
		Pos = Pos + 1
	end
	return tab
end --PosIntTab_NPar

function p.ZeroOrPosIntTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local tab = p.IntTab_NPar (Args, NArgs, ParId, MinItemNum, MaxItemNum, LimInf, LimSup, OnEmpty)
	if p.Error.yes then return end
	local Pos = ParId
	for _, W in ipairs(tab) do
		if W ~= nil then
			p.CheckNumIsZeroOrPos (W, Pos)
		end
		if p.Error.yes then return end
		Pos = Pos + 1
	end
	return tab
end --ZeroOrPosIntTab_NPar

----------

function p.TemplateName(frame)
	local S = frame:getParent():getTitle()
	return string.sub(S, string.find(S,':')+1)
end	

function p.MainTemplateName(frame)
	local S = p.TemplateName(frame)
	return string.sub (S, 1, #S - S:reverse():find("/"))
end	

local function CheckIsStrNotEmpty (v, S)
	if v == '' then
		error ('"'..S..'" has not assigned value') -- Translation not required
	else	
		return v
	end	
end --CheckIsStrNotEmpty

function p.CheckIsStr (v, S)
	if type(v) == "string" then
		return CheckIsStrNotEmpty (v, S)
	elseif type(v) == nil then
		error ('Not found item for "'..S..'"') -- Translation not required
	else	
		SD.vtos (v)
		error ('"'..SD.s..'" (for "'..S..'") is not a string') -- Translation not required
	end	
end --CheckIsStr

local function WhenNoStrOrTab (v, S)
	SD.vtos (v)
	error ('"'..SD.s..'" (for "'..S..'") is not a string/table') -- Translation not required
end

function p.CheckIsStrOrTab (v, S)
	--It does not allow empty strings
	if type(v) == "string" then
		return CheckIsStrNotEmpty (v, S)
	elseif type(v) == "table" then
		for _, vv in ipairs(v) do
			if type(vv) == "string" then
				CheckIsStrNotEmpty (vv, S)
			end	
		end	
		return v
	else
		WhenNoStrOrTab (v, S)
	end	
end --CheckIsStrOrTab

function p.CheckIsAnyStrOrTab (v, S)
	--It allows empty strings
	if (type(v) == "string") or (type(v) == "table") then
		return v
	else	
		WhenNoStrOrTab (v, S)
	end	
end --CheckIsAnyStrOrTab

----------------------------------------

function p.rgbToHex(rgb) --not used
	local hexadecimal = '#'
	for key, value in ipairs(rgb) do
		local hex = ''
		if (value < 0) or (value > 255) then
			error ('Invalid color number: '..value)
		end	
		while value > 0 do
			local index = math.fmod(value, 16) + 1
			value = math.floor (value/16)
			hex = string.sub('0123456789ABCDEF', index, index) .. hex			
		end
		if string.len(hex) == 0 then
			hex = '00'
		elseif string.len(hex) == 1 then
			hex = '0' .. hex
		end
		hexadecimal = hexadecimal .. hex
	end
	return hexadecimal
end --p.rgbToHex

function p._ReverseColor0 (rgb) --not used
  return p.rgbToHex ({255-GetRValue(rgb), 255-GetGValue(rgb), 255-GetBValue(rgb)});
end

function p._ReverseColor (rgb)
	-- rgb is a string with 6 characters in format NNNNNN i.e. '008000'
	-- (Better contrast than _ReverseColor0 for some colors: i.g. grey)
	local BreakGrey = 150
	local BreakColor = 180
	local RValue = GetRValue (rgb)
	local BValue = GetBValue (rgb)
	local GValue = GetGValue (rgb)
	if     (RValue < BreakGrey) and (GValue < BreakGrey) and  (BValue < BreakGrey)	then  
		return 'White'
	elseif (RValue >= BreakGrey) and (GValue >= BreakGrey) and  (BValue >= BreakGrey) then
		return 'Black'
	elseif (RValue > BreakColor) and (GValue > BreakColor)	then
		return 'Blue'
	elseif (RValue > BreakColor) and (BValue > BreakColor)	then
		return 'Green'
	elseif (GValue > BreakColor) and  (BValue > BreakColor)	then
		return 'Red'
	elseif (RValue > BreakColor) then
		return 'Aqua' --Blue + Green
	elseif (GValue > BreakColor) then
		return 'Fuchsia' --Red + Blue
	elseif (BValue > BreakColor) then
		return 'Yellow' --Red + Green
	else  
		return 'White'	  
	end
end --_ReverseColor

function p.ReverseColor (frame)
    local args = p.GetArgs (frame)
	local color = p.RColor_Par (args, 1)
	return p._ReverseColor (color)
end --ReverseColor

function p._color_black_contrast (color)
	local colors = {
		GetRValue(color), 
		GetGValue(color), 
		GetBValue(color), 
	}
	local dif = math.max (colors[1], colors[2], colors[3]) - math.min (colors[1], colors[2], colors[3])
	local val = 0.28*colors[1] + 0.64*colors[2] + 0.08*colors[3]
	local to_subtract = math.abs(dif-255)
	local to_dif = 128-math.abs(val-128)
	local to_subtract2 = to_subtract * to_dif * 0.002 
	local res = math.floor ((val - to_subtract2) + 0.5)
	return res
end --_color_black_contrast

function p.color_black_contrast (frame)
	-- Determine the luminosity of a color, with some corrections for grays, 
	-- for the background of an overlay text, in part from:
	-- https://www.johndcook.com/blog/2009/08/24/algorithms-convert-color-grayscale/
	local args = p.GetArgs (frame)
	local color = p.RColor_Par (args, 1)
	return p._color_black_contrast (color)
end --color_black_contrast

function p._txtcolor_for_bg (rgb)
	if p._color_black_contrast (rgb) < 90 then
		return 'white'
	else
		return 'black'
	end	
end --_txtcolor_for_bg

function p.txtcolor_for_bg (frame)
	-- For better readability, determine whether the color of the letters 
	-- should be white or black 
	-- depending on the background color.
    local args = p.GetArgs (frame)
	local color = p.RColor_Par (args, 1)
	return p._txtcolor_for_bg (color)
end --txtcolor_for_bg

----------------------------------------

return p