Mudanças entre as edições de "Módulo:Info.Skills"

De Wiki Gla
Ir para navegação Ir para pesquisar
m
Etiqueta: Revertido
m
Etiqueta: Revertido
Linha 64: Linha 64:
     end
     end
     local meta = fetchSkillMeta(char, ref)
     local meta = fetchSkillMeta(char, ref)
 
   
     -- Se não encontrou no cache, tenta buscar diretamente do módulo
     -- Se não encontrou no cache, tenta buscar diretamente do módulo
     if not meta then
     if not meta then
Linha 87: Linha 87:
         end
         end
     end
     end
 
   
     if not meta then
     if not meta then
         return sub
         return sub
Linha 114: Linha 114:
     hydrated.powerpvp = pick(sub.powerpvp, meta.powerpvp) or meta.powerpvp
     hydrated.powerpvp = pick(sub.powerpvp, meta.powerpvp) or meta.powerpvp
     hydrated.cooldown = pick(sub.cooldown, meta.cooldown) or meta.cooldown
     hydrated.cooldown = pick(sub.cooldown, meta.cooldown) or meta.cooldown
 
   
     -- Garante que name nunca seja vazio
     -- Garante que name nunca seja vazio
     if not hydrated.name or trim(hydrated.name) == "" then
     if not hydrated.name or trim(hydrated.name) == "" then
Linha 459: Linha 459:
         local order = data.order or {}
         local order = data.order or {}
         local key = idx and order[idx] or ""
         local key = idx and order[idx] or ""
 
       
         -- Tenta buscar do cache primeiro (pode não estar pronto ainda)
         -- Tenta buscar do cache primeiro (pode não estar pronto ainda)
         local meta = fetchSkillMeta(char, refM)
         local meta = fetchSkillMeta(char, refM)
 
       
         -- Se não tem no cache, busca direto do módulo
         -- Se não tem no cache, busca direto do módulo
         if not meta and key ~= "" then
         if not meta and key ~= "" then
Linha 480: Linha 480:
             end
             end
         end
         end
 
       
         -- Garante que sempre tenha um name válido (mesmo que temporário)
         -- Garante que sempre tenha um name válido (mesmo que temporário)
         local resolvedName = meta and meta.name or key or ("Skill " .. refM)
         local resolvedName = meta and meta.name or key or ("Skill " .. refM)
 
       
         local obj = {
         local obj = {
             refM = refM,
             refM = refM,
Linha 497: Linha 497:
             back = (trim(a.back or "") == "yes" or trim(a.back or "") == "true") and true or nil
             back = (trim(a.back or "") == "yes" or trim(a.back or "") == "true") and true or nil
         }
         }
 
       
         -- Busca descrição do módulo
         -- Busca descrição do módulo
         if key ~= "" then
         if key ~= "" then
Linha 520: Linha 520:
             end
             end
         end
         end
 
       
         local flagsArr = parseFlags(a.flags)
         local flagsArr = parseFlags(a.flags)
         if #flagsArr > 0 then
         if #flagsArr > 0 then
             obj.flags = flagsArr
             obj.flags = flagsArr
         end
         end
 
       
         -- Aplica herança do cache se disponível (sobrescreve com dados mais completos)
         -- Aplica herança do cache se disponível (sobrescreve com dados mais completos)
         obj = inheritSubFromMeta(char, obj)
         obj = inheritSubFromMeta(char, obj)
 
       
         -- Garante que name nunca seja vazio
         -- Garante que name nunca seja vazio
         if not obj.name or trim(obj.name) == "" then
         if not obj.name or trim(obj.name) == "" then
Linha 536: Linha 536:
             obj.n = obj.name
             obj.n = obj.name
         end
         end
 
       
         -- Garante que icon nunca seja vazio (fallback para Nada.png)
         -- Garante que icon nunca seja vazio (fallback para Nada.png)
         if not obj.icon or trim(obj.icon) == "" then
         if not obj.icon or trim(obj.icon) == "" then
             obj.icon = "Nada.png"
             obj.icon = "Nada.png"
         end
         end
 
       
         return mw.text.jsonEncode(obj)
         return mw.text.jsonEncode(obj)
     end
     end

Edição das 18h49min de 30 de novembro de 2025

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

-- Módulo:Info.Skills — skill(), subskill(), emote()
local p = {}

