Consider helping out in updating the wiki for 3.3.0 - see Path_of_Exile_Wiki:To-do_list/3.3.0

Various people have reported issues with the ads on here. Make sure to report those advertisements

Module:Data tables

From Path of Exile Wiki
Jump to: navigation, search

Documentation for this module may be created at Module:Data tables/doc

--[[
    Module for data tables
    
    Attempts to create advanced infoboxes in a standardized way.
]]

local getArgs = require('Module:Arguments').getArgs
local m_util = require('Module:Util')
local m_game = require('Module:Game')
local f_infocard = require('Module:Infocard')._main
local mw_language = mw.getLanguage('en')

local p = {}

-- Internationalization of public strings:
local i18n = {
    character_class = {
        name = 'Name',
        id = 'Id',
    },
    generic_stats = {
        name = 'Name',
        id = 'Id',
        stat_text = 'Text',
        value = 'Value',
    },
    ascendancy_class = {
        name = 'Name',
        id = 'Id',
        flavour_text = 'Flavour text',
        character_id = 'Character id',
        character = 'Character',
    },
    event = {
        name = 'Name',
        id = 'Id',
        type = 'Type',
        type_challenge = 'Challenge league',
        type_expansion = 'Expansion',
        type_pvp = 'PvP season',
        type_race = 'Race season',
        release_version = 'Release version',
        release_date = 'Release date',
        end_date = 'End date',
        standard = 'Standard',
        hardcore = 'Hardcore',
        ordinal = 'Ordinal number',
        short_name = 'Short name',
        match_challenge = 'league',
        match_expansion = 'Please, do not match this.',
        match_pvp = 'pvp season',
        match_race = 'race season',
        number_of_events = 'Number of events',
        price = 'Price',
        rewards = 'Rewards',
        links = 'Links',
    },
}

-- ---------------------------------------------------------------------
-- Utility / Helper functions
-- ---------------------------------------------------------------------
local h = {}

function h.date(value, args)
    --[[
    Format dates in correct and useable form.
    
    value = date string
    args = table of extra args.
    
    To do:
    Remove hours if it isn't specified.
    ]]
    
    local args = args or {}
    
    -- List of allowed extra arguments:
    local arg_list = {
        format = {
            default = 'Y-m-d H:i:s',
            cargo = 'Y-m-d H:i:s',
        },
    }
    
    local date_format = arg_list.format.default
    for i,v in pairs(args) do
        if i == 'format' then
            date_format = arg_list[i][v]            
        end
    end
    local out
    if value ~= nil then
        out = mw_language:formatDate(date_format, value)
    else
        out =  nil
    end
    
    return out
end

function h.timezone(str)
    --[[
    Check if the string contains Z at the end, if it does it implies 
    the time is in UTC and then return UTC.
    ]]
    
	if str ~= nil and str:sub(-1,-1) == 'Z' then  
		return 'UTC'
	else
		return ''
	end
end

function h.cargo_query(tpl_args)
    --[[
    Returns a Cargo query of all the results.
    
    tpl_args should include these keys:
    tpl_args.tables
    tpl_args.fields
    tpl_args.q_*
    
    ]]
    
    local tables = m_util.string.split(tpl_args.tables, ', ')
    local fields = m_util.string.split(tpl_args.fields, ', ')
    
    -- Parse query arguments
    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
    
    -- Query cargo rows:
    local results = m_util.cargo.query(tables, fields, query, args)
    
    return results
end

function h.parse_map(tpl_args, frame, map)
    --[[
        Parse the map
        
        Input:
        
    ]]
    local cargo_data = {
        _table = map.main.table,
    }
    for _, key in pairs(map.main.parse_order) do
        local data = map.main.fields[key]
        local value 
        if data.func ~= nil then
            if data.name then
                value = data.func(tpl_args, frame, tpl_args[data.name])
            else
                value = data.func(tpl_args, frame)
            end
        else
            value = tpl_args[data.name]
        end
        
        tpl_args[key] = value

        if data.field ~= nil then
            if data.func_cargo then
                cargo_data[data.field] = data.func_cargo(tpl_args, frame)
            else
                cargo_data[data.field] = value
            end
        end
    end
    
    local out = {
        tpl_args = tpl_args,
        cargo_data = cargo_data,
    }
    
    return out
