Module:Passive skill table: Difference between revisions

From Path of Exile Wiki
Jump to navigation Jump to search
>Illviljan
No edit summary
(make icon optional for name column)
 
(13 intermediate revisions by 2 users not shown)
Line 7: Line 7:
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs
local f_passive_skill_link = require('Module:passive_skill_link').passive_skill_link
local f_passive_skill_link = require('Module:passive_skill_link').passive_skill_link
local p = {}


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
Line 17: Line 14:
local i18n = {
local i18n = {
     icon_name = 'File:%s passive skill icon.png',
     icon_name = 'File:%s passive skill icon.png',
   
 
     cats = {
     cats = {
         data = 'Passive skill data',
         data = 'Passive skill data',
       
 
         keystone = 'Keystone passive skills',
         keystone = 'Keystone passive skills',
         notable = 'Notable passive skills',
         notable = 'Notable passive skills',
         basic = 'Basic passive skills',
         basic = 'Small passive skills',
         ascendancy_notable = 'Ascendancy notable passive skills',
         ascendancy_notable = 'Ascendancy notable passive skills',
         ascendancy_basic = 'Ascendancy basic passive skills',
         ascendancy_basic = 'Ascendancy small passive skills',
     },
     },
       
 
     passive_table = {
     passive_table = {
         ascendancy_class = 'Ascendancy<br>Class',
         ascendancy_class = 'Ascendancy<br>Class',
Line 34: Line 31:
         int_id = 'Integer id',
         int_id = 'Integer id',
         stats = 'Stats',
         stats = 'Stats',
        skill_points = 'Skill points',
         connections = 'Connections',
         connections = 'Connections',
        flavour_text = 'Flavour text',
        reminder_text = 'Reminder text',
         is_notable = 'Notable',
         is_notable = 'Notable',
         is_keystone = 'Keystone',
         is_keystone = 'Keystone',
     },
     },
   
 
     errors = {
     errors = {
         invalid_args = 'Passive skill link: id, int_id, page or name must be specified',
         invalid_args = 'Passive skill table: q_where must be specified',
         no_passives_found = 'No passive skills with the given name found',
         no_passives_found = 'No passive skills with the given name found',
        cats = 'Pages with broken passive skill tables',
     },
     },
}
}
Line 68: Line 69:
         if args.fmt then
         if args.fmt then
             val = args.fmt(row[field])
             val = args.fmt(row[field])
         end  
         end
         -- Can't show results here that don't have a value:
         -- Can't show results here that don't have a value:
         if val then
         if val then
Line 79: Line 80:
         end
         end
     end
     end
   
 
     return values, order
     return values, order
end
end
Line 89: Line 90:
         for j, row in ipairs(values[key]) do
         for j, row in ipairs(values[key]) do
             links[#links+1] = string.format(
             links[#links+1] = string.format(
                 '[[%s|&#91;%s&#93;]]',  
                 '[[%s|&#91;%s&#93;]]',
                 row['passive_skills._pageName'],  
                 row['passive_skills._pageName'],
                 j + (i-1) * #values[key]
                 j + (i-1) * #values[key]
             )
             )
         end
         end
         out[i] = string.format(
         out[i] = string.format(
             '<span class="passive-line">%s <span class="passive-hover">%s</span></span>',  
             '<span class="passive-line">%s <span class="passive-hover">%s</span></span>',
             key,  
             key,
             table.concat(links, ' ')
             table.concat(links, ' ')
         )
         )
     end
     end
   
 
     return table.concat(out, '<hr>')
     return table.concat(out, '<hr>')
end
end


h.type_order = {
    'basic',
    'notable',
    'keystone',
    'ascendancy_basic',
    'ascendancy_notable'
}
function h.get_type(passive)
function h.get_type(passive)
    --[[
    Determine what type of passive skill this passive is.
    ]]
     local key
     local key
     if tonumber(passive['passive_skills.is_keystone']) == 1 then
     if tonumber(passive['passive_skills.is_keystone']) == 1 then
