Módulo:referencia

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

local export = {}

local m_fecha = require("Módulo:fecha")
local m_str = require("Módulo:String")
local generar_error = require("Módulo:traza")

local substr = m_str.sub
local strsubn = m_str.gsub
local strsplit = m_str.split
local strfind = m_str.find
local strcount = m_str.count
local strmatch = m_str.match

local function mod(n,d) return n - d*math.floor(n/d) end

local params = {
    [1] = {},
    ["c"] = {},
    ["cat"] = {alias_de = "c"},
    ["categoría"] = {alias_de = "c"},
    ["clase"] = {alias_de = "c"},
    --Libros, artículos y general
    ["a"] = {lista = true},
    ["autor"] = {alias_de = "a"},
    ["artista"] = {alias_de = "a"},
    ["remitente"] = {alias_de = "a"},
    ["de"] = {alias_de = "a"},
    ["v"] = {},
    ["version"] = {alias_de = "v"},
    ["versión"] = {alias_de = "v"},
    ["editor"] = {lista = true},
    ["ilustrador"] = {lista = true},
    ["compositor"] = {lista=true},
    ["traductor"] = {lista=true},
    ["compilador"] = {lista=true},
    ["interprete"] = {lista=true},
    ["intérprete"] = {alias_de = "interprete"},
    --Títulos
    ["t0"] = {},
    ["revista"] = {alias_de = "t0"},
    ["colección"] = {alias_de = "t0"},
    ["coleccion"] = {alias_de = "t0"},
    ["t"] = {},
    ["t1"] = {alias_de = "t"},
    ["título"] = {alias_de = "t"},
    ["titulo"] = {alias_de = "t"},
    ["t2"] = {},
    ["subtítulo"] = {alias_de = "t2"},
    ["subtitulo"] = {alias_de = "t2"},
    ["parte"] = {alias_de = "t2"},
    ["art"] = {alias_de = "t2"},
    ["artículo"] = {alias_de = "t2"},
    ["termino"] = {alias_de = "t2"},
    ["término"] = {alias_de = "t2"},
    ["t3"] = {},
    ["cap"] = {alias_de = "t3"},
    ["capítulo"] = {alias_de = "t3"},
    ["capitulo"] = {alias_de = "t3"},
    ["subsubtítulo"] = {alias_de = "t3"},
    ["subsubtitulo"] = {alias_de = "t3"},
    ["sección"] = {alias_de = "t3"},
    ["seccion"] = {alias_de = "t3"},
    ["sec"] = {alias_de = "t3"},
    ["inc"] = {alias_de = "t3"},
    ["inciso"] = {alias_de = "t3"},
    ---Otros campos de libros
    ["p"] = {},
    ["pp"] = {alias_de = "p"},
    ["pag"] = {alias_de = "p"},
    ["pág"] = {alias_de = "p"},
    ["pags"] = {alias_de = "p"},
    ["págs"] = {alias_de = "p"},
    ["pagina"] = {alias_de = "p"},
    ["página"] = {alias_de = "p"},
    ["paginas"] = {alias_de = "p"},
    ["páginas"] = {alias_de = "p"},
    ["tomo"] = {tipo = "num"},
    ["vol"] = {tipo = "num"},
    ["volumen"] = {alias_de = "vol"},
    ["edicion"] = {tipo = "num"},
    ["edición"] = {alias_de = "edicion"},
    ["pasaje"] = {}, --Por ejemplo LA Biblia o las obras griegas clásicas; también aplica para revistas de artículos
    --Instituciones, editoriales, etc.
    ["editorial"] = {},
    ["sitio"] = {alias_de = "editorial"},
    ["diario"] = {alias_de = "editorial"},
    ["inst"] = {}, --institución
    ["institución"] = {alias_de = "inst"},
    ["institucion"] = {alias_de = "inst"},
    ["org"] = {alias_de = "inst"},
    ["organizacion"] = {alias_de = "inst"},
    ["organización"] = {alias_de = "inst"},
    --Fecha YYYY-MM-DD
    ["f"] = {},
    ["fecha"] = {alias_de = "f"},
    ["fo"] = {},
    ["fechaoriginal"] = {alias_de = "fo"},
    --también fc = fecha consulta
    --Lugar
    ["l"] = {},
    ["lugar"] = {alias_de = "l"},
    ["ubicacion"] = {alias_de = "l"},
    ["ubicación"] = {alias_de = "l"},
    ["lo"] = {},
    ["lugaroriginal"] = {alias_de = "lo"},
    ["ubicaciónoriginal"] = {alias_de = "lo"},
    --Códigos de identifiación
    ["id"] = {},
    ["pid"] = {alias_de = "id"},
    ["patenteid"] = {alias_de = "id"},
    ["doi"] = {},
    ["isan"] = {},
    ["isbn"] = {},
    ["ismn"] = {},
    ["isrc"] = {},
    ["issn"] = {},
    ["iswc"] = {},
    ["oclc"] = {},
    -- diccionario
    ["urlprefijo"] = {},
    ["urlsufijo"] = {},
    --musica y audio
    ["album"] = {},
    ["álbum"] = {alias_de = "album"},
    ["n"] = {tipo = "num"},
    ["num"] = {alias_de = "n"},
    ["núm"] = {alias_de = "n"},
    ["nro"] = {alias_de = "n"},
    ["numero"] = {alias_de = "n"},
    ["número"] = {alias_de = "n"},
    ["estudio"] = {},
    --audiovisual
    ["clip"] = {},
    ["temporada"] = {tipo = "num"},
    ["m"] = {},
    ["modo"] = {alias_de = "m"},
    ["formato"] = {alias_de = "m"},
    ["plataforma"] = {alias_de = "m"},
    --cartas e emails
    ["destinatario"] = {lista = true},
    ["receptor"] = {alias_de = "destinatario"},
    ["para"] = {alias_de = "destinatario"},
    ["lr"] = {alias_de = "l"},
    ["lugarremitente"] = {alias_de = "l"},
    ["lugar remitente"] = {},
    ["ld"] = {},
    ["lugardestinatario"] = {alias_de = "ld"},
    ["lugar destinatario"] = {},
    --URL
    ["u"] = {},
    ["url"] = {alias_de = "u"},
    ["fc"] = {},
    ["fechaconsulta"] = {alias_de = "fc"},
    ["fecha consulta"] = {},
    ["fechaacceso"] = {alias_de = "fc"},
    --Si es esbozo o no
    ["esbozo"] = {tipo = "bool"},
    ["borrador"] = {alias_de = "esbozo"},
    --Observaciones
    ["obs"] = {},
    ["nota"] = {alias_de = "obs"}
}