end

h.parse_stat_args = function(tpl_args, args)
    --[[
    Parse template args that starts with stat.+ and return them as a 
    array.
    
    ]]
    
    out = {}
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 4) == 'stat' then
            local n,k = string.match(key, 'stat(%d+)_(.+)')
            n = tostring(n)
            if out[n] == nil then 
                out[n] = {} 
            end
            if n ~= nil and k ~= nil then 
                out[n][k] = value
            else 
                error(string.format(
                    'Wrong stat argument format. The template argument "%s" matched "%s" and "%s".', 
                    key or 'n/a', n or 'n/a', k or 'n/a'
                    )
                )
            end
        end
    end

    return out
end


-- ---------------------------------------------------------------------
-- Template: Generic stats
-- ---------------------------------------------------------------------

local generic_stats_map = {
    main = {
        table = 'generic_stats',
        order = {},
        parse_order = {'name', 'id', 'stat_text', 'value'},
        fields = {
            name = {
                name = 'name',
                field = 'name',
                type = 'String',
                wikitext = i18n.generic_stats.name,
            },
            id = {
                name = 'row',
                field = 'id',
                type = 'String',
                wikitext = i18n.generic_stats.id,
                func = function(tpl_args, frame, value)
                    return tpl_args.stats[value].id
                end,
            },
            stat_text = {
                name = 'row',
                field = 'stat_text',
                type = 'Wikitext',
                wikitext = i18n.generic_stats.stat_text,
                func = function(tpl_args, frame, value)
                    return tpl_args.stats[value].stat_text
                end,
            },
            value = {
                name = 'row',
                field = 'value',
                type = 'Integer',
                wikitext = i18n.generic_stats.value,
                func = function(tpl_args, frame, val)
                    return tpl_args.stats[val].value
                end,
            },
        },
    },
}

-- Declare cargo table:
p.declare_generic_stats = m_util.cargo.declare_factory{
    data=generic_stats_map.main,
}

-- Attach cargo table:
p.attach_generic_stats = m_util.cargo.attach_factory{
    data=generic_stats_map.main,
}

function p.generic_stats(frame)
    --[[
    Displays a infobox and stores cargo data for characters shared stats
    = p.generic_stats{
        name = 'Character',
        stat1_id = 'accuracy_rating_per_level',
        stat1_value = 2,
        stat1_stat_text = '+2 Accuracy Rating per Level',
        stat12_id = 'base_attack_speed_+%_per_frenzy_charge',
        stat12_value = 4,
        stat12_stat_text = '4% increased Attack Speed per Frenzy Charge',
    }
    ]]
    
    -- Get template args:
    local tpl_args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)

    -- Parse stat args:
    tpl_args.stats = h.parse_stat_args(tpl_args)
    
    -- Loop through the stats then for each cargo row parse the map and 
    -- store the cargo fields:
    local map = generic_stats_map
    for i,_ in pairs(tpl_args.stats) do
        tpl_args.row = i
        local parsed_map = h.parse_map(tpl_args, frame, map)
        tpl_args = parsed_map.tpl_args
        local cargo_data = parsed_map.cargo_data
        m_util.cargo.store(frame, cargo_data)
    end
    
    -- Main sections, loop through
    local tbl = mw.html.create('table')
    for _, key in ipairs(map.main.order) do
        local data = map.main.fields[key]
        
        if data.display == nil then
            text = tpl_args[key]
        else
            text = data.display(tpl_args, frame, tpl_args[key])
        end
        
        if text ~= nil then           
            tbl
                :tag('tr')
                    :tag('th')
                        :wikitext(data.wikitext)
                        :done()
                    :tag('td')
                        :wikitext(text)
                        :done()
                    :done()
        elseif text then
            tbl
                :tag('tr')
                    :tag('td')
                        -- :attr('colspan', '2')
                        :wikitext(text)
                        :done()
                    :done()
        end
    end
    
    -- Output Infocard
	local infocard_args = {
		['class'] = 'generic_stats',
		['header'] = tpl_args.name,
		['subheader'] = nil,
		[1] = string.format('[[File:%s_stats.png|250px]]', tpl_args.name),
		[2] = tostring(tbl),
    }
    
    -- Add categories:
    local cats = {
        'Data pages'
    }
    
    return f_infocard(infocard_args) .. m_util.misc.add_category(cats)
