Module:Passive skill table/sandbox: Difference between revisions

From Path of Exile 2 Wiki
Jump to navigation Jump to search
(Create sandbox version of Module:Passive skill table)
 
(Added simple_passive_skill_list)
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
--
-------------------------------------------------------------------------------
-- Module for passive skill table
--  
--
--                             Module:Passive skill table
--
-- This module implements [[Template:Passive skill table]] and other templates
-- that query and display tables or lists of passive skills.
-------------------------------------------------------------------------------


require('strict')
local m_util = require('Module:Util')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
local getArgs = require('Module:Arguments').getArgs
local f_passive_skill_link = require('Module:passive_skill_link').passive_skill_link
-- ----------------------------------------------------------------------------
-- Strings
-- ----------------------------------------------------------------------------


local i18n = {
-- Should we use the sandbox version of our submodules?
    icon_name = 'File:%s passive skill icon.png',
local use_sandbox = m_util.misc.maybe_sandbox('Passive skill table')


    cats = {
local m_cargo = use_sandbox and require('Module:Cargo/sandbox') or require('Module:Cargo')
        data = 'Passive skill data',
 
        keystone = 'Keystone passive skills',
        notable = 'Notable passive skills',
        basic = 'Small passive skills',
        ascendancy_notable = 'Ascendancy notable passive skills',
        ascendancy_basic = 'Ascendancy small passive skills',
    },
 
    passive_table = {
        ascendancy_class = 'Ascendancy<br>Class',
        name = 'Name',
        id = 'Id',
        int_id = 'Integer id',
        stats = 'Stats',
        skill_points = 'Skill points',
        connections = 'Connections',
        flavour_text = 'Flavour text',
        reminder_text = 'Reminder text',
        is_notable = 'Notable',
        is_keystone = 'Keystone',
    },
 
    errors = {
        invalid_args = 'Passive skill table: q_where must be specified',
        no_passives_found = 'No passive skills with the given name found',
        cats = 'Pages with broken passive skill tables',
    },
}


local f_passive_skill_link = use_sandbox and require('Module:Passive skill link/sandbox').passive_skill_link or require('Module:Passive skill link').passive_skill_link


-- ----------------------------------------------------------------------------
-- The cfg table contains all localisable strings and configuration, to make it
-- Constants & Data
-- easier to port this module to another wiki.
-- ----------------------------------------------------------------------------
local cfg = use_sandbox and mw.loadData('Module:Passive skill table/config/sandbox') or mw.loadData('Module:Passive skill table/config')


-- local c = {}
local i18n = cfg.i18n


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
Line 120: Line 90:
     if passive['passive_skills.ascendancy_class'] ~= nil then
     if passive['passive_skills.ascendancy_class'] ~= nil then
         key = 'ascendancy_' .. key
         key = 'ascendancy_' .. key
    elseif tonumber(passive['passive_skills.is_atlas_passive']) == 1 then
        key = 'atlas_' .. key
     end
     end


Line 208: Line 180:
function h.tbl.display.factory.merged_values(args)
function h.tbl.display.factory.merged_values(args)
     local args = args or {}
     local args = args or {}
     return function (tpl_args, frame, tr, rows, rowinfo)
     return function (tpl_args, tr, rows, rowinfo)
         local values, order = h.make_order(rows, args.field, args)
         local values, order = h.make_order(rows, args.field, args)
         local value = h.data_page_links(values, order)
         local value = h.data_page_links(values, order)
         h.na_or_val(tr, value, func, args)
         h.na_or_val(tr, value, nil, args)
     end
     end
end
end
Line 219: Line 191:
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------