local function insertar_punto(s, deb)
    if strfind(s, "[!?.][ %]\"“”']-(<[^>]->)[ %]\"“”']-$") or strfind(s, "[!?.][ %]\"“”']-$") then
    	return s.." "
    else
    	return s..". "
    end
end

local function f_autores(autores, lbl)
	if not autores or #autores < 1 then
		return nil
	end
    local A = #autores
    local ret
    if A == 1 then
        return insertar_punto((lbl and lbl..": " or "")..autores[1])
    elseif A == 2 then
        return insertar_punto((lbl and lbl..": " or "")..autores[1].." &amp; "..autores[2])
    elseif A >= 2 then
        return insertar_punto((lbl and lbl..": " or "")..autores[1].." et al. ")
    else
        return nil
    end

end

local function f_autores_np(autores, lbl)
	if not autores then
		return nil
	end
    local A = #autores
    if A == 1 then
        return (lbl and lbl..": " or "")..autores[1]
    elseif A == 2 then
        return (lbl and lbl..": " or "")..autores[1].." &amp; "..autores[2]
    elseif A >= 2 then
        return (lbl and lbl..": " or "")..autores[1].." et al."
    else
        return nil
    end
end

local function f_cursiva(t)
    if not t or t == "" then
        return nil
    end
    return "''"..t.."''"
end

local function f_titulo_cursiva(t, pasaje, lfo, tomo, vol, esbozo) --lfo: lugar y fecha original
    if not t or t == "" then
        return nil
    end
    return insertar_punto(("''"..t)..(pasaje and " "..pasaje or "")..(lfo and " ("..lfo..")" or "").."''"..(tomo and " tomo "..tostring(tomo) or "")..(vol and " vol. "..tostring(vol) or "")..(esbozo and " &#91;obra no terminada formalmente&#93;" or ""))
end

local function f_comillas(a)
    if not a or a == "" then
        return nil
    end
    return a and "«"..a.."»" or nil
end

local function f_titulo_comillas(t, pasaje, lfo, tomo, vol, esbozo)
    if not t or t == "" then
        return nil
    end
    return insertar_punto(("«"..t.."»")..(pasaje and " "..pasaje or "")..(lfo and " ("..lfo..")" or "")..(tomo and " tomo "..tostring(tomo) or "")..(vol and " vol. "..tostring(vol) or "")..(esbozo and " &#91;obra no terminada formalmente&#93;" or ""))
end

local function f_titulo_clip(t, lfo, clip, esbozo)
    if not t or t == "" then
        return nil
    end
    return insertar_punto(("''"..t)..(lfo and " ("..lfo..")" or "").."''"..(clip and " ("..clip..")" or "")..(esbozo and " &#91;obra no terminada formalmente&#93;" or ""))
end

local function f_ley_art_inc(ley, art, inc, lfo) --para leyes: ley, artículo e inciso
    if not ley or ley == "" then
        return nil
    end

    return insertar_punto(ley..(art and " art. "..art or "")..(inc and " inc. "..inc or "")..(lfo and " ("..lfo..")" or ""))
end

local function f_editorial(editorial)
    return editorial and insertar_punto(("Editorial: ")..editorial) or nil
end

local function f_sitio(sitio)
	return sitio and insertar_punto(sitio) or nil
end