Line 120: Line 117:
         key = 'basic'
         key = 'basic'
     end
     end
   
 
     if passive['passive_skills.ascendancy_class'] ~= nil then
     if passive['passive_skills.ascendancy_class'] ~= nil then
         key = 'ascendancy_' .. key
         key = 'ascendancy_' .. key
     end
     end
   
 
     return key
     return key
end
end


function h.sort_by_type(results)
function h.na_or_val(tr, value, func, args)
     local new = {}
     --[[
     for _, key in ipairs(h.type_order) do
 
        new[key] = {}
     Parameters
     end
    ----------
   
     tr :
     for _, passive in ipairs(results) do
 
        table.insert(new[h.get_type(passive)], passive)
     value :
     end
 
      
     func : function
    return new
 
end
     args : list of
        Valid keys are:
        * colour


function h.na_or_val(tr, value, func)
    ]]
    local args = args or {}
     if value == nil or value == '' then
     if value == nil or value == '' then
         tr:wikitext(m_util.html.td.na())
         tr:wikitext(m_util.html.td.na())
Line 149: Line 149:
             value = func(value)
             value = func(value)
         end
         end
         tr
 
            :tag('td')
         local td = tr:tag('td')
                :attr('data-sort-value', raw_value)
        td:attr('data-sort-value', raw_value)
                :wikitext(value)
        td:wikitext(value)
                :done()
        if args.colour then
            td:attr('class', 'tc -' .. args.colour)
        end
     end
     end
end
end
Line 161: Line 163:
function h.fmt.link(field)
function h.fmt.link(field)
     local fields = m_util.string.split(field, ',')
     local fields = m_util.string.split(field, ',')
     for i, v in ipairs(fields) do  
     for i, v in ipairs(fields) do
         fields[i] = string.format('[[%s]]', v)
         fields[i] = string.format('[[%s]]', v)
     end  
     end
     return table.concat(fields, ', ')
     return table.concat(fields, ', ')
end  
end
function h.fmt.passive_skill_link(field)
function h.fmt.passive_skill_link(field)
     local fields = m_util.string.split(field, ',')
     local fields = m_util.string.split(field, ',')
     for i, v in ipairs(fields) do
     for i, v in ipairs(fields) do
         fields[i] = f_passive_skill_link{id=v}
         fields[i] = f_passive_skill_link{id=v}
     end  
     end
     return table.concat(fields, ', ')
     return table.concat(fields, ', ')
end
end
Line 178: Line 180:
         if m_util.cast.boolean(v) then
         if m_util.cast.boolean(v) then
             fields[i] = '<div class="table-yes" data-sort-value=1><span>&#x2713;</span></div>'
             fields[i] = '<div class="table-yes" data-sort-value=1><span>&#x2713;</span></div>'
         else  
         else
             fields[i] = '<div class="table-no" data-sort-value=0><span>&#x2717;</span></div>'
             fields[i] = '<div class="table-no" data-sort-value=0><span>&#x2717;</span></div>'
         end
         end
     end  
     end
     return table.concat(fields, ', ')
     return table.concat(fields, ', ')
end
end
function h.fmt.link_passive(field)
function h.fmt.link_passive(field)
     --[[
     --[[
     Create a link to the passive skill data page from the ID.  
     Create a link to the passive skill data page from the ID.
     ]]
     ]]
     local fields = m_util.string.split(field, ',')
     local fields = m_util.string.split(field, ',')
Line 195: Line 197:
             v
             v
         )
         )
     end  
     end
     return table.concat(fields, ', ')
     return table.concat(fields, ', ')
end
end


-- Display  
-- Display
h.tbl = {}
h.tbl = {}
h.tbl.display = {}
h.tbl.display = {}
function h.tbl.display.merged_values(args)
 
h.tbl.display.factory = {}
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, frame, tr, rows, rowinfo)
         local values, order = h.make_order(rows, args.field, args)
         local values, order = h.make_order(rows, args.field, args)
         h.na_or_val(tr, h.data_page_links(values, order), nil)
         local value = h.data_page_links(values, order)
        h.na_or_val(tr, value, func, args)
     end
     end
