Difference between revisions of "Module:Loot table"

From Idlescape Wiki
Jump to navigation Jump to search
(Fix return the table as a string and not mw.html element)
(Fix change html / wikitext parsing.)
Line 5: Line 5:
 
local leagueData = mw.loadData("Module:Leagues/data")
 
local leagueData = mw.loadData("Module:Leagues/data")
  
 +
 +
---Creates a mw.html 'DIV' element containing an "error" message.
 +
---@param message string # Message as wikitext.
 +
---@return mw.html # The message wrapped in a mw.html 'DIV' element as red text.
 +
local function createErrorMessage(message)
 +
    local e = mw.html.create("div")
 +
        :css("color", "red")
 +
        :wikitext(message)
 +
    return e
 +
end
  
 
---Finds the source ID for a given name.
 
---Finds the source ID for a given name.
---@param name string The name of the source.
+
---@param name string # The name of the source.
---@return number|nil The source ID (monsterID or locationID), or nil if the source is not found.
+
---@return number|nil # The source ID (monsterID or locationID), or nil if the source is not found.
---@return string|mw.html.element The source type ("monster" or "location"), or error <div> if the source is not found.
+
---@return string|mw.html # The source type ("monster" or "location"), or mw.html element with error message if the source is not found.
 
local function findSourceId(name)
 
local function findSourceId(name)
  for _, source in ipairs({ "monster", "location" }) do
+
    for _, source in ipairs({ "monster", "location" }) do
    local id = findId._findId({ name, source })
+
        local id = findId._findId({ name, source })
    if id ~= "id not found" then
+
        if id ~= "id not found" then
      return id, source
+
            return id, source
 +
        end
 
     end
 
     end
  end
+
    local errorMessage = createErrorMessage("No monster or location named '" ..
  local div = mw.html.create("div")
+
    name .. "'. The Module:Loot/data may be outdated.")
    :css("color", "red")
+
    return nil, errorMessage
    :wikitext("No monster or location named '" .. name .. "' The Module:Loot/data may be outdated.")
 
  return nil, div
 
 
end
 
end
  
Line 34: Line 43:
 
---@param id string|number # The ID of the item to find.
 
---@param id string|number # The ID of the item to find.
 
---@return Item? # The item data, or nil if the item is not found.
 
---@return Item? # The item data, or nil if the item is not found.
---@return string? # The monsterId where the item was found ("-1" for locations), or nil if the item was not found.
+
---@return string? # The monsterId where the item was found ("-1" for locations), or nil if the item is not found.
---@return string? # The locationId where the item was found, or nil if the item was not found.
+
---@return string? # The locationId where the item was found, or nil if the item is not found.
 
local function getFirstLootMatch(loot, id)
 
local function getFirstLootMatch(loot, id)
 
     id = tostring(id)
 
     id = tostring(id)
  for locationId, location in pairs(loot) do
+
    for locationId, location in pairs(loot) do
    for sourceId, source in pairs(location) do
+
        for sourceId, source in pairs(location) do
      for _, item in ipairs(source) do
+
            for _, item in ipairs(source) do
        if item.id == id then
+
                if item.id == id then
          return item, sourceId, locationId
+
                    return item, sourceId, locationId
 +
                end
 +
            end
 
         end
 
         end
      end
 
 
     end
 
     end
  end
 
 
end
 
end
  
Line 52: Line 61:
 
---@param loot table # The loot table from Module:Loot/data.
 
---@param loot table # The loot table from Module:Loot/data.
 
---@param id string|number # The ID of the source to find.
 
---@param id string|number # The ID of the source to find.
---@return Item[]? # The source data, or nil if the source was not found.
+
---@return Item[]? # The source data, or nil if the source is not found.
---@return string? # The locationId where the source was found, or nil if the source was not found.
+
---@return string? # The locationId where the source was found, or nil if the source is not found.
 
local function getFirstSourceMatch(loot, id)
 
local function getFirstSourceMatch(loot, id)
 
     id = tostring(id)
 
     id = tostring(id)
  for locationId, location in pairs(loot) do
+
    for locationId, location in pairs(loot) do
    for sourceId, source in pairs(location) do
+
        for sourceId, source in pairs(location) do
      if sourceId == id then
+
            if sourceId == id then
        return source, locationId
+
                return source, locationId
      end
+
            end
 +
        end
 
     end
 
     end
  end
 
 
end
 