end


-- ---------------------------------------------------------------------
-- Template: Character class
-- ---------------------------------------------------------------------
local character_map = {
    main = {
        table = 'character_classes',
        order = {'flavour_text'},
        parse_order = {'name', 'flavour_text', 'id'},
        fields = {
            -- Header:
            name = {
                name = 'name',
                field = 'name',
                type = 'String',
                wikitext = i18n.character_class.name,
            },
            -- Main text:
            flavour_text = {
                name = 'flavour_text',
                field = 'flavour_text',
                type = 'String',
                display = function(tpl_args, frame, value)
                    return m_util.html.poe_color('unique', value)
                end,
            },
            -- Fields only:
            id = {
                name = nil,
                field = 'id',
                type = 'String',
                wikitext = i18n.character_class.id,
                func = function(tpl_args, frame)
                    return m_game.constants.characters[tpl_args.name].id
                end,
            },
        },
    },
}

local character_test_map = {
    main = {
        table = 'character_classes_test',
        order = {},
        parse_order = {'flavour_text_author'},
        fields = {
            flavour_text_author = {
                name = nil,
                field = 'flavour_text_author',
                type = 'String',
                func = function(tpl_args, frame, value)
                    return 'John (JD) Doe'
                end,
            },
        },
    },
}

-- Declare cargo table:
p.declare_character_classes = m_util.cargo.declare_factory{data=character_map.main}
p.declare_character_classes_test = m_util.cargo.declare_factory{data=character_test_map.main}

-- Attach cargo table:
p.attach_character_classes = m_util.cargo.attach_factory{data=character_map.main}
p.attach_character_classes_test = m_util.cargo.attach_factory{data=character_test_map.main}


function p.character_class(frame)
    --[[
    Displays a infobox and stores cargo data for character classes.
    
    Examples:
    = p.character_class{
        name='Marauder',
        flavour_text='testing'
    }
    ]]
    
    
    -- Get template args:
    local tpl_args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)
	    
    -- Parse character map:
    local map = character_map
    local parsed_map = h.parse_map(tpl_args, frame, map)
    tpl_args = parsed_map.tpl_args
    local cargo_data = parsed_map.cargo_data
    
    -- Store cargo fields:
    m_util.cargo.store(frame, cargo_data)  
    
    -- Parse and store character test map:
    m_util.cargo.store(frame, h.parse_map(tpl_args, frame, character_test_map).cargo_data)
 
    -- Main sections, loop through
    local tbl = mw.html.create('table')
    for _, key in ipairs(map.main.order) do
        local data = map.main.fields[key]
        
        if data.display == nil then
            text = tpl_args[key]
        else
            text = data.display(tpl_args, frame, tpl_args[key])
        end
        
        if text ~= nil then           
            tbl
                :tag('tr')
                    :tag('th')
                        :wikitext(data.wikitext)
                        :done()
                    :tag('td')
                        :wikitext(text)
                        :done()
                    :done()
        elseif text then
            tbl
                :tag('tr')
                    :tag('td')
                        -- :attr('colspan', '2')
                        :wikitext(text)
                        :done()
                    :done()
        end
    end
    
    -- Output Infocard
	local infocard_args = {
		['class'] = 'character_class',
		['header'] = tpl_args.name,
		['subheader'] = nil,
		[1] = string.format(
            '[[File:%s character class.png|250px]]',
            tpl_args.name
        ),
		[2] = tostring(tbl),
    }
    
    -- Add categories:
    local cats = {
        'Character classes'
    }
    
    return f_infocard(infocard_args) .. m_util.misc.add_category(cats)
end

-- ---------------------------------------------------------------------
-- Template: Ascendancy Class
-- ---------------------------------------------------------------------