end
end


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- Page functions
-- Data mappings
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
local p = {}


data = {}
data = {}
Line 223: Line 225:
             join='passive_skills._pageID=passive_skill_stats._pageID',
             join='passive_skills._pageID=passive_skill_stats._pageID',
         },
         },
        main_pages = {
            join='passive_skills.id=main_pages.id'
        }
     },
     },
     -- display data
     -- Display data
     {
     {
         args = nil,
         args = nil,
Line 235: Line 240:
             'passive_skills.main_page',
             'passive_skills.main_page',
             'main_pages._pageName',
             'main_pages._pageName',
            'passive_skills.icon',
             -- 'passive_skills.html',
             -- 'passive_skills.html',
           
 
             -- Required:
             -- Required:
             'passive_skills._pageName',
             'passive_skills._pageName',
             'passive_skills.name',
             'passive_skills.name',
            'passive_skills.icon',
         },
         },      
         display = function (tpl_args, frame, tr, data)
         display = function (tpl_args, frame, tr, data)
             local passive = data[1]
             local passive = data[1]
            local type_key = h.get_type(passive)
 
           
             local psl_args = {
             local psl_args = {
                 skip_query = true,
                 skip_query = true,
                 page = passive['passive_skills.main_page']  
                 page = passive['passive_skills.main_page']
                     or passive['main_pages._pageName']  
                     or passive['main_pages._pageName']
                     or passive['passive_skills.name']
                     or passive['passive_skills.name']
                     or passive['passive_skills._pageName'],  
                     or passive['passive_skills._pageName'],
                 name = passive['passive_skills.name'],  
                 name = passive['passive_skills.name'],
                 icon = passive['passive_skills.icon'],  
                 icon = passive['passive_skills.icon'],
                 is_keystone = passive['passive_skills.is_keystone'],
                 is_keystone = passive['passive_skills.is_keystone'],
                 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'],
                html = passive['passive_skills.is_notable'],
             }
             }
             if tpl_args.no_html == nil then
             if tpl_args.no_html == nil then
Line 265: Line 268:
                 psl_args.large = tpl_args.large
                 psl_args.large = tpl_args.large
             end
             end
               
 
             tr
             tr
                 :tag('td')
                 :tag('td')
                     :attr(
                     :attr(
                         'data-sort-value',  
                         'data-sort-value',
                         passive['passive_skills.name'] .. type_key
                         passive['passive_skills.name'] .. h.get_type(passive)
                     )
                     )
                    :attr('style', 'text-align:center;')
                     :wikitext(f_passive_skill_link(psl_args))
                     :wikitext(f_passive_skill_link(psl_args))
                     :done()
                     :done()
           
 
         end,
         end,
         order = 1000,
         order = 1000,
Line 284: Line 288:
             [4] = {optional=true},
             [4] = {optional=true},
             [5] = {optional=true},
             [5] = {optional=true},
            [6] = {optional=true},
         },
         },
     },
     },