end
  
 
--TODO: Add option to show percentages as fractions
 
--TODO: Add option to show percentages as fractions
  
---Converts a floating-point number to a fraction.
+
---Converts a floating-point number to a approximation of fraction.
---@param num number The floating-point number to convert.
+
---@param num number # The floating-point number to convert.
---@param maxDenominator number The maximum denominator for the fraction.
+
---@param maxDenominator number # The maximum denominator for the fraction.
---@return number The numerator of the fraction.
+
---@return number # The numerator of the fraction.
---@return number The denominator of the fraction.
+
---@return number # The denominator of the fraction.
 
local function floatToFraction(num, maxDenominator)
 
local function floatToFraction(num, maxDenominator)
 
     local sign = num < 0 and -1 or 1
 
     local sign = num < 0 and -1 or 1
Line 80: Line 89:
  
 
     for d = 1, maxDenominator do
 
     for d = 1, maxDenominator do
         local n = math.floor(num*d + 0.5)
+
         local n = math.floor(num * d + 0.5)
 
         local approx = n / d
 
         local approx = n / d
 
         local difference = math.abs(num - approx)
 
         local difference = math.abs(num - approx)
Line 93: Line 102:
 
end
 
end
  
---Converts a floating-point number to a fraction string.  
+
---Converts a floating-point number to a approximation of fraction as string.
---@param num number The floating-point number to convert.
+
---@param num number # The floating-point number to convert.
---@param maxDenominator number The maximum denominator for the fraction.
+
---@param maxDenominator number # The maximum denominator for the fraction.
---@return string The fraction string.
+
---@return string # The fraction as string ('numerator/denominator').
 
local function floatToFractionString(num, maxDenominator)
 
local function floatToFractionString(num, maxDenominator)
 
     local numerator, denominator = floatToFraction(num, maxDenominator)
 
     local numerator, denominator = floatToFraction(num, maxDenominator)
Line 106: Line 115:
  
 
---Formats a number up to a given number of decimal places, removing trailing zeros.
 
---Formats a number up to a given number of decimal places, removing trailing zeros.
---@param num number The number to format.
+
---@param num number # The number to format.
---@param digits number The number of decimal places to include (must be a non-negative integer).
+
---@param digits number # The number of decimal places to include (must be a non-negative integer).
---@return string The formatted number as a string.
+
---@return string # The formatted number as a string.
 
local function toFixed(num, digits)
 
local function toFixed(num, digits)
  digits = math.max(0, math.floor(digits))
+
    digits = math.max(0, math.floor(digits))
  local formatted = string.format("%." .. digits .. "f", num)
+
    local formatted = string.format("%." .. digits .. "f", num)
  formatted = formatted:gsub("%.?0+$", "")
+
    formatted = formatted:gsub("%.?0+$", "")
  return formatted
+
    return formatted
 
end
 
end
 +
  
 
function p.lootTable(frame)
 
function p.lootTable(frame)
  local args = frame:getParent().args
+
    local args = frame:getParent().args
  return p._lootTable(args)
+
    local output = tostring(p._lootTable(args))
 +
    return args.raw and output or frame:preprocess("<html>" .. output .. "</html>")
 
end
 
end
  
function p._lootTable(_args)
+
function p._lootTable(args)
     local sourceName = _args["name"] or _args["title"] or mw.title.getCurrentTitle().text
+
     local sourceName = args.name or args.title or mw.title.getCurrentTitle().text
 
     local sourceId, sourceTypeOrError = findSourceId(sourceName)
 
     local sourceId, sourceTypeOrError = findSourceId(sourceName)
 
     if not sourceId then
 
     if not sourceId then
Line 137: Line 148:
 
     end
 
     end
  
     ---@param name any
+
    local lang = mw.language.getContentLanguage()
     ---@return mw.html.element
+
 
     local function makeTh(name)
+
     ---@param name string # Wikitext.
 +
     ---@return mw.html # 'TH' element.
 +
     local function createTh(name)
 
         local th = mw.html.create("th")
 
         local th = mw.html.create("th")
 
             :addClass("headerSort")
 
             :addClass("headerSort")
 
             :attr("tabindex", 0)
 
             :attr("tabindex", 0)
            :attr("role", "button")
 
 
             :attr("title", "Sort ascending")
 
             :attr("title", "Sort ascending")
 
             :wikitext(name)
 
             :wikitext(name)
