Editing Module:IconGive feedback
Jump to navigation
Jump to search
The edit can be undone.
Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.
| Latest revision | Your text | ||
| Line 1: | Line 1: | ||
local p = {} | local p = {} | ||
-- Load language codes | |||
local | local lang_codes = mw.loadJsonData("Data:LangCodes.json") | ||
local | -- Cache for compiled icon data, split by type (persists during page render) | ||
local heroCacheBuilt = false | |||
local abilityCacheBuilt = false | |||
local itemCacheBuilt = false | |||
local iconDataCache = {} | |||
local cachedLangCode, cachedLangData | |||
for | -- Cache for expensive existence checks (persists during page render) | ||
local existenceCache = {} | |||
local | -- Heroes/Items that don't have images uploaded yet | ||
local missingImageOverrides = { | |||
-- Unreleased Heroes | |||
["boho"] = "Module-Icon missing.png", | |||
["druid"] = "Module-Icon missing.png", | |||
["fortuna"] = "Module-Icon missing.png", | |||
["graf"] = "Module-Icon missing.png", | |||
["gunslinger"] = "Module-Icon missing.png", | |||
["skyrunner"] = "Module-Icon missing.png", | |||
["swan"] = "Module-Icon missing.png", | |||
["thumper"] = "Module-Icon missing.png", | |||
-- Add any others that show red links | |||
} | |||
local | -- Build lookup table from bot-uploaded JSONs, only loading the relevant JSON per type | ||
-- to reduce CPU cost on pages with many icon calls | |||
local function buildIconData(iconType) | |||
-- Only load hero data if not already built | |||
if iconType == "hero" and not heroCacheBuilt then | |||
local heroData = mw.loadJsonData("Data:HeroData.json") | |||
-- Process Heroes | |||
for heroKey, hero in pairs(heroData) do | |||
if type(hero) == "table" and hero.Name then | |||
local | local name = hero.Name | ||
local | local lowerName = name:lower() | ||
local | -- Check for missing image override | ||
local imageName = missingImageOverrides[lowerName] or (name .. ".png") | |||
iconDataCache[lowerName] = { | |||
name = name, | |||
key = heroKey, | |||
link = name, | |||
image = imageName, | |||
type = "hero" | |||
} | |||
end | |||
end | |||
heroCacheBuilt = true | |||
-- | -- Only load ability data (and hero data for parent linking) if not already built | ||
local | elseif iconType == "ability" and not abilityCacheBuilt then | ||
local heroData = mw.loadJsonData("Data:HeroData.json") | |||
local abilityData = mw.loadJsonData("Data:AbilityData.json") | |||
-- Track which hero owns which ability (for linking) | |||
local abilityToHero = {} | |||
for heroKey, hero in pairs(heroData) do | |||
end | if type(hero) == "table" and hero.BoundAbilities then | ||
for slot, ability in pairs(hero.BoundAbilities) do | |||
if ability.Key then | |||
abilityToHero[ability.Key] = { | |||
heroName = hero.Name, | |||
abilityName = ability.Name | |||
} | |||
end | |||
end | |||
end | |||
end | |||
-- | -- Process Abilities | ||
for abilityKey, ability in pairs(abilityData) do | |||
if type(ability) == "table" and ability.Name then | |||
local name = ability.Name | |||
local lowerName = name:lower() | |||
local parentInfo = abilityToHero[abilityKey] | |||
-- If we already have this ability name, prefer the one linked to a hero | |||
if iconDataCache[lowerName] and iconDataCache[lowerName].type == "ability" then | |||
local existingHasParent = iconDataCache[lowerName].link ~= iconDataCache[lowerName].name | |||
local newHasParent = parentInfo ~= nil | |||
-- Skip if existing is linked to hero but this one isn't (prevents orphans from overwriting real ones) | |||
if existingHasParent and not newHasParent then | |||
-- Skip this orphan ability | |||
else | |||
-- Overwrite: new has parent and existing doesn't, or both/neither have parents | |||
local link = parentInfo and parentInfo.heroName or name | |||
iconDataCache[lowerName] = { | |||
name = name, | |||
key = abilityKey, | |||
link = link, | |||
image = name .. ".png", | |||
type = "ability", | |||
class = "theme" | |||
} | |||
end | |||
else | |||
-- No existing entry, add normally | |||
local link = parentInfo and parentInfo.heroName or name | |||
iconDataCache[lowerName] = { | |||
name = name, | |||
key = abilityKey, | |||
link = link, | |||
image = name .. ".png", | |||
type = "ability", | |||
class = "theme" | |||
} | |||
end | |||
end | |||
end | end | ||
abilityCacheBuilt = true | |||
-- Only load item data if not already built | |||
elseif iconType == "item" and not itemCacheBuilt then | |||
local itemData = mw.loadJsonData("Data:ItemData.json") | |||
-- Process Items | |||
for itemKey, item in pairs(itemData) do | |||
if type(item) == "table" and item.Name then | |||
local name = item.Name | |||
local lowerName = name:lower() | |||
iconDataCache[lowerName] = { | |||
name = name, | |||
key = itemKey, | |||
link = name, | |||
image = name .. ".png", | |||
type = "item" | |||
} | |||
end | |||
end | end | ||
itemCacheBuilt = true | |||
end | end | ||
end | end | ||
-- | -- Get icon data by name and type (with alias support) | ||
local function getIconData(iconName, iconType) | |||
buildIconData(iconType) | |||
function | local lowerName = iconName:lower() | ||
local lowerName = | |||
-- Check main lookup | |||
if iconDataCache[lowerName] then | |||
return iconDataCache[lowerName], nil | |||
end | end | ||
local | -- Check aliases (for edge cases like "doorman" -> "the doorman") | ||
local aliases = { | |||
["doorman"] = "the doorman", | |||
["mo and krill"] = "mo & krill", | |||
["mo"] = "mo & krill", | |||
["krill"] = "mo & krill", | |||
["curse"] = "cursed relic", | |||
["debuff remover"] = "dispel magic" | |||
} | |||
if aliases[lowerName] then | |||
local entry = iconDataCache[aliases[lowerName]] | |||
if entry then | |||
return entry, nil | |||
end | |||
end | end | ||
return nil, string.format("[[:Category:Module:Icon ERROR|Icon not found]] ('%s'). [[Category:Module:Icon ERROR]]", iconName) | |||
end | |||
if | -- Language handling | ||
return | local function getLangData() | ||
if cachedLangData then | |||
return cachedLangCode, cachedLangData | |||
end | end | ||
local title = mw.title.getCurrentTitle().fullText | |||
local subpageLang = title:match("/([^/]+)$") | |||
local langCode = (subpageLang and lang_codes[subpageLang]) and subpageLang or "en" | |||
local | local langData = {} | ||
if langCode ~= "en" then | |||
local success, data = pcall(function() | |||
return mw.loadJsonData("Data:Lang " .. langCode .. ".json") | |||
end) | |||
if success and type(data) == "table" then | |||
langData = data | |||
end | |||
end | |||
cachedLangCode = langCode | |||
cachedLangData = langData | |||
return langCode, langData | |||
end | |||
-- Main render function | |||
function p.render(frame) | |||
local args = frame:getParent().args | |||
local name = args[1] or "" | |||
local customText = args.l1 or "" | |||
-- type is passed via #invoke so must be read from frame.args, not parent args | |||
local iconType = frame.args.type or "item" | |||
-- Set Defaults | |||
local size = args.size or "20px" | local size = args.size or "20px" | ||
local noLink = args["no-link"] == "true" | local noLink = args["no-link"] == "true" | ||
local iconOnly = args["icon-only"] == "true" | local iconOnly = args["icon-only"] == "true" | ||
-- Scan unnamed parameters | |||
for k, v in pairs(args) do | for k, v in pairs(args) do | ||
if type(k) == "number" and k > 1 then | if type(k) == "number" and k > 1 then | ||
local val = mw.text.trim(v) | local val = mw.text.trim(v) | ||
if val == "icon-only" then | if val == "icon-only" then | ||
iconOnly = true | iconOnly = true | ||
| Line 170: | Line 230: | ||
end | end | ||
local | -- Get icon data automatically from JSON, using type to limit which JSON is loaded | ||
local iconData, err = getIconData(name, iconType) | |||
if not iconData then | |||
return "Error: " .. err | |||
end | |||
local displayName | local langCode, langData = getLangData() | ||
if customText then | |||
-- Determine display name | |||
local displayName = "" | |||
if customText ~= "" then | |||
displayName = customText | displayName = customText | ||
elseif | elseif iconData.key and langData[iconData.key] then | ||
displayName = langData[iconData.key] | |||
displayName = | |||
else | else | ||
displayName = | displayName = iconData.name | ||
end | end | ||
-- Localize link if on translated subpage | |||
local baseLink = iconData.link or "" | |||
local link = baseLink | local link = baseLink | ||
if | if langCode ~= "en" and baseLink ~= "" then | ||
link = baseLink .. "/" .. langCode | link = baseLink .. "/" .. langCode | ||
end | end | ||
-- | -- Style logic (only abilities get theme class by default) | ||
local class = iconData.class or "" | |||
local style | local style | ||
if | if class:find("theme", 1, true) then | ||
style = 'class="module-icon-ability" style="position:relative; bottom:2px;"' | style = 'class="module-icon-ability" style="position:relative; bottom:2px;"' | ||
else | else | ||
style = ' | style = 'style="position:relative; bottom:2px;"' | ||
end | end | ||
-- Generate icon HTML | |||
local imageLink = noLink and "" or link | local imageLink = noLink and "" or link | ||
local | local iconHtml = string.format('<span %s>[[File:%s|%s|link=%s]]</span>', style, iconData.image, size, imageLink) | ||
if iconOnly or displayName == "" then | if iconOnly or displayName == "" then | ||
| Line 222: | Line 274: | ||
local textHtml = (noLink or link == "") and displayName or string.format('[[%s|%s]]', link, displayName) | local textHtml = (noLink or link == "") and displayName or string.format('[[%s|%s]]', link, displayName) | ||
return '<span style="white-space:nowrap;">' .. iconHtml .. " " .. textHtml .. '</span>' | |||
end | end | ||
return p | return p | ||