Module:PerkSearchResultsView

From Against the Storm Official Wiki
Revision as of 05:59, 13 December 2023 by Glyph (talk | contribs) (Fixed bug in previous edit)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

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

--- @module PerkSearchResultsView
local PerkSearchResultsView = {}



local PerksData = require("Module:PerksData")



--region Private member variables

beginning = ""
middle = ""
ending = ""

local imgS = mw.getCurrentFrame():expandTemplate{ title = "ImgS" }
local imgM = mw.getCurrentFrame():expandTemplate{ title = "ImgM" }
local imgL = mw.getCurrentFrame():expandTemplate{ title = "ImgL" }

local amberIcon = mw.getCurrentFrame():expandTemplate{ title= "Amber" }

--endregion



--region Private constants

local PARAMETER_DISPLAY_OVERRIDE_LIST = "list"

-- wikitable classes for tables
local CLASS_WIKITABLE_SORTABLE_COLLAPSIBLE = "wikitable sortable mw-collapsible"
local CSS_TD_SHRINK_TO_FIT = "max-width: 20em"
local CLASS_UNSORTABLE = "unsortable"

local RARITY_COLORS = {
	[PerksData.RARITY_UNCOMMON] = "#15cf00",
	[PerksData.RARITY_RARE] = "#015db9",
	[PerksData.RARITY_EPIC] = "#7f27b9",
	[PerksData.RARITY_LEGENDARY] = "#da6d00",
	[PerksData.NONE] = "#444444"
}

local BORDER_SIZES = {
	[imgS] = "1.5px",
	[imgM] = "2px",
	[imgL] = "4px"
}

-- Shortcut markup that's easier for Lua string concatenations
local NBSP = " "
local BLANK = "—"

--endregion



--region Private methods




---
--- Formats a standard link to a perk. Does not invoke the template, but it
--- could be modified to do so in the future.
---
---@param perkName string the display name of the perk
---@param perkIcon string the filename for the icon
---@param iconSize string representing the desired size of the icon
---@param borderColor string the color of the border to put on the icon
---@return string an assembled link, with icon and text
local function perkLink(perkName, perkIcon, iconSize, borderColor)

	if not perkName or not perkIcon or not iconSize then
		error("Perk parameters cannot be nil.")
	end

	local displayStyle = "display: inline-block;"

    --- Set absolute height of border span to stop it being bigger than the image
    if iconSize == imgS then
        displayStyle = displayStyle .. "height: 18px;"
    end

	local startBorderSpan = "<span style=\" " .. displayStyle ..
			" border: " .. BORDER_SIZES[iconSize] .. " solid " .. borderColor .. "\">"
	local endBorderSpan = "</span>"

    --- Hacky fix for weird image offset
    if iconSize == imgS then 
        --- Add a second containing span to move the image into position
        startBorderSpan = startBorderSpan ..
          "<span style=\"display: block; transform: translateY(-4px)\">"
        endBorderSpan = "</span></span>"
    end

	local iconPart = "[[File:" .. perkIcon .. "|" .. iconSize ..
			"|alt=" .. perkName .. "|link=" .. perkName .. "]]"

	local linkPart = NBSP .. "[[" .. perkName .. "]]"

	return startBorderSpan .. iconPart .. endBorderSpan .. linkPart
end



---
--- Creates a header row under the provided MediaWiki HTML table entity.
---
---@param tableNode table MediaWiki HTML table entity
---@param needsPriceColumn boolean if the table need to include a price column
local function makeStandardPerkTableHeaderRow(tableNode, needsPriceColumn)

	local row = tableNode:tag("tr")

	row:tag("th"):wikitext("Perk"):done()
	row:tag("th"):wikitext("Rarity"):done()
	row:tag("th"):addClass(CLASS_UNSORTABLE):wikitext("Description"):done()
	row:tag("th"):addClass(CLASS_UNSORTABLE):wikitext("Source(s)"):done()
	if needsPriceColumn then
		row:tag("th"):wikitext("Price"):done()
	end

	row:done():newline()
end



---
--- Returns a standard MediaWiki HTML table entity created with standard
--- display and interactivity classes.
---
---@return table standard MediaWiki HTML table entity
local function startNewWikiTable()

	return mw.html.create("table"):addClass(CLASS_WIKITABLE_SORTABLE_COLLAPSIBLE):newline()