Line 149: Line 161:
 
     end
 
     end
  
     ---@param names any[]
+
     ---@param names string[] # Array of wikitext.
     ---@return mw.html.element
+
     ---@return mw.html # 'TR' element containing names as 'TH' elements.
     local function makeThs(names)
+
     local function createThs(names)
 
         local tr = mw.html.create("tr")
 
         local tr = mw.html.create("tr")
 
         for _, name in ipairs(names) do
 
         for _, name in ipairs(names) do
             tr:node(makeTh(name))
+
             tr:node(createTh(name))
 
         end
 
         end
 
         return tr
 
         return tr
 
     end
 
     end
  
     ---Creates a mw.html.element image for loot tables.
+
     ---Creates a 'A' element containing an image.
 
     ---@param name string # The name of the item.
 
     ---@param name string # The name of the item.
    ---@param href string # The URI of the item page. Should be "/p/" .. name
+
     ---@param src string # The URI to the item image.
     ---@param src string # The URI of the item image.
+
     ---@param width? string|integer # The width of the item image as integer.
     ---@param width string # The width of the item image as css param. Default is "auto".
+
     ---@param height? string|integer # The height of the item image as integer.
     ---@param height string? # The height of the item image as css param. Default is "auto".
+
     ---@return mw.html # mw.html 'A' element containing an image.
     ---@return mw.html.element
+
     local function createImg(name, src, alt, width, height)
     local function makeImg(name, alt, href, src, width, height, parent)
+
         local e = mw.html.create("a")
         local img = mw.html.create("img")
+
            :attr("href", tostring(mw.uri.localUrl(name)))
 +
            :attr("title", name)
 +
            :tag("img")
 
             :attr("src", src)
 
             :attr("src", src)
 
             :attr("alt", alt)
 
             :attr("alt", alt)
             :attr("width", width or "auto")
+
        if width then
 +
             e:attr("width", width)
 +
        end
 
         if height then
 
         if height then
             img:attr("height", height)
+
             e:attr("height", height)
 +
        end
 +
        return e:done()
 +
    end
 +
 
 +
    ---Formats the value of an item as string.
 +
    ---@param item table # Item table from Module:Items/data
 +
    ---@return string # The formatted value of the item as string, or '-' if no value.
 +
    local function createValueText(item)
 +
        if item.id == 1 then -- Gold
 +
            return "1"
 
         end
 
         end
         local a = (parent or mw.html.create("td"))
+
         return item.value and lang:formatNum(item.value) or "-"
            :tag("a")
+
    end
            :attr("href", mw.uri.encode(href, "WIKI"))
+
 
             :attr("title", name)
+
    ---Formats the Item drop quantities as string.
            :node(img)
+
    ---@param item Item # Item table from Module:Loot/data
        return a:done()
+
    ---@return string # The formatted quantity range as string.
 +
    local function createQuantityText(item)
 +
        if item.minAmount == item.maxAmount then
 +
             return tostring(item.minAmount)
 +
        end
 +
        return lang:formatNum(item.minAmount) .. " - " .. lang:formatNum(item.maxAmount)
 
     end
 
     end
  
 
     local tableHtml = mw.html.create("table")
 
     local tableHtml = mw.html.create("table")
 
         :addClass("wikitable sortable jquery-tablesorter")
 
         :addClass("wikitable sortable jquery-tablesorter")
         :node(makeThs({ "Image", "Item", "Quantity", "Rarity", "Price", "Tradeable", "Leagues" }))
+
         :node(createThs({ "Image", "Item", "Quantity", "Rarity", "Value", "Tradeable", "Leagues" }))
  
 
     for _, item in ipairs(loot) do
 
     for _, item in ipairs(loot) do
Line 191: Line 222:
 
         local src = item2.itemImage
 
         local src = item2.itemImage
 
         src = src:sub(1, 1) ~= "/" and "/" .. src or src
 
         src = src:sub(1, 1) ~= "/" and "/" .. src or src
         local imageHtml = makeImg(
+
         local imageHtml = mw.html.create("td")
             item2.name,
+
             :node(createImg(
            item2.name .. (item2.extraTooltip and "\n" .. item2.extraTooltip or ""),
+
                item2.name,
            "/p/" .. item2.name,
+
                "https://www.play.idlescape.com" .. src,
            "https://www.play.idlescape.com" .. src,
+
                item2.name .. (item2.extraTooltip and "\n" .. item2.extraTooltip or ""),
             "45"
+
                args.width or 45,
        )
+
                args.height or nil
 +
             ))
 
         local itemHtml = mw.html.create("td")
 
         local itemHtml = mw.html.create("td")
 
             :tag("a")
 
             :tag("a")
             :attr("href", "/p/" .. item2.name)