data = {}
local data_map = {}
data.passive_skill_table = {
data_map.passive_skill_table = {
     tables = {
     tables = {
         passive_skill_stats = {
         passive_skill_stats = {
Line 231: Line 203:
     -- Display data
     -- Display data
     {
     {
        order = 1000,
         args = nil,
         args = nil,
         header = i18n.passive_table.name,
         header = i18n.passive_table.name,
Line 238: Line 211:
             'passive_skills.is_notable',
             'passive_skills.is_notable',
             'passive_skills.ascendancy_class',
             'passive_skills.ascendancy_class',
            'passive_skills.is_atlas_passive',
             'passive_skills.main_page',
             'passive_skills.main_page',
             'main_pages._pageName',
             'main_pages._pageName',
Line 247: Line 221:
             'passive_skills.name',
             'passive_skills.name',
         },
         },
         display = function (tpl_args, frame, tr, data)
         display = function (tpl_args, tr, data)
             local passive = data[1]
             local passive = data[1]


Line 261: Line 235:
                 is_notable = passive['passive_skills.is_notable'],
                 is_notable = passive['passive_skills.is_notable'],
                 ascendancy_class = passive['passive_skills.ascendancy_class'],
                 ascendancy_class = passive['passive_skills.ascendancy_class'],
                is_atlas_passive = passive['passive_skills.is_atlas_passive'],
             }
             }
             if tpl_args.no_html == nil then
             if tpl_args.no_html == nil then
Line 280: Line 255:


         end,
         end,
        order = 1000,
         sort_type = 'text',
         sort_type = 'text',
         options = {
         options = {
Line 292: Line 266:
     },
     },
     {
     {
        order = 0,
         args = {'ascendancy'},
         args = {'ascendancy'},
         header = i18n.passive_table.ascendancy_class,
         header = i18n.passive_table.ascendancy_class,
         fields = {'passive_skills.ascendancy_class'},
         fields = {'passive_skills.ascendancy_class'},
         display = function (tpl_args, frame, tr, data)
         display = function (tpl_args, tr, data)
             local passive = data[1]
             local passive = data[1]
             h.na_or_val(tr, passive['passive_skills.ascendancy_class'],
             h.na_or_val(tr, passive['passive_skills.ascendancy_class'],
Line 308: Line 283:
             )
             )
         end,
         end,
        order = 0,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
        order = 1001,
         args = 'id',
         args = 'id',
         header = i18n.passive_table.id,
         header = i18n.passive_table.id,
         fields = {'passive_skills.id'},
         fields = {'passive_skills.id'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.id'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.id'},
        order = 1001,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
        order = 1002,
         args = 'int_id',
         args = 'int_id',
         header = i18n.passive_table.int_id,
         header = i18n.passive_table.int_id,
         fields = {'passive_skills.int_id'},
         fields = {'passive_skills.int_id'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.int_id'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.int_id'},
        order = 1002,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
        order = 2000,
         args = {'stat', 'stats', 'stat_text'},
         args = {'stat', 'stats', 'stat_text'},
         header = i18n.passive_table.stats,
         header = i18n.passive_table.stats,
         fields = {'passive_skills.stat_text'},
         fields = {'passive_skills.stat_text'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.stat_text', colour='mod'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.stat_text', colour='mod'},
        order = 2000,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
        order = 2001,
         args = 'skill_points',
         args = 'skill_points',
         header = i18n.passive_table.skill_points,
         header = i18n.passive_table.skill_points,
         fields = {'passive_skills.skill_points'},
         fields = {'passive_skills.skill_points'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.skill_points'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.skill_points'},
         order = 2001,
        sort_type = 'number',
         sort_type = 'text',
    },
    {
         order = 2002,
        args = 'weapon_set_points',
        header = i18n.passive_table.weapon_set_points,
        fields = {'passive_skills.weapon_set_points'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.weapon_set_points'},
         sort_type = 'number',
     },
     },
     {
     {
        order = 3000,
         args = 'connections',
         args = 'connections',
         header = i18n.passive_table.connections,
         header = i18n.passive_table.connections,
Line 351: Line 334:
             fmt=h.fmt.passive_skill_link,
             fmt=h.fmt.passive_skill_link,
         },
         },
        order = 3000,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
        order = 3001,
         args = 'flavour_text',
         args = 'flavour_text',
         header = i18n.passive_table.flavour_text,
         header = i18n.passive_table.flavour_text,
Line 362: Line 345:
             colour='flavour',
             colour='flavour',
         },
         },
        order = 3001,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
        order = 3002,
         args = 'reminder_text',
         args = 'reminder_text',
         header = i18n.passive_table.reminder_text,
         header = i18n.passive_table.reminder_text,
Line 373: Line 356:
             colour='help',
             colour='help',
         },
         },
        order = 3002,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
        order = 4000,
         args = 'is_notable',
         args = 'is_notable',
         header = i18n.passive_table.is_notable,
         header = i18n.passive_table.is_notable,
Line 384: Line 367:
             fmt=h.fmt.boolean
             fmt=h.fmt.boolean
         },
         },
        order = 4000,
         sort_type = 'number',
         sort_type = 'number',
     },
     },
     {
     {
        order = 4001,
         args = 'is_keystone',
         args = 'is_keystone',
         header = i18n.passive_table.is_keystone,
         header = i18n.passive_table.is_keystone,
Line 395: Line 378:
             fmt=h.fmt.boolean
             fmt=h.fmt.boolean
         },
         },
        order = 4001,
         sort_type = 'number',
         sort_type = 'number',
     },
     },