end



---
--- Makes the content of the name column under the provided HTML table row
--- entity.
---
---@param htmlTableRow table MediaWiki HTML table row entity
---@param perkName string the name of the building
---@param perkIcon string the filename for the icon
---@param borderColor string the color of the border to put on the icon
local function makeNameColumn(htmlTableRow, perkName, perkIcon, borderColor)

	htmlTableRow:tag("td"):wikitext(perkLink(perkName, perkIcon, imgL, borderColor)):done():newline()
end



---
--- Makes the content of the name column under the provided HTML table row
--- entity.
---
---@param htmlTableRow table MediaWiki HTML table row entity
---@param rarity string the rarity of the perk
local function makeRarityColumn(htmlTableRow, rarity)

	htmlTableRow:tag("td"):wikitext(rarity):done():newline()
end



---
--- Makes the content of the description column under the provided HTML table
--- row node.
---
---@param htmlTableRow table MediaWiki HTML table row entity
---@param description string the long description
local function makeDescriptionColumn(htmlTableRow, description)

	htmlTableRow:tag("td"):cssText(CSS_TD_SHRINK_TO_FIT)
			:wikitext(description):done():newline()
end



---
--- Builds an unordered bulleted list based on the specified perk's sources.
---
---@param htmlTableRow table the MediaWiki html node we're building on
---@param isFromCornerstone boolean whether the perk is a Cornerstone
---@param isFromEvent boolean whether the perk is a reward from an Event
---@param isFromOrder boolean whether the perk is a reward from an Order
---@param isFromTrader boolean whether the perk is purchasable from a Trader
local function makeSourcesColumn(htmlTableRow, isFromCornerstone, isFromEvent, isFromOrder, isFromTrader)

	local td = htmlTableRow:tag("td")
	local list = td:tag("ul")

	if isFromCornerstone then
		list:tag("li"):wikitext("as a Cornerstone"):done()
	end
	if isFromEvent then
		list:tag("li"):wikitext("from Glade Event rewards"):done()
	end
	if isFromOrder then
		list:tag("li"):wikitext("from Order rewards"):done()
	end
	if isFromTrader then
		list:tag("li"):wikitext("purchased from a Trader"):done()
	end

	list:done()
	td:done()
end



---
--- Adds a table cell containing the price and the amber icon
---
---@param htmlTableRow table the Mediawiki HTML node we're building on
---@param price number value in amber
local function makePriceColumn(htmlTableRow, price)

	htmlTableRow:tag("td"):wikitext(price .. NBSP .. amberIcon):done()
end

--endregion



--region Public methods

---
--- Initializes the table, populates the caption, and writes and header row,
--- all appropriate for a table focusing on one perk.
---
--- The default is a wikitable output. With the displayOverride flag, this
--- method will instead make a list.
---
---@param perkName string the name of the perk this table is built for
---@param displayOverride string a flag for the output type if not the default
---@param needsPriceColumn boolean whether this perk needs a price column
function PerkSearchResultsView.startViewForPerk(perkName, displayOverride, needsPriceColumn)

	-- These should never be nil at runtime.
	if not perkName then
		error("Parameter is invalid for perk name.")
	end

	local newNode
	if displayOverride == PARAMETER_DISPLAY_OVERRIDE_LIST then
		newNode = mw.html.create("ul")
	else
		newNode = startNewWikiTable()
		newNode:tag("caption"):wikitext("Perk: " .. perkName .. "."):done():newline()

		makeStandardPerkTableHeaderRow(newNode, needsPriceColumn)
	end

	beginning = newNode
	-- Return value is not used by the Controller, but for debugging.
	return newNode
end