+
             :attr("href", tostring(mw.uri.localUrl(item2.name)))
 
             :attr("title", item2.name)
 
             :attr("title", item2.name)
 
             :wikitext(item2.name)
 
             :wikitext(item2.name)
 
             :done()
 
             :done()
 
         local quantityHtml = mw.html.create("td")
 
         local quantityHtml = mw.html.create("td")
             :wikitext(item.minAmount .. " - " .. item.maxAmount)
+
             :wikitext(createQuantityText(item))
 
         local chanceHtml = mw.html.create("td")
 
         local chanceHtml = mw.html.create("td")
 
             :wikitext(toFixed(item.chance * 100, 3) .. "%")
 
             :wikitext(toFixed(item.chance * 100, 3) .. "%")
 
         local priceHtml = mw.html.create("td")
 
         local priceHtml = mw.html.create("td")
             :wikitext(item2.value or "-")
+
             :wikitext(createValueText(item2))
 
         local tradeableHtml = mw.html.create("td")
 
         local tradeableHtml = mw.html.create("td")
 
             :wikitext(item2.tradeable and "Yes" or "No")
 
             :wikitext(item2.tradeable and "Yes" or "No")
         local leaguesHtml = mw.html.create("td")
+
         local leagueHtml = mw.html.create("td")
         --TODO: Add indicator if league is active, the field is present in Module:Leagues/data but is incorrect
+
         --TODO: Add indicator if league is inactive, the field is present in
 +
        --      leagueList.ts / Module:Leagues/data but some are incorrect
 
         if item.allowedLeagues then
 
         if item.allowedLeagues then
 
             for _, leagueId in ipairs(item.allowedLeagues) do
 
             for _, leagueId in ipairs(item.allowedLeagues) do
 
                 local league = leagueData[tostring(leagueId)]
 
                 local league = leagueData[tostring(leagueId)]
 
                 if league then
 
                 if league then
                     makeImg(
+
                     leagueHtml:node(createImg(
 
                         league.name,
 
                         league.name,
 +
                        "https://www.play.idlescape.com" .. league.icon,
 
                         league.name,
 
                         league.name,
                         "/p/" .. league.name,
+
                         args.width or 45,
                         "https://www.play.idlescape.com" .. league.icon,
+
                         args.height or nil
                        "45",
+
                     ))
                        "45",
 
                        leaguesHtml
 
                     )
 
 
                 else
 
                 else
                     leaguesHtml:wikitext("?")
+
                     leagueHtml:wikitext("?")
 
                 end
 
                 end
 
             end
 
             end
 
         else
 
         else
             leaguesHtml:wikitext("All")
+
             leagueHtml:wikitext("All")
 
         end
 
         end
 +
 
         local rowHtml = mw.html.create("tr")
 
         local rowHtml = mw.html.create("tr")
 
             :node(imageHtml)
 
             :node(imageHtml)
Line 241: Line 273:
 
             :node(priceHtml)
 
             :node(priceHtml)
 
             :node(tradeableHtml)
 
             :node(tradeableHtml)
             :node(leaguesHtml)
+
             :node(leagueHtml)
  
 
         tableHtml:node(rowHtml)
 
         tableHtml:node(rowHtml)
 
     end
 
     end
  
     return tostring(tableHtml)
+
     return tableHtml
 
end
 
end
  
 
return p
 
return p

Revision as of 16:26, 3 April 2025


local p = {}
local findId = require("Module:FindId")
local lootData = mw.loadData("Module:Loot/data")
local itemData = mw.loadData("Module:Items/data")
local leagueData = mw.loadData("Module:Leagues/data")


---Creates a mw.html 'DIV' element containing an "error" message.
---@param message string # Message as wikitext.
---@return mw.html # The message wrapped in a mw.html 'DIV' element as red text.
local function createErrorMessage(message)
    local e = mw.html.create("div")
        :css("color", "red")
        :wikitext(message)
    return e
end