local utils = require("Módulo:Info.Utils")
local trim = utils.trim
local safeArgs = utils.safeArgs
local collectJsonObjects = utils.collectJsonObjects
local requireCharacterModule = utils.requireCharacterModule
local resolveCharFromFrames = utils.resolveCharFromFrames
local colorize = utils.colorize
local nz = utils.nz
local parseFlags = utils.parseFlags

-- ===== Cache de metadados das skills para herança dos Subskills =====
local SkillMetaStore = {}

local function currentPageKey()
    local title = mw.title.getCurrentTitle()
    return title and title.fullText or "__page__"
end

local function metaBucket(char)
    local key = currentPageKey() .. "::" .. (trim(char or "") ~= "" and trim(char) or "__char__")
    if not SkillMetaStore[key] then
        SkillMetaStore[key] = { byIndex = {}, byName = {} }
    end
    return SkillMetaStore[key]
end

local function storeSkillMeta(char, index, name, data)
    if not data then return end
    local bucket = metaBucket(char)
    if index then
        local idx = tonumber(index) or index
        bucket.byIndex[idx] = data
    end
    if name and trim(name) ~= "" then
        bucket.byName[trim(name)] = data
    end
end

local function fetchSkillMeta(char, ref)
    if not ref then return nil end
    local bucket = metaBucket(char)
    local idxNum = tonumber(ref)
    if idxNum and bucket.byIndex[idxNum] then
        return bucket.byIndex[idxNum]
    end
    if bucket.byIndex[ref] then
        return bucket.byIndex[ref]
    end
    local refName = trim(ref)
    if refName ~= "" and bucket.byName[refName] then
        return bucket.byName[refName]
    end
    return nil
end

local function inheritSubFromMeta(char, sub)
    if type(sub) ~= "table" then return sub end
    local ref = sub.refM or sub.M or sub.m or sub.name or sub.n
    if not ref or trim(ref) == "" then
        return sub
    end
    local meta = fetchSkillMeta(char, ref)
    
    -- Se não encontrou no cache, tenta buscar diretamente do módulo
    if not meta then
        local data = requireCharacterModule(char) or {}
        local idx = tonumber(ref)
        if idx then
            local order = data.order or {}
            local key = order[idx] or ""
            if key ~= "" then
                -- Cria meta básico do módulo (só nome e descrição, atributos vêm do template)
                meta = {
                    name = key,
                    icon = "", -- será herdado do template quando disponível
                    level = "",
                    video = "",
                    energy = nil,
                    powerpve = nil,
                    powerpvp = nil,
                    cooldown = nil
                }
            end
        end
    end
    
    if not meta then
        return sub
    end

    local function pick(current, fallback)
        if current == false or current == 0 or current == "0" then
            return current
        end
        if current ~= nil and trim(tostring(current)) ~= "" then
            return current
        end
        return fallback
    end

    local hydrated = {}
    for k, v in pairs(sub) do
        hydrated[k] = v
    end
    hydrated.name = pick(sub.name or sub.n, meta.name) or (meta.name or "")
    hydrated.icon = pick(sub.icon, meta.icon) or "Nada.png"
    hydrated.level = pick(sub.level, meta.level) or (meta.level or "")
    hydrated.video = pick(sub.video, meta.video) or (meta.video or "")
    hydrated.energy = pick(sub.energy, meta.energy) or meta.energy
    hydrated.powerpve = pick(sub.powerpve, meta.powerpve) or meta.powerpve
    hydrated.powerpvp = pick(sub.powerpvp, meta.powerpvp) or meta.powerpvp
    hydrated.cooldown = pick(sub.cooldown, meta.cooldown) or meta.cooldown
    
    -- Garante que name nunca seja vazio
    if not hydrated.name or trim(hydrated.name) == "" then
        hydrated.name = hydrated.n or meta.name or ("Skill " .. tostring(ref))
    end
    if not hydrated.n or trim(hydrated.n) == "" then
        hydrated.n = hydrated.name
    end

    hydrated.desc = pick(sub.desc, meta.desc)
    hydrated.descPt = pick(sub.descPt, meta.descPt)
    hydrated.descEn = pick(sub.descEn, meta.descEn)
    hydrated.descEs = pick(sub.descEs, meta.descEs)
    hydrated.descPl = pick(sub.descPl, meta.descPl)

    if type(sub.desc_i18n) == "table" then
        hydrated.desc_i18n = sub.desc_i18n
    else
        hydrated.desc_i18n = {
            pt = hydrated.descPt,
            en = hydrated.descEn,
            es = hydrated.descEs,
            pl = hydrated.descPl
        }
    end

    if type(sub.subs) == "table" then
        hydrated.subs = {}
        for i, nested in ipairs(sub.subs) do
            hydrated.subs[i] = inheritSubFromMeta(char, nested)
        end
    end

    return hydrated
