Module:PerksView: Difference between revisions
From Against the Storm Official Wiki
Created to replace the table templates that are way too slow |
Anteprefix (talk | contribs) No edit summary |
||
| (10 intermediate revisions by 2 users not shown) | |||
| Line 12: | Line 12: | ||
--region Private constants | --region Private constants | ||
local DEFAULT_CAPTION = "Perks and Cornerstones" | local DEFAULT_CAPTION = "Perks and Cornerstones" | ||
| Line 32: | Line 18: | ||
local CLASS_UNSORTABLE = "unsortable" | local CLASS_UNSORTABLE = "unsortable" | ||
local | local PERK_LINK_ICON_SIZE = "large" | ||
local HEADER_ID = "ID" | local HEADER_ID = "ID" | ||
| Line 46: | Line 32: | ||
local HEADER_ABBR_RELIC = tostring(mw.html.create("abbr"):attr({ title="Relic (Glade Event)" }):wikitext("Rel")) | local HEADER_ABBR_RELIC = tostring(mw.html.create("abbr"):attr({ title="Relic (Glade Event)" }):wikitext("Rel")) | ||
local HEADER_ABBR_TRADER = tostring(mw.html.create("abbr"):attr({ title="Trader" }):wikitext("Tra")) | local HEADER_ABBR_TRADER = tostring(mw.html.create("abbr"):attr({ title="Trader" }):wikitext("Tra")) | ||
local HEADER_ABBR_DECORATION = tostring(mw.html.create("abbr"):attr({ title="Decoration" }):wikitext("Dec")) | |||
local HEADER_ABBR_PORT = tostring(mw.html.create("abbr"):attr({ title="Strider Port" }):wikitext("Por")) | |||
local HEADER_ABBR_RACIAL = tostring(mw.html.create("abbr"):attr({ title="Firekeeper"}):wikitext("Fir")) | |||
local HEADER_ABBR_INSTITUTION = tostring(mw.html.create("abbr"):attr({ title="Service Building" }):wikitext("Ser")) | |||
local HEADER_ABBR_BIOME = tostring(mw.html.create("abbr"):attr({ title="Biome" }):wikitext("Bio")) | |||
local RED_X = mw.getCurrentFrame():expandTemplate{ title = "c", args = { [1] = "Red", [2] = "x" } } | local RED_X = mw.getCurrentFrame():expandTemplate{ title = "c", args = { [1] = "Red", [2] = "x" } } | ||
| Line 68: | Line 59: | ||
local ARG_SHOW_SOURCE_RELIC = "show_source_relic" | local ARG_SHOW_SOURCE_RELIC = "show_source_relic" | ||
local ARG_SHOW_SOURCE_TRADER = "show_source_trader" | local ARG_SHOW_SOURCE_TRADER = "show_source_trader" | ||
local ARG_SHOW_SOURCE_DECORATION = "show_source_decoration" | |||
local ARG_SHOW_SOURCE_PORT = "show_source_port" | |||
local ARG_SHOW_SOURCE_RACIAL = "show_source_racial" | |||
local ARG_SHOW_SOURCE_INSTITUTION = "show_source_institution" | |||
local ARG_SHOW_SOURCE_BIOME = "show_source_biome" | |||
local ARG_SHOW_PRICE = "show_price" | local ARG_SHOW_PRICE = "show_price" | ||
-- Flags | -- Flags | ||
| Line 102: | Line 98: | ||
function PerksView.ViewParameters.isShowingSourceTrader(self) | function PerksView.ViewParameters.isShowingSourceTrader(self) | ||
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_TRADER] | return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_TRADER] | ||
end | |||
function PerksView.ViewParameters.isShowingSourceDecoration(self) | |||
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_DECORATION] | |||
end | |||
function PerksView.ViewParameters.isShowingSourcePort(self) | |||
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_PORT] | |||
end | |||
function PerksView.ViewParameters.isShowingSourceRacial(self) | |||
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_RACIAL] | |||
end | |||
function PerksView.ViewParameters.isShowingSourceInstitution(self) | |||
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_INSTITUTION] | |||
end | |||
function PerksView.ViewParameters.isShowingSourceBiome(self) | |||
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_BIOME] | |||
end | end | ||
function PerksView.ViewParameters.isShowingPrice(self) | function PerksView.ViewParameters.isShowingPrice(self) | ||
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_PRICE] | return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_PRICE] | ||
end | |||
function PerksView.ViewParameters.countShownSourceColumns(self) | |||
local count = 0 | |||
if self:isShowingSourceAltar() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourceCornerstone() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourceOrder() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourceRelic() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourceTrader() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourceDecoration() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourcePort() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourceRacial() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourceInstitution() then | |||
count = count + 1 | |||
end | |||
if self:isShowingSourceBiome() then | |||
count = count + 1 | |||
end | |||
-- No matter what, one column. | |||
if 0 == count then | |||
return 1 | |||
else | |||
return count | |||
end | |||
end | end | ||
| Line 118: | Line 171: | ||
[HEADER_ABBR_ORDER] = PerksView.ViewParameters.isShowingSourceOrder, | [HEADER_ABBR_ORDER] = PerksView.ViewParameters.isShowingSourceOrder, | ||
[HEADER_ABBR_RELIC] = PerksView.ViewParameters.isShowingSourceRelic, | [HEADER_ABBR_RELIC] = PerksView.ViewParameters.isShowingSourceRelic, | ||
[HEADER_ABBR_TRADER] = PerksView.ViewParameters.isShowingSourceTrader | [HEADER_ABBR_TRADER] = PerksView.ViewParameters.isShowingSourceTrader, | ||
[HEADER_ABBR_DECORATION] = PerksView.ViewParameters.isShowingSourceDecoration, | |||
[HEADER_ABBR_PORT] = PerksView.ViewParameters.isShowingSourcePort, | |||
[HEADER_ABBR_RACIAL] = PerksView.ViewParameters.isShowingSourceRacial, | |||
[HEADER_ABBR_INSTITUTION] = PerksView.ViewParameters.isShowingSourceInstitution, | |||
[HEADER_ABBR_BIOME] = PerksView.ViewParameters.isShowingSourceBiome | |||
} | } | ||
| Line 133: | Line 191: | ||
function PerksView.constructViewParametersFromTemplateFrame(frame) | function PerksView.constructViewParametersFromTemplateFrame(frame) | ||
local newInstance = {} | local newInstance = {} | ||
newInstance[ARG_SKIP_SOURCES] = frame.args[ARG_SKIP_SOURCES] or | newInstance[ARG_SKIP_SOURCES] = frame.args[ARG_SKIP_SOURCES] or "not by default" | ||
newInstance[ARG_SHOW_ID] = frame.args[ARG_SHOW_ID] or | newInstance[ARG_SHOW_ID] = frame.args[ARG_SHOW_ID] or "not by default" | ||
newInstance[ARG_SHOW_RARITY] = frame.args[ARG_SHOW_RARITY] or ARG_SHOW_FLAG_VALUE | newInstance[ARG_SHOW_RARITY] = frame.args[ARG_SHOW_RARITY] or ARG_SHOW_FLAG_VALUE | ||
newInstance[ARG_SHOW_DESCRIPTION] = frame.args[ARG_SHOW_DESCRIPTION] or ARG_SHOW_FLAG_VALUE | newInstance[ARG_SHOW_DESCRIPTION] = frame.args[ARG_SHOW_DESCRIPTION] or ARG_SHOW_FLAG_VALUE | ||
| Line 142: | Line 200: | ||
newInstance[ARG_SHOW_SOURCE_RELIC] = frame.args[ARG_SHOW_SOURCE_RELIC] or ARG_SHOW_FLAG_VALUE | newInstance[ARG_SHOW_SOURCE_RELIC] = frame.args[ARG_SHOW_SOURCE_RELIC] or ARG_SHOW_FLAG_VALUE | ||
newInstance[ARG_SHOW_SOURCE_TRADER] = frame.args[ARG_SHOW_SOURCE_TRADER] or ARG_SHOW_FLAG_VALUE | newInstance[ARG_SHOW_SOURCE_TRADER] = frame.args[ARG_SHOW_SOURCE_TRADER] or ARG_SHOW_FLAG_VALUE | ||
newInstance[ARG_SHOW_SOURCE_DECORATION] = frame.args[ARG_SHOW_SOURCE_DECORATION] or ARG_SHOW_FLAG_VALUE | |||
newInstance[ARG_SHOW_SOURCE_PORT] = frame.args[ARG_SHOW_SOURCE_PORT] or ARG_SHOW_FLAG_VALUE | |||
newInstance[ARG_SHOW_SOURCE_RACIAL] = frame.args[ARG_SHOW_SOURCE_RACIAL] or ARG_SHOW_FLAG_VALUE | |||
newInstance[ARG_SHOW_SOURCE_INSTITUTION] = frame.args[ARG_SHOW_SOURCE_INSTITUTION] or ARG_SHOW_FLAG_VALUE | |||
newInstance[ARG_SHOW_SOURCE_BIOME] = frame.args[ARG_SHOW_SOURCE_BIOME] or ARG_SHOW_FLAG_VALUE | |||
newInstance[ARG_SHOW_PRICE] = frame.args[ARG_SHOW_PRICE] or ARG_SHOW_FLAG_VALUE | newInstance[ARG_SHOW_PRICE] = frame.args[ARG_SHOW_PRICE] or ARG_SHOW_FLAG_VALUE | ||
| Line 202: | Line 265: | ||
-- Assign the methods so they can be chained | -- Assign the methods so they can be chained | ||
cell.spanTwoRows = function(self) | cell.spanTwoRows = function(self) | ||
self:attr( | self:attr({ rowspan="2" }) | ||
return self | return self | ||
end | end | ||
| Line 209: | Line 272: | ||
return self | return self | ||
end | end | ||
cell.spanMultipleColumns = function(self, columnsToSpan) | |||
self:attr({ colspan=columnsToSpan }) | |||
return self | |||
end | |||
return cell | return cell | ||
| Line 247: | Line 315: | ||
{ label = HEADER_RARITY, cell = createHeaderCell(HEADER_RARITY):spanTwoRows() }, | { label = HEADER_RARITY, cell = createHeaderCell(HEADER_RARITY):spanTwoRows() }, | ||
{ label = HEADER_DESCRIPTION, cell = createHeaderCell(HEADER_DESCRIPTION):spanTwoRows():makeUnsortable() }, | { label = HEADER_DESCRIPTION, cell = createHeaderCell(HEADER_DESCRIPTION):spanTwoRows():makeUnsortable() }, | ||
{ label = HEADER_SOURCES, cell = createHeaderCell(HEADER_SOURCES) }, | { label = HEADER_SOURCES, cell = createHeaderCell(HEADER_SOURCES):spanMultipleColumns(viewParameters:countShownSourceColumns()) }, | ||
{ label = HEADER_PRICE, cell = createHeaderCell(HEADER_PRICE):spanTwoRows() } | { label = HEADER_PRICE, cell = createHeaderCell(HEADER_PRICE):spanTwoRows() } | ||
} | } | ||
| Line 255: | Line 323: | ||
{ label = HEADER_ABBR_ORDER, cell = createHeaderCell(HEADER_ABBR_ORDER) }, | { label = HEADER_ABBR_ORDER, cell = createHeaderCell(HEADER_ABBR_ORDER) }, | ||
{ label = HEADER_ABBR_RELIC, cell = createHeaderCell(HEADER_ABBR_RELIC) }, | { label = HEADER_ABBR_RELIC, cell = createHeaderCell(HEADER_ABBR_RELIC) }, | ||
{ label = HEADER_ABBR_TRADER, cell = createHeaderCell(HEADER_ABBR_TRADER) } | { label = HEADER_ABBR_TRADER, cell = createHeaderCell(HEADER_ABBR_TRADER) }, | ||
{ label = HEADER_ABBR_DECORATION, cell = createHeaderCell(HEADER_ABBR_DECORATION) }, | |||
{ label = HEADER_ABBR_PORT, cell = createHeaderCell(HEADER_ABBR_PORT) }, | |||
{ label = HEADER_ABBR_RACIAL, cell = createHeaderCell(HEADER_ABBR_RACIAL) }, | |||
{ label = HEADER_ABBR_INSTITUTION, cell = createHeaderCell(HEADER_ABBR_INSTITUTION) }, | |||
{ label = HEADER_ABBR_BIOME, cell = createHeaderCell(HEADER_ABBR_BIOME) } | |||
} | } | ||
| Line 303: | Line 376: | ||
-- Configure the table; we'll figure out which to actually show in a moment. Separating the setup from the logic makes this significantly easier to read and understand. These cannot be key-value pairs, like [label] = cell, because we have to use numerical indexes to preserve their order. | -- Configure the table; we'll figure out which to actually show in a moment. Separating the setup from the logic makes this significantly easier to read and understand. These cannot be key-value pairs, like [label] = cell, because we have to use numerical indexes to preserve their order. | ||
local | local cellsInOrderWithoutSources = { | ||
{ label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or "—") }, | |||
{ label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or "—") }, | |||
{ label = HEADER_RARITY, cell = createDataCell(data[HEADER_RARITY] or "—") }, | |||
{ label = HEADER_DESCRIPTION, cell = createDataCell(data[HEADER_DESCRIPTION] or "—") }, | |||
{ label = HEADER_PRICE, cell = createDataCell(data[HEADER_PRICE] or "—") } | |||
} | |||
local cellsInOrderWithSources = { | |||
{ label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or "—") }, | { label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or "—") }, | ||
{ label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or "—") }, | { label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or "—") }, | ||
| Line 313: | Line 393: | ||
{ label = HEADER_ABBR_RELIC, cell = createDataCell(data[HEADER_ABBR_RELIC] and HEADER_ABBR_RELIC or RED_X) }, | { label = HEADER_ABBR_RELIC, cell = createDataCell(data[HEADER_ABBR_RELIC] and HEADER_ABBR_RELIC or RED_X) }, | ||
{ label = HEADER_ABBR_TRADER, cell = createDataCell(data[HEADER_ABBR_TRADER] and HEADER_ABBR_TRADER or RED_X) }, | { label = HEADER_ABBR_TRADER, cell = createDataCell(data[HEADER_ABBR_TRADER] and HEADER_ABBR_TRADER or RED_X) }, | ||
{ label = HEADER_ABBR_DECORATION, cell = createDataCell(data[HEADER_ABBR_DECORATION] and HEADER_ABBR_DECORATION or RED_X) }, | |||
{ label = HEADER_ABBR_PORT, cell = createDataCell(data[HEADER_ABBR_PORT] and HEADER_ABBR_PORT or RED_X) }, | |||
{ label = HEADER_ABBR_RACIAL, cell = createDataCell(data[HEADER_ABBR_RACIAL] and HEADER_ABBR_RACIAL or RED_X) }, | |||
{ label = HEADER_ABBR_INSTITUTION, cell = createDataCell(data[HEADER_ABBR_INSTITUTION] and HEADER_ABBR_INSTITUTION or RED_X) }, | |||
{ label = HEADER_ABBR_BIOME, cell = createDataCell(data[HEADER_ABBR_BIOME] and HEADER_ABBR_BIOME or RED_X) }, | |||
{ label = HEADER_PRICE, cell = createDataCell(data[HEADER_PRICE] or "—") } | { label = HEADER_PRICE, cell = createDataCell(data[HEADER_PRICE] or "—") } | ||
} | } | ||
-- If skipping the sources altogether overrides individual column visibility. | |||
if viewParameters:isShowingSources() then | |||
local dataRow = mw.html.create("tr") | |||
dataRow:newline() | |||
addAllCellsThatShouldBeShowing(dataRow, cellsInOrderWithSources, viewParameters) | |||
return dataRow | htmlTable:node(dataRow):newline() | ||
return dataRow | |||
else | |||
local dataRow = mw.html.create("tr") | |||
dataRow:newline() | |||
addAllCellsThatShouldBeShowing(dataRow, cellsInOrderWithoutSources, viewParameters) | |||
htmlTable:node(dataRow):newline() | |||
return dataRow | |||
end | |||
end | end | ||
| Line 363: | Line 462: | ||
end | end | ||
end | end | ||
end | |||
local function createPerkLink(id) | |||
return mw.getCurrentFrame():expandTemplate{ | |||
title = "pl", | |||
args = { ["id"] = id, ["iconsize"] = PERK_LINK_ICON_SIZE } | |||
} | |||
end | end | ||
| Line 372: | Line 479: | ||
---startTable | ---startTable | ||
---@param caption | ---@param caption string the desired caption | ||
---@param viewParameters table | ---@param viewParameters table a ViewParameters initialized with its constructor | ||
---@return table | ---@return table an html node, the htmlTable member variable | ||
function PerksView.startTable(caption, viewParameters) | function PerksView.startTable(caption, viewParameters) | ||
| Line 384: | Line 491: | ||
---addRow | ---addRow | ||
---@param id | ---@param id string the ID of the perk | ||
---@param name | ---@param _ string the name of the perk is not actually used, but asked for because the Controller doesn't need to know | ||
---@param rarity | ---@param rarity string the rarity of the perk expressed as a string | ||
---@param description | ---@param description string the description of the perk | ||
---@param isSourceAltar | ---@param isSourceAltar boolean whether the perk is acquired at the altar | ||
---@param isSourceCornerstone | ---@param isSourceCornerstone boolean whether the perk is acquired as a cornerstone | ||
---@param isSourceOrder | ---@param isSourceOrder boolean whether the perk is acquired from orders | ||
---@param isSourceRelic | ---@param isSourceRelic boolean whether the perk is acquired from relics | ||
---@param isSourceTrader | ---@param isSourceTrader boolean whether the perk is acquired from traders | ||
---@param price | ---@param price number the purchase price at a trader | ||
---@param viewParameters table | ---@param viewParameters table a ViewParameters initialized with its constructor | ||
---@return table | ---@return table an html node, the htmlTable member variable | ||
function PerksView.addRow(id, | function PerksView.addRow(id, _, rarity, description, isSourceAltar, isSourceCornerstone, isSourceOrder, isSourceRelic, isSourceTrader, isSourceDecoration, isSourcePort, isSourceRacial, isSourceInstitution, isSourceBiome, price, viewParameters) | ||
local data = { | local data = { | ||
[HEADER_ID] = validateStringData(id), | [HEADER_ID] = validateStringData(id), | ||
[HEADER_NAME] = validateStringData( | [HEADER_NAME] = createPerkLink(validateStringData(id)), | ||
[HEADER_RARITY] = validateStringData(rarity), | [HEADER_RARITY] = validateStringData(rarity), | ||
[HEADER_DESCRIPTION] = validateStringData(description), | [HEADER_DESCRIPTION] = validateStringData(description), | ||
| Line 408: | Line 515: | ||
[HEADER_ABBR_RELIC] = validateBooleanData(isSourceRelic), | [HEADER_ABBR_RELIC] = validateBooleanData(isSourceRelic), | ||
[HEADER_ABBR_TRADER] = validateBooleanData(isSourceTrader), | [HEADER_ABBR_TRADER] = validateBooleanData(isSourceTrader), | ||
[HEADER_ABBR_DECORATION] = validateBooleanData(isSourceDecoration), | |||
[HEADER_ABBR_PORT] = validateBooleanData(isSourcePort), | |||
[HEADER_ABBR_RACIAL] = validateBooleanData(isSourceRacial), | |||
[HEADER_ABBR_INSTITUTION] = validateBooleanData(isSourceInstitution), | |||
[HEADER_ABBR_BIOME] = validateBooleanData(isSourceBiome), | |||
[HEADER_PRICE] = validateNumberData(price) | [HEADER_PRICE] = validateNumberData(price) | ||
} | } | ||
addDataRow(data, viewParameters) | addDataRow(data, viewParameters) | ||
return htmlTable | |||
end | |||
---finalize | |||
---@return table the complete html table node, fully rendered | |||
function PerksView.finalize() | |||
return htmlTable | return htmlTable | ||
Latest revision as of 17:26, 28 February 2025
Documentation for this module may be created at Module:PerksView/doc
---
--- Serves the Perks searching template by capturing input and using it to control the display of data.
---
---@module PerksView
local PerksView = {}
--region Dependencies
--endregion
--region Private constants
local DEFAULT_CAPTION = "Perks and Cornerstones"
local CLASS_PERKS_TABLE = "wikitable sortable mw-collapsible"
local CLASS_UNSORTABLE = "unsortable"
local PERK_LINK_ICON_SIZE = "large"
local HEADER_ID = "ID"
local HEADER_NAME = "Name"
local HEADER_RARITY = "Rarity"
local HEADER_DESCRIPTION = "Description"
local HEADER_SOURCES = "Sources"
local HEADER_PRICE = "Price"
local HEADER_ABBR_ALTAR = tostring(mw.html.create("abbr"):attr({ title="Altar (Stormforged Cornerstone)" }):wikitext("Alt"))
local HEADER_ABBR_CORNERSTONE = tostring(mw.html.create("abbr"):attr({ title="Cornerstone" }):wikitext("Cor"))
local HEADER_ABBR_ORDER = tostring(mw.html.create("abbr"):attr({ title="Order"}):wikitext("Ord"))
local HEADER_ABBR_RELIC = tostring(mw.html.create("abbr"):attr({ title="Relic (Glade Event)" }):wikitext("Rel"))
local HEADER_ABBR_TRADER = tostring(mw.html.create("abbr"):attr({ title="Trader" }):wikitext("Tra"))
local HEADER_ABBR_DECORATION = tostring(mw.html.create("abbr"):attr({ title="Decoration" }):wikitext("Dec"))
local HEADER_ABBR_PORT = tostring(mw.html.create("abbr"):attr({ title="Strider Port" }):wikitext("Por"))
local HEADER_ABBR_RACIAL = tostring(mw.html.create("abbr"):attr({ title="Firekeeper"}):wikitext("Fir"))
local HEADER_ABBR_INSTITUTION = tostring(mw.html.create("abbr"):attr({ title="Service Building" }):wikitext("Ser"))
local HEADER_ABBR_BIOME = tostring(mw.html.create("abbr"):attr({ title="Biome" }):wikitext("Bio"))
local RED_X = mw.getCurrentFrame():expandTemplate{ title = "c", args = { [1] = "Red", [2] = "x" } }
--endregion
--region Public sub-class
--- This subclass should never be created directly, but only via the constructFromTemplateFrame method, to ensure data integrity and minimize chance for errors.
PerksView.ViewParameters = {}
-- Indexes
local ARG_SKIP_SOURCES = "skip_sources"
local ARG_SHOW_ID = "show_id"
local ARG_SHOW_RARITY = "show_rarity"
local ARG_SHOW_DESCRIPTION = "show_description"
local ARG_SHOW_SOURCE_ALTAR = "show_source_altar"
local ARG_SHOW_SOURCE_CORNERSTONE = "show_source_cornerstone"
local ARG_SHOW_SOURCE_ORDER = "show_source_order"
local ARG_SHOW_SOURCE_RELIC = "show_source_relic"
local ARG_SHOW_SOURCE_TRADER = "show_source_trader"
local ARG_SHOW_SOURCE_DECORATION = "show_source_decoration"
local ARG_SHOW_SOURCE_PORT = "show_source_port"
local ARG_SHOW_SOURCE_RACIAL = "show_source_racial"
local ARG_SHOW_SOURCE_INSTITUTION = "show_source_institution"
local ARG_SHOW_SOURCE_BIOME = "show_source_biome"
local ARG_SHOW_PRICE = "show_price"
-- Flags
local ARG_SKIP_FLAG_VALUE = "skip"
local ARG_SHOW_FLAG_VALUE = "show"
function PerksView.ViewParameters.isShowingID(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_ID]
end
function PerksView.ViewParameters.isShowingName()
return true
end
function PerksView.ViewParameters.isShowingRarity(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_RARITY]
end
function PerksView.ViewParameters.isShowingDescription(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_DESCRIPTION]
end
function PerksView.ViewParameters.isShowingSources(self)
return ARG_SKIP_FLAG_VALUE ~= self[ARG_SKIP_SOURCES]
end
function PerksView.ViewParameters.isShowingSourceAltar(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_ALTAR]
end
function PerksView.ViewParameters.isShowingSourceCornerstone(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_CORNERSTONE]
end
function PerksView.ViewParameters.isShowingSourceOrder(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_ORDER]
end
function PerksView.ViewParameters.isShowingSourceRelic(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_RELIC]
end
function PerksView.ViewParameters.isShowingSourceTrader(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_TRADER]
end
function PerksView.ViewParameters.isShowingSourceDecoration(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_DECORATION]
end
function PerksView.ViewParameters.isShowingSourcePort(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_PORT]
end
function PerksView.ViewParameters.isShowingSourceRacial(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_RACIAL]
end
function PerksView.ViewParameters.isShowingSourceInstitution(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_INSTITUTION]
end
function PerksView.ViewParameters.isShowingSourceBiome(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SOURCE_BIOME]
end
function PerksView.ViewParameters.isShowingPrice(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_PRICE]
end
function PerksView.ViewParameters.countShownSourceColumns(self)
local count = 0
if self:isShowingSourceAltar() then
count = count + 1
end
if self:isShowingSourceCornerstone() then
count = count + 1
end
if self:isShowingSourceOrder() then
count = count + 1
end
if self:isShowingSourceRelic() then
count = count + 1
end
if self:isShowingSourceTrader() then
count = count + 1
end
if self:isShowingSourceDecoration() then
count = count + 1
end
if self:isShowingSourcePort() then
count = count + 1
end
if self:isShowingSourceRacial() then
count = count + 1
end
if self:isShowingSourceInstitution() then
count = count + 1
end
if self:isShowingSourceBiome() then
count = count + 1
end
-- No matter what, one column.
if 0 == count then
return 1
else
return count
end
end
local checkerSwitch = {
[HEADER_ID] = PerksView.ViewParameters.isShowingID,
[HEADER_NAME] = PerksView.ViewParameters.isShowingName,
[HEADER_RARITY] = PerksView.ViewParameters.isShowingRarity,
[HEADER_DESCRIPTION] = PerksView.ViewParameters.isShowingDescription,
[HEADER_SOURCES] = PerksView.ViewParameters.isShowingSources,
[HEADER_PRICE] = PerksView.ViewParameters.isShowingPrice,
[HEADER_ABBR_ALTAR] = PerksView.ViewParameters.isShowingSourceAltar,
[HEADER_ABBR_CORNERSTONE] = PerksView.ViewParameters.isShowingSourceCornerstone,
[HEADER_ABBR_ORDER] = PerksView.ViewParameters.isShowingSourceOrder,
[HEADER_ABBR_RELIC] = PerksView.ViewParameters.isShowingSourceRelic,
[HEADER_ABBR_TRADER] = PerksView.ViewParameters.isShowingSourceTrader,
[HEADER_ABBR_DECORATION] = PerksView.ViewParameters.isShowingSourceDecoration,
[HEADER_ABBR_PORT] = PerksView.ViewParameters.isShowingSourcePort,
[HEADER_ABBR_RACIAL] = PerksView.ViewParameters.isShowingSourceRacial,
[HEADER_ABBR_INSTITUTION] = PerksView.ViewParameters.isShowingSourceInstitution,
[HEADER_ABBR_BIOME] = PerksView.ViewParameters.isShowingSourceBiome
}
---getCheckerMethod
---@param headerLabel string the label displayed on the table
---@return function the function to call to see whether that column should be shown
function PerksView.ViewParameters.getCheckerMethod(headerLabel)
return checkerSwitch[headerLabel]
end
---constructViewParametersFromTemplateFrame
---@param frame table the mediawiki template's calling frame
---@return table an instance of the ViewParameters class
function PerksView.constructViewParametersFromTemplateFrame(frame)
local newInstance = {}
newInstance[ARG_SKIP_SOURCES] = frame.args[ARG_SKIP_SOURCES] or "not by default"
newInstance[ARG_SHOW_ID] = frame.args[ARG_SHOW_ID] or "not by default"
newInstance[ARG_SHOW_RARITY] = frame.args[ARG_SHOW_RARITY] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_DESCRIPTION] = frame.args[ARG_SHOW_DESCRIPTION] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_ALTAR] = frame.args[ARG_SHOW_SOURCE_ALTAR] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_CORNERSTONE] = frame.args[ARG_SHOW_SOURCE_CORNERSTONE] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_ORDER] = frame.args[ARG_SHOW_SOURCE_ORDER] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_RELIC] = frame.args[ARG_SHOW_SOURCE_RELIC] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_TRADER] = frame.args[ARG_SHOW_SOURCE_TRADER] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_DECORATION] = frame.args[ARG_SHOW_SOURCE_DECORATION] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_PORT] = frame.args[ARG_SHOW_SOURCE_PORT] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_RACIAL] = frame.args[ARG_SHOW_SOURCE_RACIAL] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_INSTITUTION] = frame.args[ARG_SHOW_SOURCE_INSTITUTION] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_SOURCE_BIOME] = frame.args[ARG_SHOW_SOURCE_BIOME] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_PRICE] = frame.args[ARG_SHOW_PRICE] or ARG_SHOW_FLAG_VALUE
-- Attach methods to the instance
setmetatable(newInstance, { __index = PerksView.ViewParameters })
return newInstance
end
--endregion
--region Private member variables
local htmlTable
--endregion
--region Private methods
---createCaptionNode
---@param captionText string the desired caption
---@return table the html node of the caption tags
local function createCaptionNode(captionText)
captionNode = mw.html.create("caption")
if null == captionText or "" == captionText then
captionNode:wikitext(DEFAULT_CAPTION)
else
captionNode:wikitext(captionText)
end
return captionNode
end
---openTable
---@param caption string the desired caption
---@return table the complete html node, htmlTable, the class variable
local function openTable(caption)
htmlTable = mw.html.create("table")
htmlTable:addClass(CLASS_PERKS_TABLE):newline()
htmlTable:node(createCaptionNode(caption)):newline()
return htmlTable
end
---createHeaderCell
---@param label string the label to put in the header cell
---@return table a new html table header cell
local function createHeaderCell(label)
local cell = mw.html.create("th")
cell:wikitext(label)
-- Assign the methods so they can be chained
cell.spanTwoRows = function(self)
self:attr({ rowspan="2" })
return self
end
cell.makeUnsortable = function(self)
self:addClass(CLASS_UNSORTABLE)
return self
end
cell.spanMultipleColumns = function(self, columnsToSpan)
self:attr({ colspan=columnsToSpan })
return self
end
return cell
end
---loopThroughHeaderCells
---@param row table the html table row node to use
---@param preconfiguredRowToUse table the row already setup to choose from
---@param viewParameters table the ViewParameters specified by the author
---@return table the same row, now with more stuff in it
local function addAllCellsThatShouldBeShowing(row, preconfiguredRowToUse, viewParameters)
for _, rowItem in ipairs(preconfiguredRowToUse) do
local isShowingIn = PerksView.ViewParameters.getCheckerMethod(rowItem.label)
if isShowingIn(viewParameters) then
row:node(rowItem.cell):newline()
end
end
end
---addHeaderRow adds header rows to the member variable htmlTable and returns what was added.
---
---@param viewParameters table a ViewParameters initialized with its constructor
---@return table, table the header row added, sometimes a second sub-header row
local function addHeaderRow(viewParameters)
-- Configure the options; we'll figure out which to create in a moment. Separating the setup from the logic makes this significantly easier to read and understand. These cannot be key-value pairs, like [label] = cell, because we have to use numerical indexes to preserve their order.
local headersInOrderWithoutSources = {
{ label = HEADER_ID, cell = createHeaderCell(HEADER_ID) },
{ label = HEADER_NAME, cell = createHeaderCell(HEADER_NAME) },
{ label = HEADER_RARITY, cell = createHeaderCell(HEADER_RARITY) },
{ label = HEADER_DESCRIPTION, cell = createHeaderCell(HEADER_DESCRIPTION):makeUnsortable() },
{ label = HEADER_PRICE, cell = createHeaderCell(HEADER_PRICE) }
}
local headersInOrderWithSources = {
{ label = HEADER_ID, cell = createHeaderCell(HEADER_ID):spanTwoRows() },
{ label = HEADER_NAME, cell = createHeaderCell(HEADER_NAME):spanTwoRows() },
{ label = HEADER_RARITY, cell = createHeaderCell(HEADER_RARITY):spanTwoRows() },
{ label = HEADER_DESCRIPTION, cell = createHeaderCell(HEADER_DESCRIPTION):spanTwoRows():makeUnsortable() },
{ label = HEADER_SOURCES, cell = createHeaderCell(HEADER_SOURCES):spanMultipleColumns(viewParameters:countShownSourceColumns()) },
{ label = HEADER_PRICE, cell = createHeaderCell(HEADER_PRICE):spanTwoRows() }
}
local subheadersInOrderUnderSources = {
{ label = HEADER_ABBR_ALTAR, cell = createHeaderCell(HEADER_ABBR_ALTAR) },
{ label = HEADER_ABBR_CORNERSTONE, cell = createHeaderCell(HEADER_ABBR_CORNERSTONE) },
{ label = HEADER_ABBR_ORDER, cell = createHeaderCell(HEADER_ABBR_ORDER) },
{ label = HEADER_ABBR_RELIC, cell = createHeaderCell(HEADER_ABBR_RELIC) },
{ label = HEADER_ABBR_TRADER, cell = createHeaderCell(HEADER_ABBR_TRADER) },
{ label = HEADER_ABBR_DECORATION, cell = createHeaderCell(HEADER_ABBR_DECORATION) },
{ label = HEADER_ABBR_PORT, cell = createHeaderCell(HEADER_ABBR_PORT) },
{ label = HEADER_ABBR_RACIAL, cell = createHeaderCell(HEADER_ABBR_RACIAL) },
{ label = HEADER_ABBR_INSTITUTION, cell = createHeaderCell(HEADER_ABBR_INSTITUTION) },
{ label = HEADER_ABBR_BIOME, cell = createHeaderCell(HEADER_ABBR_BIOME) }
}
-- If there are subheaders, populate two rows then add them to the main htmlTable
if viewParameters:isShowingSources() then
local headerRow = mw.html.create("tr")
headerRow:newline()
addAllCellsThatShouldBeShowing(headerRow, headersInOrderWithSources, viewParameters)
local subheaderRow = mw.html.create("tr")
subheaderRow:newline()
addAllCellsThatShouldBeShowing(subheaderRow, subheadersInOrderUnderSources, viewParameters)
htmlTable:node(headerRow):newline()
htmlTable:node(subheaderRow):newline()
return headerRow, subheaderRow
else
-- No subheaders, just populate one row then add it to the main htmlTable.
local headerRow = mw.html.create("tr")
headerRow:newline()
addAllCellsThatShouldBeShowing(headerRow, headersInOrderWithoutSources, viewParameters)
htmlTable:node(headerRow):newline()
return headerRow
end
end
---createDataCell
---@param label string the label to put in the cell
---@return table a new html table data cell
local function createDataCell(label)
local cell = mw.html.create("td")
cell:wikitext(label)
return cell
end
---addDataRow
---@param data table a table of data configured with keys to match the labels in cellsInOrder in this method
---@param viewParameters table a ViewParameters initialized with its constructor
---@return table the data row added, sometimes a second sub-header row
local function addDataRow(data, viewParameters)
-- Configure the table; we'll figure out which to actually show in a moment. Separating the setup from the logic makes this significantly easier to read and understand. These cannot be key-value pairs, like [label] = cell, because we have to use numerical indexes to preserve their order.
local cellsInOrderWithoutSources = {
{ label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or "—") },
{ label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or "—") },
{ label = HEADER_RARITY, cell = createDataCell(data[HEADER_RARITY] or "—") },
{ label = HEADER_DESCRIPTION, cell = createDataCell(data[HEADER_DESCRIPTION] or "—") },
{ label = HEADER_PRICE, cell = createDataCell(data[HEADER_PRICE] or "—") }
}
local cellsInOrderWithSources = {
{ label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or "—") },
{ label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or "—") },
{ label = HEADER_RARITY, cell = createDataCell(data[HEADER_RARITY] or "—") },
{ label = HEADER_DESCRIPTION, cell = createDataCell(data[HEADER_DESCRIPTION] or "—") },
{ label = HEADER_ABBR_ALTAR, cell = createDataCell(data[HEADER_ABBR_ALTAR] and HEADER_ABBR_ALTAR or RED_X) },
{ label = HEADER_ABBR_CORNERSTONE, cell = createDataCell(data[HEADER_ABBR_CORNERSTONE] and HEADER_ABBR_CORNERSTONE or RED_X) },
{ label = HEADER_ABBR_ORDER, cell = createDataCell(data[HEADER_ABBR_ORDER] and HEADER_ABBR_ORDER or RED_X) },
{ label = HEADER_ABBR_RELIC, cell = createDataCell(data[HEADER_ABBR_RELIC] and HEADER_ABBR_RELIC or RED_X) },
{ label = HEADER_ABBR_TRADER, cell = createDataCell(data[HEADER_ABBR_TRADER] and HEADER_ABBR_TRADER or RED_X) },
{ label = HEADER_ABBR_DECORATION, cell = createDataCell(data[HEADER_ABBR_DECORATION] and HEADER_ABBR_DECORATION or RED_X) },
{ label = HEADER_ABBR_PORT, cell = createDataCell(data[HEADER_ABBR_PORT] and HEADER_ABBR_PORT or RED_X) },
{ label = HEADER_ABBR_RACIAL, cell = createDataCell(data[HEADER_ABBR_RACIAL] and HEADER_ABBR_RACIAL or RED_X) },
{ label = HEADER_ABBR_INSTITUTION, cell = createDataCell(data[HEADER_ABBR_INSTITUTION] and HEADER_ABBR_INSTITUTION or RED_X) },
{ label = HEADER_ABBR_BIOME, cell = createDataCell(data[HEADER_ABBR_BIOME] and HEADER_ABBR_BIOME or RED_X) },
{ label = HEADER_PRICE, cell = createDataCell(data[HEADER_PRICE] or "—") }
}
-- If skipping the sources altogether overrides individual column visibility.
if viewParameters:isShowingSources() then
local dataRow = mw.html.create("tr")
dataRow:newline()
addAllCellsThatShouldBeShowing(dataRow, cellsInOrderWithSources, viewParameters)
htmlTable:node(dataRow):newline()
return dataRow
else
local dataRow = mw.html.create("tr")
dataRow:newline()
addAllCellsThatShouldBeShowing(dataRow, cellsInOrderWithoutSources, viewParameters)
htmlTable:node(dataRow):newline()
return dataRow
end
end
---validateStringData
---@param stringData string a string to display
---@return string the same string, or nil if it's empty
local function validateStringData(stringData)
if "" == stringData or " " == stringData then
return nil
else
return stringData
end
end
---validateBooleanData
---@param booleanData boolean a boolean to display
---@return boolean the same boolean, or false if it's anything besides a true value
local function validateBooleanData(booleanData)
if true == booleanData or "true" == booleanData then
return true
else
return nil
end
end
---validateNumberData
---@param numberData number a number to display
---@return number the same number, or nil if it was invalid or 0
local function validateNumberData(numberData)
if type(numberData) ~= "number" then
return tonumber(numberData)
else
if 0 > numberData then
return nil
else
return numberData
end
end
end
local function createPerkLink(id)
return mw.getCurrentFrame():expandTemplate{
title = "pl",
args = { ["id"] = id, ["iconsize"] = PERK_LINK_ICON_SIZE }
}
end
--endregion
--region Public methods
---startTable
---@param caption string the desired caption
---@param viewParameters table a ViewParameters initialized with its constructor
---@return table an html node, the htmlTable member variable
function PerksView.startTable(caption, viewParameters)
openTable(caption)
addHeaderRow(viewParameters)
return htmlTable
end
---addRow
---@param id string the ID of the perk
---@param _ string the name of the perk is not actually used, but asked for because the Controller doesn't need to know
---@param rarity string the rarity of the perk expressed as a string
---@param description string the description of the perk
---@param isSourceAltar boolean whether the perk is acquired at the altar
---@param isSourceCornerstone boolean whether the perk is acquired as a cornerstone
---@param isSourceOrder boolean whether the perk is acquired from orders
---@param isSourceRelic boolean whether the perk is acquired from relics
---@param isSourceTrader boolean whether the perk is acquired from traders
---@param price number the purchase price at a trader
---@param viewParameters table a ViewParameters initialized with its constructor
---@return table an html node, the htmlTable member variable
function PerksView.addRow(id, _, rarity, description, isSourceAltar, isSourceCornerstone, isSourceOrder, isSourceRelic, isSourceTrader, isSourceDecoration, isSourcePort, isSourceRacial, isSourceInstitution, isSourceBiome, price, viewParameters)
local data = {
[HEADER_ID] = validateStringData(id),
[HEADER_NAME] = createPerkLink(validateStringData(id)),
[HEADER_RARITY] = validateStringData(rarity),
[HEADER_DESCRIPTION] = validateStringData(description),
[HEADER_ABBR_ALTAR] = validateBooleanData(isSourceAltar),
[HEADER_ABBR_CORNERSTONE] = validateBooleanData(isSourceCornerstone),
[HEADER_ABBR_ORDER] = validateBooleanData(isSourceOrder),
[HEADER_ABBR_RELIC] = validateBooleanData(isSourceRelic),
[HEADER_ABBR_TRADER] = validateBooleanData(isSourceTrader),
[HEADER_ABBR_DECORATION] = validateBooleanData(isSourceDecoration),
[HEADER_ABBR_PORT] = validateBooleanData(isSourcePort),
[HEADER_ABBR_RACIAL] = validateBooleanData(isSourceRacial),
[HEADER_ABBR_INSTITUTION] = validateBooleanData(isSourceInstitution),
[HEADER_ABBR_BIOME] = validateBooleanData(isSourceBiome),
[HEADER_PRICE] = validateNumberData(price)
}
addDataRow(data, viewParameters)
return htmlTable
end
---finalize
---@return table the complete html table node, fully rendered
function PerksView.finalize()
return htmlTable
end
--endregion
return PerksView


