Módulo:Info

De UBRwiki

A documentação para este módulo pode ser criada em Módulo:Info/doc

-- Tabela para funções especiais chamadas colocando # no começo do rótulo ou dados
local especial = {}

-- Tabela para tabelas de debug
debug = {}
debug.erros = {}

-- Cores padrões --
local cores = { -- tons das linhas: {'escuro', 'mediano', 'claro', 'bem claro'}
    {'#B60000', '#FFAFAF', '#FFD7D7', '#FFEBEB'}, -- vermelho
    {'#B00058', '#FFABD5', '#FFD5EA', '#FFEAF5'}, -- bordô
    {'#840084', '#FFA4FF', '#FFD2FF', '#FFE9FF'}, -- púrpura
    {'#5800B1', '#DAB5FF', '#EDDAFF', '#F6EDFF'}, -- roxo
    {'#2020C9', '#BFBFFF', '#DFDFFF', '#EFEFFF'}, -- azul
    {'#0057AF', '#93C9FF', '#C9E4FF', '#E4F2FF'}, -- azul~ciano
    {'#00626F', '#11E4FF', '#88F2FF', '#C4F9FF'}, -- ciano
    {'#00654E', '#27FFCF', '#93FFE7', '#C9FFF3'}, -- verde~ciano
    {'#006800', '#7BFF7B', '#BDFFBD', '#DEFFDE'}, -- verde
    {'#4A6000', '#CFFF27', '#E7FF93', '#F3FFC9'}, -- verde~amarelo
    {'#655900', '#FFE411', '#FFF288', '#FFF9C4'}, -- amarelo
    {'#8C4600', '#FFB56B', '#FFDAB5', '#FFEDDA'}, -- laranja
    {'#585858', '#C7C7C7', '#E3E3E3', '#F1F1F1'}  -- cinza
}