Line 401: Line 383:


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- Page functions
-- Main functions
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------


local p = {}
local function _passive_skill_table(tpl_args)
 
function p.passive_skill_table(frame)
     --[[
     --[[
     Displays a table of passive skills.
     Displays a table of passive skills.
Line 426: Line 406:
     ]]
     ]]
     local t = os.clock()
     local t = os.clock()
    -- Get args
    local tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)


     -- Check if the correct parameters have been set:
     -- Check if the correct parameters have been set:
     if tpl_args.q_where == nil then
     if tpl_args.q_where == nil then
         return m_util.html.error{msg=i18n.errors.invalid_args .. m_util.misc.add_category(i18n.errors.cats)}
         return m_util.html.error{msg=i18n.errors.invalid_args .. m_util.misc.add_category(i18n.errors.category)}
     end
     end


Line 450: Line 424:
     local out = m_cargo.table_query{
     local out = m_cargo.table_query{
         tpl_args=tpl_args,
         tpl_args=tpl_args,
        frame=frame,
         main_table='passive_skills',
         main_table='passive_skills',
         row_unique_fields={'passive_skills.name'},
         row_unique_fields={'passive_skills.name'},
         data=data.passive_skill_table,
         data=data_map.passive_skill_table,
         empty_cell=tostring(m_util.html.table_cell('na'))
         empty_cell=tostring(m_util.html.table_cell('na'))
     }
     }
Line 462: Line 435:
end
end