local function f_doi(doi)
    if type(doi) ~= "string" or doi == "" then
        return nil
    end
	if not strfind(doi, "^10%.%d%d%d%d%d?%d?%d?%d?%d?/[%w%-%._;%(%)/:]+$") then --https://www.crossref.org/blog/dois-and-matching-regular-expressions/
        error("DOI incorrecto: "..doi..". Por favor, ingrese un DOI válido comenzando por \"10.\" No incluya el prefijo \"doi.org\"")
    end
    return "DOI: [//www.doi.org/"..doi.." "..doi.."]. "
end

local function __validar_isan(isan)
    if type(isan) ~= "string" or isan == "" then
        return nil
    end
    local L = #isan
    if L == 17 or L == 26 then
    	local carry = 36
    	for i=1,16 do
    		carry = mod((tonumber(isan:sub(i,i), 16) + carry), 36)
    		if carry == 0 then
    			carry = 36
    		end
    		carry = mod(carry*2, 37)
    	end    	
    	
    	local a = isan:sub(17,17)
    	local checksum = a >= 'A' and string.byte(string.upper(a)) - 65 + 10 or tonumber(a)
    	if 37 - carry ~= checksum then
    		error()
    	end
    	
    	if L == 17 then
    		return 17
    	end
    	
	    for i=18,25 do
			carry = mod((tonumber(isan:sub(i,i), 16) + carry), 36)
    		if carry == 0 then
    			carry = 36
    		end
			carry = mod(carry*2, 37)
	    end
	    
	    a = isan:sub(26,26)
    	checksum = a >= 'A' and string.byte(string.upper(a)) - 65 + 10 or tonumber(a)
    	if 37 - carry ~= checksum then
    		error()
    	end
    	
    	return 26
    
    else
    	error()
    end
end

local function f_isan(isan)
	local bien, hay_isan = pcall(__validar_isan, isan)
    if bien then
        return hay_isan and "ISAN: "..tostring(isan)..". " or nil
    else
        error("ISAN incorrecto: "..isan..". Por favor, ingrese un código válido de 17 o 26 caracteres sin guiones.")
    end
end

local function __luhn(cod, v, p, m, b)
    if type(cod) ~= "string" or cod == "" then
        return nil
    end
    
    if not v then
    	error()
    end
    
    b = b or 10
    m = m or 10
    local L = #cod
    local suma = 0

    for i=1,L do
        suma = suma + tonumber(cod:sub(i,i), b)*p[i]
    end

    local c = mod(m - suma, m) --lo que falte para llegar a ser divisible por m
    if c ~= v then
    	error()
	end
	return true
end

local function f_isbn(isbn)
	if type(isbn) ~= "string" or isbn == "" then
        return nil
	end
	
	local L = #isbn
	local bien, hay_isbn = nil, nil
	
	if L == 10 then
		bien, hay_isbn = pcall(__luhn, isbn:sub(1, L-1), (isbn:sub(L,L) == 'X' or isbn:sub(L,L) == 'x') and 10 or tonumber(isbn:sub(L,L)), {10,9,8,7,6,5,4,3,2}, 11, 10)
	elseif L == 13 then
		bien, hay_isbn = pcall(__luhn, isbn:sub(1, L-1), tonumber(isbn:sub(L,L)), {1,3,1,3,1,3,1,3,1,3,1,3}, 10, 10)
	end
	
    if bien then
        return hay_isbn and "ISBN: ".."[[Especial:FuentesDeLibros/"..tostring(isbn).."|"..tostring(isbn).."]]. " or nil
    else
        error("ISBN incorrecto: "..isbn..". Por favor, ingrese un código válido de 10 o 13 dígitos sin guiones.")
    end
end

local function f_ismn(ismn)
	if type(ismn) ~= "string" or ismn == "" then
        return nil
	end
	
	local bien, hay_ismn = nil, nil
    local L = #ismn
	
	if L == 13 then
		bien, hay_ismn = pcall(__luhn, ismn:sub(1, L-1), tonumber(ismn:sub(L,L)), {1,3,1,3,1,3,1,3,1,3,1,3}, 10, 10) --el proceso es el mismo que en un ISBN de 13 dígitos
	end
	
    if bien then
        return hay_ismn and "ISMN: "..tostring(ismn)..". " or nil
    else
        error("ISMN incorrecto: "..ismn..". Por favor, ingrese un código válido de 13 dígitos sin guiones.")
    end
end

local function f_isrc(isrc)
	if type(isrc) ~= "string" or isrc == "" then
        return nil
    end
	if not strfind(isrc, "^%a%a%w%w%w%d%d%d%d%d%d%d$") then --https://en.wikipedia.org/wiki/International_Standard_Recording_Code
        error("ISRC incorrecto: "..isrc..". Por favor, ingrese un ISRC válido sin guiones.")
    end
    return "ISRC: "..isrc..". "
end

local function f_issn(issn)
	if type(issn) ~= "string" or issn == "" then
        return nil
	end
	
	local L = #issn
	
	local bien, hay_issn = nil, nil
	
	if L == 8 then
		bien, hay_issn = pcall(__luhn, issn:sub(1, L-1), (issn:sub(L,L) == 'X' or issn:sub(L,L) == 'x') and 10 or tonumber(issn:sub(L,L)), {8,7,6,5,4,3,2}, 11, 10)
	end

	if bien then
    	return hay_issn and "ISSN: "..tostring(issn)..". " or nil
    else
    	error("ISSN incorrecto: "..issn..". Por favor, ingrese un código válido de 8 dígitos sin guiones.")
    end