local ascendancy_map = {
    main = {
        table = 'ascendancy_classes',
        order = {'flavour_text'},
        parse_order = {'name', 'character', 'flavour_text', 'id', 'character_id'},
        fields = {
            -- Header:
            name = {
                name = 'name',
                field = 'name',
                type = 'String',
                wikitext = i18n.ascendancy_class.name,
            },
            -- Subheader:
            character = {
                name = nil,
                field = 'character_class',
                type = 'String',
                wikitext = i18n.ascendancy_class.character,
                func = function(tpl_args, frame, value)
                    local id = m_game.constants.ascendancy[tpl_args.name].id
                    local character_id = m_game.constants.ascendancy[tpl_args.name].character
                    
                    local character
                    for i,v in pairs(m_game.constants.characters) do
                        if v.id == character_id then 
                            character = i
                            break
                        end
                    end
                    
                    return character 
                end,
            },
            -- Main text:
            flavour_text = {
                name = 'flavour_text',
                field = 'flavour_text',
                type = 'String',
                display = function(tpl_args, frame, value)
                    return m_util.html.poe_color('unique', value)
                end,
            },
            -- Fields only:
            id = {
                name = nil,
                field = 'id',
                type = 'Integer',
                wikitext = i18n.ascendancy_class.id,
                func = function(tpl_args, frame, value)
                    return m_game.constants.ascendancy[tpl_args.name].id
                end,
            },
            character_id = {
                name = nil,
                field = 'character_id',
                type = 'Integer',
                wikitext = i18n.ascendancy_class.character_id,
                func = function(tpl_args, frame, value)
                    return m_game.constants.ascendancy[tpl_args.name].character
                end,
            },
        },
    },
}

-- Declare cargo table:
p.declare_ascendancy_classes = m_util.cargo.declare_factory{data=ascendancy_map.main}

-- Attach cargo table:
p.attach_ascendancy_classes = m_util.cargo.attach_factory{data=ascendancy_map.main}


function p.ascendancy_class (frame)
    --[[
    Displays a infobox and stores cargo data for ascendancy classes.
    
    Examples:
    = p.ascendancy_class{
        name='Slayer',
        flavour_text='testing'
    }
    ]]
    
    
    -- Get template args:
    local tpl_args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)
	    
    -- Parse ascendancy map:
    local map = ascendancy_map
    local parsed_map = h.parse_map(tpl_args, frame, map)
    tpl_args = parsed_map.tpl_args
    local cargo_data = parsed_map.cargo_data
    
    -- Store cargo fields:
    m_util.cargo.store(frame, cargo_data)  
 
    -- Main sections, loop through
    local tbl = mw.html.create('table')
    for _, key in ipairs(map.main.order) do
        local data = map.main.fields[key]
        
        if data.display == nil then
            text = tpl_args[key]
        else
            text = data.display(tpl_args, frame, tpl_args[key])
        end
        
        if text ~= nil then           
            tbl
                :tag('tr')
                    :tag('th')
                        :wikitext(data.wikitext)
                        :done()
                    :tag('td')
                        :wikitext(text)
                        :done()
                    :done()
        elseif text then
            tbl
                :tag('tr')
                    :tag('td')
                        -- :attr('colspan', '2')
                        :wikitext(text)
                        :done()
                    :done()
        end
    end
    
    -- Output Infocard
	local infocard_args = {
		['class'] = 'ascendancy_class',
		['header'] = tpl_args.name,
		['subheader'] = string.format('[[%s]]', tpl_args.character),
		[1] = string.format(
            '[[File:%s ascendancy class.png|250px]]',
            tpl_args.name
        ),
		[2] = tostring(tbl),
    }
    
    -- Add categories:
    local cats = {
        'Ascendancy classes', 
        tpl_args.character .. ' ascendancy classes',
    }
    
    return f_infocard(infocard_args) .. m_util.misc.add_category(cats)
end



-- ---------------------------------------------------------------------
-- Template: Event
-- ---------------------------------------------------------------------