-- Função para montar a infobox depois que os parâmetros foram pré-processados
local montarinfo = function(extra)
    local partes = {}
    -- Cabeçalho --
    if params['cabeçalho'] then
        partes['cabeçalho'] = '<caption>' .. params['cabeçalho'] .. '</caption>'
    end
    -- Pictograma --
    if params['pictograma'] and string.match(params['pictograma'], '^[^%]%[:]+%.%a%a%a%a?$') then
        partes['pictograma'] = '<div style="position:absolute; top:0; right:0;' ..
          '">[[Ficheiro:' .. params['pictograma'] .. '|' .. 'x45px]]</div>'
    end
    -- Título --
    partes['cor'] = cor1
    partes['título'] = params['título']
    if partes['pictograma'] then
        -- é preciso colocar o título dentro de um elemento com position para ficar acima do pictograma
        partes['título'] = '<div style="position:relative">' .. partes['título'] .. '</div>'
    end
    -- Campos --
    local c = {}
    local vazio = params['debug'] == 'vazio'
    local i = 1
    while i <= #campos do
        local campo = campos[i]
        if campo['tópico'] then
            local n = i + 1
            while campos[n] do
                if campos[n][1] and campos[n][1] ~= '' then
                    table.insert(c, '|-\n|colspan=2 style="text-align:center; font-weight:bold;' .. cor2 .. '"|' ..
                      campo['tópico'])
                    break
                elseif campos[n]['tópico'] then
                    break
                end
                n = n + 1
            end
        elseif campo['rótulo'] and (campo[1] or vazio) then
            table.insert(c, '|-\n|scope="row" ' .. 'style="vertical-align:top; text-align:left; font-weight:bold;' ..
              cor3 .. '"|' .. campo['rótulo'] .. '\n|' .. (campo['wikidata'] and 'class="dadoswd" ' or '') ..
              'style="vertical-align:top; text-align:left;"|' .. (campo[1] or '(vazio)'))
        elseif campo[1] or vazio then
            table.insert(c, '|-\n|colspan=2 ' .. (campo['wikidata'] and 'class="dadoswd" ' or '') ..
              'style="vertical-align: top; text-align: center' .. (campo['peq'] and '; font-size: 90%' or '') ..
              '"|' .. (campo[1] or '(vazio)'))
        elseif campo['info'] then -- infobox incluída em um campo
            table.insert(c, campo['info'])
        end
        i = i + 1
    end
    partes['campos'] = table.concat(c, '\n')
    -- Rodapé --
    if params['rodapé'] then
        partes['rodapé'] = '|-\n|colspan=2 style="text-align:center;' .. cor2 .. '"|' .. params['rodapé']
    end
    if params['nome'] and string.match(params['nome'], '^Predefinição:') then
        partes['ver-editar'] = '|-\n|colspan=2 class="plainlinks" style="text-align:right"|[[' ..
        params['nome'] .. '|ver]]'
    end
    -- Extra (geralmente categorias) --
        partes['extra'] = extra
    -- Debug --
    if params['debug'] and type(debug[params['debug']]) == 'table' then
        local lista = table.concat(debug[params['debug']], '\n')
        if lista == '' then
            lista = '(debug vazio)'
        end
        partes['rodapé'] = '|-\n|colspan=2|<pre>' .. lista .. '</pre>'
    end

    -- Categorias de número de campos --
    if ncampos then
        local num = ncampos == 0 and 'nenhum campo' or ncampos == 1 and '1 campo' or ncampos < 10 and
          tostring(ncampos) .. ' campos' or '10 ou mais campos'
        partes['catcampos'] = '[[Categoria:!Infoboxes com ' .. num .. ']]'
    end
    if ncamposwd then
        local num = ncamposwd == 0 and 'nenhum campo' or ncamposwd == 1 and '1 campo' or ncamposwd < 10 and
          tostring(ncamposwd) .. ' campos' or '10 ou mais campos'
        partes['catcampos'] = (partes['catcampos'] or '') .. '[[Categoria:!Infoboxes com ' .. num .. ' do Wikidata]]'
    end
    if todoscamposwd then
        partes['catcampos'] = (partes['catcampos'] or '') ..
          '[[Categoria:!Infoboxes em que todos campos são do Wikidata]]'
    end

    -- Montar a partes --
    local template = [=[{|class="infobox" style="font-size:90%; line-height:1.2em; float:right; clear:right; margin:0 0 .5em 1em; width:20em; border:1px solid #C7C7C7; padding:2px; background-color:#FAFAFA; border-spacing:3px"
{ cabeçalho }
|-
!colspan=2 style="height:45px; vertical-align:middle; text-align:center; font-size:120%; font-weight:bolder; line-height:1.3em; position:relative;{ cor }"|{ pictograma }{ título }
{ campos }
{ rodapé }
{ ver-editar }
|}
{ extra }
{ catcampos }]=]
   local infobox = string.gsub(template, '(\n?){ ([^}]+) }',
      function(nl, parte)
          if partes[parte] and partes[parte] ~= "" then
              return nl .. partes[parte] else return ''
          end
      end)
   return infobox
end

-- Campos com rótulo '#imagem' adicionam imagens com 200px de largura
especial.imagem = function(campo)
    if not campo[1] then
        return nil
    end
    local dominio, img = string.match(campo[1], '^%[%[(%w+):([^%]%|%.\n]+%.%w%w%w%w?)[%]%|]')
    if dominio then
        for _, d in pairs({'Ficheiro', 'Imagem', 'File', 'Image', 'ficheiro', 'imagem', 'file', 'image'}) do
            if dominio == d then
                dominio = nil
                break
            end
        end
        if dominio then img = nil end
    elseif string.match(campo[1], '^[^%]%|%.\n]+%.%w%w%w%w?$') then
        img = campo[1]
    end
    if img then
        campo['rótulo'] = nil
        campo[1] = '[[Ficheiro:' .. img .. '|200px]]'
        return campo
    else
        table.insert(debug.erros, 'nome de imagem não encontrado em "' .. campo[1] .. '"')
        return nil
    end