Line 292: Line 297:
         display = function (tpl_args, frame, tr, data)
         display = function (tpl_args, frame, 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'],
                 function ()
                 function ()
                     return string.format(
                     return string.format(
                         '[[%s]]<br>[[File:%s avatar.png|link=%s]]',  
                         '[[%s]]<br>[[File:%s avatar.png|link=%s]]',
                         passive['passive_skills.ascendancy_class'],  
                         passive['passive_skills.ascendancy_class'],
                         passive['passive_skills.ascendancy_class'],  
                         passive['passive_skills.ascendancy_class'],
                         passive['passive_skills.ascendancy_class']
                         passive['passive_skills.ascendancy_class']
                     )
                     )
Line 309: Line 314:
         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.merged_values{field='passive_skills.id'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.id'},
         order = 1001,
         order = 1001,
         sort_type = 'text',
         sort_type = 'text',
Line 317: Line 322:
         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.merged_values{field='passive_skills.int_id'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.int_id'},
         order = 1002,
         order = 1002,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
         arg = {'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.merged_values{field='passive_skills.stat_text'},
         display = h.tbl.display.factory.merged_values{field='passive_skills.stat_text', colour='mod'},
         order = 2000,
         order = 2000,
         sort_type = 'text',
         sort_type = 'text',
     },
     },
     {
     {
         arg = 'connections',
         args = 'skill_points',
         header = 'Connections',
         header = i18n.passive_table.skill_points,
        fields = {'passive_skills.skill_points'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.skill_points'},
        order = 2001,
        sort_type = 'text',
    },
    {
        args = 'connections',
        header = i18n.passive_table.connections,
         fields = {'passive_skills.connections'},
         fields = {'passive_skills.connections'},
         display = h.tbl.display.merged_values{
         display = h.tbl.display.factory.merged_values{
             field='passive_skills.connections',
             field='passive_skills.connections',
            -- fmt=h.fmt.link_passive,
             fmt=h.fmt.passive_skill_link,
             fmt=h.fmt.passive_skill_link,
         },
         },
         order = 3000,
         order = 3000,
         sort_type = 'text',  
        sort_type = 'text',
    },
    {
        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',
        },
        order = 3001,
        sort_type = 'text',
    },
    {
        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',
        },
        order = 3002,
         sort_type = 'text',
     },
     },
     {
     {
         args = 'is_notable',
         args = 'is_notable',
         header = i18n.passive_table.is_notable,
         header = i18n.passive_table.is_notable,
         fields = {'passive_skills.is_notable'},      
         fields = {'passive_skills.is_notable'},
         display = h.tbl.display.merged_values{
         display = h.tbl.display.factory.merged_values{
             field='passive_skills.is_notable',  
             field='passive_skills.is_notable',
             fmt=h.fmt.boolean
             fmt=h.fmt.boolean
         },
         },
Line 356: Line 390:
         args = 'is_keystone',
         args = 'is_keystone',
         header = i18n.passive_table.is_keystone,
         header = i18n.passive_table.is_keystone,
         fields = {'passive_skills.is_keystone'},      
         fields = {'passive_skills.is_keystone'},
         display = h.tbl.display.merged_values{
         display = h.tbl.display.factory.merged_values{
             field='passive_skills.is_keystone',  
             field='passive_skills.is_keystone',
             fmt=h.fmt.boolean
             fmt=h.fmt.boolean
         },
         },
Line 365: Line 399:
     },
     },
}
}
-- ----------------------------------------------------------------------------
-- Page functions
-- ----------------------------------------------------------------------------
local p = {}