end

-- ===== Herança automática de descrições =====

local function findDescInSkills(data, skillName)
    if not data or not data.skills then return nil end
    local sk = data.skills[skillName]
    if sk and type(sk.desc) == "table" then
        return sk.desc
    end
    return nil
end

local function findDescInAnySubskills(data, skillName)
    if not data or not data.skills then return nil end
    for _, sk in pairs(data.skills) do
        if type(sk) == "table" and type(sk.subskills) == "table" then
            local sub = sk.subskills[skillName]
            if sub and type(sub.desc) == "table" then
                return sub.desc
            end
        end
    end
    return nil
end

local function resolveDescWithInheritance(data, skillName, directDesc)
    if directDesc and type(directDesc) == "table" and next(directDesc) then
        return directDesc
    end
    local fromSkills = findDescInSkills(data, skillName)
    if fromSkills then
        return fromSkills
    end
    local fromSubskills = findDescInAnySubskills(data, skillName)
    if fromSubskills then
        return fromSubskills
    end
    return nil
end

-- ===== Weapon =====

function p.weapon(frame)
    local a = safeArgs(frame)
    local obj = {
        icon = (trim(a.icon or "") ~= "" and a.icon or "Nada.png"),
        energy = nz(a.energy),
        powerpve = nz(a.powerpve),
        powerpvp = nz(a.powerpvp),
        cooldown = nz(a.cooldown),
        video = a.video or ""
    }
    return mw.text.jsonEncode(obj)
end

-- ===== Skill (com M) =====

