Module:Modifier table: Difference between revisions

From Path of Exile Wiki
Jump to navigation Jump to search
>Illviljan
m (Use the in-game name instead of the internal tag.)
>Illviljan
(p.drop_down_table - Use m_cargo to map results to pagename.)
Line 35: Line 35:
         granted_skill = m_util.html.abbr('Skill', 'ID of the skill granted'),
         granted_skill = m_util.html.abbr('Skill', 'ID of the skill granted'),
         tags = '[[Tags]]',
         tags = '[[Tags]]',
       
 
         iiq = m_util.html.abbr('IIQ', 'increased Quantity of Items found in this Area'),
         iiq = m_util.html.abbr('IIQ', 'increased Quantity of Items found in this Area'),
         iir = m_util.html.abbr('IIR', 'increased Rarity of Items found in this Area'),
         iir = m_util.html.abbr('IIR', 'increased Rarity of Items found in this Area'),
         pack_size = m_util.html.abbr('Pack<br>Size', 'Monster pack size'),
         pack_size = m_util.html.abbr('Pack<br>Size', 'Monster pack size'),
       
 
         spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
         spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
         generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
         generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
       
 
         normal_labyrinth = 'Normal Labyrinth',
         normal_labyrinth = 'Normal Labyrinth',
         cruel_labyrinth = 'Cruel Labyrinth',
         cruel_labyrinth = 'Cruel Labyrinth',
