Module:AbilitySection: Difference between revisions

Vergir (talk | contribs)
Identify interaction boxes by their data attributes rather than CSS class names, so box styling can change independently (with help from vergir-bot LLM)
Use Data:Dictionary keys for ability card tab labels instead of hardcoded English (with help from vergir-bot LLM)
 
(3 intermediate revisions by 2 users not shown)
Line 4: Line 4:
local utils          = require "Module:Abilities/utils"
local utils          = require "Module:Abilities/utils"
local lang            = require "Module:Lang"
local lang            = require "Module:Lang"
local icon_module    = require "Module:Icon"
local AbilityHints    = require "Module:AbilityHints"
local AbilityHints    = require "Module:AbilityHints"
local resource_lookup = mw.loadJsonData("Data:ResourceLookup.json")
local resource_lookup = mw.loadJsonData("Data:ResourceLookup.json")
local dictionary_data = mw.loadJsonData("Data:Dictionary")


-- Tab definitions — order here determines display order.
-- Tab definitions — order here determines display order.
-- label_dict_key names the [[Data:Dictionary]] entry holding the tab label; the Notes
-- tab reuses the existing 'Notes' entry rather than adding a duplicate.
local TABS = {
local TABS = {
{ key = 'notes',        label = 'Notes'       },
    { key = 'notes',        label_dict_key = 'Notes'                     },
{ key = 'tips',        label = 'Tips'        },
    { key = 'tips',        label_dict_key = 'AbilityCardTabTips'        },
{ key = 'interactions', label = 'Interactions' },
    { key = 'interactions', label_dict_key = 'AbilityCardTabInteractions' },
}
}
local SHOW_ALL_DICT_KEY = 'AbilityCardTabShowAll'
-- Tab labels are read from the dictionary directly instead of going through
-- Module:Dictionary, which appends Template:NeedsTranslationTooltip markup to
-- anything untranslated. mw.ext.tabber.render HTML-escapes a label and reuses it as a
-- DOM id, so that markup would show up as literal text in the tab and mangle the
-- anchor. Untranslated labels therefore fall back to English silently.
local function tab_label(dict_key)
    local entry = dictionary_data[dict_key]
    if entry == nil then return dict_key end
    return entry[lang.get_lang_code()] or entry.en or dict_key
end


local function get_hero_key(hero_name)
local function get_hero_key(hero_name)
if hero_name == nil or hero_name == '' then return nil end
    if hero_name == nil or hero_name == '' then return nil end
if string.sub(hero_name, 1, 5) == "hero_" then return hero_name end
    if string.sub(hero_name, 1, 5) == "hero_" then return hero_name end
local entry = resource_lookup[hero_name:lower()]
    local entry = resource_lookup[hero_name:lower()]
if entry == nil then return nil end
    if entry == nil then return nil end
return entry.key
    return entry.key
end
end


local function trim(s)
local function trim(s)
return s:match('^%s*(.-)%s*$')
    return s:match('^%s*(.-)%s*$')
end
end


-- Process content in the Interactions tab to build list of icons to draw in ability hints beside ability card
local function process_interactions(html, ability_num)
local function process_interactions(html, ability_num)
local manual, idx, seen = {}, 0, {}
    local manual, idx, seen = {}, 0, {}