function p.skill(frame)
    local a = safeArgs(frame)
    local lang = trim((a.lang or (frame:getParent() and frame:getParent().args.lang) or "pt"):lower())
    local char = resolveCharFromFrames(frame, a)
    local data = requireCharacterModule(char) or {}

    local name, desc_i18n = nil, {}
    if a.M and trim(a.M) ~= "" then
        local m = tonumber(a.M) or 0
        local order = data.order or {}
        local key = order[m] or ""
        local sk = (data.skills or {})[key] or {}
        name = trim(sk.name or key or "")
        if type(sk.desc) == "table" then
            local langs = { "pt", "en", "es", "pl" }
            for _, code in ipairs(langs) do
                local d = sk.desc[code]
                if d and trim(d) ~= "" then
                    desc_i18n[code] = colorize(d)
                end
            end
        elseif sk.desc and trim(sk.desc) ~= "" then
            desc_i18n["pt"] = colorize(sk.desc)
        end
    end

    local chosen = desc_i18n[lang] or desc_i18n["pt"] or ""
    local flagsArr = parseFlags(a.flags)

    local obj = {
        icon = (trim(a.icon or "") ~= "" and a.icon or "Nada.png"),
        level = (trim(a.level or "") ~= "" and a.level or "NIVEL"),
        energy = nz(a.energy),
        powerpve = nz(a.powerpve),
        powerpvp = nz(a.powerpvp),
        cooldown = nz(a.cooldown),
        video = a.video or "",
        name = name
    }
    if #flagsArr > 0 then
        obj.flags = flagsArr
    end
    if chosen ~= "" then
        obj.desc = chosen
    end
    if next(desc_i18n) ~= nil then
        obj.desc_i18n = desc_i18n
        obj.descPt = desc_i18n.pt
        obj.descEn = desc_i18n.en
        obj.descEs = desc_i18n.es
        obj.descPl = desc_i18n.pl
    end

    -- Registra metadados desta skill para herança futura
    if a.M and trim(a.M) ~= "" then
        local metaData = {
            name = obj.name or name or "",
            icon = obj.icon,
            level = obj.level,
            energy = obj.energy,
            powerpve = obj.powerpve,
            powerpvp = obj.powerpvp,
            cooldown = obj.cooldown,
            video = obj.video,
            desc = obj.desc,
            descPt = obj.descPt,
            descEn = obj.descEn,
            descEs = obj.descEs,
            descPl = obj.descPl
        }
        storeSkillMeta(char, tonumber(a.M) or a.M, metaData.name, metaData)
    end

    -- Subskills vindas via |subs=
    do
        local subsPacked = a.subs or ""
        local subsArr = {}
        for _, chunk in ipairs(collectJsonObjects(subsPacked)) do
            local ok, sk = pcall(mw.text.jsonDecode, chunk)
            if ok and type(sk) == "table" then
                -- Garante que objetos com refM sempre tenham name válido
                if (sk.refM or sk.M or sk.m) and (not sk.name or trim(sk.name) == "") and (not sk.n or trim(sk.n) == "") then
                    local refIdx = tonumber(sk.refM or sk.M or sk.m)
                    if refIdx then
                        local order = data.order or {}
                        local key = order[refIdx] or ""
                        if key ~= "" then
                            sk.name = key
                            sk.n = key
                        end
                    end
                end
                table.insert(subsArr, sk)
            end
        end
        -- Fallback: se tem poucos objetos ou nenhum, busca do módulo .lua
        -- Isso garante que mesmo que {{Subskill |M=1}} não retorne JSON válido,
        -- ainda conseguimos buscar do módulo e aplicar herança depois
        if (#subsArr == 0 or (#subsArr < 3 and a.M and tonumber(a.M))) and a.M and tonumber(a.M) then
            local m = tonumber(a.M)
            local order = (data and data.order) or {}
            local key = order[m] or ""
            local sk = (data and data.skills and data.skills[key]) or {}
            local suborder = type(sk) == "table" and sk.suborder or nil
            local subskills = type(sk) == "table" and sk.subskills or nil
            if type(suborder) == "table" and type(subskills) == "table" then
                for _, n in ipairs(suborder) do
                    local sub = subskills[n]
                    if type(sub) == "table" then
                        local resolvedDesc = resolveDescWithInheritance(data, n, sub.desc)
                        local subObj = {
                            n = n,
                            name = n,
                            icon = sub.icon or "",
                            level = sub.level or "",
                            video = sub.video or "",
                            energy = sub.energy,
                            powerpve = sub.powerpve,
                            powerpvp = sub.powerpvp,
                            cooldown = sub.cooldown,
                            desc_i18n = resolvedDesc,
                            descPt = resolvedDesc and resolvedDesc.pt or nil,
                            descEn = resolvedDesc and resolvedDesc.en or nil,
                            descEs = resolvedDesc and resolvedDesc.es or nil,
                            descPl = resolvedDesc and resolvedDesc.pl or nil
                        }
                        if type(sub.suborder) == "table" and type(sub.subskills) == "table" then
                            local nested = {}
                            for _, nn in ipairs(sub.suborder) do
                                local s2 = sub.subskills[nn]
                                if type(s2) == "table" then
                                    local nestedDesc = resolveDescWithInheritance(data, nn, s2.desc)
                                    table.insert(nested, {
                                        n = nn,
                                        name = nn,
                                        icon = s2.icon or "",
                                        level = s2.level or "",
                                        video = s2.video or "",
                                        energy = s2.energy,
                                        powerpve = s2.powerpve,
                                        powerpvp = s2.powerpvp,
                                        cooldown = s2.cooldown,
                                        desc_i18n = nestedDesc,
                                        descPt = nestedDesc and nestedDesc.pt or nil,
                                        descEn = nestedDesc and nestedDesc.en or nil,
                                        descEs = nestedDesc and nestedDesc.es or nil,
                                        descPl = nestedDesc and nestedDesc.pl or nil
                                    })
                                end
                            end
                            if #nested > 0 then subObj.subs = nested end
                        end
                        table.insert(subsArr, subObj)
                    end
                end
            end
        end
        if #subsArr > 0 then
            for i, sub in ipairs(subsArr) do
                subsArr[i] = inheritSubFromMeta(char, sub)
            end
            obj.subs = subsArr
        end

        -- suborder do módulo
        if a.M and tonumber(a.M) then
            local m = tonumber(a.M)
            local order = (data and data.order) or {}
            local key = order[m] or ""
            local sk = (data and data.skills and data.skills[key]) or {}
            if type(sk) == "table" and type(sk.suborder) == "table" then
                obj.suborder = sk.suborder
            end
        end
    end

    -- Weapon
    do
        local weaponPacked = trim(a.weapon or "")
        if weaponPacked ~= "" then
            local ok, weaponData = pcall(mw.text.jsonDecode, weaponPacked)
            if ok and type(weaponData) == "table" then
                local weaponDesc_i18n = {}
                if a.M and tonumber(a.M) then
                    local m = tonumber(a.M)
                    local order = (data and data.order) or {}
                    local key = order[m] or ""
                    local sk = (data and data.skills and data.skills[key]) or {}
                    if type(sk) == "table" and type(sk.weapon) == "table" and type(sk.weapon.desc) == "table" then
                        local langs = { "pt", "en", "es", "pl" }
                        for _, code in ipairs(langs) do
                            local d = sk.weapon.desc[code]
                            if d and trim(d) ~= "" then
                                weaponDesc_i18n[code] = colorize(d)
                            end
                        end
                    end
                end
                local weaponObj = {
                    icon = weaponData.icon or "Nada.png",
                    energy = weaponData.energy,
                    powerpve = weaponData.powerpve,
                    powerpvp = weaponData.powerpvp,
                    cooldown = weaponData.cooldown,
                    video = weaponData.video or ""
                }
                if next(weaponDesc_i18n) ~= nil then
                    weaponObj.desc_i18n = weaponDesc_i18n
                    weaponObj.descPt = weaponDesc_i18n.pt
                    weaponObj.descEn = weaponDesc_i18n.en
                    weaponObj.descEs = weaponDesc_i18n.es
                    weaponObj.descPl = weaponDesc_i18n.pl
                end
                obj.weapon = weaponObj
            end
        end
    end

    return mw.text.jsonEncode(obj)
end

-- ===== Emote =====

function p.emote(frame)
    local a = safeArgs(frame)
    local obj = {
        name = "Emote",
        desc = "",
        icon = (trim(a.icon or "") ~= "" and a.icon or "Nada.png"),
        level = (trim(a.level or "") ~= "" and a.level or "NIVEL"),
        video = a.video or ""
    }
    return mw.text.jsonEncode(obj)
end

-- ===== Subskill =====
-- Gera JSON simples. Herança COMPLETA de atributos é feita no JavaScript.
-- Se |M= for passado sem |n=, retorna apenas refM para o JS resolver.

function p.subskill(frame)
    local a = safeArgs(frame)
    local lang = trim((a.lang or (frame:getParent() and frame:getParent().args.lang) or "pt"):lower())

    local n = trim(a.n or "")
    local refM = trim(a.m or a.M or "")
    local char = resolveCharFromFrames(frame, a)

    -- Se só tem refM (sem nome), resolve nome e atributos DIRETAMENTE do módulo
    if n == "" and refM ~= "" then
        local idx = tonumber(refM)
        local data = requireCharacterModule(char) or {}
        local order = data.order or {}
        local key = idx and order[idx] or ""
        
        -- Tenta buscar do cache primeiro (pode não estar pronto ainda)
        local meta = fetchSkillMeta(char, refM)
        
        -- Se não tem no cache, busca direto do módulo
        if not meta and key ~= "" then
            local sk = (data.skills or {})[key] or {}
            local name = trim(sk.name or key or "")
            if name ~= "" then
                meta = {
                    name = name,
                    icon = "", -- será herdado do template Skill quando disponível
                    level = "",
                    video = "",
                    energy = nil,
                    powerpve = nil,
                    powerpvp = nil,
                    cooldown = nil
                }
            end
        end
        
        -- Garante que sempre tenha um name válido (mesmo que temporário)
        local resolvedName = meta and meta.name or key or ("Skill " .. refM)
        
        local obj = {
            refM = refM,
            name = resolvedName,
            n = resolvedName, -- também como 'n' para compatibilidade
            icon = trim(a.icon or "") ~= "" and trim(a.icon or "") or (meta and meta.icon or ""),
            level = trim(a.level or "") ~= "" and trim(a.level or "") or (meta and meta.level or ""),
            video = trim(a.video or "") ~= "" and trim(a.video or "") or (meta and meta.video or ""),
            energy = nz(a.energy) or (meta and meta.energy),
            powerpve = nz(a.powerpve) or (meta and meta.powerpve),
            powerpvp = nz(a.powerpvp) or (meta and meta.powerpvp),
            cooldown = nz(a.cooldown) or (meta and meta.cooldown),
            back = (trim(a.back or "") == "yes" or trim(a.back or "") == "true") and true or nil
        }
        
        -- Busca descrição do módulo
        if key ~= "" then
            local sk = (data.skills or {})[key] or {}
            local resolvedDesc = resolveDescWithInheritance(data, key, sk.desc)
            if resolvedDesc then
                local desc_i18n = {}
                local langs = { "pt", "en", "es", "pl" }
                for _, code in ipairs(langs) do
                    local d = resolvedDesc[code]
                    if d and trim(d) ~= "" then
                        desc_i18n[code] = colorize(d)
                    end
                end
                if next(desc_i18n) ~= nil then
                    obj.desc_i18n = desc_i18n
                    obj.descPt = desc_i18n.pt
                    obj.descEn = desc_i18n.en
                    obj.descEs = desc_i18n.es
                    obj.descPl = desc_i18n.pl
                end
            end
        end
        
        local flagsArr = parseFlags(a.flags)
        if #flagsArr > 0 then
            obj.flags = flagsArr
        end
        
        -- Aplica herança do cache se disponível (sobrescreve com dados mais completos)
        obj = inheritSubFromMeta(char, obj)
        
        -- Garante que name nunca seja vazio
        if not obj.name or trim(obj.name) == "" then
            obj.name = obj.n or key or ("Skill " .. refM)
        end
        if not obj.n or trim(obj.n) == "" then
            obj.n = obj.name
        end
        
        -- Garante que icon nunca seja vazio (fallback para Nada.png)
        if not obj.icon or trim(obj.icon) == "" then
            obj.icon = "Nada.png"
        end
        
        return mw.text.jsonEncode(obj)
    end

    -- Se não tem nome nem refM, retorna vazio
    if n == "" then
        return mw.text.jsonEncode({})
    end

    local char = resolveCharFromFrames(frame, a)
    local data = requireCharacterModule(char) or {}

    -- Busca descrição do módulo .lua
    local desc_i18n = {}
    local foundDesc = nil
    for _, key in ipairs(data.order or {}) do
        local sk = (data.skills or {})[key]
        if type(sk) == "table" and type(sk.subskills) == "table" then
            local sub = sk.subskills[n]
            if sub and type(sub.desc) == "table" then
                foundDesc = sub.desc
                break
            end
        end
    end
    if not foundDesc then
        foundDesc = resolveDescWithInheritance(data, n, nil)
    end
    if foundDesc then
        local langs = { "pt", "en", "es", "pl" }
        for _, code in ipairs(langs) do
            local d = foundDesc[code]
            if d and trim(d) ~= "" then
                desc_i18n[code] = colorize(d)
            end
        end
    end

    local flagsArr = parseFlags(a.flags)

    -- Objeto com nome explícito
    local obj = {
        name = n,
        icon = trim(a.icon or ""),
        level = trim(a.level or ""),
        video = trim(a.video or ""),
        energy = nz(a.energy),
        powerpve = nz(a.powerpve),
        powerpvp = nz(a.powerpvp),
        cooldown = nz(a.cooldown),
        back = (trim(a.back or "") == "yes" or trim(a.back or "") == "true") and true or nil
    }

    if refM ~= "" then
        obj.refM = refM
    end
    if #flagsArr > 0 then
        obj.flags = flagsArr
    end
    if next(desc_i18n) ~= nil then
        obj.desc_i18n = desc_i18n
        obj.descPt = desc_i18n.pt
        obj.descEn = desc_i18n.en
        obj.descEs = desc_i18n.es
        obj.descPl = desc_i18n.pl
    end

    -- Nested subs
    do
        local subsPacked = a.subs or ""
        local subsArr = {}
        for _, chunk in ipairs(collectJsonObjects(subsPacked)) do
            local ok, sub = pcall(mw.text.jsonDecode, chunk)
            if ok and type(sub) == "table" then
                table.insert(subsArr, sub)
            end
        end
        if #subsArr > 0 then
            obj.subs = subsArr
        end
    end

    return mw.text.jsonEncode(obj)
end

return p