---
--- Initializes the table, populates the caption, and writes and header row,
--- all appropriate for a table focusing on one perk.
---
--- The default is a wikitable output. With the displayOverride flag, this
--- method will instead make a list.
---
---@param perkName string the name of the perk this table is built for
---@param displayOverride string a flag for the output type if not the default
---@param numPerks number of perks that will be shown
---@param needsPriceColumn boolean whether this perk needs a price column
function PerkSearchResultsView.startViewForName(perkName, displayOverride, numPerks, needsPriceColumn)

	-- These should never be nil at runtime.
	if not perkName then
		error("Parameter is invalid for perk name.")
	end
	if not numPerks then
		error("Parameter is invalid for number of perks.")
	end

	local number = tonumber(numPerks)
	if not number then
		error("Number of perks could not be converted to a number: " .. numPerks)
	end

	local newNode
	if displayOverride == PARAMETER_DISPLAY_OVERRIDE_LIST then
		newNode = mw.html.create("ul")
	else
		newNode = startNewWikiTable()
		newNode:tag("caption"):wikitext(number .. " perks named " .. perkName .. "."):done():newline()

		makeStandardPerkTableHeaderRow(newNode, needsPriceColumn)
	end

	beginning = newNode
	-- Return value is not used by the Controller, but for debugging.
	return newNode
end



function PerkSearchResultsView.startViewForSearchResults(searchTerm, source, rarity,
														 displayOverride, numPerks, needsPriceColumn)

	-- These should never be nil at runtime
	if not searchTerm then
		error("Parameter is invalid for search term.")
	end
	if not numPerks then
		error("Parameter is invalid for number of perks.")
	end

	local number = tonumber(numPerks)
	if not number then
		error("Number of perks could not be converted to a number: " .. numPerks)
	end

	local newNode
	if displayOverride == PARAMETER_DISPLAY_OVERRIDE_LIST then
		newNode = mw.html.create("ul")
	else
		local header = number .. NBSP
		if rarity then
			header = header .. rarity .. NBSP
		end
		header = header .. "Perks mentioning '" .. searchTerm .. "'"
		if source then
			header = header .. " from " .. source
		end
		header = header .. "."

		newNode = startNewWikiTable()
		newNode:tag("caption"):wikitext(header):done():newline()

		makeStandardPerkTableHeaderRow(newNode, needsPriceColumn)
	end

	beginning = newNode
	-- Return value is not used by the Controller, but for debugging.
	return newNode
end



---
--- Adds a new row to the content being built. This is typically called from a
--- Controller that knows how many rows to add. PerkSearchResultsView maintains
--- the output in-progress while rows are added, so that layout and formatting
--- can be owned by this module instead of the Controller.
---
---@param displayOverride string a flag for the output type if not the default
---@param perkName string the name of the perk
---@param description string the flavor text description
---@param iconFilename string the icon of the perk
---@param rarity string the rarity
---@param price number price of the perk at a trader
---@param isFromCornerstone boolean whether this perk is earned as a Cornerstone
---@param isFromEvent boolean whether this perk is rewarded from an event
---@param isFromOrder boolean whether this perk is earned from an Order
---@param isFromTrader boolean whether this perk is purchasable
---@param hasLastColumn boolean whether to render the last column (regardless of isFromTrader)
function PerkSearchResultsView.addRowForPerk(displayOverride, perkName,
											 description, iconFilename, rarity, price,
											 isFromCornerstone, isFromEvent, isFromOrder, isFromTrader,
											 hasLastColumn)

	local newNode
	if displayOverride == PARAMETER_DISPLAY_OVERRIDE_LIST then
		-- List version.
		newNode = mw.html.create("li"):wikitext(perkLink(perkName, iconFilename, imgS, RARITY_COLORS[rarity]))
	else
		-- Table version.
		newNode = mw.html.create("tr"):newline()
		makeNameColumn(newNode, perkName, iconFilename, RARITY_COLORS[rarity])
		makeRarityColumn(newNode, rarity)
		makeDescriptionColumn(newNode, description)
		makeSourcesColumn(newNode, isFromCornerstone, isFromEvent, isFromOrder, isFromTrader)
		if hasLastColumn then
			if isFromTrader then
				makePriceColumn(newNode, price)
			else
				newNode:tag("td"):wikitext(BLANK):done()
			end
		end
	end

	newNode:done()
	middle = middle .. tostring(newNode)

	return newNode
end



---
--- Wraps up anything outstanding for the view, then returns the entire
--- MediaWiki entity originally started in the respective method.
---
---@return string the HTML markup of the completed view
function PerkSearchResultsView.endView()

	beginning:node(middle)
	beginning:done()

	middle = ""

	return beginning
end

--endregion

return PerkSearchResultsView