Line 48: Line 48:
         eternal_labyrinth = 'Eternal Labyrinth',
         eternal_labyrinth = 'Eternal Labyrinth',
     },
     },
   
 
     drop_down_table = {
     drop_down_table = {
         collapse_all = 'Collapse all',
         collapse_all = 'Collapse all',
Line 55: Line 55:
         prefix = 'Prefix',
         prefix = 'Prefix',
         suffix = 'Suffix',
         suffix = 'Suffix',
         corrupted = 'Corrupted',    
         corrupted = 'Corrupted',
         enchant = 'Enchantment',
         enchant = 'Enchantment',
         elder_prefix = 'Elder prefix',
         elder_prefix = 'Elder prefix',
Line 74: Line 74:
         back_to_top = 'Back to top',
         back_to_top = 'Back to top',
     },
     },
   
 
     errors = {
     errors = {
         --
         --
Line 81: Line 81:
         sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.',
         sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.',
         sell_price_missing_argument = 'Both %s and %s must be specified',
         sell_price_missing_argument = 'Both %s and %s must be specified',
       
 
         --
         --
         -- Modifier link template
         -- Modifier link template
Line 101: Line 101:
function h.header(str)
function h.header(str)
     --[[
     --[[
     This function replaces specific numbers with a generic #.  
     This function replaces specific numbers with a generic #.
     ]]
     ]]
   
 
     local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
     local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
     s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
     s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
     s = table.concat(m_util.string.split(s, '<br>'), ', ')
     s = table.concat(m_util.string.split(s, '<br>'), ', ')
     s = table.concat(m_util.string.split(s, '<br />'), ' ')
     s = table.concat(m_util.string.split(s, '<br />'), ' ')
 
 
   return s
   return s
end
end
Line 126: Line 126:
         error('Hit maximum cargo results')
         error('Hit maximum cargo results')
     end
     end
   
 
     return m_util.cargo.map_results_to_id{results=results,field='mods._pageID'}
     return m_util.cargo.map_results_to_id{results=results,field='mods._pageID'}
end
end
Line 137: Line 137:
     for i,v in pairs(results) do
     for i,v in pairs(results) do
         str[#str+1] = string.format(
         str[#str+1] = string.format(
             '%s - %s ([[%s|page]])',  
             '%s - %s ([[%s|page]])',
             v['mods.id'] or v['mods._pageName'] or '',
             v['mods.id'] or v['mods._pageName'] or '',
             string.gsub(
             string.gsub(
                 v['mods.stat_text_raw'] or 'N/A',  
                 v['mods.stat_text_raw'] or 'N/A',
                 '<br>',  
                 '<br>',
                 ', '
                 ', '
             ) or '',
             ) or '',
             v['mods._pageName'] or ''
             v['mods._pageName'] or ''
         )
         )
     end  
     end
     return table.concat(str, '<br>')
     return table.concat(str, '<br>')
end
end
Line 153: Line 153:
     --[[
     --[[
     Returns a Cargo query of all the results.
     Returns a Cargo query of all the results.
   
 
     tpl_args to be added to the cargo table needs to prefixed with q_.
     tpl_args to be added to the cargo table needs to prefixed with q_.
     * tpl_args.q_*
     * tpl_args.q_*
   
 
     ]]
     ]]
   
 
     -- Parse query arguments
     -- Parse query arguments
     local query = {
     local query = {
Line 164: Line 164:
         offset = 0,
         offset = 0,
     }
     }
     for key, value in pairs(tpl_args) do  
     for key, value in pairs(tpl_args) do
         if string.sub(key, 0, 2) == 'q_' then
         if string.sub(key, 0, 2) == 'q_' then
             query[string.sub(key, 3)] = value
             query[string.sub(key, 3)] = value
         end
         end
     end
     end
   
 
     -- Query cargo table. If there are too many results then repeat,  
     -- Query cargo table. If there are too many results then repeat,
     -- offset, re-query and add the remaining results:
     -- offset, re-query and add the remaining results:
     local results = {}    
     local results = {}
     repeat
     repeat
         local result = cargo.query(
         local result = cargo.query(
Line 184: Line 184:
             results[#results + 1] = v
             results[#results + 1] = v
         end
         end
     until #result < query.limit  
     until #result < query.limit
   
 
     return results
     return results
end
end
Line 286: Line 286:
         fields = {string.format([[
         fields = {string.format([[
CONCAT(
CONCAT(
     CASE mods.required_level  
     CASE mods.required_level
         WHEN 32 THEN "%s"  
         WHEN 32 THEN "%s"
         WHEN 53 THEN "%s"  
         WHEN 53 THEN "%s"
         WHEN 66 THEN "%s"  
         WHEN 66 THEN "%s"
         WHEN 75 THEN "%s"  
         WHEN 75 THEN "%s"
     END
     END
)=labyrinth_text
)=labyrinth_text
Line 312: Line 312:
                 local texts = m_util.string.split(data['mods.stat_text'], '<br>')
                 local texts = m_util.string.split(data['mods.stat_text'], '<br>')
                 local out = {}
                 local out = {}
               
 
                 local valid
                 local valid
                 for _, v in ipairs(texts) do
                 for _, v in ipairs(texts) do
Line 322: Line 322:
                         end
                         end
                     end
                     end
                   
 
                     if valid then
                     if valid then
                         table.insert(out, v)
                         table.insert(out, v)
                     end
                     end
                 end
                 end
               
 
                 text = table.concat(out, '<br>')
                 text = table.concat(out, '<br>')
             else
             else
                 text = data['mods.stat_text']
                 text = data['mods.stat_text']
             end
             end
           
 
             tr
             tr
                 :tag('td')
                 :tag('td')
Line 397: Line 397:
     --[[
     --[[
     Creates a generic table for modifiers.
     Creates a generic table for modifiers.
   
 
     Examples
     Examples
     --------
     --------
Line 409: Line 409:
         enchantment=1,
         enchantment=1,
     }
     }
   
 
     ]]
     ]]
   
 
   
 
     tpl_args = getArgs(frame, {
     tpl_args = getArgs(frame, {
         parentFirst = true
         parentFirst = true
     })
     })
     frame = m_util.misc.get_frame(frame)
     frame = m_util.misc.get_frame(frame)
   
 
     -- default to enabled
     -- default to enabled
     tpl_args.name = tpl_args.name or true
     tpl_args.name = tpl_args.name or true
   
 
     for _, key in ipairs(mod_table.weights) do
     for _, key in ipairs(mod_table.weights) do
         tpl_args[key] = m_util.cast.boolean(tpl_args[key])
         tpl_args[key] = m_util.cast.boolean(tpl_args[key])
     end
     end
   
 
     if string.find(tpl_args.q_where, '%[%[') ~= nil then
     if string.find(tpl_args.q_where, '%[%[') ~= nil then
         error('SMW leftover in where clause')
         error('SMW leftover in where clause')
     end
     end
   
 
     local row_infos = {}
     local row_infos = {}
     for _, row_info in ipairs(mod_table.data) do
     for _, row_info in ipairs(mod_table.data) do
Line 436: Line 436:
         elseif type(row_info.arg) == 'string' and m_util.cast.boolean(tpl_args[row_info.arg]) then
         elseif type(row_info.arg) == 'string' and m_util.cast.boolean(tpl_args[row_info.arg]) then
             enabled = true
             enabled = true
         elseif type(row_info.arg) == 'table' then  
         elseif type(row_info.arg) == 'table' then
             for _, argument in ipairs(row_info.arg) do
             for _, argument in ipairs(row_info.arg) do
                 if m_util.cast.boolean(tpl_args[argument]) then
                 if m_util.cast.boolean(tpl_args[argument]) then
Line 444: Line 444:
             end
             end
         end
         end
       
 
         if enabled then
         if enabled then
             row_info.options = row_info.options or {}
             row_info.options = row_info.options or {}
Line 450: Line 450:
         end
         end
     end
     end
   
 
     -- sort the rows
     -- sort the rows
     table.sort(row_infos, function (a, b)
     table.sort(row_infos, function (a, b)
         return (a.order or 0) < (b.order or 0)
         return (a.order or 0) < (b.order or 0)
     end)
     end)
   
 
     -- Set tables
     -- Set tables
     local tables = 'mods'  
     local tables = 'mods'
     if tpl_args.q_tables then
     if tpl_args.q_tables then
         tables = tables .. ',' .. tpl_args.q_tables
         tables = tables .. ',' .. tpl_args.q_tables
     end
     end
   
 
   
 
     -- Set required fields
     -- Set required fields
     local fields = {
     local fields = {
Line 476: Line 476:
         end
         end
     end
     end
   
 
     -- Parse query arguments
     -- Parse query arguments
     local query = {
     local query = {
Line 482: Line 482:
         groupBy='mods._pageID',
         groupBy='mods._pageID',
     }
     }
     for key, value in pairs(tpl_args) do  
     for key, value in pairs(tpl_args) do
         if string.sub(key, 0, 2) == 'q_' then
         if string.sub(key, 0, 2) == 'q_' then
             query[string.sub(key, 3)] = value
             query[string.sub(key, 3)] = value
         end
         end
     end
     end
   
 
   
 
     fields = table.concat(fields, ',')
     fields = table.concat(fields, ',')
     if tpl_args.q_fields then
     if tpl_args.q_fields then
         fields = fields .. ' ,' .. tpl_args.q_fields
         fields = fields .. ' ,' .. tpl_args.q_fields
     end
     end
   
 
     local results = cargo.query(
     local results = cargo.query(
         tables,
         tables,
Line 499: Line 499:
         query
         query
     )
     )
   
 
     if #results == 0 then
     if #results == 0 then
         if tpl_args.default ~= nil then
         if tpl_args.default ~= nil then
Line 507: Line 507:
         end
         end
     end
     end
   
 
     -- this might be needed in other queries, currently not checking if it's actually needed
     -- this might be needed in other queries, currently not checking if it's actually needed
     -- because performance impact should be neglible
     -- because performance impact should be neglible
Line 515: Line 515:
     end
     end
     page_ids = table.concat(page_ids, ' OR ')
     page_ids = table.concat(page_ids, ' OR ')
   
 
     local weights = {}
     local weights = {}
     for _, key in ipairs(mod_table.weights) do
     for _, key in ipairs(mod_table.weights) do
Line 522: Line 522:
         end
         end
     end
     end
   
 
     local stats
     local stats
     if tpl_args.type == 'map' then
     if tpl_args.type == 'map' then
Line 529: Line 529:
             query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
             query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
         end
         end
       
 
         local stat_results = cargo.query(
         local stat_results = cargo.query(
             'mods,mod_stats',
             'mods,mod_stats',
Line 543: Line 543:
             error('Hit maximum cargo results')
             error('Hit maximum cargo results')
         end
         end
       
 
         stats = m_util.cargo.map_results_to_id{results=stat_results, field='mods._pageID'}
         stats = m_util.cargo.map_results_to_id{results=stat_results, field='mods._pageID'}
         -- In addition map stats to stat <-> min/max pairs
         -- In addition map stats to stat <-> min/max pairs
Line 554: Line 554:
         end
         end
     end
     end
   
 
     --
     --
     -- Display
     -- Display
     --
     --
   
 
     -- Preformance optimization
     -- Preformance optimization
     if tpl_args.q_fields then
     if tpl_args.q_fields then
Line 570: Line 570:
         tpl_args._extra_fields = {}
         tpl_args._extra_fields = {}
     end
     end
   
 
     local tbl = mw.html.create('table')
     local tbl = mw.html.create('table')
     tbl:attr('class', 'wikitable sortable modifier-table')
     tbl:attr('class', 'wikitable sortable modifier-table')
     -- Header
     -- Header
   
 
     local tr = tbl:tag('tr')
     local tr = tbl:tag('tr')
     for _, row_info in ipairs(row_infos) do
     for _, row_info in ipairs(row_infos) do
Line 583: Line 583:
                 :done()
                 :done()
     end
     end
   
 
     if tpl_args.type == 'map' then
     if tpl_args.type == 'map' then
         for stat_id, data in pairs(mod_table.stat_ids) do
         for stat_id, data in pairs(mod_table.stat_ids) do
Line 593: Line 593:
         end
         end
     end
     end
   
 
     for _, key in ipairs(mod_table.weights) do
     for _, key in ipairs(mod_table.weights) do
         if tpl_args[key] then
         if tpl_args[key] then
Line 601: Line 601:
         end
         end
     end
     end
   
 
     for _, field in ipairs(tpl_args._extra_fields) do
     for _, field in ipairs(tpl_args._extra_fields) do
         tr
         tr
Line 607: Line 607:
                 :wikitext(field)
                 :wikitext(field)
     end
     end
   
 
     -- Body
     -- Body
   
 
     for _, row in ipairs(results) do
     for _, row in ipairs(results) do
         tr = tbl:tag('tr')
         tr = tbl:tag('tr')
               
 
         for _, rowinfo in ipairs(row_infos) do
         for _, rowinfo in ipairs(row_infos) do
             -- this has been cast from a function in an earlier step
             -- this has been cast from a function in an earlier step
Line 633: Line 633:
             end
             end
         end
         end
       
 
         if tpl_args.type == 'map' then
         if tpl_args.type == 'map' then
             for stat_id, data in pairs(mod_table.stat_ids) do
             for stat_id, data in pairs(mod_table.stat_ids) do
Line 655: Line 655:
             end
             end
         end
         end
       
 
         for _, key in ipairs(mod_table.weights) do
         for _, key in ipairs(mod_table.weights) do
             if tpl_args[key] then
             if tpl_args[key] then
Line 667: Line 667:
                                 :wikitext(table.concat(weight_out, '<br>'))
                                 :wikitext(table.concat(weight_out, '<br>'))
                                 :done()
                                 :done()
                 else  
                 else
                     tr:wikitext(m_util.html.td.na())
                     tr:wikitext(m_util.html.td.na())
                 end
                 end
             end
             end
         end
         end
       
 
         for _, field in ipairs(tpl_args._extra_fields) do
         for _, field in ipairs(tpl_args._extra_fields) do
             if row[field] then
             if row[field] then
Line 683: Line 683:
         end
         end
     end
     end
   
 
     return tostring(tbl)
     return tostring(tbl)
end
end
Line 698: Line 698:
     --[[
     --[[
     Finds and links to a modifier in formatted form.
     Finds and links to a modifier in formatted form.
   
 
     To do list  
     To do list
     ----------
     ----------
     * Standardize hoverbox so it can be used in multiple places easily  
     * Standardize hoverbox so it can be used in multiple places easily
     and make it easy to add rows of data.  
     and make it easy to add rows of data.
   
 
     Examples
     Examples
     --------
     --------
Line 711: Line 711:
     = p.modifier_link{"StrDexMaster%"}
     = p.modifier_link{"StrDexMaster%"}
     = p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'}
     = p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'}
   
 
     ]]
     ]]


Line 717: Line 717:
     local tpl_args = getArgs(frame, {parentFirst = true})
     local tpl_args = getArgs(frame, {parentFirst = true})
     local frame = m_util.misc.get_frame(frame)
     local frame = m_util.misc.get_frame(frame)
       
 
     -- Aliases:
     -- Aliases:
     tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or ''
     tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or ''
   
 
     -- Define query arguments:
     -- Define query arguments:
     local tables = {'mods', 'mod_stats', 'spawn_weights'}
     local tables = {'mods', 'mod_stats', 'spawn_weights'}
Line 727: Line 727:
         join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName',
         join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName',
         where = string.format(
         where = string.format(
             '(mods.name LIKE "%s" or mods.id LIKE "%s" or mods.stat_text LIKE "%s" or mods.stat_text_raw LIKE "%s") AND mod_stats.id LIKE "%%%s%%"',  
             '(mods.name LIKE "%s" or mods.id LIKE "%s" or mods.stat_text LIKE "%s" or mods.stat_text_raw LIKE "%s") AND mod_stats.id LIKE "%%%s%%"',
             tpl_args.modid,  
             tpl_args.modid,
             tpl_args.modid,
             tpl_args.modid,
             tpl_args.modid,
             tpl_args.modid,
             tpl_args.modid,
             tpl_args.modid,
             tpl_args.statid or '%'  
             tpl_args.statid or '%'
         ),
         ),
         -- groupBy = 'mods._pageID, mod_stats.id, spawn_weights.tag',
         -- groupBy = 'mods._pageID, mod_stats.id, spawn_weights.tag',
     }
     }
   
 
     -- Query cargo rows:
     -- Query cargo rows:
     local results = m_util.cargo.query(tables, fields, query, args)
     local results = m_util.cargo.query(tables, fields, query, args)
   
 
     -- Create own list for each cargo table and group by page name:
     -- Create own list for each cargo table and group by page name:
     tpl_args.tbl = {}
     tpl_args.tbl = {}
     for _,v in ipairs(tables) do  
     for _,v in ipairs(tables) do
         tpl_args.tbl[v] = {}
         tpl_args.tbl[v] = {}
     end
     end
   
 
     tpl_args.results_unique = {}
     tpl_args.results_unique = {}
     local hash = {}
     local hash = {}
     for _,v in ipairs(results) do
     for _,v in ipairs(results) do
         for ii, vv in pairs(tpl_args.tbl) do  
         for ii, vv in pairs(tpl_args.tbl) do
             if tpl_args.tbl[ii][v['mods._pageName']] == nil then  
             if tpl_args.tbl[ii][v['mods._pageName']] == nil then
                 tpl_args.tbl[ii][v['mods._pageName']] = {}
                 tpl_args.tbl[ii][v['mods._pageName']] = {}
             end
             end
             local n = #tpl_args.tbl[ii][v['mods._pageName']] or 0
             local n = #tpl_args.tbl[ii][v['mods._pageName']] or 0
             tpl_args.tbl[ii][v['mods._pageName']][n+1] = v
             tpl_args.tbl[ii][v['mods._pageName']][n+1] = v
           
 
             -- Get a sorted list that only has unique page names:
             -- Get a sorted list that only has unique page names:
             if hash[v['mods._pageName']] ~= true then
             if hash[v['mods._pageName']] ~= true then
Line 763: Line 763:
             end
             end
         end
         end
     end  
     end


     -- If no results are found then just create a empty list to avoid  
     -- If no results are found then just create a empty list to avoid
     -- index errors.
     -- index errors.
     if tpl_args.results_unique[1] == nil then
     if tpl_args.results_unique[1] == nil then
         tpl_args.results_unique[1] = {}
         tpl_args.results_unique[1] = {}
     end
     end
   
 
     -- Helpful error handling:
     -- Helpful error handling:
     local err_tbl = {
     local err_tbl = {
Line 790: Line 790:
             disp = {
             disp = {
                 string.gsub(
                 string.gsub(
                     i18n.errors.incorrect_modid,  
                     i18n.errors.incorrect_modid,
                     '%%s',
                     '%%s',
                     tpl_args.modid,
                     tpl_args.modid,
Line 799: Line 799:
         },
         },
     }
     }
     for _,v in ipairs(err_tbl) do  
     for _,v in ipairs(err_tbl) do
         if v.bool then
         if v.bool then
             local cats = {'Pages with modifier link errors'}
             local cats = {'Pages with modifier link errors'}
Line 807: Line 807:
         end
         end
     end
     end
   
 
     -- Display formats:
     -- Display formats:
     local display = {
     local display = {
Line 813: Line 813:
             display = function(tpl_args, frame)
             display = function(tpl_args, frame)
                 local name = m_util.html.poe_color(
                 local name = m_util.html.poe_color(
                     'mod',  
                     'mod',
                     string.format(
                     string.format(
                         '[[%s|%s]]',  
                         '[[%s|%s]]',
                         tpl_args.results_unique[1]['mods._pageName'],  
                         tpl_args.results_unique[1]['mods._pageName'],
                         tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                         tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                     )
                     )
                 )
                 )
               
 
                 local tooltip_table = {
                 local tooltip_table = {
                     m_util.html.poe_color(
                     m_util.html.poe_color(
Line 837: Line 837:
                         -- 'help',
                         -- 'help',
                       -- table.concat(
                       -- table.concat(
                             -- m_util.string.split(tpl_args.results_unique[1]['mods.tags'] or '', ','),  
                             -- m_util.string.split(tpl_args.results_unique[1]['mods.tags'] or '', ','),
                             -- ', '
                             -- ', '
                         -- )
                         -- )
Line 846: Line 846:
                     ),
                     ),
                 }
                 }
               
 
                 local tt_tbl_fltrd = {}
                 local tt_tbl_fltrd = {}
                 for _,v in ipairs(tooltip_table) do
                 for _,v in ipairs(tooltip_table) do
                     if v ~= nil and v ~= '' then  
                     if v ~= nil and v ~= '' then
                         tt_tbl_fltrd[#tt_tbl_fltrd+1] = v
                         tt_tbl_fltrd[#tt_tbl_fltrd+1] = v
                     end
                     end
                 end
                 end
                 local tooltip = table.concat(tt_tbl_fltrd, '<br>')
                 local tooltip = table.concat(tt_tbl_fltrd, '<br>')
               
 
                 return m_util.html.tooltip(name, tooltip, class)
                 return m_util.html.tooltip(name, tooltip, class)
             end,
             end,
Line 863: Line 863:
                     '%s - %s (%s)',
                     '%s - %s (%s)',
                     m_util.html.poe_color(
                     m_util.html.poe_color(
                         'mod',  
                         'mod',
                         string.format(
                         string.format(
                             '[[%s|%s]]',  
                             '[[%s|%s]]',
                             tpl_args.results_unique[1]['mods._pageName'],  
                             tpl_args.results_unique[1]['mods._pageName'],
                             tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                             tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                         )
                         )
                     ),
                     ),
                     m_util.html.poe_color(
                     m_util.html.poe_color(
                         'mod',  
                         'mod',
                         string.gsub(
                         string.gsub(
                             tpl_args.results_unique[1]['mods.stat_text'],  
                             tpl_args.results_unique[1]['mods.stat_text'],
                             '<br>',  
                             '<br>',
                             ', '
                             ', '
                         )
                         )
Line 889: Line 889:
             display = function(tpl_args, frame)
             display = function(tpl_args, frame)
                 return m_util.html.poe_color(
                 return m_util.html.poe_color(
                     'mod',  
                     'mod',
                     tpl_args.results_unique[1]['mods.stat_text']
                     tpl_args.results_unique[1]['mods.stat_text']
                 )
                 )
Line 897: Line 897:
             display = function(tpl_args, frame)
             display = function(tpl_args, frame)
                 local statid = {}
                 local statid = {}
                 for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do  
                 for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do
                     statid[#statid+1] = v['mod_stats.id']
                     statid[#statid+1] = v['mod_stats.id']
                     if tpl_args.statid == v['mod_stats.id'] then
                     if tpl_args.statid == v['mod_stats.id'] then
Line 903: Line 903:
                     end
                     end
                 end
                 end
               
 
                 if tpl_args.statid == nil then
                 if tpl_args.statid == nil then
                     return m_util.html.error(
                     return m_util.html.error(
                         {
                         {
                             msg = string.format(
                             msg = string.format(
                                 i18n.errors.undefined_statid,  
                                 i18n.errors.undefined_statid,
                                 table.concat(statid, ', ')
                                 table.concat(statid, ', ')
                             )
                             )
Line 919: Line 919:
             display = function(tpl_args, frame)
             display = function(tpl_args, frame)
                 local statid = {}
                 local statid = {}
                 for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do  
                 for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do
                     statid[#statid+1] = v['mod_stats.id']
                     statid[#statid+1] = v['mod_stats.id']
                     if tpl_args.statid == v['mod_stats.id'] then
                     if tpl_args.statid == v['mod_stats.id'] then
Line 925: Line 925:
                     end
                     end
                 end
                 end
               
 
                 if tpl_args.statid == nil then
                 if tpl_args.statid == nil then
                     return m_util.html.error(
                     return m_util.html.error(
                         {
                         {
                             msg = string.format(
                             msg = string.format(
                                 i18n.errors.undefined_statid,  
                                 i18n.errors.undefined_statid,
                                 table.concat(statid, ', ')
                                 table.concat(statid, ', ')
                             )
                             )
Line 939: Line 939:
         },
         },
     }
     }
   
 
     return display[tpl_args.display or 'abbr'].display(tpl_args, frame)
     return display[tpl_args.display or 'abbr'].display(tpl_args, frame)
end
end
Line 952: Line 952:
function h.get_spawn_chance(frame)
function h.get_spawn_chance(frame)
     --[[
     --[[
     Calculates the spawn chance of a set of mods that all have a  
     Calculates the spawn chance of a set of mods that all have a
     spawn weight.
     spawn weight.
   
 
     ]]
     ]]
   
 
     -- Get template arguments:
     -- Get template arguments:
     local tpl_args = getArgs(frame, {parentFirst=true})
     local tpl_args = getArgs(frame, {parentFirst=true})
     local frame = m_util.misc.get_frame(frame)
     local frame = m_util.misc.get_frame(frame)
   
 
     -- Get the table:
     -- Get the table:
     local tbl = tpl_args['tbl']
     local tbl = tpl_args['tbl']
   
 
     -- Probabilities affecting the result besides the spawn weight:
     -- Probabilities affecting the result besides the spawn weight:
     local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1  
     local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1
   
 
     -- Total number of outcomes.
     -- Total number of outcomes.
     local N = 0
     local N = 0
Line 972: Line 972:
         N = N + tbl[i]['spawn_weights.weight']
         N = N + tbl[i]['spawn_weights.weight']
     end
     end
   
 
     for i,_ in ipairs(tbl) do
     for i,_ in ipairs(tbl) do
         -- Number of ways it can happen:
         -- Number of ways it can happen:
Line 979: Line 979:
         -- Truncated value:
         -- Truncated value:
         tbl[i]['spawn_weights.chance'] = string.format(
         tbl[i]['spawn_weights.chance'] = string.format(
             "%0.2f%%",  
             "%0.2f%%",
             n/N * chance_multiplier*100
             n/N * chance_multiplier*100
         )
         )
     end  
     end
   
 
     return tbl
     return tbl
end
end
   
 
function p.drop_down_table(frame)
function p.drop_down_table(frame)
     --[[
     --[[
     This function queries mods that can spawn on an item. It compares  
     This function queries mods that can spawn on an item. It compares
     the item tags and the spawn weight tags. If there's a match and  
     the item tags and the spawn weight tags. If there's a match and
     the spawn weight is larger than zero, then that mod is added to a  
     the spawn weight is larger than zero, then that mod is added to a
     drop down list.
     drop down list.
   
 
     To Do:
     To Do:
     * Add support to:
     * Add support to:
         * Forsaken masters
         * Forsaken masters
         * Bestiary
         * Bestiary
     * Add a proper expand/collapse toggle for the entire header row so  
     * Add a proper expand/collapse toggle for the entire header row so
         it reacts together with mw-collapsible.  
         it reacts together with mw-collapsible.
     * Show Mod group in a better way perhaps:  
     * Show Mod group in a better way perhaps:
         Mod group [Collapsible, default=Expanded]
         Mod group [Collapsible, default=Expanded]
             # to Damage [Collapsible, default=Collapsed]
             # to Damage [Collapsible, default=Collapsed]
                 3 to Damage
                 3 to Damage
                 5 to Damage
                 5 to Damage
     * Add a where condition that somehow filters out mods that obviously  
     * Add a where condition that somehow filters out mods that obviously
       wont match with the item. spawn_weights.weight>0 isn't enough due  
       wont match with the item. spawn_weights.weight>0 isn't enough due
       to possible edge cases.
       to possible edge cases.
       
 
   
 
     Examples:  
     Examples:
     Weapons
     Weapons
     = p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
     = p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
Line 1,017: Line 1,017:
     Accessories
     Accessories
     = p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
     = p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
   
 
     Jewels
     Jewels
     = p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
     = p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
   
 
     Armour
     Armour
     = p.drop_down_table{item = 'Plate Vest', header = 'Body armours'}
     = p.drop_down_table{item = 'Plate Vest', header = 'Body armours'}
   
 
     Boots
     Boots
     = p.drop_down_table{item = 'Iron Greaves', header = 'Boots'}
     = p.drop_down_table{item = 'Iron Greaves', header = 'Boots'}
   
 
     = p.drop_down_table{
     = p.drop_down_table{
         item = 'Fishing Rod',  
         item = 'Fishing Rod',
         header = 'FISH PLEASE',  
         header = 'FISH PLEASE',
         item_tags = 'fishing_rod',  
         item_tags = 'fishing_rod',
         extra_fields = 'mods.tags'
         extra_fields = 'mods.tags'
     }
     }
   
 
     = p.drop_down_table{
     = p.drop_down_table{
         item = 'Fishing Rod',
         item = 'Fishing Rod',
Line 1,039: Line 1,039:
         extra_item_tags = 'fishing_rod'
         extra_item_tags = 'fishing_rod'
     }
     }
   
 
     = p.drop_down_table{
     = p.drop_down_table{
         item = 'Vaal Blade',
         item = 'Vaal Blade',
     }
     }
       
 
     ]]
     ]]


Line 1,049: Line 1,049:
     local tpl_args = getArgs(frame, {parentFirst=true})
     local tpl_args = getArgs(frame, {parentFirst=true})
     local frame = m_util.misc.get_frame(frame)
     local frame = m_util.misc.get_frame(frame)
   
 
     -- Format the cargo query:
     -- Format the cargo query:
     local where
     local where
Line 1,055: Line 1,055:
             {tpl_args.page, 'items._pageName = "%s"'},
             {tpl_args.page, 'items._pageName = "%s"'},
             {tpl_args.item, 'items.name = "%s"'},
             {tpl_args.item, 'items.name = "%s"'},
     }) do  
     }) do
         if v[1] ~= nil then
         if v[1] ~= nil then
             where = string.format(v[2], v[1])
             where = string.format(v[2], v[1])
Line 1,061: Line 1,061:
         end
         end
     end
     end
   
 
     local item_info = m_cargo.query(
     local item_info = m_cargo.query(
         {'items'},
         {'items'},
Line 1,071: Line 1,071:
         }
         }
     )[1]
     )[1]
   
 
     -- Set the item class as key and the corresponding mod domain as  
     -- Set the item class as key and the corresponding mod domain as
     -- value:
     -- value:
     local class_to_domain = {
     local class_to_domain = {
Line 1,085: Line 1,085:
         ['AbyssJewel']=13,
         ['AbyssJewel']=13,
     }
     }
     -- Get the domain, if it's not defined in the table assume it's  
     -- Get the domain, if it's not defined in the table assume it's
     -- in the item domain.
     -- in the item domain.
     item_info['items.domain'] = class_to_domain[item_info['items.class_id']] or 1
     item_info['items.domain'] = class_to_domain[item_info['items.class_id']] or 1
   
 
     -- Convert the mod domain number to understandable text:
     -- Convert the mod domain number to understandable text:
     item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower']
     item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower']
   
 
     -- For some reason cargo queried item tags, are not comma-space  
     -- For some reason cargo queried item tags, are not comma-space
     -- separated any more.
     -- separated any more.
     if tpl_args.item_tags ~= nil then
     if tpl_args.item_tags ~= nil then
Line 1,101: Line 1,101:
     if tpl_args.extra_item_tags then
     if tpl_args.extra_item_tags then
         local extra_item_tags = m_util.string.split(tpl_args.extra_item_tags, ', ')
         local extra_item_tags = m_util.string.split(tpl_args.extra_item_tags, ', ')
         for _,v in ipairs(extra_item_tags) do  
         for _,v in ipairs(extra_item_tags) do
             tpl_args.item_tags[#tpl_args.item_tags+1] = v
             tpl_args.item_tags[#tpl_args.item_tags+1] = v
         end
         end
     end
     end
   
 
     -- Create drop down lists in these sections and query in these
     -- Create drop down lists in these sections and query in these
     -- generation types.
     -- generation types.
   
 
     local elder_tag = m_game.constants.item.classes[item_info['items.class_id']]['elder_tag']
     local elder_tag = m_game.constants.item.classes[item_info['items.class_id']]['elder_tag']
     local shaper_tag = m_game.constants.item.classes[item_info['items.class_id']]['shaper_tag']
     local shaper_tag = m_game.constants.item.classes[item_info['items.class_id']]['shaper_tag']
Line 1,115: Line 1,115:
     local basilisk_tag = m_game.constants.item.classes[item_info['items.class_id']]['basilisk_tag']
     local basilisk_tag = m_game.constants.item.classes[item_info['items.class_id']]['basilisk_tag']
     local adjudicator_tag = m_game.constants.item.classes[item_info['items.class_id']]['adjudicator_tag']
     local adjudicator_tag = m_game.constants.item.classes[item_info['items.class_id']]['adjudicator_tag']
   
 
     local section = {}
     local section = {}
     section = {
     section = {
Line 1,122: Line 1,122:
             where = function(tpl_args, frame, value)
             where = function(tpl_args, frame, value)
                 local where = {}
                 local where = {}
                 for _, item_tag in ipairs(tpl_args.item_tags) do  
                 for _, item_tag in ipairs(tpl_args.item_tags) do
                     where[#where+1] = string.format(
                     where[#where+1] = string.format(
                         [[
                         [[
                             (spawn_weights.tag="%s"  
                             (spawn_weights.tag="%s"
                             AND mods.generation_type=%s  
                             AND mods.generation_type=%s
                             AND mods.domain=%s  
                             AND mods.domain=%s
                             AND mods.stat_text IS NOT NULL)
                             AND mods.stat_text IS NOT NULL)
                         ]],
                         ]],
Line 1,135: Line 1,135:
                     )
                     )
                 end
                 end
               
 
                 return table.concat(where, ' OR ')  
                 return table.concat(where, ' OR ')
             end,
             end,
         },
         },
Line 1,143: Line 1,143:
             where = function(tpl_args, frame, value)
             where = function(tpl_args, frame, value)
                 local where = {}
                 local where = {}
                 for _, item_tag in ipairs(tpl_args.item_tags) do  
                 for _, item_tag in ipairs(tpl_args.item_tags) do
                     where[#where+1] = string.format(
                     where[#where+1] = string.format(
                         [[
                         [[
                             (spawn_weights.tag="%s"  
                             (spawn_weights.tag="%s"
                             AND mods.generation_type=%s  
                             AND mods.generation_type=%s
                             AND mods.domain=%s  
                             AND mods.domain=%s
                             AND mods.stat_text IS NOT NULL)
                             AND mods.stat_text IS NOT NULL)
                         ]],
                         ]],
Line 1,156: Line 1,156:
                     )
                     )
                 end
                 end
               
 
                 return table.concat(where, ' OR ')  
                 return table.concat(where, ' OR ')
             end,
             end,
         },
         },
Line 1,166: Line 1,166:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,183: Line 1,183:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,200: Line 1,200:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,217: Line 1,217:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,232: Line 1,232:
             where = function(tpl_args, frame, value)
             where = function(tpl_args, frame, value)
                 local where = {}
                 local where = {}
                 for _, item_tag in ipairs(tpl_args.item_tags) do  
                 for _, item_tag in ipairs(tpl_args.item_tags) do
                     where[#where+1] = string.format(
                     where[#where+1] = string.format(
                         [[
                         [[
                             (spawn_weights.tag="%s"  
                             (spawn_weights.tag="%s"
                             AND mods.generation_type=%s  
                             AND mods.generation_type=%s
                             AND mods.domain=%s  
                             AND mods.domain=%s
                             AND mods.stat_text IS NOT NULL)
                             AND mods.stat_text IS NOT NULL)
                         ]],  
                         ]],
                         item_tag,
                         item_tag,
                         1,
                         1,
Line 1,245: Line 1,245:
                     )
                     )
                 end
                 end
               
 
                 return table.concat(where, ' OR ')  
                 return table.concat(where, ' OR ')
             end,
             end,
         },
         },
Line 1,253: Line 1,253:
             where = function(tpl_args, frame, value)
             where = function(tpl_args, frame, value)
                 local where = {}
                 local where = {}
                 for _, item_tag in ipairs(tpl_args.item_tags) do  
                 for _, item_tag in ipairs(tpl_args.item_tags) do
                     where[#where+1] = string.format(
                     where[#where+1] = string.format(
                         [[
                         [[
                             (spawn_weights.tag="%s"  
                             (spawn_weights.tag="%s"
                             AND mods.generation_type=%s  
                             AND mods.generation_type=%s
                             AND mods.domain=%s  
                             AND mods.domain=%s
                             AND mods.stat_text IS NOT NULL)
                             AND mods.stat_text IS NOT NULL)
                         ]],
                         ]],
Line 1,266: Line 1,266:
                     )
                     )
                 end
                 end
               
 
                 return table.concat(where, ' OR ')  
                 return table.concat(where, ' OR ')
             end,
             end,
         },
         },
Line 1,276: Line 1,276:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,293: Line 1,293:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,310: Line 1,310:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,327: Line 1,327:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,344: Line 1,344:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,361: Line 1,361:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,378: Line 1,378:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,395: Line 1,395:
                 return string.format(
                 return string.format(
                     [[
                     [[
                         (spawn_weights.tag="%s"  
                         (spawn_weights.tag="%s"
                         AND mods.generation_type=%s  
                         AND mods.generation_type=%s
                         AND mods.domain=%s  
                         AND mods.domain=%s
                         AND mods.stat_text IS NOT NULL)
                         AND mods.stat_text IS NOT NULL)
                     ]],
                     ]],
Line 1,410: Line 1,410:
             where = function(tpl_args, frame, value)
             where = function(tpl_args, frame, value)
                 local where = {}
                 local where = {}
                 for _, item_tag in ipairs(tpl_args.item_tags) do  
                 for _, item_tag in ipairs(tpl_args.item_tags) do
                     where[#where+1] = string.format(
                     where[#where+1] = string.format(
                         [[
                         [[
                             (spawn_weights.tag="%s"  
                             (spawn_weights.tag="%s"
                             AND mods.generation_type=%s  
                             AND mods.generation_type=%s
                             AND mods.domain=%s  
                             AND mods.domain=%s
                             AND mods.stat_text IS NOT NULL)
                             AND mods.stat_text IS NOT NULL)
                         ]],
                         ]],
Line 1,423: Line 1,423:
                     )
                     )
                 end
                 end
               
 
                 return table.concat(where, ' OR ')  
                 return table.concat(where, ' OR ')
             end,
             end,
             chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.  
             chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
             is_implicit = true,
             is_implicit = true,
         },
         },
Line 1,433: Line 1,433:
             where = function(tpl_args, frame, value)
             where = function(tpl_args, frame, value)
                 local where = {}
                 local where = {}
                 for _, item_tag in ipairs(tpl_args.item_tags) do  
                 for _, item_tag in ipairs(tpl_args.item_tags) do
                     where[#where+1] = string.format(
                     where[#where+1] = string.format(
                         [[
                         [[
                             (spawn_weights.tag="%s"  
                             (spawn_weights.tag="%s"
                             AND mods.generation_type=%s  
                             AND mods.generation_type=%s
                             AND mods.domain=%s  
                             AND mods.domain=%s
                             AND mods.stat_text IS NOT NULL)
                             AND mods.stat_text IS NOT NULL)
                         ]],
                         ]],
Line 1,446: Line 1,446:
                     )
                     )
                 end
                 end
               
 
                 return table.concat(where, ' OR ')  
                 return table.concat(where, ' OR ')
             end,
             end,
             is_implicit = true,
             is_implicit = true,
         },
         },
     }
     }
   
 
     -- Save the original tag format:
     -- Save the original tag format:
     local item_tags_orig = {}
     local item_tags_orig = {}
Line 1,458: Line 1,458:
         item_tags_orig[i] = v
         item_tags_orig[i] = v
     end
     end
   
 
     local item_mods = {}
     local item_mods = {}
     local mod_group_counter = {}
     local mod_group_counter = {}
     mod_group_counter['all'] = {}
     mod_group_counter['all'] = {}
     local extra_fieldss = {}
     local extra_fieldss = {}
     local table_index_base = -1
     local table_index_base = -1
     for _, sctn in ipairs(section) do
     for _, sctn in ipairs(section) do
         item_mods[sctn['header']] = {}
         item_mods[sctn['header']] = {}
       
 
         -- Preallocate the mod group counter, implicit and explicit mods
         -- Preallocate the mod group counter, implicit and explicit mods
         -- are counted separetely because they can spawn together:
         -- are counted separetely because they can spawn together:
         mod_group_counter[sctn['header']] = {}
         mod_group_counter[sctn['header']] = {}
         local adj = 'explicit'
         local adj = 'explicit'
         if sctn['is_implicit'] then  
         if sctn['is_implicit'] then
             adj = 'implicit'
             adj = 'implicit'
         end
         end
Line 1,479: Line 1,479:
             end
             end
         end
         end
       
 
         local continue = true
         local continue = true
         local current_tags
         local current_tags
Line 1,496: Line 1,496:
             end
             end
         end
         end
       
 
         if continue then          
         if continue then
             -- Cargo preparation:
             -- Cargo preparation:
             tpl_args.q_tables = 'mods, spawn_weights, mod_stats'
             tpl_args.q_tables = 'mods, spawn_weights, mod_stats'
Line 1,505: Line 1,505:
             tpl_args.q_groupBy = 'mods._pageName, spawn_weights.tag, spawn_weights.weight'
             tpl_args.q_groupBy = 'mods._pageName, spawn_weights.tag, spawn_weights.weight'
             tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods.required_level, mods._pageName, spawn_weights.ordinal'
             tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods.required_level, mods._pageName, spawn_weights.ordinal'
           
 
             local extra_fields = {}
             local extra_fields = {}
             if tpl_args.extra_fields ~= nil then
             if tpl_args.extra_fields ~= nil then
                 extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
                 extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
                 tpl_args.q_fields = string.format(
                 tpl_args.q_fields = string.format(
                     '%s, %s',  
                     '%s, %s',
                     tpl_args.q_fields,  
                     tpl_args.q_fields,
                     table.concat(extra_fields, ', ')
                     table.concat(extra_fields, ', ')
                 )
                 )
             end
             end
           
 
             -- Query mods:
             -- Query mods and map the results to the pagename:
             results = h.cargo_query(tpl_args)
             local results = m_cargo.map_results_to_id{
           
                results=h.cargo_query(tpl_args),
            -- Create own list for spawn weights and group by page name:
                 field='mods._pageName',
            local spawn_weights = {}
                 keep_id_field=true,
            local results_unique = {}
                 append_id_field=true,
            local hash = {}
             }
            for _,v in ipairs(results) do
 
                 if spawn_weights[v['mods._pageName']] == nil then
             if #results > 0 then
                    spawn_weights[v['mods._pageName']] = {}
                end
                 local n = #spawn_weights[v['mods._pageName']] or 0
                spawn_weights[v['mods._pageName']][n+1] = v
               
                -- Get a sorted list that only has unique page names:
                 if hash[v['mods._pageName']] ~= true then
                    results_unique[#results_unique+1] = v
                    hash[v['mods._pageName']] = true
                end
             end
           
             if #results_unique > 0 then  
                 -- Loop through all found modifiers:
                 -- Loop through all found modifiers:
                 local last
                 local last
                 for _, v in ipairs(results_unique) do  
                 for _, id in ipairs(results) do
                    local pagename = v['spawn_weights._pageName']
                   
                     if sctn['header'] == i18n.drop_down_table.elder_prefix then
                     if sctn['header'] == i18n.drop_down_table.elder_prefix then
                     --  error(mw.dumpObject(spawn_weights))
                     --  error(mw.dumpObject(results))
                     end
                     end
                     -- Loop through all the modifier tags until they match  
                     -- Loop through all the modifier tags until they match
                     -- the item tags:
                     -- the item tags:
                     local j = 0
                     local j = 0
                     local tag_match_stop
                     local tag_match_stop
                     repeat  
                     repeat
                         j = j+1
                         j = j+1
                         local mod_tag = spawn_weights[pagename][j]['spawn_weights.tag']
                         local mod_tag = results[id][j]['spawn_weights.tag']
                         local mod_tag_weight = tonumber(
                         local mod_tag_weight = tonumber(
                             spawn_weights[pagename][j]['spawn_weights.weight']
                             results[id][j]['spawn_weights.weight']
                         )
                         )


                         -- Loop through the item tags until it matches the  
                         -- Loop through the item tags until it matches the
                         -- spawn weight tag and the mod tag has a value larger than  
                         -- spawn weight tag and the mod tag has a value larger than
                         -- zero:
                         -- zero:
                         local y = 0
                         local y = 0
                         local tag_match_add = false
                         local tag_match_add = false
                         repeat    
                         repeat
                             y = y+1
                             y = y+1
                             tag_match_stop = ((mod_tag == current_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (spawn_weights[pagename][j] == nil)
                             tag_match_stop = ((mod_tag == current_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (results[id][j] == nil)
                             tag_match_add  =  (mod_tag == current_tags[y]) and ((mod_tag_weight or -1) > 0)
                             tag_match_add  =  (mod_tag == current_tags[y]) and ((mod_tag_weight or -1) > 0)
                         until tag_match_stop or y == #current_tags
                         until tag_match_stop or y == #current_tags
                       
 
                         -- If there's a match then save that mod and other  
                         -- If there's a match then save that mod and other
                         -- interesting information:
                         -- interesting information:
                         if tag_match_add then  
                         if tag_match_add then
                           
 
                             -- Assume that the mod is global then go through  
                             -- Assume that the mod is global then go through
                             -- all the stat ids and check if any of the  
                             -- all the stat ids and check if any of the
                             -- stats are local:
                             -- stats are local:
                             local mod_scope = 'Global'  
                             local mod_scope = 'Global'
                             for _, vv in ipairs(spawn_weights[pagename]) do  
                             for _, vv in ipairs(results[id]) do
                                 if vv['mod_stats.id']:find('.*local.*') ~= nil then  
                                 if vv['mod_stats.id']:find('.*local.*') ~= nil then
                                     mod_scope = 'Local'
                                     mod_scope = 'Local'
                                 end
                                 end
                             end  
                             end
                           
 
                             -- Save the matching modifier tag:
                             -- Save the matching modifier tag:
                             local a = #item_mods[sctn['header']]
                             local a = #item_mods[sctn['header']]
                             item_mods[sctn['header']][a+1] = spawn_weights[pagename][j]
                             item_mods[sctn['header']][a+1] = results[id][j]
                           
 
                             -- Save other interesting fields:
                             -- Save other interesting fields:
                             item_mods[sctn['header']][a+1]['mods.scope'] = mod_scope    
                             item_mods[sctn['header']][a+1]['mods.scope'] = mod_scope
                             item_mods[sctn['header']][a+1]['spawn_weight.idx_match'] = j                      
                             item_mods[sctn['header']][a+1]['spawn_weight.idx_match'] = j
                             item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add    
                             item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add
                             item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop
                             item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop
                           
 
                             -- Count the mod groups:
                             -- Count the mod groups:
                             local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group'
                             local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group'
Line 1,598: Line 1,583:
                                     mod_group_counter[header][adj][group] = {}
                                     mod_group_counter[header][adj][group] = {}
                                 end
                                 end
                                 local tp = spawn_weights[pagename][j]['mods.mod_type']
                                 local tp = results[id][j]['mods.mod_type']
                                 local bef = mod_group_counter[header][adj][group][tp] or 0
                                 local bef = mod_group_counter[header][adj][group][tp] or 0
                                 mod_group_counter[header][adj][group][tp] = 1 + bef
                                 mod_group_counter[header][adj][group][tp] = 1 + bef
                             end
                             end
                         end
                         end
                     until tag_match_stop  
                     until tag_match_stop
                 end
                 end
               
 
                 -- If the user wants to see the spawn chance then do the  
                 -- If the user wants to see the spawn chance then do the
                 -- calculations and save that result as well:
                 -- calculations and save that result as well:
                 if tpl_args.spawn_chance ~= nil then  
                 if tpl_args.spawn_chance ~= nil then
                     extra_fields[#extra_fields+1] = 'spawn_weights.chance'
                     extra_fields[#extra_fields+1] = 'spawn_weights.chance'
                     item_mods[sctn['header']] = h.get_spawn_chance{
                     item_mods[sctn['header']] = h.get_spawn_chance{
                         tbl = item_mods[sctn['header']],  
                         tbl = item_mods[sctn['header']],
                         chance_multiplier = sctn['chance_multiplier']
                         chance_multiplier = sctn['chance_multiplier']
                     }
                     }
                 end
                 end
               
 
                 extra_fieldss[sctn['header']] = extra_fields
                 extra_fieldss[sctn['header']] = extra_fields
             end
             end
         end
         end
     end
     end
   
 
   
 
     --  
     --
     -- Display the item mods
     -- Display the item mods
     --  
     --
   
 
     -- Introductory text:
     -- Introductory text:
     local out = {}
     local out = {}
     out[#out+1] = string.format(
     out[#out+1] = string.format(
         '==%s== \n',  
         '==%s== \n',
         tpl_args['header'] or table.concat(tpl_args.item_tags, ', ')
         tpl_args['header'] or table.concat(tpl_args.item_tags, ', ')
     )
     )
Line 1,638: Line 1,623:
     )
     )
     out[#out+1] = expand_button
     out[#out+1] = expand_button
     out[#out+1] = string.format('%s %s.<br><br><br>',  
     out[#out+1] = string.format('%s %s.<br><br><br>',
         i18n.drop_down_table.table_intro,  
         i18n.drop_down_table.table_intro,
         f_item_link{
         f_item_link{
             page=item_info['items._pageName'],  
             page=item_info['items._pageName'],
             name=item_info['items.name'],
             name=item_info['items.name'],
             inventory_icon=item_info['items.inventory_icon'] or '',  
             inventory_icon=item_info['items.inventory_icon'] or '',
             html=item_info['items.html'] or '',  
             html=item_info['items.html'] or '',
             skip_query=true
             skip_query=true
         }
         }
     )
     )
   
 
     -- Loop through the sections:
     -- Loop through the sections:
     for _, sctn in ipairs(section) do
     for _, sctn in ipairs(section) do
Line 1,655: Line 1,640:
         if sctn['is_implicit'] then
         if sctn['is_implicit'] then
             adj = 'implicit'
             adj = 'implicit'
         end  
         end
       
 
         -- Create html container:
         -- Create html container:
         local container = mw.html.create('div')
         local container = mw.html.create('div')
             :attr('style', 'vertical-align:top; display:inline-block;')
             :attr('style', 'vertical-align:top; display:inline-block;')
       
 
         -- Create the drop down table with <table></table>:
         -- Create the drop down table with <table></table>:
         local headers = container  
         local headers = container
         if #item_mods[sctn['header']] > 0 then  
         if #item_mods[sctn['header']] > 0 then
             headers
             headers
                 :tag('h3')
                 :tag('h3')
Line 1,670: Line 1,655:
                 :done()
                 :done()
         end
         end
               
 
         local total_mod_groups = 0
         local total_mod_groups = 0
         for _ in pairs(mod_group_counter[sctn['header']][adj]) do
         for _ in pairs(mod_group_counter[sctn['header']][adj]) do
             total_mod_groups = 1+total_mod_groups
             total_mod_groups = 1+total_mod_groups
         end
         end
       
 
         -- Loop through and add all matching mods to the <table>.  
         -- Loop through and add all matching mods to the <table>.
         local tbl, last_group, last_type
         local tbl, last_group, last_type
         for _, rows in ipairs(item_mods[sctn['header']]) do  
         for _, rows in ipairs(item_mods[sctn['header']]) do
       
 
             -- If the last mod group is different to the current  
             -- If the last mod group is different to the current
             -- mod group then assume the mod isn't related and start  
             -- mod group then assume the mod isn't related and start
             -- a new drop down list, if there's only one mod group  
             -- a new drop down list, if there's only one mod group
             -- then use mod type instead:
             -- then use mod type instead:
             if rows['mods.mod_group'] ~= last_group or (total_mod_groups == 1 and rows['mods.mod_type'] ~= last_type) then
             if rows['mods.mod_group'] ~= last_group or (total_mod_groups == 1 and rows['mods.mod_type'] ~= last_type) then
                 -- Check through all the mods and see if there are  
                 -- Check through all the mods and see if there are
                 -- multiple mod types within the same mod group:
                 -- multiple mod types within the same mod group:
                 local count = {}
                 local count = {}
                 for _, n in ipairs(item_mods[sctn['header']]) do  
                 for _, n in ipairs(item_mods[sctn['header']]) do
                   
 
                     -- If the mod has the same mod group, then add  
                     -- If the mod has the same mod group, then add
                     -- the mod type to the counter. Only unique mod  
                     -- the mod type to the counter. Only unique mod
                     -- types matter so the number is just a dummy  
                     -- types matter so the number is just a dummy
                     -- value:
                     -- value:
                     if n['mods.mod_group'] == rows['mods.mod_group'] then
                     if n['mods.mod_group'] == rows['mods.mod_group'] then
                         count[n['mods.mod_type']] = 1  
                         count[n['mods.mod_type']] = 1
                     end
                     end
                 end
                 end
               
 
                 -- Calculate how many unique mod types with the  
                 -- Calculate how many unique mod types with the
                 -- same mod group there are for all explicit or implicit  
                 -- same mod group there are for all explicit or implicit
                 -- sections since a mod with the same mod group can't  
                 -- sections since a mod with the same mod group can't
                 -- spawn. Doesn't matter if it's prefix or suffix.  
                 -- spawn. Doesn't matter if it's prefix or suffix.
                 local number_of_mod_types = 0
                 local number_of_mod_types = 0
                 for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do  
                 for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do
                     number_of_mod_types = 1 + number_of_mod_types
                     number_of_mod_types = 1 + number_of_mod_types
                 end
                 end
             
 
                 -- If there are multiple unique mod types with the  
                 -- If there are multiple unique mod types with the
                 -- same mod group then change the style of the drop  
                 -- same mod group then change the style of the drop
                 -- down list to indicate it, if there's only one  
                 -- down list to indicate it, if there's only one
                 -- mod group in the generation type then ignore it:
                 -- mod group in the generation type then ignore it:
                 local table_index_mod_group
                 local table_index_mod_group
                 if number_of_mod_types > 1 and total_mod_groups > 1 then
                 if number_of_mod_types > 1 and total_mod_groups > 1 then
                     table_index_mod_group = table.concat(
                     table_index_mod_group = table.concat(
                         {string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},  
                         {string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},
                         ''
                         ''
                     )
                     )
                   
 
                     tbl_caption = string.format(
                     tbl_caption = string.format(
                         '%s',  
                         '%s',
                         m_util.html.poe_color(
                         m_util.html.poe_color(
                             'stat',  
                             'stat',
                             string.format(
                             string.format(
                                 '%s %s',  
                                 '%s %s',
                                 i18n.drop_down_table.mod_group,  
                                 i18n.drop_down_table.mod_group,
                                 rows['mods.mod_group']
                                 rows['mods.mod_group']
                             )
                             )
Line 1,731: Line 1,716:
                     )
                     )


                 else  
                 else
                     tbl_caption = string.format(
                     tbl_caption = string.format(
                         '%s (%s)',  
                         '%s (%s)',
                         m_util.html.poe_color(
                         m_util.html.poe_color(
                             'mod',  
                             'mod',
                             h.header(rows['mods.stat_text_raw'])
                             h.header(rows['mods.stat_text_raw'])
                         ) or '',  
                         ) or '',
                         rows['mods.scope']
                         rows['mods.scope']
                     )
                     )
                 end
                 end
                               
 
                 -- Create a table index for handling the collapsible:
                 -- Create a table index for handling the collapsible:
                 table_index_base = table_index_base+1
                 table_index_base = table_index_base+1
                 if table_index_mod_group ~= nil then  
                 if table_index_mod_group ~= nil then
                     table_index = table_index_mod_group
                     table_index = table_index_mod_group
                 else
                 else
                     table_index = table_index_base
                     table_index = table_index_base
                 end
                 end
               
 
                 -- Add class and style to the <table>:
                 -- Add class and style to the <table>:
                 tbl = container:tag('table')
                 tbl = container:tag('table')
                 tbl
                 tbl
                     :attr('class', 'mw-collapsible mw-collapsed')
                     :attr('class', 'mw-collapsible mw-collapsed')
                     :attr('style',  
                     :attr('style',
                         'text-align:left; line-height:1.60em; width:810px;'
                         'text-align:left; line-height:1.60em; width:810px;'
                     )
                     )
                     :tag('th')
                     :tag('th')
                         :attr('class',  
                         :attr('class',
                             string.format('mw-customtoggle-%s', table_index)
                             string.format('mw-customtoggle-%s', table_index)
                         )
                         )
                         :attr('style',  
                         :attr('style',
                             'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
                             'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
                         )
                         )
Line 1,767: Line 1,752:
                         :wikitext(tbl_caption)
                         :wikitext(tbl_caption)
                         :done()
                         :done()
                     :done()  
                     :done()
             end
             end
           
 
             -- If the mod has no name then use the mod type:
             -- If the mod has no name then use the mod type:
             local mod_name = rows['mods.name']
             local mod_name = rows['mods.name']
             if  mod_name == '' or mod_name == nil then  
             if  mod_name == '' or mod_name == nil then
                 mod_name = rows['mods.mod_type']
                 mod_name = rows['mods.mod_type']
             end
             end
           
 
             -- Check if there are any extra properties to show in  
             -- Check if there are any extra properties to show in
             -- the drop down list and then add a cell for that,  
             -- the drop down list and then add a cell for that,
             -- add this node at the end of the table row:
             -- add this node at the end of the table row:
             local td = mw.html.create('td')
             local td = mw.html.create('td')
             if extra_fields ~= nil then  
             if extra_fields ~= nil then
                 for _, extra_field in ipairs(extra_fields) do  
                 for _, extra_field in ipairs(extra_fields) do
                     td
                     td
                         :attr('width', '*')
                         :attr('width', '*')
                         :wikitext(string.format(
                         :wikitext(string.format(
                             '%s:&nbsp;%s ',  
                             '%s:&nbsp;%s ',
                             extra_field,  
                             extra_field,
                             rows[extra_field]
                             rows[extra_field]
                             )
                             )
Line 1,793: Line 1,778:
                 end
                 end
             end
             end
           
 
             -- Style mods.tags:
             -- Style mods.tags:
             local mods_tags = table.concat(m_util.string.split(rows['mods.tags'], ','), ', ')
             local mods_tags = table.concat(m_util.string.split(rows['mods.tags'], ','), ', ')
             if mods_tags ~= '' then
             if mods_tags ~= '' then
                 mods_tags = m_util.html.tooltip('*', mods_tags, class)
                 mods_tags = m_util.html.tooltip('*', mods_tags, class)
             end  
             end
           
 
             -- Add a table row with the interesting properties that  
             -- Add a table row with the interesting properties that
             -- modifier has:
             -- modifier has:
             tbl
             tbl
Line 1,806: Line 1,791:
                     :attr('class', 'mw-collapsible mw-collapsed')
                     :attr('class', 'mw-collapsible mw-collapsed')
                     :attr(
                     :attr(
                         'id',  
                         'id',
                         string.format('mw-customcollapsible-%s', table_index)
                         string.format('mw-customcollapsible-%s', table_index)
                     )
                     )
Line 1,813: Line 1,798:
                         :wikitext(
                         :wikitext(
                             string.format(
                             string.format(
                                 '&nbsp;&nbsp;&nbsp;[[%s|%s]]',  
                                 '&nbsp;&nbsp;&nbsp;[[%s|%s]]',
                                 rows['mods._pageName'],  
                                 rows['mods._pageName'],
                                 mod_name:gsub('%s', '&nbsp;')
                                 mod_name:gsub('%s', '&nbsp;')
                             )
                             )
Line 1,825: Line 1,810:
                                 '%s&nbsp;%s',
                                 '%s&nbsp;%s',
                                 m_game.level_requirement['short_upper']
                                 m_game.level_requirement['short_upper']
                                     :gsub('%s', '&nbsp;'),  
                                     :gsub('%s', '&nbsp;'),
                                 rows['mods.required_level']
                                 rows['mods.required_level']
                             )
                             )
                         )
                         )
                         :done()      
                         :done()
                     :tag('td')
                     :tag('td')
                         :attr('width', '*')
                         :attr('width', '*')
Line 1,836: Line 1,821:
                                 '%s%s',
                                 '%s%s',
                                 m_util.html.poe_color(
                                 m_util.html.poe_color(
                                     'mod',  
                                     'mod',
                                     rows['mods.stat_text']
                                     rows['mods.stat_text']
                                         :gsub('<br>', ', ')
                                         :gsub('<br>', ', ')
                                         :gsub('<br />', ' ')
                                         :gsub('<br />', ' ')
                                 ) or '',  
                                 ) or '',
                                 mods_tags
                                 mods_tags
                             )
                             )
Line 1,848: Line 1,833:
                     :done()
                     :done()
                 :done()
                 :done()
           
 
             -- Save the last mod group for later comparison:
             -- Save the last mod group for later comparison:
             last_group = rows['mods.mod_group']
             last_group = rows['mods.mod_group']
Line 1,855: Line 1,840:
         out[#out+1] = tostring(container)
         out[#out+1] = tostring(container)
     end
     end
   
 
     -- Outro text:
     -- Outro text:
     out[#out+1] = '<br>'
     out[#out+1] = '<br>'
Line 1,862: Line 1,847:
         string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
         string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
     )
     )
   
 
     return table.concat(out,'')
     return table.concat(out,'')
end
end
Line 1,874: Line 1,859:
     keys = {}
     keys = {}
     for _, data in ipairs(mod_table.data) do
     for _, data in ipairs(mod_table.data) do
         if type(data.arg) == 'string' then  
         if type(data.arg) == 'string' then
             keys[data.arg] = 1
             keys[data.arg] = 1
         elseif type(data.arg) == 'table' then
         elseif type(data.arg) == 'table' then
Line 1,882: Line 1,867:
         end
         end
     end
     end
   
 
     for _, key in ipairs(mod_table.weights) do
     for _, key in ipairs(mod_table.weights) do
         keys[key] = 1
         keys[key] = 1
     end
     end
   
 
     local out = {}
     local out = {}
     for key, _ in pairs(keys) do
     for key, _ in pairs(keys) do
         out[#out+1] = string.format("['%s'] = '1'", key)
         out[#out+1] = string.format("['%s'] = '1'", key)
     end
     end
   
 
     return table.concat(out, ', ')
     return table.concat(out, ', ')
end
end

Revision as of 22:58, 8 January 2020

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


Implements {{Modifier table}}.

--[[
Module responsible for displaying modifiers in various ways.

]]

local m_util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local m_game = require('Module:Game')
local f_item_link = require('Module:Item link').item_link
local m_cargo = require('Module:Cargo')

local cargo = mw.ext.cargo

local p = {}

-- ----------------------------------------------------------------------------
-- Strings
-- ----------------------------------------------------------------------------
-- This section contains strings used by this module.
-- Add new strings here instead of in-code directly, this will help other
-- people to correct spelling mistakes easier and help with translation to
-- other PoE wikis.

local i18n = {
    mod_table = {
        name = m_util.html.abbr('Name', 'Name of the modifier if available or its internal identifier instead'),
        mod_group = m_util.html.abbr('Group', 'Only one modifier from the specified group can appear at a time under normal circumstances'),
        mod_type = 'Type',
        domain = '[[Modifiers#Mod_Domain|Domain]]',
        generation_type = '[[Modifiers#Mod_Generation_Type|Generation Type]]',
        required_level = '[[Image:Level_up_icon_small.png|link=|For generated item/monster modifiers the minimum item/monster level respectively. Some generation types may not require this condition to be met, however item level restrictions may be raised to 80% of this value.]]',
        labyrinth = '[[The Lord\'s Labyrinth|Labyrinth]]',
        stat_text = m_util.html.abbr('Stats', 'Stats of the modifier and the range they can roll in (if applicable)'),
        buff = m_util.html.abbr('Buff', 'ID of the buff granted and the values associated'),
        granted_skill = m_util.html.abbr('Skill', 'ID of the skill granted'),
        tags = '[[Tags]]',

        iiq = m_util.html.abbr('IIQ', 'increased Quantity of Items found in this Area'),
        iir = m_util.html.abbr('IIR', 'increased Rarity of Items found in this Area'),
        pack_size = m_util.html.abbr('Pack<br>Size', 'Monster pack size'),

        spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
        generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),

        normal_labyrinth = 'Normal Labyrinth',
        cruel_labyrinth = 'Cruel Labyrinth',
        merciless_labyrinth = 'Merciless Labyrinth',
        eternal_labyrinth = 'Eternal Labyrinth',
    },

    drop_down_table = {
        collapse_all = 'Collapse all',
        expand_all = 'Expand all',
        table_intro = 'The table below displays the available [[modifiers]] for [[item]]s such as',
        prefix = 'Prefix',
        suffix = 'Suffix',
        corrupted = 'Corrupted',
        enchant = 'Enchantment',
        elder_prefix = 'Elder prefix',
        elder_suffix = 'Elder suffix',
        shaper_prefix = 'Shaper prefix',
        shaper_suffix = 'Shaper suffix',
        delve_prefix = 'Delve prefix',
        delve_suffix = 'Delve suffix',
        crusader_prefix = 'Crusader prefix',
        crusader_suffix = 'Crusader suffix',
        eyrie_prefix = 'Redeemer prefix',
        eyrie_suffix = 'Redeemer suffix',
        basilisk_prefix = 'Hunter prefix',
        basilisk_suffix = 'Hunter suffix',
        adjudicator_prefix = 'Warlord prefix',
        adjudicator_suffix = 'Warlord suffix',
        mod_group = 'Mod group:',
        back_to_top = 'Back to top',
    },

    errors = {
        --
        -- Mod template
        --
        sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.',
        sell_price_missing_argument = 'Both %s and %s must be specified',

        --
        -- Modifier link template
        --
        undefined_statid = 'Please define any of these stat ids: %s',
        incorrect_modid = 'Please change the name from "%s" to any of these modifier ids:<br>%s',
        multiple_results = 'Please choose only one of these modifier ids:<br>%s',
        no_results = 'No results found.',
    },
}


--
-- Helper/Utility functions
--

local h = {}

function h.header(str)
    --[[
    This function replaces specific numbers with a generic #.
    ]]

    local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
    s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
    s = table.concat(m_util.string.split(s, '<br>'), ', ')
    s = table.concat(m_util.string.split(s, '<br />'), ' ')

   return s
end

function h.query_weights(table_name, page_ids)
    results = cargo.query(
        string.format('mods,%s', table_name),
        string.format('mods._pageID,%s.tag,%s.weight', table_name, table_name),
        {
            where=page_ids,
            join=string.format('mods._pageID=%s._pageID', table_name),
            orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
            limit=5000,
        }
    )
    if #results == 5000 then
        error('Hit maximum cargo results')
    end

    return m_util.cargo.map_results_to_id{results=results,field='mods._pageID'}
end

function h.disambiguate_mod_name(results)
    --[[
        Disambiguates results from a mods query.
    ]]
    local str = {}
    for i,v in pairs(results) do
        str[#str+1] = string.format(
            '%s - %s ([[%s|page]])',
            v['mods.id'] or v['mods._pageName'] or '',
            string.gsub(
                v['mods.stat_text_raw'] or 'N/A',
                '<br>',
                ', '
            ) or '',
            v['mods._pageName'] or ''
        )
    end
    return table.concat(str, '<br>')
end

function h.cargo_query(tpl_args)
    --[[
    Returns a Cargo query of all the results.

    tpl_args to be added to the cargo table needs to prefixed with q_.
    * tpl_args.q_*

    ]]

    -- Parse query arguments
    local query = {
        limit = 5000,
        offset = 0,
    }
    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 table. If there are too many results then repeat,
    -- offset, re-query and add the remaining results:
    local results = {}
    repeat
        local result = cargo.query(
            query.tables,
            query.fields,
            query
        )
        query.offset = query.offset + #result

        for _,v in ipairs(result) do
            results[#results + 1] = v
        end
    until #result < query.limit

    return results
end

-- ----------------------------------------------------------------------------
-- Template: Mod table
-- ----------------------------------------------------------------------------

local mod_table = {}
mod_table.data = {
    {
        arg = 'name',
        header = i18n.mod_table.name,
        fields = {'mods._pageName', 'mods.id', 'mods.name'},
        options = {
            [3] = {
                optional=true,
           },
        },
        display = function(tpl_args, frame, tr, data)
            local name
            if data['mods.name'] then
                name = data['mods.name']
            else
                name = data['mods.id']
            end
            tr
                :tag('td')
                    :wikitext(string.format('[[%s|%s]]', data['mods._pageName'], name))
        end,
        order = 1000,
        sort_type = 'text',
    },
    {
        arg = 'domain',
        header = i18n.mod_table.domain,
        fields = {'mods.domain'},
        display = function(tpl_args, frame, tr, data)
            local value = data['mods.domain']
            tr
                :tag('td')
                    :attr('data-sort-value', value)
                    :wikitext(m_game.constants.mod.domains[tonumber(value)]['short_upper'])
        end,
        order = 2000,
        sort_type = 'text',
    },
    {
        arg = 'generation_type',
        header = i18n.mod_table.generation_type,
        fields = {'mods.generation_type'},
        display = function(tpl_args, frame, tr, data)
            local value = data['mods.generation_type']
            tr
                :tag('td')
                    :attr('data-sort-value', value)
                    :wikitext(m_game.constants.mod.generation_types[tonumber(value)]['short_upper'])
        end,
        order = 2001,
        sort_type = 'text',
    },
    {
        arg = {'group', 'mod_group'},
        header = i18n.mod_table.mod_group,
        fields = {'mods.mod_group'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['mods.mod_group'])
        end,
        order = 2002,
        sort_type = 'text',
    },
    {
        arg = {'mod_type'},
        header = i18n.mod_table.mod_type,
        fields = {'mods.mod_type'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['mods.mod_type'])
        end,
        order = 2003,
        sort_type = 'text',
    },
    {
        arg = {'level', 'required_level'},
        header = i18n.mod_table.required_level,
        fields = {'mods.required_level'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['mods.required_level'])
        end,
        order = 2004,
    },
    {
        arg = {'enchantment', 'labyrinth'},
        header = i18n.mod_table.labyrinth,
        fields = {string.format([[
CONCAT(
    CASE mods.required_level
        WHEN 32 THEN "%s"
        WHEN 53 THEN "%s"
        WHEN 66 THEN "%s"
        WHEN 75 THEN "%s"
    END
)=labyrinth_text
            ]], i18n.mod_table.normal_labyrinth, i18n.mod_table.cruel_labyrinth, i18n.mod_table.merciless_labyrinth, i18n.mod_table.eternal_labyrinth)},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['labyrinth_text'])
        end,
        order = 2005,
        sort_type = 'text',
    },
    {
        arg = {'stat_text'},
        header = i18n.mod_table.stat_text,
        fields = {'mods.stat_text'},
        display = function(tpl_args, frame, tr, data)
            local text
            -- map display type shows this in another column, remove this text to avoid clogging up the list
            if tpl_args.type == 'map' then
                local texts = m_util.string.split(data['mods.stat_text'], '<br>')
                local out = {}

                local valid
                for _, v in ipairs(texts) do
                    valid = true
                    for _, data in pairs(mod_table.stat_ids) do
                        if string.find(v, data.pattern) ~= nil then
                            valid = false
                            break
                        end
                    end

                    if valid then
                        table.insert(out, v)
                    end
                end

                text = table.concat(out, '<br>')
            else
                text = data['mods.stat_text']
            end

            tr
                :tag('td')
                    :wikitext(text)
        end,
        order = 3000,
        sort_type = 'text',
    },

    {
        arg = 'buff',
        header = i18n.mod_table.buff,
        fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(string.format('%s %s', data['mods.granted_buff_id'], data['mods.granted_buff_value']))
        end,
        order = 4000,
        sort_type = 'text',
    },
    {
        arg = {'skill', 'granted_skill'},
        header = i18n.mod_table.granted_skill,
        fields = {'mods.granted_skill'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(data['mods.granted_skill'])
        end,
        order = 4001,
        sort_type = 'text',
    },
    {
        arg = {'tags'},
        header = i18n.mod_table.tags,
        fields = {'mods.tags'},
        display = function(tpl_args, frame, tr, data)
            tr
                :tag('td')
                    :wikitext(table.concat(m_util.string.split(data['mods.tags'], ','), ', '))
        end,
        order = 5000,
        sort_type = 'text',
    },
}
mod_table.stat_ids = {
    ['map_item_drop_quantity_+%'] = {
        header = i18n.mod_table.iiq,
        pattern = '%d+%% increased Quantity of Items found in this Area',
    },
    ['map_item_drop_rarity_+%'] = {
        header = i18n.mod_table.iir,
        pattern = '%d+%% increased Rarity of Items found in this Area',
    },
    ['map_pack_size_+%'] = {
        header = i18n.mod_table.pack_size,
        pattern = '%+%d+%% Monster pack size',
    }
}
mod_table.weights = {'spawn_weights', 'generation_weights'}

function p.mod_table(frame)
    --[[
    Creates a generic table for modifiers.

    Examples
    --------
    = p.mod_table{
        q_tables='spawn_weights',
        q_join='mods._pageID=spawn_weights._pageID',
        q_where='mods.generation_type = 10 AND spawn_weights.tag = "boots" AND spawn_weights.weight > 0',
        q_orderBy='mods.id, mods.required_level',
        q_limit=100,
        stat_text=1,
        enchantment=1,
    }

    ]]


    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)

    -- default to enabled
    tpl_args.name = tpl_args.name or true

    for _, key in ipairs(mod_table.weights) do
        tpl_args[key] = m_util.cast.boolean(tpl_args[key])
    end

    if string.find(tpl_args.q_where, '%[%[') ~= nil then
        error('SMW leftover in where clause')
    end

    local row_infos = {}
    for _, row_info in ipairs(mod_table.data) do
        local enabled = false
        if row_info.arg == nil then
            enabled = true
        elseif type(row_info.arg) == 'string' and m_util.cast.boolean(tpl_args[row_info.arg]) then
            enabled = true
        elseif type(row_info.arg) == 'table' then
            for _, argument in ipairs(row_info.arg) do
                if m_util.cast.boolean(tpl_args[argument]) then
                    enabled = true
                    break
                end
            end
        end

        if enabled then
            row_info.options = row_info.options or {}
            row_infos[#row_infos+1] = row_info
        end
    end

    -- sort the rows
    table.sort(row_infos, function (a, b)
        return (a.order or 0) < (b.order or 0)
    end)

    -- Set tables
    local tables = 'mods'
    if tpl_args.q_tables then
        tables = tables .. ',' .. tpl_args.q_tables
    end


    -- Set required fields
    local fields = {
        'mods._pageID',
    }
    for _, rowinfo in ipairs(row_infos) do
        if type(rowinfo.fields) == 'function' then
            rowinfo.fields = rowinfo.fields()
        end
        for index, field in ipairs(rowinfo.fields) do
            rowinfo.options[index] = rowinfo.options[index] or {}
            fields[#fields+1] = field
        end
    end

    -- Parse query arguments
    local query = {
        -- Workaround: fix duplicates
        groupBy='mods._pageID',
    }
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 2) == 'q_' then
            query[string.sub(key, 3)] = value
        end
    end


    fields = table.concat(fields, ',')
    if tpl_args.q_fields then
        fields = fields .. ' ,' .. tpl_args.q_fields
    end

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

    if #results == 0 then
        if tpl_args.default ~= nil then
            return tpl_args.default
        else
            return 'No results found'
        end
    end

    -- this might be needed in other queries, currently not checking if it's actually needed
    -- because performance impact should be neglible
    local page_ids = {}
    for _, row in ipairs(results) do
        page_ids[#page_ids+1] = string.format('mods._pageID="%s"', row['mods._pageID'])
    end
    page_ids = table.concat(page_ids, ' OR ')

    local weights = {}
    for _, key in ipairs(mod_table.weights) do
        if tpl_args[key] then
            weights[key] = h.query_weights(key, page_ids)
        end
    end

    local stats
    if tpl_args.type == 'map' then
        local query_stat_ids = {}
        for k, _ in pairs(mod_table.stat_ids) do
            query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
        end

        local stat_results = cargo.query(
            'mods,mod_stats',
            'mods._pageID,mod_stats.id,mod_stats.min,mod_stats.max',
            {
                where=string.format('(%s) AND (%s)', page_ids, table.concat(query_stat_ids, ' OR ')),
                join='mods._pageID=mod_stats._pageID',
                orderBy='mods.id ASC',
                limit=5000,
            }
        )
        if #stat_results == 5000 then
            error('Hit maximum cargo results')
        end

        stats = m_util.cargo.map_results_to_id{results=stat_results, field='mods._pageID'}
        -- In addition map stats to stat <-> min/max pairs
        for page_id, rows in pairs(stats) do
            local stat_id_map = {}
            for _, row in ipairs(rows) do
                stat_id_map[row['mod_stats.id']] = {min=tonumber(row['mod_stats.min']), max=tonumber(row['mod_stats.max'])}
            end
            stats[page_id] = stat_id_map
        end
    end

    --
    -- Display
    --

    -- Preformance optimization
    if tpl_args.q_fields then
        tpl_args._extra_fields = m_util.string.split(tpl_args.q_fields, ',')
        for index, field in ipairs(tpl_args._extra_fields) do
            field = m_util.string.split(field, '=')
            -- field[2] will be nil if there is no alias
            tpl_args._extra_fields[index] = field[2] or field[1]
        end
    else
        tpl_args._extra_fields = {}
    end

    local tbl = mw.html.create('table')
    tbl:attr('class', 'wikitable sortable modifier-table')
    -- Header

    local tr = tbl:tag('tr')
    for _, row_info in ipairs(row_infos) do
        tr
            :tag('th')
                :attr('data-sort-type', row_info.sort_type or 'number')
                :wikitext(row_info.header)
                :done()
    end

    if tpl_args.type == 'map' then
        for stat_id, data in pairs(mod_table.stat_ids) do
            tr
                :tag('th')
                    :attr('data-sort-type', 'number')
                    :wikitext(data.header)
                    :done()
        end
    end

    for _, key in ipairs(mod_table.weights) do
        if tpl_args[key] then
            tr
                :tag('th')
                    :wikitext(i18n.mod_table[key])
        end
    end

    for _, field in ipairs(tpl_args._extra_fields) do
        tr
            :tag('th')
                :wikitext(field)
    end

    -- Body

    for _, row in ipairs(results) do
        tr = tbl:tag('tr')

        for _, rowinfo in ipairs(row_infos) do
            -- this has been cast from a function in an earlier step
            local display = true
            for index, field in ipairs(rowinfo.fields) do
                -- this will bet set to an empty value not nil confusingly
                if row[field] == '' then
                    if rowinfo.options[index].optional ~= true then
                        display = false
                        break
                    else
                        row[field] = nil
                    end
                end
            end
            if display then
                rowinfo.display(tpl_args, frame, tr, row, rowinfo.fields)
            else
                tr:wikitext(m_util.html.td.na())
            end
        end

        if tpl_args.type == 'map' then
            for stat_id, data in pairs(mod_table.stat_ids) do
                local stat_data = stats[row['mods._pageID']]
                if stat_data and stat_data[stat_id] then
                    local v = stat_data[stat_id]
                    local text
                    if v.min == v.max then
                        text = v.min
                    else
                        text = string.format('(%s to %s)', v.min, v.max)
                    end
                    tr
                        :tag('td')
                            :attr('data-sort-value', (v.min+v.max)/2)
                            :wikitext(string.format('%s%%', text))
                            :done()
                else
                    tr:wikitext(m_util.html.td.na())
                end
            end
        end

        for _, key in ipairs(mod_table.weights) do
            if tpl_args[key] then
                local weight_out = {}
                for _, wrow in ipairs(weights[key][row['mods._pageID']]) do
                    weight_out[#weight_out+1] = string.format('%s %s', wrow[key .. '.tag'], wrow[key .. '.weight'])
                end
                if #weight_out > 0 then
                    tr
                            :tag('td')
                                :wikitext(table.concat(weight_out, '<br>'))
                                :done()
                else
                    tr:wikitext(m_util.html.td.na())
                end
            end
        end

        for _, field in ipairs(tpl_args._extra_fields) do
            if row[field] then
                tr
                    :tag('td')
                        :wikitext(row[field])
            else
                tr:wikitext(m_util.html.td.na())
            end
        end
    end

    return tostring(tbl)
end




-- ---------------------------------------------------------------------
-- Modifier link
-- ---------------------------------------------------------------------


function p.modifier_link(frame)
    --[[
    Finds and links to a modifier in formatted form.

    To do list
    ----------
    * Standardize hoverbox so it can be used in multiple places easily
    and make it easy to add rows of data.

    Examples
    --------
    = p.modifier_link{"Tyrannical"}
    = p.modifier_link{"Flaring"}
    = p.modifier_link{"Dictator's"}
    = p.modifier_link{"StrDexMaster%"}
    = p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'}

    ]]

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

    -- Aliases:
    tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or ''

    -- Define query arguments:
    local tables = {'mods', 'mod_stats', 'spawn_weights'}
    local fields = {'mods.name', 'mods.stat_text', 'mods.stat_text_raw', 'mods.generation_type', 'mods.tags', 'mods._pageName', 'mod_stats.max', 'mod_stats.min', 'spawn_weights.tag', 'spawn_weights.weight', 'mods.id', 'mod_stats.id'}
    local query = {
        join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName',
        where = string.format(
            '(mods.name LIKE "%s" or mods.id LIKE "%s" or mods.stat_text LIKE "%s" or mods.stat_text_raw LIKE "%s") AND mod_stats.id LIKE "%%%s%%"',
            tpl_args.modid,
            tpl_args.modid,
            tpl_args.modid,
            tpl_args.modid,
            tpl_args.statid or '%'
        ),
        -- groupBy = 'mods._pageID, mod_stats.id, spawn_weights.tag',
    }

    -- Query cargo rows:
    local results = m_util.cargo.query(tables, fields, query, args)

    -- Create own list for each cargo table and group by page name:
    tpl_args.tbl = {}
    for _,v in ipairs(tables) do
        tpl_args.tbl[v] = {}
    end

    tpl_args.results_unique = {}
    local hash = {}
    for _,v in ipairs(results) do
        for ii, vv in pairs(tpl_args.tbl) do
            if tpl_args.tbl[ii][v['mods._pageName']] == nil then
                tpl_args.tbl[ii][v['mods._pageName']] = {}
            end
            local n = #tpl_args.tbl[ii][v['mods._pageName']] or 0
            tpl_args.tbl[ii][v['mods._pageName']][n+1] = v

            -- Get a sorted list that only has unique page names:
            if hash[v['mods._pageName']] ~= true then
                local m = #tpl_args.results_unique
                tpl_args.results_unique[m+1] = v
                hash[v['mods._pageName']] = true
            end
        end
    end

    -- If no results are found then just create a empty list to avoid
    -- index errors.
    if tpl_args.results_unique[1] == nil then
        tpl_args.results_unique[1] = {}
    end

    -- Helpful error handling:
    local err_tbl = {
        {
            bool = #results == 0,
            disp = {
                i18n.errors.no_results,
            }
        },
        {
            bool = #tpl_args.results_unique > 1,
            disp = {
                i18n.errors.multiple_results,
                h.disambiguate_mod_name(tpl_args.results_unique),
            },
        },
        {
            bool = tpl_args.modid ~= tpl_args.results_unique[1]['mods.id'],
            disp = {
                string.gsub(
                    i18n.errors.incorrect_modid,
                    '%%s',
                    tpl_args.modid,
                    1
                ),
                h.disambiguate_mod_name(tpl_args.results_unique),
            },
        },
    }
    for _,v in ipairs(err_tbl) do
        if v.bool then
            local cats = {'Pages with modifier link errors'}
            return m_util.html.error(
                {msg = string.format(v.disp[1], v.disp[2]) .. m_util.misc.add_category(cats)}
            )
        end
    end

    -- Display formats:
    local display = {
        abbr = {
            display = function(tpl_args, frame)
                local name = m_util.html.poe_color(
                    'mod',
                    string.format(
                        '[[%s|%s]]',
                        tpl_args.results_unique[1]['mods._pageName'],
                        tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                    )
                )

                local tooltip_table = {
                    m_util.html.poe_color(
                        'mod',
                        tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                    ),
                    m_util.html.poe_color(
                        'help',
                        m_game.constants.mod.generation_types[
                            tonumber(
                                tpl_args.results_unique[1]['mods.generation_type']
                            )
                        ].full
                    ),
                    -- m_util.html.poe_color(
                        -- 'help',
                       -- table.concat(
                            -- m_util.string.split(tpl_args.results_unique[1]['mods.tags'] or '', ','),
                            -- ', '
                        -- )
                    -- ),
                    m_util.html.poe_color(
                        'normal',
                        tpl_args.results_unique[1]['mods.stat_text_raw']
                    ),
                }

                local tt_tbl_fltrd = {}
                for _,v in ipairs(tooltip_table) do
                    if v ~= nil and v ~= '' then
                        tt_tbl_fltrd[#tt_tbl_fltrd+1] = v
                    end
                end
                local tooltip = table.concat(tt_tbl_fltrd, '<br>')

                return m_util.html.tooltip(name, tooltip, class)
            end,
        },
        verbose = {
            display = function(tpl_args, frame)
                return string.format(
                    '%s - %s (%s)',
                    m_util.html.poe_color(
                        'mod',
                        string.format(
                            '[[%s|%s]]',
                            tpl_args.results_unique[1]['mods._pageName'],
                            tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
                        )
                    ),
                    m_util.html.poe_color(
                        'mod',
                        string.gsub(
                            tpl_args.results_unique[1]['mods.stat_text'],
                            '<br>',
                            ', '
                        )
                    ),
                    m_game.constants.mod.generation_types[
                        tonumber(
                            tpl_args.results_unique[1]['mods.generation_type']
                        )
                    ].full
                )
            end,
        },
        stat_text = {
            display = function(tpl_args, frame)
                return m_util.html.poe_color(
                    'mod',
                    tpl_args.results_unique[1]['mods.stat_text']
                )
            end,
        },
        max = {
            display = function(tpl_args, frame)
                local statid = {}
                for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do
                    statid[#statid+1] = v['mod_stats.id']
                    if tpl_args.statid == v['mod_stats.id'] then
                        return v['mod_stats.max']
                    end
                end

                if tpl_args.statid == nil then
                    return m_util.html.error(
                        {
                            msg = string.format(
                                i18n.errors.undefined_statid,
                                table.concat(statid, ', ')
                            )
                        }
                    )
                end
            end
        },
        min = {
            display = function(tpl_args, frame)
                local statid = {}
                for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do
                    statid[#statid+1] = v['mod_stats.id']
                    if tpl_args.statid == v['mod_stats.id'] then
                        return v['mod_stats.min']
                    end
                end

                if tpl_args.statid == nil then
                    return m_util.html.error(
                        {
                            msg = string.format(
                                i18n.errors.undefined_statid,
                                table.concat(statid, ', ')
                            )
                        }
                    )
                end
            end
        },
    }

    return display[tpl_args.display or 'abbr'].display(tpl_args, frame)
end




-- ---------------------------------------------------------------------
-- Drop down list
-- ---------------------------------------------------------------------

function h.get_spawn_chance(frame)
    --[[
    Calculates the spawn chance of a set of mods that all have a
    spawn weight.

    ]]

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

    -- Get the table:
    local tbl = tpl_args['tbl']

    -- Probabilities affecting the result besides the spawn weight:
    local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1

    -- Total number of outcomes.
    local N = 0
    for i,_ in ipairs(tbl) do
        N = N + tbl[i]['spawn_weights.weight']
    end

    for i,_ in ipairs(tbl) do
        -- Number of ways it can happen:
        local n = tbl[i]['spawn_weights.weight']

        -- Truncated value:
        tbl[i]['spawn_weights.chance'] = string.format(
            "%0.2f%%",
            n/N * chance_multiplier*100
        )
    end

    return tbl
end

function p.drop_down_table(frame)
    --[[
    This function queries mods that can spawn on an item. It compares
    the item tags and the spawn weight tags. If there's a match and
    the spawn weight is larger than zero, then that mod is added to a
    drop down list.

    To Do:
    * Add support to:
        * Forsaken masters
        * Bestiary
    * Add a proper expand/collapse toggle for the entire header row so
        it reacts together with mw-collapsible.
    * Show Mod group in a better way perhaps:
        Mod group [Collapsible, default=Expanded]
            # to Damage [Collapsible, default=Collapsed]
                3 to Damage
                5 to Damage
    * Add a where condition that somehow filters out mods that obviously
      wont match with the item. spawn_weights.weight>0 isn't enough due
      to possible edge cases.


    Examples:
    Weapons
    = p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
    = p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}

    Accessories
    = p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}

    Jewels
    = p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}

    Armour
    = p.drop_down_table{item = 'Plate Vest', header = 'Body armours'}

    Boots
    = p.drop_down_table{item = 'Iron Greaves', header = 'Boots'}

    = p.drop_down_table{
        item = 'Fishing Rod',
        header = 'FISH PLEASE',
        item_tags = 'fishing_rod',
        extra_fields = 'mods.tags'
    }

    = p.drop_down_table{
        item = 'Fishing Rod',
        item_tags = 'axe, one_hand_weapon, onehand, weapon, default',
        extra_item_tags = 'fishing_rod'
    }

    = p.drop_down_table{
        item = 'Vaal Blade',
    }

    ]]

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

    -- Format the cargo query:
    local where
    for _,v in ipairs({
            {tpl_args.page, 'items._pageName = "%s"'},
            {tpl_args.item, 'items.name = "%s"'},
    }) do
        if v[1] ~= nil then
            where = string.format(v[2], v[1])
            break
        end
    end

    local item_info = m_cargo.query(
        {'items'},
        {'items.name', 'items.tags', 'items.class_id', 'items.inventory_icon', 'items.html', 'items.release_version', 'items._pageName'},
        {
            where=where,
            groupBy='items._pageName',
            orderBy = 'items.name, items.release_version DESC',
        }
    )[1]

    -- Set the item class as key and the corresponding mod domain as
    -- value:
    local class_to_domain = {
        ['LifeFlask']=2,
        ['ManaFlask']=2,
        ['HybridFlask']=2,
        ['UtilityFlask']=2,
        ['UtilityFlaskCritical']=2,
        ['Map']=5,
        ['Jewel']=10,
        ['Leaguestone']=12,
        ['AbyssJewel']=13,
    }
    -- Get the domain, if it's not defined in the table assume it's
    -- in the item domain.
    item_info['items.domain'] = class_to_domain[item_info['items.class_id']] or 1

    -- Convert the mod domain number to understandable text:
    item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower']

    -- For some reason cargo queried item tags, are not comma-space
    -- separated any more.
    if tpl_args.item_tags ~= nil then
        tpl_args.item_tags = m_util.string.split(tpl_args.item_tags, ', ')
    else
        tpl_args.item_tags = m_util.string.split(item_info['items.tags'], ',')
    end
    if tpl_args.extra_item_tags then
        local extra_item_tags = m_util.string.split(tpl_args.extra_item_tags, ', ')
        for _,v in ipairs(extra_item_tags) do
            tpl_args.item_tags[#tpl_args.item_tags+1] = v
        end
    end

    -- Create drop down lists in these sections and query in these
    -- generation types.

    local elder_tag = m_game.constants.item.classes[item_info['items.class_id']]['elder_tag']
    local shaper_tag = m_game.constants.item.classes[item_info['items.class_id']]['shaper_tag']
    local crusader_tag = m_game.constants.item.classes[item_info['items.class_id']]['crusader_tag']
    local eyrie_tag = m_game.constants.item.classes[item_info['items.class_id']]['eyrie_tag']
    local basilisk_tag = m_game.constants.item.classes[item_info['items.class_id']]['basilisk_tag']
    local adjudicator_tag = m_game.constants.item.classes[item_info['items.class_id']]['adjudicator_tag']

    local section = {}
    section = {
        {
            header = i18n.drop_down_table.prefix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        1,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.suffix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        2,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            tags = {elder_tag},
            header = i18n.drop_down_table.elder_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    elder_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {elder_tag},
            header = i18n.drop_down_table.elder_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    elder_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {shaper_tag},
            header = i18n.drop_down_table.shaper_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    shaper_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {shaper_tag},
            header = i18n.drop_down_table.shaper_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    shaper_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            header = i18n.drop_down_table.delve_prefix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        1,
                        16
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            header = i18n.drop_down_table.delve_suffix,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        2,
                        16
                    )
                end

                return table.concat(where, ' OR ')
            end,
        },
        {
            tags = {crusader_tag},
            header = i18n.drop_down_table.crusader_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    crusader_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {crusader_tag},
            header = i18n.drop_down_table.crusader_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    crusader_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {eyrie_tag},
            header = i18n.drop_down_table.eyrie_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    eyrie_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {eyrie_tag},
            header = i18n.drop_down_table.eyrie_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    eyrie_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {basilisk_tag},
            header = i18n.drop_down_table.basilisk_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    basilisk_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {basilisk_tag},
            header = i18n.drop_down_table.basilisk_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    basilisk_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {adjudicator_tag},
            header = i18n.drop_down_table.adjudicator_prefix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    adjudicator_tag,
                    1,
                    item_info['items.domain']
                )
            end,
        },
        {
            tags = {adjudicator_tag},
            header = i18n.drop_down_table.adjudicator_suffix,
            where = function(tpl_args, frame, value)
                return string.format(
                    [[
                        (spawn_weights.tag="%s"
                        AND mods.generation_type=%s
                        AND mods.domain=%s
                        AND mods.stat_text IS NOT NULL)
                    ]],
                    adjudicator_tag,
                    2,
                    item_info['items.domain']
                )
            end,
        },
        {
            header = i18n.drop_down_table.corrupted,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        5,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
            chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
            is_implicit = true,
        },
        {
            header = i18n.drop_down_table.enchant,
            where = function(tpl_args, frame, value)
                local where = {}
                for _, item_tag in ipairs(tpl_args.item_tags) do
                    where[#where+1] = string.format(
                        [[
                            (spawn_weights.tag="%s"
                            AND mods.generation_type=%s
                            AND mods.domain=%s
                            AND mods.stat_text IS NOT NULL)
                        ]],
                        item_tag,
                        10,
                        item_info['items.domain']
                    )
                end

                return table.concat(where, ' OR ')
            end,
            is_implicit = true,
        },
    }

    -- Save the original tag format:
    local item_tags_orig = {}
    for i,v in ipairs(tpl_args.item_tags) do
        item_tags_orig[i] = v
    end

    local item_mods = {}
    local mod_group_counter = {}
    mod_group_counter['all'] = {}
    local extra_fieldss = {}
    local table_index_base = -1
    for _, sctn in ipairs(section) do
        item_mods[sctn['header']] = {}

        -- Preallocate the mod group counter, implicit and explicit mods
        -- are counted separetely because they can spawn together:
        mod_group_counter[sctn['header']] = {}
        local adj = 'explicit'
        if sctn['is_implicit'] then
            adj = 'implicit'
        end
        for _, header in ipairs({sctn['header'], 'all'}) do
            if mod_group_counter[header][adj] == nil then
                mod_group_counter[header][adj] = {}
            end
        end

        local continue = true
        local current_tags
        if sctn['tags'] then
            -- some item classes do not have shaper/elder items, so the table will not contain any tags:
            if #sctn['tags'] == 0 then
                continue = false
            else
                current_tags = sctn['tags']
            end
        else
            current_tags = {}
            -- Reset to original tags:
            for i,v in ipairs(item_tags_orig) do
                current_tags[i] = v
            end
        end

        if continue then
            -- Cargo preparation:
            tpl_args.q_tables = 'mods, spawn_weights, mod_stats'
            tpl_args.q_fields = 'mods.name, mods.id, mods.required_level, mods.generation_type, mods.domain, mods.mod_group, mods.mod_type, mods.stat_text, mods.stat_text_raw, mods.tags, mods._pageName, mod_stats.id, spawn_weights.tag, spawn_weights.weight, spawn_weights.ordinal, spawn_weights._pageName'
            tpl_args.q_join = 'mods._pageName=spawn_weights._pageName, mods._pageName=mod_stats._pageName'
            tpl_args.q_where = sctn['where'](tpl_args, frame, value)
            tpl_args.q_groupBy = 'mods._pageName, spawn_weights.tag, spawn_weights.weight'
            tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods.required_level, mods._pageName, spawn_weights.ordinal'

            local extra_fields = {}
            if tpl_args.extra_fields ~= nil then
                extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
                tpl_args.q_fields = string.format(
                    '%s, %s',
                    tpl_args.q_fields,
                    table.concat(extra_fields, ', ')
                )
            end

            -- Query mods and map the results to the pagename:
            local results = m_cargo.map_results_to_id{
                results=h.cargo_query(tpl_args),
                field='mods._pageName',
                keep_id_field=true,
                append_id_field=true,
            }

            if #results > 0 then
                -- Loop through all found modifiers:
                local last
                for _, id in ipairs(results) do
                    if sctn['header'] == i18n.drop_down_table.elder_prefix then
                     --   error(mw.dumpObject(results))
                    end
                    -- Loop through all the modifier tags until they match
                    -- the item tags:
                    local j = 0
                    local tag_match_stop
                    repeat
                        j = j+1
                        local mod_tag = results[id][j]['spawn_weights.tag']
                        local mod_tag_weight = tonumber(
                            results[id][j]['spawn_weights.weight']
                        )

                        -- Loop through the item tags until it matches the
                        -- spawn weight tag and the mod tag has a value larger than
                        -- zero:
                        local y = 0
                        local tag_match_add = false
                        repeat
                            y = y+1
                            tag_match_stop = ((mod_tag == current_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (results[id][j] == nil)
                            tag_match_add  =  (mod_tag == current_tags[y]) and ((mod_tag_weight or -1) > 0)
                        until tag_match_stop or y == #current_tags

                        -- If there's a match then save that mod and other
                        -- interesting information:
                        if tag_match_add then

                            -- Assume that the mod is global then go through
                            -- all the stat ids and check if any of the
                            -- stats are local:
                            local mod_scope = 'Global'
                            for _, vv in ipairs(results[id]) do
                                if vv['mod_stats.id']:find('.*local.*') ~= nil then
                                    mod_scope = 'Local'
                                end
                            end

                            -- Save the matching modifier tag:
                            local a = #item_mods[sctn['header']]
                            item_mods[sctn['header']][a+1] = results[id][j]

                            -- Save other interesting fields:
                            item_mods[sctn['header']][a+1]['mods.scope'] = mod_scope
                            item_mods[sctn['header']][a+1]['spawn_weight.idx_match'] = j
                            item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add
                            item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop

                            -- Count the mod groups:
                            local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group'
                            for _, header in ipairs({sctn['header'], 'all'}) do
                                if mod_group_counter[header][adj][group] == nil then
                                    mod_group_counter[header][adj][group] = {}
                                end
                                local tp = results[id][j]['mods.mod_type']
                                local bef = mod_group_counter[header][adj][group][tp] or 0
                                mod_group_counter[header][adj][group][tp] = 1 + bef
                            end
                        end
                    until tag_match_stop
                end

                -- If the user wants to see the spawn chance then do the
                -- calculations and save that result as well:
                if tpl_args.spawn_chance ~= nil then
                    extra_fields[#extra_fields+1] = 'spawn_weights.chance'
                    item_mods[sctn['header']] = h.get_spawn_chance{
                        tbl = item_mods[sctn['header']],
                        chance_multiplier = sctn['chance_multiplier']
                    }
                end

                extra_fieldss[sctn['header']] = extra_fields
            end
        end
    end


    --
    -- Display the item mods
    --

    -- Introductory text:
    local out = {}
    out[#out+1] = string.format(
        '==%s== \n',
        tpl_args['header'] or table.concat(tpl_args.item_tags, ', ')
    )
    local expand_button = string.format(
        '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[%s]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[%s]</div></div>',
        i18n.drop_down_table.collapse_all,
        i18n.drop_down_table.expand_all
    )
    out[#out+1] = expand_button
    out[#out+1] = string.format('%s %s.<br><br><br>',
        i18n.drop_down_table.table_intro,
        f_item_link{
            page=item_info['items._pageName'],
            name=item_info['items.name'],
            inventory_icon=item_info['items.inventory_icon'] or '',
            html=item_info['items.html'] or '',
            skip_query=true
        }
    )

    -- Loop through the sections:
    for _, sctn in ipairs(section) do
        local extra_fields = extra_fieldss[sctn['header']]
        local adj = 'explicit'
        if sctn['is_implicit'] then
            adj = 'implicit'
        end

        -- Create html container:
        local container = mw.html.create('div')
            :attr('style', 'vertical-align:top; display:inline-block;')

        -- Create the drop down table with <table></table>:
        local headers = container
        if #item_mods[sctn['header']] > 0 then
            headers
                :tag('h3')
                    :wikitext(string.format('%s', sctn['header']))
                    :done()
                :done()
        end

        local total_mod_groups = 0
        for _ in pairs(mod_group_counter[sctn['header']][adj]) do
            total_mod_groups = 1+total_mod_groups
        end

        -- Loop through and add all matching mods to the <table>.
        local tbl, last_group, last_type
        for _, rows in ipairs(item_mods[sctn['header']]) do

            -- If the last mod group is different to the current
            -- mod group then assume the mod isn't related and start
            -- a new drop down list, if there's only one mod group
            -- then use mod type instead:
            if rows['mods.mod_group'] ~= last_group or (total_mod_groups == 1 and rows['mods.mod_type'] ~= last_type) then
                -- Check through all the mods and see if there are
                -- multiple mod types within the same mod group:
                local count = {}
                for _, n in ipairs(item_mods[sctn['header']]) do

                    -- If the mod has the same mod group, then add
                    -- the mod type to the counter. Only unique mod
                    -- types matter so the number is just a dummy
                    -- value:
                    if n['mods.mod_group'] == rows['mods.mod_group'] then
                        count[n['mods.mod_type']] = 1
                    end
                end

                -- Calculate how many unique mod types with the
                -- same mod group there are for all explicit or implicit
                -- sections since a mod with the same mod group can't
                -- spawn. Doesn't matter if it's prefix or suffix.
                local number_of_mod_types = 0
                for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do
                    number_of_mod_types = 1 + number_of_mod_types
                end

                -- If there are multiple unique mod types with the
                -- same mod group then change the style of the drop
                -- down list to indicate it, if there's only one
                -- mod group in the generation type then ignore it:
                local table_index_mod_group
                if number_of_mod_types > 1 and total_mod_groups > 1 then
                    table_index_mod_group = table.concat(
                        {string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},
                        ''
                    )

                    tbl_caption = string.format(
                        '%s',
                        m_util.html.poe_color(
                            'stat',
                            string.format(
                                '%s %s',
                                i18n.drop_down_table.mod_group,
                                rows['mods.mod_group']
                            )
                        ) or ''
                    )

                else
                    tbl_caption = string.format(
                        '%s (%s)',
                        m_util.html.poe_color(
                            'mod',
                            h.header(rows['mods.stat_text_raw'])
                        ) or '',
                        rows['mods.scope']
                    )
                end

                -- Create a table index for handling the collapsible:
                table_index_base = table_index_base+1
                if table_index_mod_group ~= nil then
                    table_index = table_index_mod_group
                else
                    table_index = table_index_base
                end

                -- Add class and style to the <table>:
                tbl = container:tag('table')
                tbl
                    :attr('class', 'mw-collapsible mw-collapsed')
                    :attr('style',
                        'text-align:left; line-height:1.60em; width:810px;'
                    )
                    :tag('th')
                        :attr('class',
                            string.format('mw-customtoggle-%s', table_index)
                        )
                        :attr('style',
                            'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
                        )
                        :attr('colspan', '3' .. #extra_fields)
                        :wikitext(tbl_caption)
                        :done()
                    :done()
            end

            -- If the mod has no name then use the mod type:
            local mod_name = rows['mods.name']
            if  mod_name == '' or mod_name == nil then
                mod_name = rows['mods.mod_type']
            end

            -- Check if there are any extra properties to show in
            -- the drop down list and then add a cell for that,
            -- add this node at the end of the table row:
            local td = mw.html.create('td')
            if extra_fields ~= nil then
                for _, extra_field in ipairs(extra_fields) do
                    td
                        :attr('width', '*')
                        :wikitext(string.format(
                            '%s:&nbsp;%s ',
                            extra_field,
                            rows[extra_field]
                            )
                        )
                        :done()
                end
            end

            -- Style mods.tags:
            local mods_tags = table.concat(m_util.string.split(rows['mods.tags'], ','), ', ')
            if mods_tags ~= '' then
                mods_tags = m_util.html.tooltip('*', mods_tags, class)
            end

            -- Add a table row with the interesting properties that
            -- modifier has:
            tbl
                :tag('tr')
                    :attr('class', 'mw-collapsible mw-collapsed')
                    :attr(
                        'id',
                        string.format('mw-customcollapsible-%s', table_index)
                    )
                    :tag('td')
                        :attr('width', '160')
                        :wikitext(
                            string.format(
                                '&nbsp;&nbsp;&nbsp;[[%s|%s]]',
                                rows['mods._pageName'],
                                mod_name:gsub('%s', '&nbsp;')
                            )
                        )
                        :done()
                    :tag('td')
                        :attr('width', '1')
                        :wikitext(
                            string.format(
                                '%s&nbsp;%s',
                                m_game.level_requirement['short_upper']
                                    :gsub('%s', '&nbsp;'),
                                rows['mods.required_level']
                            )
                        )
                        :done()
                    :tag('td')
                        :attr('width', '*')
                        :wikitext(
                            string.format(
                                '%s%s',
                                m_util.html.poe_color(
                                    'mod',
                                    rows['mods.stat_text']
                                        :gsub('<br>', ', ')
                                        :gsub('<br />', ' ')
                                ) or '',
                                mods_tags
                            )
                        )
                        :done()
                    :node(td)
                    :done()
                :done()

            -- Save the last mod group for later comparison:
            last_group = rows['mods.mod_group']
            last_type = rows['mods.mod_type']
        end
        out[#out+1] = tostring(container)
    end

    -- Outro text:
    out[#out+1] = '<br>'
    out[#out+1] = m_util.html.poe_color(
        'normal',
        string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
    )

    return table.concat(out,'')
end

-- ----------------------------------------------------------------------------
-- Debug functions
-- ----------------------------------------------------------------------------

p.debug = {}
function p.debug.tbl_data(tbl)
    keys = {}
    for _, data in ipairs(mod_table.data) do
        if type(data.arg) == 'string' then
            keys[data.arg] = 1
        elseif type(data.arg) == 'table' then
            for _, arg in ipairs(data.arg) do
                keys[arg] = 1
            end
        end
    end

    for _, key in ipairs(mod_table.weights) do
        keys[key] = 1
    end

    local out = {}
    for key, _ in pairs(keys) do
        out[#out+1] = string.format("['%s'] = '1'", key)
    end

    return table.concat(out, ', ')
end

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

return p