end

local function f_iswc(iswc)
    if type(iswc) ~= "string" or iswc == "" then
        return nil
	end
	
	local L = #iswc
	local bien, hay_iswc = nil, nil
	
	if L == 11 then
		bien, hay_iswc = pcall(__luhn, "1"..iswc:sub(2, L-1), tonumber(iswc:sub(L,L)), {1,1,2,3,4,5,6,7,8,9}, 10, 10)
	end

	if bien then
    	return hay_iswc and "ISWC: "..tostring(iswc)..". " or nil
    else
    	error("ISWC incorrecto: "..iswc..". Por favor, ingrese un código válido de 11 caracteres sin guiones.")
    end
end

local function f_oclc(oclc)
    return oclc and "OCLC: "..tostring(oclc)..". " or nil
end

local function __validar_fecha(f_str, mayus, punto)
	if type(f_str) ~= "string" or f_str == "" then
		return nil
	end
	
    local nguiones = strcount(f_str, "%-")
    local a, m, d, especiales

    if nguiones == 0 then
        a, especiales = strmatch(f_str, "^(%d%d?%d?%d?)([A~%?]*)$")
    elseif nguiones == 1 then
        a, m, especiales = strmatch(f_str, "^(%d%d?%d?%d?)%-(%d%d?)([A~%?]*)$")
    elseif nguiones == 2 then
        a, m, d, especiales = strmatch(f_str, "^(%d%d?%d?%d?)%-(%d%d?)%-(%d%d?)([A~%?]*)$")
    else
        error()
    end
    
    if not especiales then
    	especiales = ""
    end
	
	local desc = strfind(especiales, "%?")
	local aprox = strfind(especiales, "~")
	local ac = strfind(especiales, "A")

    local f_convertida
	
	if nguiones == 0 or ac then
    	f_convertida = m_fecha(a, 1, 1):fmt("%\b")
	elseif nguiones == 1 then
	    f_convertida = m_fecha(a, m, 1):fmt("%b %\b")
	elseif nguiones == 2 then
	    f_convertida = m_fecha(a, m, d):fmt("%d %b %\b")
	end
	
	f_convertida = (aprox and "circa " or "")..f_convertida..(ac and " A.C." or "")..(desc and "?" or "")
	if punto then
		f_convertida = insertar_punto(f_convertida)
	end
	return mayus and strsubn(f_convertida, "^%l", string.upper) or f_convertida
end

local function f_fecha(f_str, mayus, punto)
	local bien, f_convertida = pcall(__validar_fecha, f_str, mayus, punto)
	if bien then
		return f_convertida
	else
		-- error(f_convertida) --para debug
		error("\""..f_str.."\"  es una fecha incorrecta o con formato no válido. Por favor, utilice el formato ISO 8601 (AAAA-MM-DD) y opcionalmente agregue al final los símbolos A y/ó ? y/ó ~")
	end
end

local function f_lugar_y_fecha(l, f, mayus, punto)
    if l and #l > 0 then
        return l..(f and (", "..f_fecha(f, false, punto)) or (punto and ". " or ""))
    elseif f and #f > 0 then
        return f_fecha(f, mayus, punto)
    else
    	return nil
    end
end

local function f_lugar_fecha_edicion(l, f, ed)
    if ed and ed > 0 then
        return (tostring(ed)..".ª ed" or "")..((l and l ~= "") and ", "..l or "")..((f and f ~= "") and (", "..f_fecha(f, false, true)) or ". ")
    elseif l and l ~= "" then
        return l..((f and f ~= "") and (", "..f_fecha(f, false, true)) or ". ")
    elseif f and f ~= "" then
    	return f_fecha(f, true, true)
    else
    	return nil
    end
end

local function f_pags(p)
	if type(p) ~= "string" or p == "" then
		return nil
	end
    p = strsubn(p, "%s+", "") --saco cualquier espacio de más
    local p1 = strsplit(p, ",")
    local P = #p1
    local pl = P > 1
    local pags = ""
    for i,p in ipairs(p1) do
    	if not strfind(p, "^[0-9]+$") and not strfind(p, "^[0-9]+%-[0-9]+$") then
    		error("Formato especificado de páginas incorrecto. Especifique las páginas con números, los rangos de páginas con un guion y separe con coma varias páginas o rangos de páginas aislados.")	
    	end
        if i == 1 then
            pags = pags..p
            pl = pl and pl or strfind(p, "%-") 
        elseif i < P then
            pags = pags..", "..p
        elseif i == P then
            pags = pags.." y "..p
        end
    end
	return pl and "Páginas "..pags..". " or "Página "..pags..". "
end