-- Boxes are identified by their data-ai-* attributes, which are the contract
    local out = html:gsub('(<div)([^>]*data%-iconbox%-icon="[^"]*"[^>]*)(>)', function(pre, attrs, post)
-- with Template:IconBox. Class names are presentational and deliberately not
        local file = attrs:match('data%-iconbox%-icon="([^"]*)"')
-- matched here, so box styling can change without touching this.
        if file and file ~= '' then
local out = html:gsub('(<div)([^>]*data%-ai%-icon="[^"]*"[^>]*)(>)', function(pre, attrs, post)
            local base  = mw.ustring.lower((file:gsub('%.%w+$', '')))
local file = attrs:match('data%-ai%-icon="([^"]*)"')
            local entry = resource_lookup[base]
if file and file ~= '' then
            if entry ~= nil and entry.type == 'item' and not seen[base] then
local base  = mw.ustring.lower((file:gsub('%.%w+$', '')))
                seen[base] = true
local entry = resource_lookup[base]
                idx = idx + 1
if entry ~= nil and entry.type == 'item' and not seen[base] then
                local box_id = 'ai-int-' .. ability_num .. '-' .. idx
seen[base] = true
                table.insert(manual, {
idx = idx + 1
                    file  = file,
local box_id = 'ai-int-' .. ability_num .. '-' .. idx
                    title  = attrs:match('data%-iconbox%-title="([^"]*)"') or '',
table.insert(manual, {
                    state  = attrs:match('data%-iconbox%-state="([^"]*)"') or 'normal',
file  = file,
                    anchor = box_id,
title  = attrs:match('data%-ai%-title="([^"]*)"') or '',
                })
state  = attrs:match('data%-ai%-state="([^"]*)"') or 'normal',
                return pre .. attrs .. ' id="' .. box_id .. '"' .. post
anchor = box_id,
            end
})
        end
return pre .. attrs .. ' id="' .. box_id .. '"' .. post
        return pre .. attrs .. post
end
    end)
end
    return out, manual
return pre .. attrs .. post
end)
return out, manual
end
end


local function build_tabber(wikitext_map, tab_overrides)
local function build_tabber(wikitext_map, tab_overrides)
tab_overrides = tab_overrides or {}
    tab_overrides = tab_overrides or {}
local filled = {}
    local filled = {}
for _, tab in ipairs(TABS) do
    for _, tab in ipairs(TABS) do
if wikitext_map[tab.key] ~= '' then
        if wikitext_map[tab.key] ~= '' then
local tab_wt = tab_overrides[tab.key]
            local tab_wt = tab_overrides[tab.key]
if tab_wt == nil or tab_wt == '' then tab_wt = wikitext_map[tab.key] end
            if tab_wt == nil or tab_wt == '' then tab_wt = wikitext_map[tab.key] end
table.insert(filled, {
            table.insert(filled, {
key    = tab.key,
                key    = tab.key,
label  = tab.label,
                label  = tab_label(tab.label_dict_key),
tab_wt = tab_wt,
                tab_wt = tab_wt,
all_wt = wikitext_map[tab.key],
                all_wt = wikitext_map[tab.key],
})
            })
end
        end
end
    end


if #filled == 0 then
    if #filled == 0 then
return ''
        return ''
end
    end


local tabs = {}
    local tabs = {}
for _, section in ipairs(filled) do
    for _, section in ipairs(filled) do
table.insert(tabs, {
        table.insert(tabs, {
label  = section.label,
            label  = section.label,
content = '<div class="ability-details-panel">\n' .. section.tab_wt .. '\n</div>',
            content = '<div class="ability-details-panel">\n' .. section.tab_wt .. '\n</div>',
})
        })
end
    end


if #filled >= 2 then
    if #filled >= 2 then
local all_parts = {}
        local all_parts = {}
for _, section in ipairs(filled) do
        for _, section in ipairs(filled) do
table.insert(all_parts,
            table.insert(all_parts,
'<div class="ability-details-section-header">' .. section.label .. '</div>'
                '<div class="ability-details-section-header">' .. section.label .. '</div>'
.. '<div class="ability-details-section-content">\n' .. section.all_wt .. '\n</div>'
                .. '<div class="ability-details-section-content">\n' .. section.all_wt .. '\n</div>'
)
            )
end
        end
table.insert(tabs, {
        table.insert(tabs, {
label  = 'Show All',
            label  = tab_label(SHOW_ALL_DICT_KEY),
content = '<div class="ability-details-panel">' .. table.concat(all_parts) .. '</div>',
            content = '<div class="ability-details-panel">' .. table.concat(all_parts) .. '</div>',
})
        })
end
    end


local rendered = mw.ext.tabber.render(tabs)
    local rendered = mw.ext.tabber.render(tabs)
if #filled == 1 then
    if #filled == 1 then
return '<div class="single-tabbed">' .. rendered .. '</div>'
        return '<div class="single-tabbed">' .. rendered .. '</div>'
end
    end
return rendered
    return rendered
end
end


