Module:OrdersView
From Against the Storm Official Wiki
Documentation for this module may be created at Module:OrdersView/doc
---
--- Serves the Orders searching template by capturing input and using it to control the display of data.
---
---@module OrdersView
local OrdersView = {}
--region Dependencies
--- remove this dependency later
local OrdersData = require("Module:OrdersData")
--endregion
--region Private constants
local DEFAULT_CAPTION = "Orders"
local CLASS_ORDERS_TABLE = "wikitable mw-collapsible table-nobands"
local CLASS_ORDERS_COLLAPSED = "wikitable mw-collapsible mw-collapsed table-nobands"
local CLASS_UNSORTABLE = "unsortable"
local ORDER_LINK_ICON_SIZE = "large"
local HEADER_ID = "ID"
local HEADER_NAME = "Name"
local HEADER_REQUIREMENTS = "Requirements"
local HEADER_REWARDS = "Rewards"
local HEADER_TIER = "Tier"
local HEADER_TIMED = "Timed Order?"
local HEADER_TIME_LIMIT = "Time Limit (seconds)"
local HEADER_DIFFICULTY = "Difficulty"
--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.
OrdersView.ViewParameters = {}
-- Indexes
local ARG_SHOW_ID = "show_id"
local ARG_SHOW_TIER = "show_tier"
local ARG_SHOW_TIMED = "show_timed"
local ARG_SHOW_TIME_LIMIT = "show_time_limit"
local ARG_SHOW_DIFFICULTY = "show_difficulty"
-- Flags
local ARG_SKIP_FLAG_VALUE = "skip"
local ARG_SHOW_FLAG_VALUE = "show"
function OrdersView.ViewParameters.isShowingID(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_ID]
end
function OrdersView.ViewParameters.isShowingName()
return true
end
function OrdersView.ViewParameters.isShowingTier(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_TIER]
end
function OrdersView.ViewParameters.isShowingTimed(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_TIMED]
end
function OrdersView.ViewParameters.isShowingTimeLimit(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_TIME_LIMIT]
end
function OrdersView.ViewParameters.isShowingRequirements()
return true
end
function OrdersView.ViewParameters.isShowingRewards()
return true
end
function OrdersView.ViewParameters.isShowingDifficulty(self)
return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_DIFFICULTY]
end
function OrdersView.ViewParameters.countShownExtraColumns(self)
local count = 0
if self:isShowingID() then
count = count + 1
end
if self:isShowingTier() then
count = count + 1
end
if self:isShowingTimed() then
count = count + 1
end
if self:isShowingTimeLimit() then
count = count + 1
end
if self:isShowingDifficulty() 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] = OrdersView.ViewParameters.isShowingID,
[HEADER_NAME] = OrdersView.ViewParameters.isShowingName,
[HEADER_TIER] = OrdersView.ViewParameters.isShowingTier,
[HEADER_TIMED] = OrdersView.ViewParameters.isShowingTimed,
[HEADER_REQUIREMENTS] = OrdersView.ViewParameters.isShowingRequirements,
[HEADER_REWARDS] = OrdersView.ViewParameters.isShowingRewards,
[HEADER_TIME_LIMIT] = OrdersView.ViewParameters.isShowingTimeLimit,
[HEADER_DIFFICULTY] = OrdersView.ViewParameters.isShowingDifficulty,
}
---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 OrdersView.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 OrdersView.constructViewParametersFromTemplateFrame(frame)
local newInstance = {}
newInstance[ARG_SHOW_ID] = frame.args[ARG_SHOW_ID] or "not by default"
newInstance[ARG_SHOW_TIER] = frame.args[ARG_SHOW_TIER] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_TIMED] = frame.args[ARG_SHOW_TIMED] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_TIME_LIMIT] = frame.args[ARG_SHOW_TIME_LIMIT] or ARG_SHOW_FLAG_VALUE
newInstance[ARG_SHOW_DIFFICULTY] = frame.args[ARG_SHOW_DIFFICULTY] or ARG_SHOW_FLAG_VALUE
-- Attach methods to the instance
setmetatable(newInstance, { __index = OrdersView.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, collapsed)
htmlTable = mw.html.create("table")
if collapsed then
htmlTable:addClass(CLASS_ORDERS_COLLAPSED):newline()
else
htmlTable:addClass(CLASS_ORDERS_TABLE):newline()
end
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.spanMultipleRows = function(self, rowsToSpan)
self:attr({ rowspan=rowsToSpan })
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 = OrdersView.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 headersInOrder = {
{ label = HEADER_ID, cell = createHeaderCell(HEADER_ID) },
{ label = HEADER_TIER, cell = createHeaderCell(HEADER_TIER) },
{ label = HEADER_TIMED, cell = createHeaderCell(HEADER_TIMED) },
{ label = HEADER_TIME_LIMIT, cell = createHeaderCell(HEADER_TIME_LIMIT) },
{ label = HEADER_NAME, cell = createHeaderCell(HEADER_NAME) },
{ label = HEADER_DIFFICULTY, cell = createHeaderCell(HEADER_DIFFICULTY):makeUnsortable() },
{ label = HEADER_REQUIREMENTS, cell = createHeaderCell(HEADER_REQUIREMENTS):makeUnsortable() },
{ label = HEADER_REWARDS, cell = createHeaderCell(HEADER_REWARDS) }
}
-- If there are subheaders, populate two rows then add them to the main htmlTable
-- No subheaders, just populate one row then add it to the main htmlTable.
local headerRow = mw.html.create("tr")
headerRow:newline()
addAllCellsThatShouldBeShowing(headerRow, headersInOrder, viewParameters)
htmlTable:node(headerRow):newline()
return headerRow
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)
-- Assign the methods so they can be chained
cell.spanMultipleRows = function(self, rowsToSpan)
self:attr({ rowspan=rowsToSpan })
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
---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, rowSpan)
-- 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 cellsInOrder = {}
if rowSpan > 0 then
cellsInOrder = {
{ label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or "—"):spanMultipleRows(rowSpan) },
{ label = HEADER_TIER, cell = createDataCell(data[HEADER_TIER] or "—"):spanMultipleRows(rowSpan) },
{ label = HEADER_TIMED, cell = createDataCell(data[HEADER_TIMED] or "—"):spanMultipleRows(rowSpan) },
{ label = HEADER_TIME_LIMIT, cell = createDataCell(data[HEADER_TIME_LIMIT] or "—"):spanMultipleRows(rowSpan) },
{ label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or "—"):spanMultipleRows(rowSpan) },
{ label = HEADER_DIFFICULTY, cell = createDataCell(data[HEADER_DIFFICULTY] or "—") },
{ label = HEADER_REQUIREMENTS, cell = createDataCell(data[HEADER_REQUIREMENTS] or "—") },
{ label = HEADER_REWARDS, cell = createDataCell(data[HEADER_REWARDS] or "—"):spanMultipleRows(rowSpan) }
}
else
cellsInOrder = {
{ label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or "—") },
{ label = HEADER_TIER, cell = createDataCell(data[HEADER_TIER] or "—") },
{ label = HEADER_TIMED, cell = createDataCell(data[HEADER_TIMED] or "—") },
{ label = HEADER_TIME_LIMIT, cell = createDataCell(data[HEADER_TIME_LIMIT] or "—") },
{ label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or "—") },
{ label = HEADER_DIFFICULTY, cell = createDataCell(data[HEADER_DIFFICULTY] or "—") },
{ label = HEADER_REQUIREMENTS, cell = createDataCell(data[HEADER_REQUIREMENTS] or "—") },
{ label = HEADER_REWARDS, cell = createDataCell(data[HEADER_REWARDS] or "—") }
}
end
local dataRow = mw.html.create("tr")
dataRow:newline()
addAllCellsThatShouldBeShowing(dataRow, cellsInOrder, viewParameters)
htmlTable:node(dataRow):newline()
return dataRow
end
---addExtraRow
---@param data table a table of data configured with keys to match the labels in cellsInOrder in this method
---@return table the data row added, which only includes the difficulty as a separate row
local function addExtraRow(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 cellsInOrder = {
{ label = HEADER_DIFFICULTY, cell = createDataCell(data[HEADER_DIFFICULTY] or "—") },
{ label = HEADER_REQUIREMENTS, cell = createDataCell(data[HEADER_REQUIREMENTS] or "—") },
}
-- If skipping the sources altogether overrides individual column visibility.
local dataRow = mw.html.create("tr")
dataRow:newline()
addAllCellsThatShouldBeShowing(dataRow, cellsInOrder, viewParameters)
htmlTable:node(dataRow):newline()
return dataRow
end
---validateStringData
---@param stringData string a string to display
---@return string the same string, or nil if it's empty
local function validateStringData(stringData)
stringData = tostring(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 createOrderLink(id)
return mw.getCurrentFrame():expandTemplate{
title = "ol",
args = { ["id"] = id, ["iconsize"] = ORDER_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 OrdersView.startTable(caption, viewParameters, collapsed)
openTable(caption, collapsed)
addHeaderRow(viewParameters)
return htmlTable
end
---addRow
---@param id string the ID of the order
---@param _ string the name of the order is not actually used, but asked for because the Controller doesn't need to know
---@param rarity string the rarity of the order expressed as a string
---@param description string the description of the order
---@param isSourceAltar boolean whether the order is acquired at the altar
---@param isSourceCornerstone boolean whether the order is acquired as a cornerstone
---@param isSourceOrder boolean whether the order is acquired from orders
---@param isSourceRelic boolean whether the order is acquired from relics
---@param isSourceTrader boolean whether the order 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 OrdersView.addRow(id, _, tier, timed, timeLimit, difficulty, requirements, rewards, viewParameters, isExtraRow, rowSpan)
local data = {
[HEADER_ID] = validateStringData(id),
---TODO: change back to createOrderLink once ready
[HEADER_NAME] = validateStringData(OrdersData.getNameByID(id)), --createOrderLink(validateStringData(id)),
[HEADER_TIER] = validateStringData(tier),
[HEADER_TIMED] = validateStringData(timed),
[HEADER_TIME_LIMIT] = validateNumberData(timeLimit),
[HEADER_DIFFICULTY] = validateStringData(difficulty),
[HEADER_REQUIREMENTS] = validateStringData(requirements),
[HEADER_REWARDS] = validateStringData(rewards),
}
if isExtraRow then
addExtraRow(data, viewParameters)
else
addDataRow(data, viewParameters, rowSpan)
end
return htmlTable
end
---finalize
---@return table the complete html table node, fully rendered
function OrdersView.finalize()
return htmlTable
end
--endregion
return OrdersView