---Finds the source ID for a given name.
---@param name string # The name of the source.
---@return number|nil # The source ID (monsterID or locationID), or nil if the source is not found.
---@return string|mw.html # The source type ("monster" or "location"), or mw.html element with error message if the source is not found.
local function findSourceId(name)
    for _, source in ipairs({ "monster", "location" }) do
        local id = findId._findId({ name, source })
        if id ~= "id not found" then
            return id, source
        end
    end
    local errorMessage = createErrorMessage("No monster or location named '" ..
    name .. "'. The Module:Loot/data may be outdated.")
    return nil, errorMessage
end

---@class Item
---@field allowedLeagues? number[] # The leagues in which the item can be found. Found in all leagues if nil.
---@field id integer # The item ID.
---@field chance number # The base chance of the item being dropped.
---@field minAmount integer # The minimum base amount of the item that can be dropped.
---@field maxAmount integer # The maximum base amount of the item that can be dropped.

---Finds the first item in the loot table that matches the given item ID.
---@param loot table # The loot table from Module:Loot/data.
---@param id string|number # The ID of the item to find.
---@return Item? # The item data, or nil if the item is not found.
---@return string? # The monsterId where the item was found ("-1" for locations), or nil if the item is not found.
---@return string? # The locationId where the item was found, or nil if the item is not found.
local function getFirstLootMatch(loot, id)
    id = tostring(id)
    for locationId, location in pairs(loot) do
        for sourceId, source in pairs(location) do
            for _, item in ipairs(source) do
                if item.id == id then
                    return item, sourceId, locationId
                end
            end
        end
    end
end

---Finds the first source in the loot table that matches the given sourceId.
---@param loot table # The loot table from Module:Loot/data.
---@param id string|number # The ID of the source to find.
---@return Item[]? # The source data, or nil if the source is not found.
---@return string? # The locationId where the source was found, or nil if the source is not found.
local function getFirstSourceMatch(loot, id)
    id = tostring(id)
    for locationId, location in pairs(loot) do
        for sourceId, source in pairs(location) do
            if sourceId == id then
                return source, locationId
            end
        end
    end
end

--TODO: Add option to show percentages as fractions

---Converts a floating-point number to a approximation of fraction.
---@param num number # The floating-point number to convert.
---@param maxDenominator number # The maximum denominator for the fraction.
---@return number # The numerator of the fraction.
---@return number # The denominator of the fraction.
local function floatToFraction(num, maxDenominator)
    local sign = num < 0 and -1 or 1
    num = math.abs(num)

    local bestNumerator, bestDenominator = 1, 1
    local minDifference = math.huge

    for d = 1, maxDenominator do
        local n = math.floor(num * d + 0.5)
        local approx = n / d
        local difference = math.abs(num - approx)

        if difference < minDifference then
            bestNumerator, bestDenominator = n, d
            minDifference = difference
        end
    end

    return sign * bestNumerator, bestDenominator
end

---Converts a floating-point number to a approximation of fraction as string.
---@param num number # The floating-point number to convert.
---@param maxDenominator number # The maximum denominator for the fraction.
---@return string # The fraction as string ('numerator/denominator').
local function floatToFractionString(num, maxDenominator)
    local numerator, denominator = floatToFraction(num, maxDenominator)
    if denominator == 1 then
        return tostring(numerator)
    end
    return string.format("%d/%d", numerator, denominator)
end

---Formats a number up to a given number of decimal places, removing trailing zeros.
---@param num number # The number to format.
---@param digits number # The number of decimal places to include (must be a non-negative integer).
---@return string # The formatted number as a string.
local function toFixed(num, digits)
    digits = math.max(0, math.floor(digits))
    local formatted = string.format("%." .. digits .. "f", num)
    formatted = formatted:gsub("%.?0+$", "")
    return formatted
end


function p.lootTable(frame)
    local args = frame:getParent().args
    local output = tostring(p._lootTable(args))
    return args.raw and output or frame:preprocess("<html>" .. output .. "</html>")
end

