Ir para o conteúdo

Módulo:Sidebar

Permanently protected module
Origem: Wikipédia, a enciclopédia livre.

require('strict')
local cfg = mw.loadData('Módulo:Sidebar/configuração')

local p = {}

local getArgs = require('Módulo:Arguments').getArgs

--[[
Categoriza predefinições e módulos de chamada com um parâmetro 'style' de qualquer tipo
para rastreamento para conversão em TemplateStyles.

TODO após uma longa limpeza: captura barras laterais em outros espaços nomeados além de Predefinição e Módulo.
TODO provavelmente desejaria remover /log e /archive como o CS1 faz
]]
local function categorizeTemplatesWithInlineStyles(args)
    local title = mw.title.getCurrentTitle()
    if title.namespace ~= 10 and title.namespace ~= 828 then return '' end
    for _, pattern in ipairs (cfg.i18n.pattern.uncategorized_conversion_titles) do
        if title.text:match(pattern) then return '' end
    end

    for key, _ in pairs(args) do
        if string.find(key, cfg.i18n.pattern.style_conversion) or key == 'width' then
            return cfg.i18n.category.conversion
        end
    end
end

--[[
Para compatibilidade com a implementação original d {{Sidebar with collapsible lists}}, que passou alguns parâmetros por meio de {{#if}} para aparar seus
espaços em branco. Isso também acionou o comportamento de nova linha automática.
]]
-- Ver ([[meta:Help:Newlines and spaces#Automatic newline]])
local function trimAndAddAutomaticNewline(s)
    s = string.gsub(s, "^%s*(.-)%s*$", "%1")
    if string.find(s, '^[#*:;]') or string.find(s, '^{|') then
        return '\n' .. s
    else
        return s
    end
end

--[[
Verifica se uma barra lateral tem uma barra lateral de subgrupo.
]]
local function hasSubgroup(s)
    if string.find(s, cfg.i18n.pattern.subgroup) then
        return true
    else
        return false
    end
end

local function has_navbar(navbar_mode, sidebar_name)
    return navbar_mode ~= cfg.i18n.navbar_none and
        navbar_mode ~= cfg.i18n.navbar_off and
        (
            sidebar_name or
            mw.getCurrentFrame():getParent():getTitle():gsub(cfg.i18n.pattern.sandbox, '') ~=
            cfg.i18n.title_not_to_add_navbar
        )
end

local function has_list_class(args, htmlclass)
    local patterns = {
        '^' .. htmlclass .. '$',
        '%s' .. htmlclass .. '$',
        '^' .. htmlclass .. '%s',
        '%s' .. htmlclass .. '%s'
    }

    for arg, value in pairs(args) do
        if type(arg) == 'string' and string.find(arg, 'class') then
            for _, pattern in ipairs(patterns) do
                if string.find(args[arg] or '', pattern) then
                    return true
                end
            end
        end
    end
    return false
end

-- Existem muitas classes de locais disponíveis, então adicionamos seus TemplateStyles
local function add_list_styles(args)
    local frame = mw.getCurrentFrame()
    local function add_list_templatestyles(htmlclass, templatestyles)
        if has_list_class(args, htmlclass) then
            return frame:extensionTag{
                name = 'templatestyles', args = { src = templatestyles }
            }
        else
            return ''
        end
    end

    local plainlist_styles = add_list_templatestyles('plainlist', cfg.i18n.plainlist_templatestyles)
    local hlist_styles = add_list_templatestyles('hlist', cfg.i18n.hlist_templatestyles)

    -- uma segunda solução alternativa para [[phab:T303378]]
    -- quando esse problema for corrigido, podemos realmente usar has_navbar para não emitir a
    -- tag aqui se quisermos
    if has_navbar(args.navbar, (args.name or args.nome)) and hlist_styles == '' then
        hlist_styles = frame:extensionTag{
            name = 'templatestyles', args = { src = cfg.i18n.hlist_templatestyles}
        }
    end

    -- hlist -> plainlist é o melhor esforço para preservar a antiga ordenação Common.css. [hlist_note]
    return hlist_styles .. plainlist_styles
end

-- solução alternativa [[phab:T303378]]
-- para cada argumento: encontra todos os marcadores de tira de templatestyles, insere-os em uma
-- tabela. então remove todos os marcadores de templatestyles do argumento
local function move_hiding_templatestyles(args)
    local gmatch = string.gmatch
    local gsub = string.gsub
    local templatestyles_markers = {}
    local strip_marker_pattern = '(\127[^\127]*UNIQ%-%-templatestyles%-%x+%-QINU[^\127]*\127)'
    for k, arg in pairs(args) do
        for marker in gmatch(arg, strip_marker_pattern) do
            table.insert(templatestyles_markers, marker)
        end
        args[k] = gsub(arg, strip_marker_pattern, '')
    end
    return templatestyles_markers
end

--[[
Função principal da barra lateral. Pega frame, args e uma collapsibleClass opcional.
A collapsibleClass é e deve ser usada apenas para barras laterais com listas
collapsíveis, como em p.collapsible.
]]
function p.sidebar(frame, args, collapsibleClass)
    if not args then args = getArgs(frame) end
    local hiding_templatestyles = table.concat(move_hiding_templatestyles(args))
    local root = mw.html.create()
    local child = (args.child or args.filha) and mw.text.trim(args.child or args.filha) == cfg.i18n.child_yes

    root = root:tag('table')
    if not child then
        root 
            :addClass(cfg.i18n.class.sidebar)
            -- força collapsibleclass a ser sidebar-collapse, caso contrário, não produzirá nada
            :addClass(collapsibleClass == cfg.i18n.class.collapse and cfg.i18n.class.collapse or nil)
            :addClass('nomobile')
            :addClass((args.float or args['flutuar'] or args['lado']) == cfg.i18n.float_none and cfg.i18n.class.float_none or nil)
            :addClass((args.float or args['flutuar'] or args['lado']) == cfg.i18n.float_left and cfg.i18n.class.float_left or nil)
            :addClass(args.wraplinks ~= cfg.i18n.wrap_true and cfg.i18n.class.wraplinks or nil)
            -- A linha antiga do bodyclass foi removida daqui
            :css('width', args.width or args.largura or nil)
            :cssText(args.bodystyle or args['estilo-corpo'] or args.style or args.estilo)

        -- TRATAMENTO DA STRING VAZIA PARA O CORPO PRINCIPAL:
        local bodyclass = args.bodyclass or args['classe-corpo'] or args.class or args.classe
        if bodyclass and bodyclass ~= '' then
            root:addClass(bodyclass)
        end

        if (args.outertitle or args['título-externo'] or args['título-exterior'] or args['cabeçalho']) then
            root
                :tag('caption')
                    :addClass(cfg.i18n.class.outer_title)
                    :addClass(args.outertitleclass or args['classe-título-externo'] or args['classe-título-exterior'] or args['classe-cabeçalho'])
                    :cssText(args.outertitlestyle or args['estilo-título-externo'] or args['estilo-título-exterior'] or args['estilo-cabeçalho'])
                    :wikitext(args.outertitle or args['título-externo'] or args['título-exterior'] or args['cabeçalho'])
        end

        if (args.topimage or args['imagem-topo'] or args['imagem2']) then
            local imageCell = root:tag('tr'):tag('td')

            imageCell
                :addClass(cfg.i18n.class.top_image)
                :addClass(args.topimageclass or args['classe-imagem-topo'] or args['classe-imagem2'])
                :cssText(args.topimagestyle or args['estilo-imagem-topo'] or args['estilo-imagem2'])
                :wikitext(args.topimage or args['imagem-topo'] or args['imagem2'])

            if (args.topcaption or args['legenda-topo'] or args['imagem_legenda2']) then
                imageCell
                    :tag('div')
                        :addClass(cfg.i18n.class.top_caption)
                        :cssText(args.topcaptionstyle or args['estilo-legenda-topo'] or args['estilo-imagem_legenda2'])
                        :wikitext(args.topcaption or args['legenda-topo'] or args['imagem_legenda2'])
            end
        end

        if (args.pretitle or args['pré-título']) then
            root
                :tag('tr')
                    :tag('td')
                        :addClass(args.topimage and cfg.i18n.class.pretitle_with_top_image
                            or cfg.i18n.class.pretitle)
                        :addClass(args.pretitleclass or args['classe-pré-título'])
                        :cssText(args.basestyle or args['estilo-base'])
                        :cssText(args.pretitlestyle or args['estilo-pré-título'])
                        :wikitext(args.pretitle or args['pré-título'])
        end
    else
        root
            :addClass(cfg.i18n.class.subgroup)
            :cssText(args.bodystyle or args['estilo-corpo'] or args.style or args.estilo)

        -- TRATAMENTO DA STRING VAZIA PARA O SUBGRUPO:
        local subclass = args.bodyclass or args['classe-corpo'] or args.class or args.classe
        if subclass and subclass ~= '' then
            root:addClass(subclass)
        end
    end

    if (args.title or args['título']) then
        if child then
            root
                :wikitext(args.title or args['título'])
        else
            root
                :tag('tr')
                    :tag('th')
                        :addClass((args.pretitle or args['pré-título']) and cfg.i18n.class.title_with_pretitle
                            or cfg.i18n.class.title)
                        :addClass(args.titleclass or args['classe-título'])
                        :css('font-size', '1.5em') -- *
                        :cssText(args.basestyle or args['estilo-base'])
                        :cssText(args.titlestyle or args['estilo-título'])
                        :wikitext(args.title or args['título'])
        end
    end

    if (args.image or args.imagem) then
        local imageCell = root:tag('tr'):tag('td')
        imageCell
            :addClass(cfg.i18n.class.image)
            :addClass(args.imageclass or args['classe-imagem'])
            :cssText(args.imagestyle or args['estilo-imagem'])
            :wikitext(args.image or args.imagem)

        if (args.caption or args.legenda or args['imagem_legenda']) then
            imageCell
                :tag('div')
                    :addClass(cfg.i18n.class.caption)
                    :cssText(args.captionstyle or args['estilo-legenda'] or args['estilo-imagem_legenda'])
                    :wikitext(args.caption or args.legenda or args['imagem_legenda'])
        end
    end

    if (args.above or args.acima) then
        root
            :tag('tr')
                :tag('td')
                    :addClass(cfg.i18n.class.above)
                    :addClass(args.aboveclass or args['classe-acima'])
                    :cssText(args.abovestyle or args['estilo-acima'])
                    :newline() -- nova linha necessária para que os marcadores funcionem
                    :wikitext(args.above or args.acima)
    end

    local rowNums = {}
    for k, v in pairs(args) do
        k = '' .. k
        local num = k:match('^heading(%d+)$') or k:match('^rótulo(%d+)$') or k:match('^content(%d+)$') or k:match('^conteúdo(%d+)$') or k:match('^dados(%d+)$')
        if num then table.insert(rowNums, tonumber(num)) end
    end
    table.sort(rowNums)
    -- remove duplicatas da lista (por exemplo, 3 será duplicado se ambos heading3
    -- e content3 forem especificados)
    for i = #rowNums, 1, -1 do
        if rowNums[i] == rowNums[i - 1] then
            table.remove(rowNums, i)
        end
    end

    for i, num in ipairs(rowNums) do
        local heading = args['heading' .. num] or args['rótulo' .. num]
        if heading then
            root
                :tag('tr')
                    :tag('th')
                        :addClass(cfg.i18n.class.heading)
                        :addClass(args.headingclass or args['classe-rótulo'])
                        :addClass(args['heading' .. num .. 'class'] or args['classe-rótulo' .. num])
                        :cssText(args.basestyle or args['estilo-base'])
                        :cssText(args.headingstyle or args['estilo-rótulo'])
                        :cssText(args['heading' .. num .. 'style'] or args['estilo-rótulo' .. num])
                        :newline()
                        :wikitext(heading)
        end

        local content = args['content' .. num] or args['conteúdo' .. num] or args['dados' .. num]
        if content then
            root
                :tag('tr')
                    :tag('td')
                        :addClass(hasSubgroup(content) and cfg.i18n.class.content_with_subgroup
                            or cfg.i18n.class.content)
                        :addClass(args.contentclass or args['classe-conteúdo'] or args['classe-dados'])
                        :addClass(args['content' .. num .. 'class'] or args['classe-conteúdo' .. num] or args['classe-dados' .. num])
                        :cssText(args.contentstyle or args['estilo-conteúdo'] or args['estilo-dados'])
                        :cssText(args['content' .. num .. 'style'] or args['estilo-conteúdo' .. num] or args['estilo-dados' .. num])
                        :newline()
                        :wikitext(content)
                        :done()
                        -- Sem uma quebra de linha após </td>, uma lista aninhada como
                        -- "* {{hlist| ...}}" não é analisada corretamente.
                    :newline()
        end
    end

    if (args.below or args['rodapé']) then
        root
            :tag('tr')
                :tag('td')
                    :addClass(cfg.i18n.class.below)
                    :addClass(args.belowclass or args['classe-rodapé'])
                    :cssText(args.belowstyle or args['estilo-rodapé'])
                    :newline()
                    :wikitext(args.below or args['rodapé'])
    end
    if not child and has_navbar(args.navbar, (args.name or args.nome)) then
        root
            :tag('tr')
                :tag('td')
                    :addClass(cfg.i18n.class.navbar)
                    :cssText(args.navbarstyle or args['estilo-navbar'])
                    :wikitext(require('Módulo:Navbar')._navbar{
                        (args.name or args.nome),
                        mini = 1,
                        fontstyle = args.navbarfontstyle or args['estilo-fonte-navbar']
                    })
    end

    local base_templatestyles = frame:extensionTag{
        name = 'templatestyles', args = { src = cfg.i18n.templatestyles }
    }

    local templatestyles = ''
    if args['templatestyles'] and args['templatestyles'] ~= '' then
        templatestyles = frame:extensionTag{
            name = 'templatestyles', args = { src = args['templatestyles'] }
        }
    end

    local child_templatestyles = ''
    if args['child templatestyles'] and args['child templatestyles'] ~= '' then
        child_templatestyles = frame:extensionTag{
            name = 'templatestyles', args = { src = args['child templatestyles'] }
        }
    end

    local grandchild_templatestyles = ''
    if args['grandchild templatestyles'] and args['grandchild templatestyles'] ~= '' then
        grandchild_templatestyles = frame:extensionTag{
            name = 'templatestyles', args = { src = args['grandchild templatestyles'] }
        }
    end

    return table.concat({
        add_list_styles(args), -- ver [hlist_note] acima sobre a ordenação 
        base_templatestyles,
        templatestyles,
        child_templatestyles,
        grandchild_templatestyles,
        hiding_templatestyles,
        tostring(root),
        (child and cfg.i18n.category.child or ''),
        categorizeTemplatesWithInlineStyles(args)
    })
end

local function list_title(args, is_centered_list_titles, num)
    local title_text = trimAndAddAutomaticNewline(args['list' .. num .. 'title'] or args['título-lista' .. num]
        or cfg.i18n.default_list_title)

    local title
    if is_centered_list_titles then
        -- recolhível pode ser complicado, então fornece alguma CSS/HTML para dar suporte
        title = mw.html.create('div')
            :addClass(cfg.i18n.class.list_title_centered)
            :wikitext(title_text)
    else
        title = mw.html.create()
            :wikitext(title_text)
    end

    local title_container = mw.html.create('div')
        :addClass(cfg.i18n.class.list_title)
        -- não /precisa/ de uma listnumtitleclass porque você pode fazer
        -- .templateclass .listnumclass .sidebar-list-title
        :addClass(args.listtitleclass or args['classe-título-lista'])
        :cssText(args.basestyle or args['estilo-base'])
        :cssText(args.listtitlestyle or args['estilo-título-lista'])
        -- :cssText('color: var(--color-base)') --LINHA REMOVIDA DAQUI PARA EVITAR TEXTO INVISÍVEL NO MODO ESCURO
        :cssText(args['list' .. num .. 'titlestyle'] or args['estilo-título-lista' .. num])
        :node(title)
        :done()

    return title_container
end

--[[Ponto de entrada principal para barra lateral com listas recolhíveis.
Faz o trabalho de criar as próprias listas recolhíveis e incluí-las
nos args.
]]
function p.collapsible(frame)
    local args = getArgs(frame)
    if not (args.name or args.nome) and
        frame:getParent():getTitle():gsub(cfg.i18n.pattern.collapse_sandbox, '') ==
        cfg.i18n.collapse_title_not_to_add_navbar then
        args.navbar = cfg.i18n.navbar_none
    end

    local contentArgs = {}

    local is_centered_list_titles = false
    if args['centered list titles'] and args['centered list titles'] ~= '' then
        is_centered_list_titles = true
    end

    for k, v in pairs(args) do
        local num = string.match(k, '^list(%d+)$') or string.match(k, '^lista(%d+)$')
        if num then
            local expand = args.expanded and
                (args.expanded == 'all' or args.expanded == 'tudo'
                or args.expanded == 'todo' or args.expanded == 'toda'
                or args.expanded == 'todos' or args.expanded == 'todas'
                or args.expanded == (args['list' .. num .. 'name'] or args['lista' .. num .. 'nome'] or args['lista' .. num .. '-nome'])) or args.expandir and
                (args.expandir == 'all' or args.expandir == 'tudo'
                or args.expandir == 'todo' or args.expandir == 'toda'
                or args.expandir == 'todos' or args.expandir == 'todas'
                or args.expandir == (args['list' .. num .. 'name'] or args['lista' .. num .. 'nome'] or args['lista' .. num .. '-nome'])) or args.expandida and
                (args.expandida == 'all' or args.expandida == 'tudo'
                or args.expandida == 'todo' or args.expandida == 'toda'
                or args.expandida == 'todos' or args.expandida == 'todas'
                or args.expandida == (args['list' .. num .. 'name'] or args['lista' .. num .. 'nome'] or args['lista' .. num .. '-nome']))
            local row = mw.html.create('div')
            row
                :addClass(cfg.i18n.class.list)
                :addClass('mw-collapsible')
                :addClass((not expand) and 'mw-collapsed' or nil)
                :addClass(args['list' .. num .. 'class'] or args['classe-lista' .. num])
                :cssText(args.listframestyle or args['estilo-quadro-lista'])
                :cssText(args['list' .. num .. 'framestyle'] or args['estilo-quadro-lista' .. num])
                :node(list_title(args, is_centered_list_titles, num))
                :tag('div')
                    :addClass(cfg.i18n.class.list_content)
                    :addClass('mw-collapsible-content')
                    :addClass(args.listclass or args['classe-lista'])
                    :cssText(args.liststyle or args['estilo-lista'])
                    :cssText(args['list' .. num .. 'style'] or args['estilo-lista' .. num])
                    :wikitext(trimAndAddAutomaticNewline(args['list' .. num] or args['lista' .. num]))

            
            contentArgs['content' .. num] = tostring(row)
        end
    end

    for k, v in pairs(contentArgs) do
        args[k] = v
    end

    return p.sidebar(frame, args, cfg.i18n.class.collapse)
end

return p