Module:ItemsNav

local p = {} local h = {}

local data = mw.loadData("Module:Imagenav/Data") local tab2 = require("Module:Tab2") local terminology = require("Module:Term") local utilsError = require("Module:UtilsError") local utilsTable = require("Module:UtilsTable") local validate = require("Module:ArgsUtil").validate

local classes = { stateContainer = "state-container", states = "state-container__states", state = "state-container__state", defaultState = "state-container__state state-container__state--active", controls = "state-container__controls state-container__controls--vertical", stateControlForward = "state-control state-control-forward", stateControlBack = "state-control state-control-back", stateControlDisabled = "state-control--disabled", } local defaultSpacing = "8px"

local currentPage = mw.title.getCurrentTitle.text

function p.Main(frame) local args = frame:getParent.args local output = p.main({		game = args[1],		type = args[2],	}) return output end

local validators = { type = { required = true, values = utilsTable.keys(data) },	game = { required = true, values = function(args) return utilsTable.keys(data[args.type]) end } }

function p.main(args) local err = validate(args, validators) if err then utilsError.logWarnings(err) return utilsError.printErrorCategories(utilsError.INVALID_TEMPLATE_ARGUMENTS) end local config = data[args.type][args.game] return h.createTabbedNav(config, args.game) end

function h.createTabbedNav(config, game) if not config[1].tab then return tostring(h.createNav(config[1], game)) end local tabs = {} local defaultTab for i, v in pairs(config) do		local tab, terms = h.createNav(v, game) table.insert(tabs, {			tabName = v.tab,			tabContent = tostring(tab),		}) if not defaultTab then defaultTab = utilsTable.hash(terms)[currentPage] and i		end end defaultTab = defaultTab or 1 return tab2.Main(tabs, defaultTab, "top", nil, nil, nil, nil, "center") end

function h.createNav(config, game) if config.imagemap then return h.createMapNav(config.imagemap, game) end if config.row then return h.createRowNav(config.row, game) end end

function h.createRowNav(data, game, options) local vertical = options and options.vertical local pages = {} local row = mw.html.create("div") :css({			["display"] = "flex",			["flex-wrap"] = "wrap",			["flex-direction"] = (vertical and "column") or "row",			["justify-content"] = "center",			["align-items"] = "flex-end",			["margin-top"] = "-" .. defaultSpacing,			["margin-left"] = "-" .. (data.spacing or defaultSpacing),		}) for _, item in ipairs(data.items) do		table.insert(pages, item) local page, term = h.getLinkParts(game, item) local fileName = data.filePattern:format(term) local fileWidth = data.scale and mw.title.new(fileName).file.width local thumbnailWidth = fileWidth and fileWidth * data.scale local size = thumbnailWidth and ("|%spx"):format(thumbnailWidth) or "" local rowItem = ("link=%s|%s"):format(fileName, size, page, term) row :tag("div") :wikitext(rowItem) :css({				["margin-top"] = defaultSpacing,				["margin-left"] = (data.spacing or defaultSpacing),			}) :done end return row, pages end function h.createMapNav(config, game) local baseImageMap, pages = h.createImageMap(game, config) if not config.upgrades then return baseImageMap, pages end

local leftColumn if config.leftColumn then leftColumn, columnPages = h.createRowNav(config.leftColumn, game, { vertical = true }) pages = utilsTable.mergeArrays(pages, columnPages) end local upgradeMaps, upgradePages, defaultState = h.resolveUpgradeMaps(config) local states = utilsTable.mergeArrays({ baseImageMap }, upgradeMaps) pages = utilsTable.mergeArrays(pages, upgradePages) return h.createStatefulMenuMap(states, defaultState, config.maxWidth, leftColumn), pages end

function h.resolveUpgradeMaps(data) local defaultState = 1 local resolvedMapData = {} local pages = {} local initialAreas = utilsTable.shallowClone(data.areas) for i, upgradeData in pairs(data.upgrades) do		local addedPages = {} local changedAreas = utilsTable.mapInPlace(initialAreas, function(area)			local change = upgradeData.changes[area.page]			if not change then				return area			end			if change == "" then				return nil			end			if type(change) == "string" then				change = { page = change }			end			table.insert(addedPages, change.page)			return {				area = change.area or area.area,				page = change.page or area.page,				display = change.display,			}		end) changedAreas = utilsTable.removeFalseEntries(changedAreas) local newAreas = utilsTable.shallowClone(upgradeData.areas or {}) local upgradeAreas = utilsTable.mergeArrays(newAreas, changedAreas) local imageMap, mapPages = h.createImageMap(game, {			image = upgradeData.image,			areas = upgradeAreas,			maxWidth = data.maxWidth,		}) table.insert(resolvedMapData, imageMap)

pages = utilsTable.mergeArrays(pages, addedPages) defaultState = (utilsTable.keyOf(addedPages, currentPage) and i+1) or defaultState initialAreas = upgradeAreas end return resolvedMapData, pages, defaultState end

function h.createStatefulMenuMap(states, defaultState, maxWidth, leftColumn) local statesNode = mw.html.create("div") :addClass(classes.states) :css({				["display"] = "flex",				["align-items"] = "center",				["justify-content"] = "center",				["flex"] = "1",				["max-width"] = maxWidth and maxWidth .. "px",		}) for i, state in ipairs(states) do 		local default = i == defaultState statesNode :tag("div") :addClass(default and classes.defaultState or classes.state) :css("flex", "1") :wikitext(state) :done end local controlCss = { ["height"] = "12%", ["min-height"] = "32px", ["margin-left"] = "15%", }	local controlsNode = mw.html.create("div") :addClass(classes.controls) :tag("div") :addClass(classes.stateControlForward) :addClass(defaultState == #states and classes.stateControlDisabled or nil) :css(controlCss) :wikitext("") :done :tag("div") :addClass(classes.stateControlBack) :addClass(defaultState == 1 and classes.stateControlDisabled or nil) :css(controlCss) :wikitext("") :done if leftColumn then leftColumn:css("margin-right", defaultSpacing) end local stateContainer = mw.html.create("div") :addClass(classes.stateContainer) :node(mw.clone(controlsNode):css("visibility", "hidden")) --Hack to "balance" the left side and keep the content centered :node(leftColumn) :node(statesNode) :node(controlsNode) :node(leftColumn and mw.clone(leftColumn):css("visibility", "hidden")) :css({			["display"] = "flex",			["justify-content"] = "center",		}) return stateContainer end

function h.createImageMap(game, data) local lines = { data.image } local pages = {} for _, area in ipairs(data.areas) do		local link = ("[%s %s]"):format(h.getLinkParts(game, area.page, area.display)) table.insert(pages, area.page) table.insert(lines, area.area .. link) end table.insert(lines, " desc none") local imagemap = mw.getCurrentFrame:extensionTag("imagemap", table.concat(lines, "\n")) local responsiveImageMap = mw.html.create("div") :addClass("resizable-image-map") :css({			["max-width"] = data.maxWidth and data.maxWidth .. "px",			["margin"] = "0 auto",		}) :wikitext(imagemap) return tostring(responsiveImageMap), pages end

-- For rendering a page link as an external link, so as to not spam Special:WhatLinksHere function h.getLinkParts(game, page, display) local url = mw.title.new(page):fullUrl if display then return url, display end local row = terminology.fetchRow({ term = page, game = game }) local alt = row and row.term or page return url, alt end

return p