function p.passive_skill_table(frame)
function p.passive_skill_table(frame)
     --[[
     --[[
     Displays a table of passive skills.
     Displays a table of passive skills.
   
 
     TODO: Why is the groupBy necessary?
     TODO: Why is the groupBy necessary?
   
 
     Examples
     Examples
     --------
     --------
     = p.passive_skill_table{
     = p.passive_skill_table{
         q_where = 'passive_skills.ascendancy_class = "Gladiator"',
         q_where = 'passive_skills.ascendancy_class = "Gladiator"',
         name=1,
         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()
     local t = os.clock()
   
 
     -- Get args
     -- Get args
     tpl_args = getArgs(frame, {
     local tpl_args = getArgs(frame, {
         parentFirst = true
         parentFirst = true
     })
     })
     frame = m_util.misc.get_frame(frame)
     frame = m_util.misc.get_frame(frame)
   
 
     -- Create cargo query:
     -- Check if the correct parameters have been set:
     if tpl_args.q_tables then
     if tpl_args.q_where == nil then
        tpl_args.q_tables = tpl_args.q_tables .. ',' .. 'passive_skill_stats, main_pages'
         return m_util.html.error{msg=i18n.errors.invalid_args .. m_util.misc.add_category(i18n.errors.cats)}
    else
        tpl_args.q_tables = 'passive_skill_stats, main_pages'
    end
    if tpl_args.q_join then  
         tpl_args.q_join = tpl_args.q_join .. ',' .. 'passive_skills._pageID=passive_skill_stats._pageID, passive_skills.id=main_pages.id'
    else
        tpl_args.q_join = 'passive_skills._pageID=passive_skill_stats._pageID, passive_skills.id=main_pages.id'
     end
     end
    -- Cargo query settings:
     tpl_args.q_groupBy = tpl_args.q_groupBy or 'passive_skills._pageID'
     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'
     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:
     -- Run the query and display the results:
     out = m_cargo.table_query{
     local out = m_cargo.table_query{
         tpl_args=tpl_args,
         tpl_args=tpl_args,
         frame=frame,
         frame=frame,
Line 409: Line 454:
         row_unique_fields={'passive_skills.name'},
         row_unique_fields={'passive_skills.name'},
         data=data.passive_skill_table,
         data=data.passive_skill_table,
         empty_cell=m_util.html.td.na()  
         empty_cell=m_util.html.td.na()
     }
     }
   
 
     mw.logObject({os.clock() - t, tpl_args})
     mw.logObject({os.clock() - t, tpl_args})


     return out  
     return out
end
end



Latest revision as of 02:14, 11 November 2023

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


Lua logo

This module depends on the following other modules:

Implements {{passive skill table}}.

--
-- Module for passive skill table
--

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 = {
    icon_name = 'File:%s passive skill icon.png',

    cats = {
        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',
    },
}


-- ----------------------------------------------------------------------------
-- Constants & Data
-- ----------------------------------------------------------------------------

-- local c = {}

-- ----------------------------------------------------------------------------
-- 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
    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:wikitext(m_util.html.td.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, frame, 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, func, args)
    end
end

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

data = {}
data.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
    {
        args = nil,
        header = i18n.passive_table.name,
        fields = {
            -- Optional:
            'passive_skills.is_keystone',
            'passive_skills.is_notable',
            'passive_skills.ascendancy_class',
            'passive_skills.main_page',
            'main_pages._pageName',
            'passive_skills.icon',
            -- 'passive_skills.html',

            -- Required:
            'passive_skills._pageName',
            'passive_skills.name',
        },
        display = function (tpl_args, frame, 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'],
            }
            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,
        order = 1000,
        sort_type = 'text',
        options = {
            [1] = {optional=true},
            [2] = {optional=true},
            [3] = {optional=true},
            [4] = {optional=true},
            [5] = {optional=true},
            [6] = {optional=true},
        },
    },
    {
        args = {'ascendancy'},
        header = i18n.passive_table.ascendancy_class,
        fields = {'passive_skills.ascendancy_class'},
        display = function (tpl_args, frame, 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,
        order = 0,
        sort_type = 'text',
    },
    {
        args = 'id',
        header = i18n.passive_table.id,
        fields = {'passive_skills.id'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.id'},
        order = 1001,
        sort_type = 'text',
    },
    {
        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'},
        order = 1002,
        sort_type = 'text',
    },
    {
        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'},
        order = 2000,
        sort_type = 'text',
    },
    {
        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'},
        order = 2001,
        sort_type = 'text',
    },
    {
        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,
        },
        order = 3000,
        sort_type = 'text',
    },
    {
        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',
        },
        order = 3001,
        sort_type = 'text',
    },
    {
        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',
        },
        order = 3002,
        sort_type = 'text',
    },
    {
        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
        },
        order = 4000,
        sort_type = 'number',
    },
    {
        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
        },
        order = 4001,
        sort_type = 'number',
    },
}

-- ----------------------------------------------------------------------------
-- Page functions
-- ----------------------------------------------------------------------------

local p = {}

function p.passive_skill_table(frame)
    --[[
    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()

    -- Get args
    local tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)

    -- 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.cats)}
    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,
        frame=frame,
        main_table='passive_skills',
        row_unique_fields={'passive_skills.name'},
        data=data.passive_skill_table,
        empty_cell=m_util.html.td.na()
    }

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

    return out
end


-- ----------------------------------------------------------------------------
-- End
-- ----------------------------------------------------------------------------

return p