local function f_url(url, fc)
    if url and url ~= "" then
    	local u = nil
    	if strfind(url, "^[wsb]:") then --strfind(url, "^[a-zA-ZÀ-ž]+:") then mejor permitir solo los elnaces a s: wikisource, a w: wikipedia o a b: wikibooks
    		u = strsubn(url, "^[wsb]:", "", 1)
    		u = strsubn(u, "^[a-z%-]+:", "", 1)
    		u = strsubn(u, "_", " ")
    		u = "[["..url.."|"..u.."]]"
    	else
    		u = url
    		if not strfind(u, "^%a+://") then
    			u = strsubn(u, "^:*/*", "")
    			u = "http://"..u
    		end
	    	--u = strsubn(url, "https://", "", 1)
	    	--u = strsubn(u, "http://", "", 1)
	    	u = "["..u.." "..u.."]"
    	end
        if fc and fc ~= "" then
            return "Consultado: "..f_fecha(fc, false, true).."Obtenido de: "..u..". "
        else
            return "Obtenido de: "..u..". "
        end
    else
        return nil
    end
end

local function f_link(url, texto)
	if type(texto) ~= "string" or texto == "" then
		return nil
	end
	if url and url ~= "" then
		url = strsubn(url, " ", "%%20") -- escapar los espacios como %20, esto evita que [[calco semántico]] aparezca formateado como [www.fbbva.es/diccionario/calco semantico calco semantico], "semantico calco semantico", redirigiendo a "calco"
		if strfind(url, "^[wsb]:") then --strfind(url, "^[a-zA-ZÀ-ž]+:") then mejor permitir solo los elnaces a s: wikisource, a w: wikipedia o a b: wikibooks
			return "[["..url.."|"..texto.."]]"
		else
			local u = url
    		if not strfind(u, "^%a+://") then
    			u = strsubn(u, "^:*/*", "")
    			u = "http://"..u
    		end
			--local u = strsubn(url, "https://", "", 1)
	    	--u = strsubn(u, "http://", "", 1)
			return "<span class='plainlinks'>["..u.." "..texto.."]</span>"
		end
	end
	return texto
end

local function f_inst_e_id(inst, id)
    if inst then
        if id then
            return insertar_punto(inst.." "..id)
        else
            return insertar_punto(inst)
        end
        
    else
        if id then
            return insertar_punto(id)
        else
            return nil
        end
    end
    return nil
end

local function f_album_y_nro(album, n)
    if album then
        return insertar_punto("Álbum: ''"..album.."''"..(n and ", pista "..tostring(n) or ""))
    else
        return nil
    end
end

local function f_parte_y_cap(parte, cap)
    if parte then
        return insertar_punto("Parte "..parte..""..(cap and ", capítulo "..cap or ""))
    elseif cap then
        return cap and insertar_punto("Capítulo "..cap) or ""
    else
    	return nil
    end
end

local function f_temp_y_cap(temp, ncap)
    if temp then
        return insertar_punto("Temporada "..temp..""..(ncap and ", capítulo "..ncap or ""))
    elseif ncap then
        return ncap and insertar_punto("Capítulo "..ncap) or ""
    else
    	return nil
    end
end

local function f_estudio(estudio)
    return estudio and insertar_punto("Estudio: "..estudio) or nil
end

local function f_version(version)
    return version and insertar_punto("Versión: "..version) or nil
end

local function f_formato(formato)
    return formato and insertar_punto("Formato: "..formato) or nil
end

local function f_remitente(r, lr)
	return insertar_punto(f_autores_np(r, "De")..(lr and ", en "..lr))
end

local function f_destinatario(d, ld)
	return insertar_punto(f_autores_np(d, "Para")..(ld and ", en "..ld))
end

local function f_obs(obs)
    return obs and "OBS.: "..obs or nil
end

local function diccionario(args)
    assert(args["t"], "Error, debe especificar cuál es el título")
    assert(args["t2"], "Error, debe especificar cuál es la palabra")
    assert(args["urlprefijo"], "Error, debe especificar el prefijo de la URL")
    local arr = {}
    table.insert(arr, f_comillas(f_link(args["urlprefijo"]..args["t2"]..(args["urlsufijo"] and args["urlsufijo"] or ""), args["t2"])))
    table.insert(arr, " en ")
    table.insert(arr, f_titulo_cursiva(args["t"], args["pasaje"], f_lugar_y_fecha(args["lo"], args["fo"]), args["tomo"], args["vol"], args["esbozo"]))
    table.insert(arr, f_pags(args["p"]))
    table.insert(arr, f_autores(args["a"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_editorial(args["editorial"]))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_doi(args["doi"]))
    table.insert(arr, f_isbn(args["isbn"]))
    table.insert(arr, f_issn(args["issn"]))
    table.insert(arr, f_oclc(args["oclc"]))
    table.insert(arr, args["fc"] and "Consultado: "..f_fecha(args["fc"], false, true) or nil)
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)	
end