local function _simple_passive_skill_list(tpl_args)
    --[[
    Creates a simple list of passive skills.
    Examples
    --------
    = p.simple_passive_skill_list{
        q_where='passive_skills.is_keystone=1',
    }
    ]]
    local query = {}
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 2) == 'q_' then
            query[string.sub(key, 3)] = value
        end
    end
    local fields = {
        'passive_skills.id',
    }
    local tables = m_util.cast.table(tpl_args.q_tables)
    table.insert(tables, 1, 'passive_skills')
    query.groupBy = query.groupBy or 'passive_skills._pageID'
    local results = m_cargo.query(
        tables,
        fields,
        query
    )
    local out = {}
    for _, row in ipairs(results) do
        local link = f_passive_skill_link{
            id=row['passive_skills.id'],
        }
        if tpl_args.format == nil then
            out[#out+1] = string.format('* %s', link)
        elseif tpl_args.format == 'none' then
            out[#out+1] = link
        elseif tpl_args.format == 'li' then
            out[#out+1] = string.format('<li>%s</li>', link)
        else
            error(string.format(i18n.errors.generic_argument_parameter, 'format', tpl_args.format))
        end
    end
    if tpl_args.format == nil then
        return table.concat(out, '\n')
    elseif tpl_args.format == 'none' then
        return table.concat(out, '\n')
    elseif tpl_args.format == 'li' then
        return table.concat(out)
    end
end
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
--
-- Template:Passive skill table
--
p.passive_skill_table = m_util.misc.invoker_factory(_passive_skill_table, {
    parentFirst=true,
})
--
-- Template:Simple passive skill list
--
p.simple_passive_skill_list = m_util.misc.invoker_factory(_simple_passive_skill_list, {
    wrappers = cfg.wrappers._simple_passive_skill_list,
})


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- End
-- Debug
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
p.debug = {}
function p.debug.preset()
    return {
        q_where = 'is_keystone = 1',
    }
end
function p.debug.test()
    return mw.log(_simple_passive_skill_list(p.debug.preset()))
end


return p
return p

Latest revision as of 16:55, 21 December 2025

Module documentation[view] [edit] [history] [purge]


Implements {{Passive skill table}} and {{Simple passive skill list}}.


-------------------------------------------------------------------------------
-- 
--                             Module:Passive skill table
-- 
-- This module implements [[Template:Passive skill table]] and other templates
-- that query and display tables or lists of passive skills.
-------------------------------------------------------------------------------

require('strict')
local m_util = require('Module:Util')

-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Passive skill table')

local m_cargo = use_sandbox and require('Module:Cargo/sandbox') or require('Module:Cargo')

local f_passive_skill_link = use_sandbox and require('Module:Passive skill link/sandbox').passive_skill_link or require('Module:Passive skill link').passive_skill_link

-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Passive skill table/config/sandbox') or mw.loadData('Module:Passive skill table/config')

local i18n = cfg.i18n

-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------

local h = {}

function h.make_order(results, field, args)
    --[[
    Merge duplicates and then create a ordered list.
    ]]
    local values = {}
    local order = {}
    for _, row in ipairs(results) do
        local val = row[field]
        if args.fmt then
            val = args.fmt(row[field])
        end
        -- Can't show results here that don't have a value:
        if val then
            if values[val] == nil then
                values[val] = {row}
                table.insert(order, val)
            else
                table.insert(values[val], row)
            end
        end
    end

    return values, order
end

function h.data_page_links(values, order)
    local out = {}
    for i, key in ipairs(order) do
        local links = {}
        for j, row in ipairs(values[key]) do
            links[#links+1] = string.format(
                '[[%s|&#91;%s&#93;]]',
                row['passive_skills._pageName'],
                j + (i-1) * #values[key]
            )
        end
        out[i] = string.format(
            '<span class="passive-line">%s <span class="passive-hover">%s</span></span>',
            key,
            table.concat(links, ' ')
        )
    end

    return table.concat(out, '<hr>')
end

function h.get_type(passive)
    --[[
    Determine what type of passive skill this passive is.
    ]]
    local key
    if tonumber(passive['passive_skills.is_keystone']) == 1 then
        key = 'keystone'
    elseif tonumber(passive['passive_skills.is_notable']) == 1 then
        key = 'notable'
    else
        key = 'basic'
    end

    if passive['passive_skills.ascendancy_class'] ~= nil then
        key = 'ascendancy_' .. key
    elseif tonumber(passive['passive_skills.is_atlas_passive']) == 1 then
        key = 'atlas_' .. key
    end

    return key
end

function h.na_or_val(tr, value, func, args)
    --[[

    Parameters
    ----------
    tr :

    value :

    func : function

    args : list of
        Valid keys are:
        * colour

    ]]
    local args = args or {}
    if value == nil or value == '' then
        tr:node(m_util.html.table_cell('na'))
    else
        local raw_value = value
        if func ~= nil then
            value = func(value)
        end

        local td = tr:tag('td')
        td:attr('data-sort-value', raw_value)
        td:wikitext(value)
        if args.colour then
            td:attr('class', 'tc -' .. args.colour)
        end
    end
end

-- Format style for field:
h.fmt = {}
function h.fmt.link(field)
    local fields = m_util.string.split(field, ',')
    for i, v in ipairs(fields) do
        fields[i] = string.format('[[%s]]', v)
    end
    return table.concat(fields, ', ')
end
function h.fmt.passive_skill_link(field)
    local fields = m_util.string.split(field, ',')
    for i, v in ipairs(fields) do
        fields[i] = f_passive_skill_link{id=v}
    end
    return table.concat(fields, ', ')
end
function h.fmt.boolean(field)
    local fields = m_util.string.split(field, ',')
    for i, v in ipairs(fields) do
        if m_util.cast.boolean(v) then
            fields[i] = '<div class="table-yes" data-sort-value=1><span>&#x2713;</span></div>'
        else
            fields[i] = '<div class="table-no" data-sort-value=0><span>&#x2717;</span></div>'
        end
    end
    return table.concat(fields, ', ')
end
function h.fmt.link_passive(field)
    --[[
    Create a link to the passive skill data page from the ID.
    ]]
    local fields = m_util.string.split(field, ',')
    for i, v in ipairs(fields) do
        fields[i] = string.format(
            '[[Passive Skill:%s|%s]]',
            string.gsub(v, '_', '~'),
            v
        )
    end
    return table.concat(fields, ', ')
end

-- Display
h.tbl = {}
h.tbl.display = {}

h.tbl.display.factory = {}
function h.tbl.display.factory.merged_values(args)
    local args = args or {}
    return function (tpl_args, tr, rows, rowinfo)
        local values, order = h.make_order(rows, args.field, args)
        local value = h.data_page_links(values, order)
        h.na_or_val(tr, value, nil, args)
    end
end

-- ----------------------------------------------------------------------------
-- Data mappings
-- ----------------------------------------------------------------------------

local data_map = {}
data_map.passive_skill_table = {
    tables = {
        passive_skill_stats = {
            join='passive_skills._pageID=passive_skill_stats._pageID',
        },
        main_pages = {
            join='passive_skills.id=main_pages.id'
        }
    },
    -- Display data
    {
        order = 1000,
        args = nil,
        header = i18n.passive_table.name,
        fields = {
            -- Optional:
            'passive_skills.is_keystone',
            'passive_skills.is_notable',
            'passive_skills.ascendancy_class',
            'passive_skills.is_atlas_passive',
            'passive_skills.main_page',
            'main_pages._pageName',
            'passive_skills.icon',
            -- 'passive_skills.html',

            -- Required:
            'passive_skills._pageName',
            'passive_skills.name',
        },
        display = function (tpl_args, tr, data)
            local passive = data[1]

            local psl_args = {
                skip_query = true,
                page = passive['passive_skills.main_page']
                    or passive['main_pages._pageName']
                    or passive['passive_skills.name']
                    or passive['passive_skills._pageName'],
                name = passive['passive_skills.name'],
                icon = passive['passive_skills.icon'],
                is_keystone = passive['passive_skills.is_keystone'],
                is_notable = passive['passive_skills.is_notable'],
                ascendancy_class = passive['passive_skills.ascendancy_class'],
                is_atlas_passive = passive['passive_skills.is_atlas_passive'],
            }
            if tpl_args.no_html == nil then
                psl_args.html = passive['passive_skills.html']
            end
            if tpl_args.large then
                psl_args.large = tpl_args.large
            end

            tr
                :tag('td')
                    :attr(
                        'data-sort-value',
                        passive['passive_skills.name'] .. h.get_type(passive)
                    )
                    :attr('style', 'text-align:center;')
                    :wikitext(f_passive_skill_link(psl_args))
                    :done()

        end,
        sort_type = 'text',
        options = {
            [1] = {optional=true},
            [2] = {optional=true},
            [3] = {optional=true},
            [4] = {optional=true},
            [5] = {optional=true},
            [6] = {optional=true},
        },
    },
    {
        order = 0,
        args = {'ascendancy'},
        header = i18n.passive_table.ascendancy_class,
        fields = {'passive_skills.ascendancy_class'},
        display = function (tpl_args, tr, data)
            local passive = data[1]
            h.na_or_val(tr, passive['passive_skills.ascendancy_class'],
                function ()
                    return string.format(
                        '[[%s]]<br>[[File:%s avatar.png|link=%s]]',
                        passive['passive_skills.ascendancy_class'],
                        passive['passive_skills.ascendancy_class'],
                        passive['passive_skills.ascendancy_class']
                    )
                end
            )
        end,
        sort_type = 'text',
    },
    {
        order = 1001,
        args = 'id',
        header = i18n.passive_table.id,
        fields = {'passive_skills.id'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.id'},
        sort_type = 'text',
    },
    {
        order = 1002,
        args = 'int_id',
        header = i18n.passive_table.int_id,
        fields = {'passive_skills.int_id'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.int_id'},
        sort_type = 'text',
    },
    {
        order = 2000,
        args = {'stat', 'stats', 'stat_text'},
        header = i18n.passive_table.stats,
        fields = {'passive_skills.stat_text'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.stat_text', colour='mod'},
        sort_type = 'text',
    },
    {
        order = 2001,
        args = 'skill_points',
        header = i18n.passive_table.skill_points,
        fields = {'passive_skills.skill_points'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.skill_points'},
        sort_type = 'number',
    },
    {
        order = 2002,
        args = 'weapon_set_points',
        header = i18n.passive_table.weapon_set_points,
        fields = {'passive_skills.weapon_set_points'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.weapon_set_points'},
        sort_type = 'number',
    },
    {
        order = 3000,
        args = 'connections',
        header = i18n.passive_table.connections,
        fields = {'passive_skills.connections'},
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.connections',
            fmt=h.fmt.passive_skill_link,
        },
        sort_type = 'text',
    },
    {
        order = 3001,
        args = 'flavour_text',
        header = i18n.passive_table.flavour_text,
        fields = {'passive_skills.flavour_text'},
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.flavour_text',
            colour='flavour',
        },
        sort_type = 'text',
    },
    {
        order = 3002,
        args = 'reminder_text',
        header = i18n.passive_table.reminder_text,
        fields = {'passive_skills.reminder_text'},
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.reminder_text',
            colour='help',
        },
        sort_type = 'text',
    },
    {
        order = 4000,
        args = 'is_notable',
        header = i18n.passive_table.is_notable,
        fields = {'passive_skills.is_notable'},
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.is_notable',
            fmt=h.fmt.boolean
        },
        sort_type = 'number',
    },
    {
        order = 4001,
        args = 'is_keystone',
        header = i18n.passive_table.is_keystone,
        fields = {'passive_skills.is_keystone'},
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.is_keystone',
            fmt=h.fmt.boolean
        },
        sort_type = 'number',
    },
}

-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------

local function _passive_skill_table(tpl_args)
    --[[
    Displays a table of passive skills.

    TODO: Why is the groupBy necessary?

    Examples
    --------
    = p.passive_skill_table{
        q_where = 'passive_skills.ascendancy_class = "Gladiator"',
        large=1,
    }
    = p.passive_skill_table{
        q_where='passive_skills.stat_text LIKE "%damage%taken%as%"',
        ascendancy=1,
        stat_text=1,
    }

    ]]
    local t = os.clock()

    -- Check if the correct parameters have been set:
    if tpl_args.q_where == nil then
        return m_util.html.error{msg=i18n.errors.invalid_args .. m_util.misc.add_category(i18n.errors.category)}
    end

    -- Cargo query settings:
    tpl_args.q_groupBy = tpl_args.q_groupBy or 'passive_skills._pageID'
    tpl_args.q_orderBy = tpl_args.q_orderBy or [[
        passive_skills.ascendancy_class IS NULL DESC,
        passive_skills.is_keystone,
        passive_skills.is_notable,
        passive_skills.name
    ]]

    -- Run the query and display the results:
    local out = m_cargo.table_query{
        tpl_args=tpl_args,
        main_table='passive_skills',
        row_unique_fields={'passive_skills.name'},
        data=data_map.passive_skill_table,
        empty_cell=tostring(m_util.html.table_cell('na'))
    }

    mw.logObject({os.clock() - t, tpl_args})

    return out
end

local function _simple_passive_skill_list(tpl_args)
    --[[
    Creates a simple list of passive skills.

    Examples
    --------
    = p.simple_passive_skill_list{
        q_where='passive_skills.is_keystone=1',
    }
    ]]

    local query = {}
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 2) == 'q_' then
            query[string.sub(key, 3)] = value
        end
    end

    local fields = {
        'passive_skills.id',
    }

    local tables = m_util.cast.table(tpl_args.q_tables)
    table.insert(tables, 1, 'passive_skills')

    query.groupBy = query.groupBy or 'passive_skills._pageID'

    local results = m_cargo.query(
        tables,
        fields,
        query
    )

    local out = {}
    for _, row in ipairs(results) do
        local link = f_passive_skill_link{
            id=row['passive_skills.id'],
        }

        if tpl_args.format == nil then
            out[#out+1] = string.format('* %s', link)
        elseif tpl_args.format == 'none' then
            out[#out+1] = link
        elseif tpl_args.format == 'li' then
            out[#out+1] = string.format('<li>%s</li>', link)
        else
            error(string.format(i18n.errors.generic_argument_parameter, 'format', tpl_args.format))
        end
    end

    if tpl_args.format == nil then
        return table.concat(out, '\n')
    elseif tpl_args.format == 'none' then
        return table.concat(out, '\n')
    elseif tpl_args.format == 'li' then
        return table.concat(out)
    end
end

-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------

local p = {}

--
-- Template:Passive skill table
--
p.passive_skill_table = m_util.misc.invoker_factory(_passive_skill_table, {
    parentFirst=true,
})

--
-- Template:Simple passive skill list
--
p.simple_passive_skill_list = m_util.misc.invoker_factory(_simple_passive_skill_list, {
    wrappers = cfg.wrappers._simple_passive_skill_list,
})

-- ----------------------------------------------------------------------------
-- Debug
-- ----------------------------------------------------------------------------

p.debug = {}

function p.debug.preset()
    return {
        q_where = 'is_keystone = 1',
    }
end
function p.debug.test()
    return mw.log(_simple_passive_skill_list(p.debug.preset()))
end

return p