local event_map = {
    main = {
        table = 'events',
        order = {'release_version', 'release_date', 'end_date', 'number_of_events', 'rewards', 'price', 'links'},
        parse_order = {'id', 'name', 'short_name', 'type', 'ordinal', 'standard', 'hardcore', 'release_version', 'release_date', 'end_date', 'number_of_events', 'rewards', 'price', 'links'},
        fields = {
            -- Header:
            name = {
                name = 'name',
                field = 'name',
                type = 'String',
                wikitext = i18n.event.name,
            },      
            -- Subheader:
            type = {
                name = 'type',
                field = 'type',
                type = 'String',
                wikitext = i18n.event.type,
                func = function(tpl_args, frame, value)
                    local type_tbl = {
                        challenge = i18n.event.type_challenge,
                        expansion = i18n.event.type_expansion,
                        pvp = i18n.event.type_pvp,
                        race = i18n.event.type_race,
                    }
                    return type_tbl[value]
                end, 
            },            
            -- Main text:
			release_version = { 
				name = 'release_version',
                field = 'release_version',
                type = 'String',
                wikitext = i18n.event.release_version,
                display = function(tpl_args, frame, value)
                    if value ~= nil then
                        return string.format('[[Version %s|%s]]', value, value)
                    end 
                end,
			},
			release_date = { 
				name = 'release_date',
                field = 'release_date',
                type = 'Datetime',
                wikitext = i18n.event.release_date,
                cargo_func = function(tpl_args, frame, value)
                    return h.date(value, {format='cargo'})
                end,
                display = function(tpl_args, frame, value)
                    local out 
                    if value ~= nil then
                        out = string.format(
                            '%s %s', 
                            h.date(value), 
                            h.timezone(value)
                        )
                    end
                    
                    return out
                end,
			},
			end_date = { 
				name = 'end_date',
                field = 'end_date',
                type = 'Datetime',
                wikitext = i18n.event.end_date,
                cargo_func = function(tpl_args, frame, value)
                    return h.date(value, {format='cargo'})
                end,
                display = function(tpl_args, frame, value)
                    local out 
                    if value ~= nil then
                        out = string.format(
                            '%s %s', 
                            h.date(value), 
                            h.timezone(value)
                        )
                    end
                    
                    return out
                end,
			},
			standard = { 
				name = 'standard',
                field = 'is_standard',
                type = 'Boolean',
                wikitext = i18n.event.standard,
                func = function(tpl_args, frame, value)
                    local bool_tbl = {
                        ['false'] = 0,
                        ['true'] = 1,
                    }
                    return bool_tbl[string.lower(value or '')]
                end,
			},
			hardcore = { 
				name = 'hardcore',
                field = 'is_hardcore',
                type = 'Boolean',
                wikitext = i18n.event.hardcore,
                func = function(tpl_args, frame, value)
                    local bool_tbl = {
                        ['false'] = 0,
                        ['true'] = 1,
                    }
                    return bool_tbl[string.lower(value or '')]
                end,
                
            },            
			number_of_events = { 
				name = 'number_of_events',
                field = 'number_of_events',
                type = 'Integer',
                wikitext = i18n.event.number_of_events,
			},	
			rewards = { 
				name = 'rewards',
                field = 'rewards',
                type = 'Wikitext',
                wikitext = 'Rewards',
                display = function(tpl_args, frame, value)
                    local out
                    if value ~= nil then
                        out = string.format(
                            '<div style="text-align: right;">%s</div>', 
                            value
                        )
                    end
                    return out
                end,
			},
			price = { 
				name = 'price',
                field = 'price',
                type = 'Wikitext',
                wikitext = i18n.event.price,
			},
			links = { 
				name = 'links',
                field = 'links',
                type = 'Wikitext', 
                wikitext = i18n.event.links,
			},
            
            -- Field calculations:
            id = {
                name = 'id',
                field = 'id',
                type = 'List (, ) of String',
                wikitext = i18n.event.id, 
            },
            short_name = {
                name = nil,
                field = 'short_name',
                type = 'String',
                wikitext = i18n.event.short_name, 
                func = function(tpl_args, frame)
                    local out = {}
                    for i,v in pairs(tpl_args) do
                        local match_tbl = {
                            challenge = i18n.event.match_challenge,
                            expansion = i18n.event.match_expansion,
                            pvp = i18n.event.match_pvp,
                            race = i18n.event.match_race,
                        }
                        for ii,vv in pairs(match_tbl) do
                            if v==ii then
                                out[#out+1] = tpl_args['name']:lower():gsub(vv, ''):gsub('^%l', string.upper)
                                break
                            end
                        end
                    end
                    return table.concat(out, ', ')
                end,
            },
            ordinal = {
                name = nil,
                field = 'ordinal',
                type = 'String',
                wikitext = i18n.event.ordinal, 
                func = function(tpl_args, frame)
                    tpl_args.tables = 'events'
                    local count = string.format(
                        'COUNT(DISTINCT %s._pageName)', 
                        tpl_args.tables
                    )
                    tpl_args.fields = count
                    tpl_args.q_where = string.format(
                        '%s.type = "%s" AND %s.release_date < "%s"', 
                        tpl_args.tables,
                        tpl_args.type or '', 
                        tpl_args.tables,
                        tpl_args.release_date or ''
                    )
                    local results = h.cargo_query(tpl_args)
                    
                    local out = 1
                    if #results > 0 then 
                        out = out + results[1][count]
                    end
                    
                    return out
                end,
            },
        },
    },
}

-- Declare cargo table:
p.declare_events = m_util.cargo.declare_factory{data=event_map.main}

-- Attach cargo table:
p.attach_events = m_util.cargo.attach_factory{data=event_map.main}


function p.event_box(tpl_args, frame, map)
    -- Main sections, loop through
    local tbl = mw.html.create('table')
    for _, key in ipairs(map.main.order) do
        local data = map.main.fields[key]
        
        if data.display == nil then
            text = tpl_args[key]
        else
            text = data.display(tpl_args, frame, tpl_args[key])
        end
        
        if text ~= nil then           
            tbl
                :tag('tr')
                    :tag('th')
                        :wikitext(data.wikitext)
                        :done()
                    :tag('td')
                        :wikitext(text)
                        :done()
                    :done()
        elseif text then
            tbl
                :tag('tr')
                    :tag('td')
                        -- :attr('colspan', '2')
                        :wikitext(text)
                        :done()
                    :done()
        end
    end
    
    -- Output Infocard
	local infocard_args = {
		['class'] = 'event',
		['header'] = tpl_args.name,
		['subheader'] = tpl_args.type,
		[1] = string.format(
            '[[File:%s|250px]]', 
            tpl_args.image or string.format('%s_logo.png', tpl_args.name)
        ),
		[2] = tostring(tbl),
    }

    return f_infocard(infocard_args)
end

    -- =p.event{name='Ascendancy', type = 'expansion', release_version = '2.2.0', release_date = '2016-03-04'}
    -- =p.event{name='Winterheart race season', type='race', release_version = '2.3.0', release_date='2016-01-29', end_date='2016-08-29T22:00:00Z', number_of_events='155', rewards="[[Asphyxia's Wrath]] <br> [[Sapphire Ring]] <br> [[The Whispering Ice]] <br> [[Dyadian Dawn]] <br> [[Call of the Brotherhood]] <br> [[Winterheart]]", prize="[[Demigod's Dominance]]", links='[http://www.pathofexile.com/seasons Schedule and overview]'  }
    -- c=p.event{name='PvP season 2', type='pvp', release_date='2015-02-15', end_date='2015-03-16', rewards="[[Wanderlust]] <br> [[Coral Ring]] <br> [[Geofri's Baptism]] <br> [[Kikazaru]] <br> [[Meginord's Girdle]] <br> [[Atziri's Foible]]", prize='[[Talisman of the Victor]]', links='[http://www.pathofexile.com/seasons/pvp/season/EUPvPSeason2 EU PvP Ladder] <br> [http://www.pathofexile.com/seasons/pvp/season/USPvPSeason2 US PvP Ladder]'  }
    -- =p.event{name='Perandus league', type='challenge ', release_version = '2.2.0', release_date='2016-03-04', end_date='2016-05-30', standard = 'True', hardcore = 'True'}


function p.event(frame)
    --[[
    Displays a infobox and stores data for various events such as 
    game expansions, leagues, races, pvp etc.
    
    Examples: 

    
    ]]
    -- Get template args:
    local tpl_args = getArgs(frame, {parentFirst = true})
    local frame = m_util.misc.get_frame(frame)
	
    -- Parse event_map:
    local parsed_map = h.parse_map(tpl_args, frame, event_map)
    tpl_args = parsed_map.tpl_args
    local cargo_data = parsed_map.cargo_data
    
    -- Store cargo fields:
    m_util.cargo.store(frame, cargo_data)   
    
    -- Display infobox:
    local out = p.event_box(tpl_args, frame, event_map)
    
    -- Add categories:
    local cats = {
		tpl_args.type .. 's',
    }
    
    return out .. m_util.misc.add_category(cats)
end

-- ---------------------------------------------------------------------

return p