end

-- Campos com rótulo '#legenda' adicionam texto sem rótulo com letra menor quando existe dados no campo acima
especial.legenda = function(campo, i)
    if campo[1] and campos[i - 1] and campos[i - 1][1] and campos[i - 1][1] ~= '' then
        local small = string.match(campo[1], '^<small>(.*)</small>$')
        return {small or campo[1], ['peq'] = true, ['wikidata'] = campo[1]['wikidata']}
    else
        return nil
    end
end

-- Separa uma infobox incluída dentro de um campo
local subInfo = function(str)
    local pos = str:find('\n{|class="infobox')
    local campo = str:sub(1, pos - 1)
    local infos = {}
    while pos do
        local inicio = pos
        pos = str:find('\n{|class="infobox', pos + 1)
        local info = str:sub(inicio, pos)
        inicio = info:find'\n|%-\n|'
        local p, fim = inicio
        while p do
            fim = p - 1
            p = info:find('\n|}', p + 1)
        end
        if inicio then
            table.insert(infos, info:sub(inicio + 1, fim))
        end
    end
    return campo, table.concat(infos, '\n')
end

-- Função principal
local base = function(args, nomebase, estender)
    local title = mw.title.getCurrentTitle()
    local namespace = title.namespace
    local vazio = args['debug'] == 'vazio'
    local extra

    params = {}
    -- Para ordenar as chaves de uma tabela em Lua é necessário separar a tabela em duas:
    local camposn = {} -- tabela para guardar os números dos campos
    local camposv = {} -- tabela para guardar os valores dos campos
    for k, v in pairs(args) do
        if v == '' then
            -- pass
        elseif string.match(k, '^%d+$') then
            local n = tonumber(k)
            local rotulo, dados = string.match(v, '^([^:]-):(.*)$')
            if rotulo then
                rotulo = string.gsub(rotulo, '^%s*(.-)%s*$', '%1')
                dados = string.gsub(dados, '^%s*(.-)%s*$', '%1')
                if rotulo == '#tópico' and dados ~= '' then
                    camposv[n] = {['tópico']=dados}
                    table.insert(camposn, n)
                elseif rotulo ~= '' then
                    camposv[n] = {['rótulo']=rotulo, dados}
                    table.insert(camposn, n)
                elseif dados ~= '' then
                    camposv[n] = {dados}
                    table.insert(camposn, n)
                elseif vazio and rotulo ~= '' then
                    camposv[n] = {['rótulo']=rotulo, '(vazio)'}
                    table.insert(camposn, n)
                end
            elseif v ~= '' then
                camposv[n] = {v}
                table.insert(camposn, n)
            end
        elseif string.match(k, '^tópico%d+%.?%d?$') or string.match(k, '^rótulo%d+%.?%d?$') or
          string.match(k, '^dados%d+%.?%d?$') then
            local tipo, n = string.match(k, '^([^%d]+)(%d+%.?%d?)$')
            if tipo == 'dados' then
                tipo = 1
            end
            n = tonumber(n)
            if v == '' then
                -- pass
            elseif camposv[n] then
                camposv[n][tipo] = v
            else
                camposv[n] = {[tipo]=v}
                table.insert(camposn, n)
            end
        else
            params[k] = v
        end
    end
    local estender = estender or params['estender']
    params['título'] = params['título'] or title.text
    params['nome'] = params['nome'] or nomebase

    -- Cor --
    if params['cor'] then
        local matiz, tom = string.match(params['cor'], '(%d%d?)%.(%d)')
        local cor
        if matiz and tonumber(tom) <= 4 then
            cor = cores[tonumber(matiz)][tonumber(tom)]
        else
            cor = string.upper(params['cor'])
        end
        for _, matiz in ipairs(cores) do
            for i, c in ipairs(matiz) do
                if cor == c then
                    if i == 1 then
                        cor1 = ' background-color:' .. c .. '; color:#FFFFFF'
                        i = 2
                    else
                        cor1 = ' background-color:' .. c
                    end
                    if i < 4 then
                        cor2 = ' background-color:' .. matiz[i + 1]
                        if i < 3 then
                            cor3 = ' background-color:' .. matiz[i + 2]
                        else
                            cor3 = ''
                        end
                    else
                        cor2 = ''
                        cor3 = ''
                    end
                    break
                end
            end
        end
    end
    if not cor1 then
        cor1 = ''
        cor2 = ''
        cor3 = ''
    end

    -- Ordenar os campos e agrupar dados de rótulos iguais --
    table.sort(camposn)
    campos = {}
    if params['debug'] == 'campos' then
        debug.campos = {}
    end
    local i = 0
    if params['subtítulo'] then
       i = 1
       campos[1] = {params['subtítulo']}
    end
    for _, n in ipairs(camposn) do
        -- Como no preenchimento antigo existem tópicos e rótulos/dados com o mesmo número,
        -- é necessário ordenar desta forma para colocar os dados um número a frente do tópico
        if camposv[n]['tópico'] then
            i = i + 1
            campos[i] = {['tópico']=camposv[n]['tópico']}
            camposv[n]['tópico'] = nil
        end
        if camposv[n]['rótulo'] then
            -- junta campos de mesmo rótulo
            if #campos > 0 and camposv[n]['rótulo'] == campos[i]['rótulo'] then
                table.insert(campos[i], camposv[n][1])
            else
                i = i + 1
                campos[i] = camposv[n]
            end
        elseif camposv[n][1] then
            i = i + 1
            campos[i] = camposv[n]
        end
        if campos[i][1] and string.match(campos[i][1], '\n{|class="infobox') then
            local info
            campos[i][1], info = subInfo(campos[i][1])
            if info and info ~= '' then
                i = i + 1
                campos[i] = {['info'] = info}
            end
        end
        -- para debug
        if debug.campos then
            local t = {}
            for k, v in pairs(campos[i]) do
                table.insert(t, tostring(k) .. '=' .. tostring(v))
            end
            table.insert(debug.campos, i .. ' => {' .. table.concat(t, ', ') .. '}')
        end
    end
    camposn, camposv = nil, nil

    -- Imagens e legendas no formato antigo --
    for i, n in ipairs({'', '1', '2', '3'}) do
        if params['imagem' .. n] then
	    table.insert(campos, 1, {['rótulo']='#imagem', params['imagem' .. n]})
	    if params['legenda' .. n] or params['imagem_legenda' .. n] then
	        table.insert(campos, 2, {params['legenda' .. n] or params['imagem_legenda' .. n]})
	    end
        end
    end

    -- Chamar extenção
    local ext
    local especial = especial
    if estender then
        local title = mw.title.new('Módulo:Info/' .. estender)
        if title.exists then
            ext = require('Módulo:Info/' .. estender)
            if ext.especial then
                for k, v in pairs(ext.especial) do
                    especial[k] = v
                end
            end
            if ext.campos then
                ext.campos(campos)
            end
        end
    end

    -- Processar campos que pedem dados do Wikidata, funções especiais e remove dados vazios --
    wdEntity = mw.wikibase.getEntity(params['item'] and params['item']:match('^Q%d+$'))
    local importar = params['wikidata'] ~= 'não'
    for i, campo in ipairs(campos) do
        while campo[1] do
            local arg = string.match(campo[1], '^#[Ww][Dd]: *(.*)')
            if arg and importar then
                if not wd or not debug.wikidata then
                    -- Só importa o módulo do Wikidata quando tiver campos do Wikidata
                    require('Módulo:Info/' .. (params['wd'] or 'wd'))
                    debug.wikidata = {}
                end
                local dados = wd.expandir(arg)
                if dados then
                    campo[1] = dados
                    campo['wikidata'] = true
                    break
                else
                    table.remove(campo, 1)
                end
                if wd.temp.debug then
                    table.insert(debug.wikidata, wd.temp.debug)
                    wd.temp = {}
                end
            elseif arg or campo[1] == '' then
                table.remove(campo, 1)
            else
                break
            end
        end
        local esp = campo['rótulo'] and string.match(campo['rótulo'], '^#(.*)') or nil
        if esp and especial[esp] then
            campos[i] = especial[esp](campo, i) or {}
        end
    end
    if ext and ext.extra then
        extra = ext.extra()
    end

    -- Contar número de campos e campos do Wikidata --
    local contarmin, contarmax = 0, 2
    local contarwdmin, contarwdmax
    local contarwdtodos = true
    if namespace ~= 0 then  -- só categoriza por padrão em artigos
        contarmin, contarmax, contarwdtodos = nil, nil, nil
    end
    if params['contar campos'] then
        contarmin, contarmax = string.match(params['contar campos'], '(%d%d?)[^%d]*(%d%d?)')
        if contarmin then
            contarmin, contarmax = tonumber(contarmin), math.min(10, tonumber(contarmax))
        elseif params['contar campos'] == 'sim' then
            contarmin, contarmax = 0, 10
        elseif params['contar campos'] == 'não' then
            contarmin, contarmax = nil, nil
        end
    end
    if params['contar wikidata'] then
        contarwdmin, contarwdmax = string.match(params['contar wikidata'], '(%d%d?)[^%d]*(%d%d?)')
        if contarwdmin then
            contarwdmin, contarwdmax = tonumber(contarwdmin), math.min(10, tonumber(contarwdmax))
        elseif params['contar wikidata'] == 'sim' then
            contarwdmin, contarwdmax = 0, 10
        elseif params['contar wikidata'] == 'não' then
            contarwdtodos = false
        end
    end
    if contarmin or contarwdmin or contarwdtodos then
        ncampos, ncamposwd = 0, 0
        for k, v in pairs(campos) do
            if v[1] then
                ncampos = ncampos + 1
            end
            if v[1] and v['wikidata'] then
                ncamposwd = ncamposwd + 1
            end
        end
        if contarwdtodos then
            todoscamposwd = ncampos >= 1 and ncampos == ncamposwd -- se todos os campos são do Wikidata
        end
        if not contarmin or (ncampos < tonumber(contarmin) or ncampos > contarmax) then
            ncampos = false
        end
        if not contarwdmin or (ncamposwd < tonumber(contarwdmin) or ncamposwd > contarwdmax) then
            ncamposwd = false
        end
    end

    -- Categoria de artigos bons e destacados em Wikipédias de outras línguas
    if namespace == 0 and wdEntity and wdEntity['sitelinks'] then
        local cats = {}
        local badges = {['Q17437796'] = '!Artigos destacados', ['Q17437798'] = '!Artigos bons',
          ['Q17506997'] = '!Listas destacadas'}
        for wiki, sitelink in pairs(wdEntity['sitelinks']) do
            for _, badge in ipairs(sitelink['badges']) do
                if badges[badge] and wiki:match('wiki$') then
                    table.insert(cats, {badges[badge], wiki:match('^(.+)wiki$')})
                end
            end
        end
        if #cats > 0 then -- só carrega a lista de prefixos se for usar
            prefixos = mw.loadData('Módulo:Prefixos de língua')
            for i, cat in ipairs(cats) do
                local lingua = prefixos[cat[2]]
                cats[i] = lingua and '[[Categoria:' .. cat[1] .. ' na Wikipédia em ' .. lingua .. ']]' or ''
            end
            extra = (extra or '') .. table.concat(cats)
        end
    end

    return montarinfo(extra, debug)
end

local m, mt = {}, {}

m.base = function(frame)
    local parent = frame:getParent()
    return base(frame.args, parent and parent:getTitle())
end

mt.__index = function(t, k)
    return function(frame)
        local parent = frame:getParent()
        return base(frame.args, parent and parent:getTitle(), k)
    end
end

return setmetatable(m, mt)