-- Entry point. Called via {{#invoke:AbilitySection|render}}.
-- Entry point. Called via {{#invoke:AbilitySection|render}}.
function p.render(frame)
function p.render(frame)
local args        = frame.args
    local args        = frame.args
local hero_name  = trim(args.hero_name or '')
    local hero_name  = trim(args.hero_name or '')
local ability_num = trim(args.ability_num or '')
    local ability_num = trim(args.ability_num or '')
local notes        = trim(args.notes or '')
    local notes        = trim(args.notes or '')
local tips        = trim(args.tips or '')
    local tips        = trim(args.tips or '')
local interactions = trim(args.interactions or '')
    local interactions = trim(args.interactions or '')


local hero_key    = get_hero_key(hero_name)
    local hero_key    = get_hero_key(hero_name)
local heading    = ''
    local heading    = ''
local ability_key = nil
    local ability_key = nil
if hero_key ~= nil then
    if hero_key ~= nil then
local ability = utils.get_ability_card_data(hero_key, ability_num)
        local ability = utils.get_ability_card_data(hero_key, ability_num)
if ability ~= nil then
        if ability ~= nil then
ability_key = ability.Key
            ability_key = ability.Key
local ability_name = lang.get_string(ability.Key)
            local ability_name = lang.get_string(ability.Key)
-- Use the English ability key for icon render; icon module indexes by English name
            -- Create a visually hidden H3 heading so the Table of Contents can parse
local ability_name_en = lang.get_string(ability.Key, 'en', ability.Key)
            -- the ability name without displaying a redundant title above the card.
local icon_html = icon_module._render(ability_name_en, { size = '30px', ['icon-only'] = 'true', ['no-link'] = 'true' })
            heading = '<h3 class="ability-name-toc">' .. ability_name .. '</h3>'
heading = '<h3 class="ability-name">' .. icon_html .. ' ' .. ability_name .. '</h3>'
        end
end
    end
end


local card = frame:expandTemplate{
    local card = frame:expandTemplate{
title = 'Ability card v2',
        title = 'Ability card v2',
args  = { hero_name = hero_name, ability_num = ability_num, link_name = 'false' }
        args  = { hero_name = hero_name, ability_num = ability_num, link_name = 'false' }
}
    }


local interactions_ided, manual = interactions, {}
    local interactions_ided, manual = interactions, {}
if interactions ~= '' then
    if interactions ~= '' then
interactions_ided, manual = process_interactions(interactions, ability_num)
        interactions_ided, manual = process_interactions(interactions, ability_num)
end
    end


local wikitext_map = {
    local wikitext_map = {
notes        = notes,
        notes        = notes,
tips        = tips,
        tips        = tips,
interactions = interactions,
        interactions = interactions,
}
    }


local tabber = build_tabber(wikitext_map, { interactions = interactions_ided })
    local tabber = build_tabber(wikitext_map, { interactions = interactions_ided })


local hints_col = ''
    local hints_col = ''
if ability_key ~= nil or #manual > 0 then
    if ability_key ~= nil or #manual > 0 then
hints_col = AbilityHints.get_icons(ability_key, ability_num, manual)
        hints_col = AbilityHints.get_icons(ability_key, ability_num, manual)
end
    end


local details_col = ''
    local details_col = ''
if tabber ~= '' then
    if tabber ~= '' then
details_col = '<div class="ability-section-details">' .. tabber .. '</div>'
        details_col = '<div class="ability-section-details">' .. tabber .. '</div>'
end
    end


local card_block = '<div class="ability-section-card">' .. card .. '</div>'
    local card_block = '<div class="ability-section-card">' .. card .. '</div>'
local left_col
    local left_col
if hints_col ~= '' then
    if hints_col ~= '' then
left_col = '<div class="ability-section-main">' .. card_block .. hints_col .. '</div>'
        left_col = '<div class="ability-section-main">' .. card_block .. hints_col .. '</div>'
else
    else
left_col = card_block
        left_col = card_block
end
    end


local ability_section = '<div class="ability-section">'
    local ability_section = '<div class="ability-section">'
.. left_col
        .. left_col
.. details_col
        .. details_col
.. '</div>'
        .. '</div>'


return '<div class="ability-section-wrapper">'
    return '<div class="ability-section-wrapper">'
.. heading
        .. heading
.. ability_section
        .. ability_section
.. '</div>'
        .. '</div>'
end
end


return p
return p