local function libro(args)
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    table.insert(arr, f_titulo_cursiva(f_link(args["u"], args["t"]), args["pasaje"], f_lugar_y_fecha(args["lo"], args["fo"]), args["tomo"], args["vol"], args["esbozo"]))
    table.insert(arr, f_parte_y_cap(args["t2"], args["t3"]))
    table.insert(arr, f_pags(args["p"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_editorial(args["editorial"]))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_doi(args["doi"]))
    table.insert(arr, f_isbn(args["isbn"]))
    table.insert(arr, f_issn(args["issn"]))
    table.insert(arr, f_oclc(args["oclc"]))
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function sublibro(args)
    assert(args["t"], "Error, debe especificar cuál es el título del libro")
    assert(args["t2"], "Error, debe especificar cuál es el subtítulo (parte, capítulo, sección, etc.) del libro")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    table.insert(arr, f_comillas(f_link(args["u"], args["t2"])))
    table.insert(arr, " en ")
    table.insert(arr, f_titulo_cursiva(args["t"], args["pasaje"], f_lugar_y_fecha(args["lo"], args["fo"]), args["tomo"], args["vol"], args["esbozo"]))
    table.insert(arr, f_pags(args["p"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_editorial(args["editorial"]))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_doi(args["doi"]))
    table.insert(arr, f_isbn(args["isbn"]))
    table.insert(arr, f_issn(args["issn"]))
    table.insert(arr, f_oclc(args["oclc"]))
    --table.insert(arr, f_url(args["u"], args["fc"]))
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function subsublibro(args)   
	assert(args["t"], "Error, debe especificar cuál es el título del libro")
    assert(args["t2"], "Error, debe especificar cuál es el subtítulo (parte, capítulo, sección, etc.) del libro")
    assert(args["t3"], "Error, debe especificar cuál es el subsubtítulo (parte, capítulo, sección, etc.) del libro")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    table.insert(arr, f_comillas(f_link(args["u"], args["t3"])))
    table.insert(arr, " en ")
    table.insert(arr, f_comillas(args["t2"]))
    table.insert(arr, " en ")
    table.insert(arr, f_titulo_cursiva(args["t"], args["pasaje"], f_lugar_y_fecha(args["lo"], args["fo"]), args["tomo"], args["vol"], args["esbozo"]))
    table.insert(arr, f_pags(args["p"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_editorial(args["editorial"]))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_doi(args["doi"]))
    table.insert(arr, f_isbn(args["isbn"]))
    table.insert(arr, f_issn(args["issn"]))
    table.insert(arr, f_oclc(args["oclc"]))
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function articulo(args, link_implicito)
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    
    if link_implicito then
    	table.insert(arr, f_titulo_comillas(f_link(args["u"], args["t"]), nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    else
    	table.insert(arr, f_titulo_comillas(args["t"], nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    end

    table.insert(arr, f_titulo_cursiva(args["t0"], args["pasaje"], nil, args["tomo"], args["vol"], nil))
    table.insert(arr, f_pags(args["p"]))
    table.insert(arr, f_inst_e_id(args["inst"], args["id"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_sitio(args["editorial"]))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_doi(args["doi"]))
    table.insert(arr, f_isbn(args["isbn"]))
    table.insert(arr, f_issn(args["issn"]))
    table.insert(arr, f_oclc(args["oclc"]))
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function noticia(args, link_implicito) --también entradas de blogs
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    
    if link_implicito then
    	table.insert(arr, f_titulo_comillas(f_link(args["u"], args["t"]), nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    else
    	table.insert(arr, f_titulo_comillas(args["t"], nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    end
    
    table.insert(arr, f_titulo_cursiva(args["t0"], args["pasaje"], nil, args["tomo"], args["vol"], nil))
    table.insert(arr, f_pags(args["p"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_sitio(args["editorial"])) --el sitio si es una entrada de un blog o similar
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end


local function sitio(args, link_implicito)
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    
    if link_implicito then
    	table.insert(arr, f_titulo_comillas(f_link(args["u"], args["t"]), nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    else
    	table.insert(arr, f_titulo_comillas(args["t"], nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    end

    table.insert(arr, f_titulo_cursiva(args["t0"], args["pasaje"], nil, args["tomo"], args["vol"], nil))
    table.insert(arr, f_inst_e_id(args["inst"], args["id"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)    
end

local function patente(args, link_implicito)
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    
    if link_implicito then
    	table.insert(arr, f_titulo_comillas(f_link(args["u"], args["t"]), nil, f_lugar_y_fecha(args["lo"], args["fo"]), args["tomo"], args["vol"], args["esbozo"]))
    else
    	table.insert(arr, f_titulo_comillas(args["t"], nil, f_lugar_y_fecha(args["lo"], args["fo"]), args["tomo"], args["vol"], args["esbozo"]))
    end
    
    table.insert(arr, f_inst_e_id(args["inst"], args["id"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_sitio(args["editorial"]))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_doi(args["doi"]))
    table.insert(arr, f_oclc(args["oclc"]))
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function legal(args, link_implicito)
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    
    if link_implicito then
    	table.insert(arr, f_ley_art_inc(f_link(args["u"], args["t"]), args["t2"], args["t3"], f_lugar_y_fecha(args["lo"], args["fo"])))
    else
    	table.insert(arr, f_ley_art_inc(args["t"], args["t2"], args["t3"], f_lugar_y_fecha(args["lo"], args["fo"])))
    end
    
    table.insert(arr, f_inst_e_id(args["inst"], nil))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_sitio(args["editorial"]))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_doi(args["doi"]))
    table.insert(arr, f_oclc(args["oclc"]))
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function cancion(args, link_implicito)
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    
    if link_implicito then
    	table.insert(arr, f_titulo_cursiva(f_link(args["u"], args["t"]), nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    else
    	table.insert(arr, f_titulo_cursiva(args["t"], nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    end
    
    table.insert(arr, f_album_y_nro(args["album"], args["n"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["interprete"], "Interpretado por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_estudio(args["estudio"]))
    table.insert(arr, f_lugar_y_fecha(args["l"], args["f"], true, true))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_ismn(args["ismn"]))
    table.insert(arr, f_iswc(args["iswc"]))
    table.insert(arr, f_isan(args["isan"]))
    table.insert(arr, f_isrc(args["isrc"]))    
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function imagen(args, link_implicito)
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    
    if link_implicito then
    	table.insert(arr, f_titulo_cursiva(f_link(args["u"], args["t"]), nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    else
    	table.insert(arr, f_titulo_cursiva(args["t"], nil, f_lugar_y_fecha(args["lo"], args["fo"]), nil, args["esbozo"]))
    end

    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_inst_e_id(args["inst"], nil))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_ismn(args["ismn"]))
    table.insert(arr, f_iswc(args["iswc"]))
    table.insert(arr, f_isan(args["isan"]))
    table.insert(arr, f_isrc(args["isrc"]))
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function av(args, link_implicito)
    assert(args["t"], "Error, debe especificar cuál es el título")
    local arr = {}
    table.insert(arr, f_autores(args["a"]))
    
    if link_implicito then
    	table.insert(arr, f_titulo_clip(f_link(args["u"], args["t"]), f_lugar_y_fecha(args["lo"], args["fo"]), args["clip"], args["esbozo"]))
    else
    	table.insert(arr, f_titulo_clip(args["t"], f_lugar_y_fecha(args["lo"], args["fo"]), args["clip"], args["esbozo"]))
    end
    
    table.insert(arr, f_temp_y_cap(args["temporada"], args["t3"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_autores(args["interprete"], "Interpretado por"))
    table.insert(arr, f_autores(args["director"], "Dirigido por"))
    table.insert(arr, f_autores(args["compositor"], "Compositor"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_inst_e_id(args["inst"], nil))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_ismn(args["ismn"]))
    table.insert(arr, f_iswc(args["iswc"]))
    table.insert(arr, f_isan(args["isan"]))
    table.insert(arr, f_isrc(args["isrc"]))    
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function carta(args, link_implicito)
	assert(args["a"], "Error, debe especificar el remitente")
	assert(args["destinatario"], "Error, debe especificar al menos un destinatario")
	
    local arr = {}
    
    if link_implicito then
    	table.insert(arr, f_link(args["u"], "Carta")..". ")
    else
    	table.insert(arr, "Carta. ")
    end
    
    table.insert(arr, f_titulo_comillas(args["t"]))
    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_remitente(args["a"], args["l"]))
    table.insert(arr, f_destinatario(args["destinatario"], args["ld"]))
    table.insert(arr, f_fecha(args["f"] or args["fo"], true, true))
    table.insert(arr, f_inst_e_id(args["inst"], nil))
    table.insert(arr, f_formato(args["m"]))
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end
    
    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end

local function misc(args, link_implicito) --software es un alias
    local arr = {}
    if args["t"] then
    	if link_implicito then
	    	table.insert(arr, f_cursiva(f_link(args["u"], args["t"])))
    	else
	    	table.insert(arr, f_cursiva(args["t"]))
    	end
	    if args["t2"] then
	        table.insert(arr, ", "..f_comillas(args["t2"]))
	    end
	    if args["t3"] then
	        table.insert(arr, ", "..f_comillas(args["t3"]))
	    end
	    table.insert(arr, ". ")
    end

    table.insert(arr, f_version(args["v"]))
    table.insert(arr, f_pags(args["p"]))
    table.insert(arr, f_lugar_y_fecha(args["lo"], args["fo"], true, true))
    table.insert(arr, f_autores(args["a"], nil))
    table.insert(arr, f_autores(args["traductor"], "Traducido por"))
    table.insert(arr, f_autores(args["editor"], "Editado por"))
    table.insert(arr, f_autores(args["ilustrador"], "Ilustrado por"))
    table.insert(arr, f_autores(args["compositor"], "Compuesto por"))
    table.insert(arr, f_autores(args["compilador"], "Compilado por"))
    table.insert(arr, f_inst_e_id(args["inst"], nil))
    table.insert(arr, f_sitio(args["editorial"]))
    table.insert(arr, f_inst_e_id(args["estudio"]))
    table.insert(arr, f_lugar_fecha_edicion(args["l"], args["f"], args["edicion"]))
    table.insert(arr, f_formato(args["m"]))
    table.insert(arr, f_doi(args["doi"]))
    table.insert(arr, f_isbn(args["isbn"]))
    table.insert(arr, f_issn(args["issn"]))
    table.insert(arr, f_ismn(args["ismn"]))
    table.insert(arr, f_iswc(args["iswc"]))
    table.insert(arr, f_isan(args["isan"]))
    table.insert(arr, f_isrc(args["isrc"]))
    table.insert(arr, f_oclc(args["oclc"]))
    
    if not link_implicito then
    	table.insert(arr, f_url(args["u"], args["fc"]))
    end

    table.insert(arr, f_obs(args["obs"]))
    return table.concat(arr)
end


local callbacks = {
	["diccionario"] = diccionario,
    ["libro"] = libro,
    ["antologia"] = libro,
    ["antología"] = libro,
    ["folleto"] = libro,
    ["manual"] = libro,
    ["diario"] = libro,
    ["revista"] = libro,
    ["periódico"] = libro,
    ["sublibro"] = sublibro,
    ["cap"] = sublibro,
    ["capítulo"] = sublibro,
    ["capitulo"] = sublibro,
    ["subsublibro"] = subsublibro,
    ["seccion"] = subsublibro,
    ["sección"] = subsublibro,
    ["articulo"] = articulo,
    ["artículo"] = articulo,
    ["publicacion"] = articulo,
    ["publicación"] = articulo,
    ["tesis"] = articulo,
    ["informe"] = articulo,
    ["reporte"] = articulo,
    ["noticia"] = noticia,
    ["blog"] = noticia,
    ["pagina"] = noticia,
    ["página"] = noticia,
    ["sitio"] = sitio,
    ["patente"] = patente,
    ["legal"] = legal,
    ["cancion"] = cancion,
    ["canción"] = cancion,
    ["poema"] = cancion,
    ["imagen"] = imagen,
    ["arte"] = imagen,
    ["cuadro"] = imagen,
    ["pintura"] = imagen,
    ["escultura"] = imagen,
    ["av"] = av,
    ["audiovisual"] = av,
    ["pelicula"] = av,
    ["película"] = av,
    ["peli"] = av,
    ["serie"] = av,
    ["programa"] = av,
    ["anime"] = av,
    ["ánime"] = av,
    ["video"] = av,
    ["obra teatral"] = av,
    ["obra de teatro"] = av,
    ["documental"] = av,
    ["carta"] = carta,
    ["mail"] = carta,
    ["email"] = carta,
    ["e-mail"] = carta,
    ["correo"] = carta,
    ["correo electrónico"] = carta,
    ["misc"] = misc,
    ["software"] = misc,
    ["juego"] = misc,
    ["videojuego"] = misc,
}

local function obtener_referencia(args, link_implicito)
	local x = args["c"]
	if not x then
		error("Debe especificar cuál es el tipo de cita que está haciendo, mediante el parámetro “c” ó “cat”.")
	end
	if strfind(x, "^ *web *$") then
		error("“web” es un valor ambiguo para citar, indique si es una página o un sitio.")
	end
	
	local function esta_desbalanceada(s)
	    local cursiva_abierta = false
	    local negrita_abierta = false
	    local parentesis_abierto = false
	    local comillas_previas = 0
	    if not s or s == "" then
	    	return false	
	    end
	    for i=1,#s do
	        if substr(s, i, i) == "'" then
	            comillas_previas = comillas_previas + 1
	        else
	            if comillas_previas == 2 then
	                cursiva_abierta = not cursiva_abierta
	            elseif comillas_previas == 3 then
	                negrita_abierta = not negrita_abierta
	            end
	            comillas_previas = 0
	            
	            if substr(s, i, i) == "(" then
	            	if parentesis_abierto then
	            		return true
	            	end
	            	parentesis_abierto = true
	            elseif substr(s, i, i) == ")" then
	            	if not parentesis_abierto then
	            		return true
	            	end
	            	parentesis_abierto = false
	            end
	        end
	    end
	    return cursiva_abierta or negrita_abierta or parentesis_abierto
	end
	
	if esta_desbalanceada(args["t"]) or esta_desbalanceada(args["a"][1])  then
		generar_error("desbalance")
	end
	
	local cb = callbacks[args["c"]]
    if cb then
        return cb(args, link_implicito)
    else
        error("categoría de cita incorrecta: "..args["c"])
    end
end

function export.generar_referencia_test(argumentos_plantilla)
	local args = require("Módulo:parámetros").obtener_parametros(argumentos_plantilla, params)
	return obtener_referencia(args, false)
end

function export.generar_referencia(frame)
	local title = mw.title.getCurrentTitle().fullText
	if title == "Plantilla:referencia" then
		return "Use esta plantilla siempre que cite una referencia."	
	end
    local parent_frame = frame:getParent()
	local args = require("Módulo:parámetros").obtener_parametros(parent_frame.args, params)	
	return obtener_referencia(args, false)
end

-- Para ser invocada desde Módulo:ejemplo
function export.generar_referencia_con_link_implicito(args)
	return obtener_referencia(args, true)
end

return export