function p._lootTable(args)
    local sourceName = args.name or args.title or mw.title.getCurrentTitle().text
    local sourceId, sourceTypeOrError = findSourceId(sourceName)
    if not sourceId then
        return sourceTypeOrError
    end

    local loot = getFirstSourceMatch(lootData, sourceId)
    if not loot then
        local div = mw.html.create("div")
            :css("color", "red")
            :wikitext("No loot found for monster or location named '" .. sourceName ..
                "' The Module:Loot/data may be outdated.")
        return div
    end

    local lang = mw.language.getContentLanguage()

    ---@param name string # Wikitext.
    ---@return mw.html # 'TH' element.
    local function createTh(name)
        local th = mw.html.create("th")
            :addClass("headerSort")
            :attr("tabindex", 0)
            :attr("title", "Sort ascending")
            :wikitext(name)
        return th
    end

    ---@param names string[] # Array of wikitext.
    ---@return mw.html # 'TR' element containing names as 'TH' elements.
    local function createThs(names)
        local tr = mw.html.create("tr")
        for _, name in ipairs(names) do
            tr:node(createTh(name))
        end
        return tr
    end

    ---Creates a 'A' element containing an image.
    ---@param name string # The name of the item.
    ---@param src string # The URI to the item image.
    ---@param width? string|integer # The width of the item image as integer.
    ---@param height? string|integer # The height of the item image as integer.
    ---@return mw.html # mw.html 'A' element containing an image.
    local function createImg(name, src, alt, width, height)
        local e = mw.html.create("a")
            :attr("href", tostring(mw.uri.localUrl(name)))
            :attr("title", name)
            :tag("img")
            :attr("src", src)
            :attr("alt", alt)
        if width then
            e:attr("width", width)
        end
        if height then
            e:attr("height", height)
        end
        return e:done()
    end

    ---Formats the value of an item as string.
    ---@param item table # Item table from Module:Items/data
    ---@return string # The formatted value of the item as string, or '-' if no value.
    local function createValueText(item)
        if item.id == 1 then -- Gold
            return "1"
        end
        return item.value and lang:formatNum(item.value) or "-"
    end

    ---Formats the Item drop quantities as string.
    ---@param item Item # Item table from Module:Loot/data
    ---@return string # The formatted quantity range as string.
    local function createQuantityText(item)
        if item.minAmount == item.maxAmount then
            return tostring(item.minAmount)
        end
        return lang:formatNum(item.minAmount) .. " - " .. lang:formatNum(item.maxAmount)
    end

    local tableHtml = mw.html.create("table")
        :addClass("wikitable sortable jquery-tablesorter")
        :node(createThs({ "Image", "Item", "Quantity", "Rarity", "Value", "Tradeable", "Leagues" }))

    for _, item in ipairs(loot) do
        local item2 = itemData[tostring(item.id)]
        --IS itemList.ts has some incorrect URIs, e.g. for Feather
        local src = item2.itemImage
        src = src:sub(1, 1) ~= "/" and "/" .. src or src
        local imageHtml = mw.html.create("td")
            :node(createImg(
                item2.name,
                "https://www.play.idlescape.com" .. src,
                item2.name .. (item2.extraTooltip and "\n" .. item2.extraTooltip or ""),
                args.width or 45,
                args.height or nil
            ))
        local itemHtml = mw.html.create("td")
            :tag("a")
            :attr("href", tostring(mw.uri.localUrl(item2.name)))
            :attr("title", item2.name)
            :wikitext(item2.name)
            :done()
        local quantityHtml = mw.html.create("td")
            :wikitext(createQuantityText(item))
        local chanceHtml = mw.html.create("td")
            :wikitext(toFixed(item.chance * 100, 3) .. "%")
        local priceHtml = mw.html.create("td")
            :wikitext(createValueText(item2))
        local tradeableHtml = mw.html.create("td")
            :wikitext(item2.tradeable and "Yes" or "No")
        local leagueHtml = mw.html.create("td")
        --TODO: Add indicator if league is inactive, the field is present in
        --      leagueList.ts / Module:Leagues/data but some are incorrect
        if item.allowedLeagues then
            for _, leagueId in ipairs(item.allowedLeagues) do
                local league = leagueData[tostring(leagueId)]
                if league then
                    leagueHtml:node(createImg(
                        league.name,
                        "https://www.play.idlescape.com" .. league.icon,
                        league.name,
                        args.width or 45,
                        args.height or nil
                    ))
                else
                    leagueHtml:wikitext("?")
                end
            end
        else
            leagueHtml:wikitext("All")
        end

        local rowHtml = mw.html.create("tr")
            :node(imageHtml)
            :node(itemHtml)
            :node(quantityHtml)
            :node(chanceHtml)
            :node(priceHtml)
            :node(tradeableHtml)
            :node(leagueHtml)

        tableHtml:node(rowHtml)
    end

    return tableHtml
end

return p