<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.hoodedhorse.com/Against_the_Storm/index.php?action=history&amp;feed=atom&amp;title=Module%3AMysteriesView</id>
	<title>Module:MysteriesView - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.hoodedhorse.com/Against_the_Storm/index.php?action=history&amp;feed=atom&amp;title=Module%3AMysteriesView"/>
	<link rel="alternate" type="text/html" href="https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Module:MysteriesView&amp;action=history"/>
	<updated>2026-10-18T12:15:43Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.44.5</generator>
	<entry>
		<id>https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Module:MysteriesView&amp;diff=8026&amp;oldid=prev</id>
		<title>76561197969631737: Creating to display tables of mysteries</title>
		<link rel="alternate" type="text/html" href="https://wiki.hoodedhorse.com/Against_the_Storm/index.php?title=Module:MysteriesView&amp;diff=8026&amp;oldid=prev"/>
		<updated>2024-07-08T00:15:28Z</updated>

		<summary type="html">&lt;p&gt;Creating to display tables of mysteries&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;---&lt;br /&gt;
--- Serves the Mysteries searching template by providing markup to the controller for display on the page.&lt;br /&gt;
---&lt;br /&gt;
---@module MysteriesView&lt;br /&gt;
&lt;br /&gt;
local MysteriesView = {}&lt;br /&gt;
&lt;br /&gt;
--region Dependencies&lt;br /&gt;
&lt;br /&gt;
local StyleUtils = require(&amp;quot;Module:StyleUtils&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
--region Private constants&lt;br /&gt;
&lt;br /&gt;
local DEFAULT_CAPTION = &amp;quot;Forest Mysteries&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local CLASS_MYSTERIES_TABLE = &amp;quot;wikitable sortable mw-collapsible&amp;quot;&lt;br /&gt;
local CLASS_UNSORTABLE = &amp;quot;unsortable&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local MYSTERY_LINK_ICON_SIZE = StyleUtils.IMG_L()&lt;br /&gt;
&lt;br /&gt;
local HEADER_ID = &amp;quot;ID&amp;quot;&lt;br /&gt;
local HEADER_NAME = &amp;quot;Name&amp;quot;&lt;br /&gt;
local HEADER_DESCRIPTION = &amp;quot;Description&amp;quot;&lt;br /&gt;
local HEADER_SEVERITY = &amp;quot;Severity&amp;quot;&lt;br /&gt;
local HEADER_HOSTILITY_LEVEL = &amp;quot;Hostility Level&amp;quot;&lt;br /&gt;
local HEADER_SEASON = &amp;quot;Season&amp;quot;&lt;br /&gt;
local HEADER_CONDITIONS = &amp;quot;Conditions&amp;quot;&lt;br /&gt;
&lt;br /&gt;
local HEADER_ABBR_HOUSING = tostring(mw.html.create(&amp;quot;abbr&amp;quot;):attr({ title=&amp;quot;Housing Need Satisfied&amp;quot; }):wikitext(&amp;quot;Hs&amp;quot;))&lt;br /&gt;
local HEADER_ABBR_FOOD = tostring(mw.html.create(&amp;quot;abbr&amp;quot;):attr({ title=&amp;quot;Complex Food Need Satisfied&amp;quot; }):wikitext(&amp;quot;CF&amp;quot;))&lt;br /&gt;
local HEADER_ABBR_SERVICES = tostring(mw.html.create(&amp;quot;abbr&amp;quot;):attr({ title=&amp;quot;Services Need Satisfied&amp;quot; }):wikitext(&amp;quot;Sv&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
local RED_X = tostring(mw.html.create(&amp;quot;span&amp;quot;):attr({ style=&amp;quot;color: #FF2A00;&amp;quot; }):wikitext(&amp;quot;×&amp;quot;))&lt;br /&gt;
&lt;br /&gt;
local REPLACEMENT_FILENAME = &amp;quot;Question_mark.png&amp;quot;&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
--region Public sub-class&lt;br /&gt;
&lt;br /&gt;
--- This subclass should never be created directly, but only via the constructFromTemplateFrame method, to ensure data integrity and minimize chance for errors.&lt;br /&gt;
MysteriesView.ViewParameters = {}&lt;br /&gt;
&lt;br /&gt;
-- Indexes&lt;br /&gt;
local ARG_SKIP_CONDITIONS = &amp;quot;skip_conditions&amp;quot;&lt;br /&gt;
local ARG_SHOW_ID = &amp;quot;show_id&amp;quot;&lt;br /&gt;
local ARG_SHOW_DESCRIPTION = &amp;quot;show_description&amp;quot;&lt;br /&gt;
local ARG_SHOW_SEVERITY = &amp;quot;show_severity&amp;quot;&lt;br /&gt;
local ARG_SHOW_HOSTILITY_LEVEL = &amp;quot;show_hostility&amp;quot;&lt;br /&gt;
local ARG_SHOW_SEASON = &amp;quot;show_season&amp;quot;&lt;br /&gt;
local ARG_SHOW_CONDITION_HOUSING = &amp;quot;show_condition_housing&amp;quot;&lt;br /&gt;
local ARG_SHOW_CONDITION_FOOD = &amp;quot;show_condition_food&amp;quot;&lt;br /&gt;
local ARG_SHOW_CONDITION_SERVICES = &amp;quot;show_condition_services&amp;quot;&lt;br /&gt;
-- Flags&lt;br /&gt;
local ARG_SKIP_FLAG_VALUE = &amp;quot;skip&amp;quot;&lt;br /&gt;
local ARG_SHOW_FLAG_VALUE = &amp;quot;show&amp;quot;&lt;br /&gt;
&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingID(self)&lt;br /&gt;
    return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_ID]&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingName()&lt;br /&gt;
    return true&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingDescription(self)&lt;br /&gt;
    return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_DESCRIPTION]&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingSeverity(self)&lt;br /&gt;
    return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SEVERITY]&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingHostilityLevel(self)&lt;br /&gt;
    return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_HOSTILITY_LEVEL]&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingSeason(self)&lt;br /&gt;
    return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_SEASON]&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingConditions(self)&lt;br /&gt;
    -- This comparison is different!&lt;br /&gt;
    return ARG_SKIP_FLAG_VALUE ~= self[ARG_SKIP_CONDITIONS]&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingConditionHousing(self)&lt;br /&gt;
    return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_CONDITION_HOUSING]&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingConditionFood(self)&lt;br /&gt;
    return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_CONDITION_FOOD]&lt;br /&gt;
end&lt;br /&gt;
function MysteriesView.ViewParameters.isShowingConditionServices(self)&lt;br /&gt;
    return ARG_SHOW_FLAG_VALUE == self[ARG_SHOW_CONDITION_SERVICES]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function MysteriesView.ViewParameters.countShownConditionColumns(self)&lt;br /&gt;
&lt;br /&gt;
    local count = 0&lt;br /&gt;
    if self:isShowingConditionHousing() then&lt;br /&gt;
        count = count + 1&lt;br /&gt;
    end&lt;br /&gt;
    if self:isShowingConditionFood() then&lt;br /&gt;
        count = count + 1&lt;br /&gt;
    end&lt;br /&gt;
    if self:isShowingConditionServices() then&lt;br /&gt;
        count = count + 1&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    -- No matter what, one column.&lt;br /&gt;
    if 0 == count then&lt;br /&gt;
        return 1&lt;br /&gt;
    else&lt;br /&gt;
        return count&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local checkerSwitch = {&lt;br /&gt;
    [HEADER_ID] = MysteriesView.ViewParameters.isShowingID,&lt;br /&gt;
    [HEADER_NAME] = MysteriesView.ViewParameters.isShowingName,&lt;br /&gt;
    [HEADER_DESCRIPTION] = MysteriesView.ViewParameters.isShowingDescription,&lt;br /&gt;
    [HEADER_SEVERITY] = MysteriesView.ViewParameters.isShowingSeverity,&lt;br /&gt;
    [HEADER_HOSTILITY_LEVEL] = MysteriesView.ViewParameters.isShowingHostilityLevel,&lt;br /&gt;
    [HEADER_SEASON] = MysteriesView.ViewParameters.isShowingSeason,&lt;br /&gt;
    [HEADER_CONDITIONS] = MysteriesView.ViewParameters.isShowingConditions,&lt;br /&gt;
    [HEADER_ABBR_HOUSING] = MysteriesView.ViewParameters.isShowingConditionHousing,&lt;br /&gt;
    [HEADER_ABBR_FOOD] = MysteriesView.ViewParameters.isShowingConditionFood,&lt;br /&gt;
    [HEADER_ABBR_SERVICES] = MysteriesView.ViewParameters.isShowingConditionServices&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
---getCheckerMethod&lt;br /&gt;
---@param headerLabel string the label displayed on the table&lt;br /&gt;
---@return function the function to call to see whether that column should be shown&lt;br /&gt;
function MysteriesView.ViewParameters.getCheckerMethod(headerLabel)&lt;br /&gt;
    return checkerSwitch[headerLabel]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---constructViewParametersFromTemplateFrame&lt;br /&gt;
---@param frame table the mediawiki template&amp;#039;s calling frame&lt;br /&gt;
---@return table an instance of the ViewParameters class&lt;br /&gt;
function MysteriesView.constructViewParametersFromTemplateFrame(frame)&lt;br /&gt;
    local newInstance = {}&lt;br /&gt;
    newInstance[ARG_SKIP_CONDITIONS] = frame.args[ARG_SKIP_CONDITIONS] or &amp;quot;not by default&amp;quot;&lt;br /&gt;
    newInstance[ARG_SHOW_ID] = frame.args[ARG_SHOW_ID] or &amp;quot;not by default&amp;quot;&lt;br /&gt;
    newInstance[ARG_SHOW_DESCRIPTION] = frame.args[ARG_SHOW_DESCRIPTION] or ARG_SHOW_FLAG_VALUE&lt;br /&gt;
    newInstance[ARG_SHOW_SEVERITY] = frame.args[ARG_SHOW_SEVERITY] or ARG_SHOW_FLAG_VALUE&lt;br /&gt;
    newInstance[ARG_SHOW_HOSTILITY_LEVEL] = frame.args[ARG_SHOW_HOSTILITY_LEVEL] or ARG_SHOW_FLAG_VALUE&lt;br /&gt;
    newInstance[ARG_SHOW_SEASON] = frame.args[ARG_SHOW_SEASON] or ARG_SHOW_FLAG_VALUE&lt;br /&gt;
    newInstance[ARG_SHOW_CONDITION_HOUSING] = frame.args[ARG_SHOW_CONDITION_HOUSING] or ARG_SHOW_FLAG_VALUE&lt;br /&gt;
    newInstance[ARG_SHOW_CONDITION_FOOD] = frame.args[ARG_SHOW_CONDITION_FOOD] or ARG_SHOW_FLAG_VALUE&lt;br /&gt;
    newInstance[ARG_SHOW_CONDITION_SERVICES] = frame.args[ARG_SHOW_CONDITION_SERVICES] or ARG_SHOW_FLAG_VALUE&lt;br /&gt;
&lt;br /&gt;
    -- Attach methods to the instance&lt;br /&gt;
    setmetatable(newInstance, { __index = MysteriesView.ViewParameters })&lt;br /&gt;
&lt;br /&gt;
    return newInstance&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
--region Private member variables&lt;br /&gt;
&lt;br /&gt;
local htmlTable&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
--region Private methods&lt;br /&gt;
&lt;br /&gt;
---createCaptionNode&lt;br /&gt;
---@param captionText string the desired caption&lt;br /&gt;
---@return table the html node of the caption tags&lt;br /&gt;
local function createCaptionNode(captionText)&lt;br /&gt;
&lt;br /&gt;
    captionNode = mw.html.create(&amp;quot;caption&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
    if null == captionText or &amp;quot;&amp;quot; == captionText then&lt;br /&gt;
        captionNode:wikitext(DEFAULT_CAPTION)&lt;br /&gt;
    else&lt;br /&gt;
        captionNode:wikitext(captionText)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return captionNode&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---openTable&lt;br /&gt;
---@param caption string the desired caption&lt;br /&gt;
---@return table the complete html node, htmlTable, the class variable&lt;br /&gt;
local function openTable(caption)&lt;br /&gt;
&lt;br /&gt;
    htmlTable = mw.html.create(&amp;quot;table&amp;quot;)&lt;br /&gt;
    htmlTable:addClass(CLASS_MYSTERIES_TABLE):newline()&lt;br /&gt;
    htmlTable:node(createCaptionNode(caption)):newline()&lt;br /&gt;
&lt;br /&gt;
    return htmlTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---createHeaderCell&lt;br /&gt;
---@param label string the label to put in the header cell&lt;br /&gt;
---@return table a new html table header cell&lt;br /&gt;
local function createHeaderCell(label)&lt;br /&gt;
&lt;br /&gt;
    local cell = mw.html.create(&amp;quot;th&amp;quot;)&lt;br /&gt;
    cell:wikitext(label)&lt;br /&gt;
&lt;br /&gt;
    -- Assign the methods so they can be chained&lt;br /&gt;
    cell.spanTwoRows = function(self)&lt;br /&gt;
        self:attr({ rowspan=&amp;quot;2&amp;quot; })&lt;br /&gt;
        return self&lt;br /&gt;
    end&lt;br /&gt;
    cell.makeUnsortable = function(self)&lt;br /&gt;
        self:addClass(CLASS_UNSORTABLE)&lt;br /&gt;
        return self&lt;br /&gt;
    end&lt;br /&gt;
    cell.spanMultipleColumns = function(self, columnsToSpan)&lt;br /&gt;
        self:attr({ colspan=columnsToSpan })&lt;br /&gt;
        return self&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    return cell&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---loopThroughHeaderCells&lt;br /&gt;
---@param row table the html table row node to use&lt;br /&gt;
---@param preconfiguredRowToUse table the row already setup to choose from&lt;br /&gt;
---@param viewParameters table the ViewParameters specified by the author&lt;br /&gt;
---@return table the same row, now with more stuff in it&lt;br /&gt;
local function addAllCellsThatShouldBeShowing(row, preconfiguredRowToUse, viewParameters)&lt;br /&gt;
&lt;br /&gt;
    for _, rowItem in ipairs(preconfiguredRowToUse) do&lt;br /&gt;
        local isShowingIn = MysteriesView.ViewParameters.getCheckerMethod(rowItem.label)&lt;br /&gt;
        if isShowingIn(viewParameters) then&lt;br /&gt;
            row:node(rowItem.cell):newline()&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---addHeaderRow adds header rows to the member variable htmlTable and returns what was added.&lt;br /&gt;
---&lt;br /&gt;
---@param viewParameters table a ViewParameters initialized with its constructor&lt;br /&gt;
---@return table, table the header row added, sometimes a second sub-header row&lt;br /&gt;
local function addHeaderRow(viewParameters)&lt;br /&gt;
&lt;br /&gt;
    -- Configure the options; we&amp;#039;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.&lt;br /&gt;
    local headersInOrderWithoutConditions = {&lt;br /&gt;
        { label = HEADER_ID, cell = createHeaderCell(HEADER_ID) },&lt;br /&gt;
        { label = HEADER_NAME, cell = createHeaderCell(HEADER_NAME) },&lt;br /&gt;
        { label = HEADER_DESCRIPTION, cell = createHeaderCell(HEADER_DESCRIPTION):makeUnsortable() },&lt;br /&gt;
        { label = HEADER_SEVERITY, cell = createHeaderCell(HEADER_SEVERITY) },&lt;br /&gt;
        { label = HEADER_HOSTILITY_LEVEL, cell = createHeaderCell(HEADER_HOSTILITY_LEVEL) },&lt;br /&gt;
        { label = HEADER_SEASON, cell = createHeaderCell(HEADER_SEASON) },&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
    local headersInOrderWithConditions = {&lt;br /&gt;
        { label = HEADER_ID, cell = createHeaderCell(HEADER_ID):spanTwoRows() },&lt;br /&gt;
        { label = HEADER_NAME, cell = createHeaderCell(HEADER_NAME):spanTwoRows() },&lt;br /&gt;
        { label = HEADER_DESCRIPTION, cell = createHeaderCell(HEADER_DESCRIPTION):spanTwoRows():makeUnsortable() },&lt;br /&gt;
        { label = HEADER_SEVERITY, cell = createHeaderCell(HEADER_SEVERITY):spanTwoRows() },&lt;br /&gt;
        { label = HEADER_HOSTILITY_LEVEL, cell = createHeaderCell(HEADER_HOSTILITY_LEVEL):spanTwoRows() },&lt;br /&gt;
        { label = HEADER_SEASON, cell = createHeaderCell(HEADER_SEASON):spanTwoRows() },&lt;br /&gt;
        { label = HEADER_CONDITIONS, cell = createHeaderCell(HEADER_CONDITIONS):spanMultipleColumns(viewParameters:countShownConditionColumns()) },&lt;br /&gt;
    }&lt;br /&gt;
    local subheadersInOrderUnderConditions = {&lt;br /&gt;
        { label = HEADER_ABBR_HOUSING, cell = createHeaderCell(HEADER_ABBR_HOUSING) },&lt;br /&gt;
        { label = HEADER_ABBR_FOOD, cell = createHeaderCell(HEADER_ABBR_FOOD) },&lt;br /&gt;
        { label = HEADER_ABBR_SERVICES, cell = createHeaderCell(HEADER_ABBR_SERVICES) },&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    -- If there are subheaders, populate two rows then add them to the main htmlTable&lt;br /&gt;
    if viewParameters:isShowingConditions() then&lt;br /&gt;
&lt;br /&gt;
        local headerRow = mw.html.create(&amp;quot;tr&amp;quot;)&lt;br /&gt;
        headerRow:newline()&lt;br /&gt;
        addAllCellsThatShouldBeShowing(headerRow, headersInOrderWithConditions, viewParameters)&lt;br /&gt;
&lt;br /&gt;
        local subheaderRow = mw.html.create(&amp;quot;tr&amp;quot;)&lt;br /&gt;
        subheaderRow:newline()&lt;br /&gt;
        addAllCellsThatShouldBeShowing(subheaderRow, subheadersInOrderUnderConditions, viewParameters)&lt;br /&gt;
&lt;br /&gt;
        htmlTable:node(headerRow):newline()&lt;br /&gt;
        htmlTable:node(subheaderRow):newline()&lt;br /&gt;
&lt;br /&gt;
        return headerRow, subheaderRow&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
        -- No subheaders, just populate one row then add it to the main htmlTable.&lt;br /&gt;
        local headerRow = mw.html.create(&amp;quot;tr&amp;quot;)&lt;br /&gt;
        headerRow:newline()&lt;br /&gt;
        addAllCellsThatShouldBeShowing(headerRow, headersInOrderWithoutConditions, viewParameters)&lt;br /&gt;
&lt;br /&gt;
        htmlTable:node(headerRow):newline()&lt;br /&gt;
&lt;br /&gt;
        return headerRow&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---createDataCell&lt;br /&gt;
---@param label string the label to put in the cell&lt;br /&gt;
---@return table a new html table data cell&lt;br /&gt;
local function createDataCell(label)&lt;br /&gt;
&lt;br /&gt;
    local cell = mw.html.create(&amp;quot;td&amp;quot;)&lt;br /&gt;
    cell:wikitext(label)&lt;br /&gt;
    return cell&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---addDataRow&lt;br /&gt;
---@param data table a table of data configured with keys to match the labels in cellsInOrder in this method&lt;br /&gt;
---@param viewParameters table a ViewParameters initialized with its constructor&lt;br /&gt;
---@return table the data row added, sometimes a second sub-header row&lt;br /&gt;
local function addDataRow(data, viewParameters)&lt;br /&gt;
&lt;br /&gt;
    -- Configure the table; we&amp;#039;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.&lt;br /&gt;
    local cellsInOrderWithoutConditions = {&lt;br /&gt;
        { label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_DESCRIPTION, cell = createDataCell(data[HEADER_DESCRIPTION] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_SEVERITY, cell = createDataCell(data[HEADER_SEVERITY] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_HOSTILITY_LEVEL, cell = createDataCell(data[HEADER_HOSTILITY_LEVEL] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_SEASON, cell = createDataCell(data[HEADER_SEASON] or &amp;quot;—&amp;quot;) }&lt;br /&gt;
    }&lt;br /&gt;
    local cellsInOrderWithConditions = {&lt;br /&gt;
        { label = HEADER_ID, cell = createDataCell(data[HEADER_ID] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_NAME, cell = createDataCell(data[HEADER_NAME] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_DESCRIPTION, cell = createDataCell(data[HEADER_DESCRIPTION] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_SEVERITY, cell = createDataCell(data[HEADER_SEVERITY] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_HOSTILITY_LEVEL, cell = createDataCell(data[HEADER_HOSTILITY_LEVEL] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_SEASON, cell = createDataCell(data[HEADER_SEASON] or &amp;quot;—&amp;quot;) },&lt;br /&gt;
        { label = HEADER_ABBR_HOUSING, cell = createDataCell(data[HEADER_ABBR_HOUSING] and HEADER_ABBR_HOUSING or RED_X) },&lt;br /&gt;
        { label = HEADER_ABBR_FOOD, cell = createDataCell(data[HEADER_ABBR_FOOD] and HEADER_ABBR_FOOD or RED_X) },&lt;br /&gt;
        { label = HEADER_ABBR_SERVICES, cell = createDataCell(data[HEADER_ABBR_SERVICES] and HEADER_ABBR_SERVICES or RED_X) },&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    -- Skipping the sources altogether overrides individual column visibility.&lt;br /&gt;
    if viewParameters:isShowingConditions() then&lt;br /&gt;
&lt;br /&gt;
        local dataRow = mw.html.create(&amp;quot;tr&amp;quot;)&lt;br /&gt;
        dataRow:newline()&lt;br /&gt;
        addAllCellsThatShouldBeShowing(dataRow, cellsInOrderWithConditions, viewParameters)&lt;br /&gt;
&lt;br /&gt;
        htmlTable:node(dataRow):newline()&lt;br /&gt;
&lt;br /&gt;
        return dataRow&lt;br /&gt;
&lt;br /&gt;
    else&lt;br /&gt;
        local dataRow = mw.html.create(&amp;quot;tr&amp;quot;)&lt;br /&gt;
        dataRow:newline()&lt;br /&gt;
        addAllCellsThatShouldBeShowing(dataRow, cellsInOrderWithoutConditions, viewParameters)&lt;br /&gt;
&lt;br /&gt;
        htmlTable:node(dataRow):newline()&lt;br /&gt;
&lt;br /&gt;
        return dataRow&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
---validateStringData&lt;br /&gt;
---@param stringData string a string to display&lt;br /&gt;
---@return string the same string, or nil if it&amp;#039;s empty&lt;br /&gt;
local function validateStringData(stringData)&lt;br /&gt;
&lt;br /&gt;
    if stringData == &amp;quot;&amp;quot; or stringData == &amp;quot; &amp;quot; then&lt;br /&gt;
        return nil&lt;br /&gt;
    else&lt;br /&gt;
        return stringData&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---validateBooleanData&lt;br /&gt;
---@param booleanData boolean a boolean to display&lt;br /&gt;
---@return boolean the same boolean, or false if it&amp;#039;s anything besides a true value&lt;br /&gt;
local function validateBooleanData(booleanData)&lt;br /&gt;
&lt;br /&gt;
    if booleanData == true or booleanData == &amp;quot;true&amp;quot; then&lt;br /&gt;
        return true&lt;br /&gt;
    else&lt;br /&gt;
        return nil&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---validateNumberData&lt;br /&gt;
---@param numberData number a number to display&lt;br /&gt;
---@return number the same number, or nil if it was invalid or 0&lt;br /&gt;
local function validateNumberData(numberData)&lt;br /&gt;
&lt;br /&gt;
    if type(numberData) ~= &amp;quot;number&amp;quot; then&lt;br /&gt;
        return tonumber(numberData)&lt;br /&gt;
    else&lt;br /&gt;
        if numberData &amp;lt; 0 then&lt;br /&gt;
            return nil&lt;br /&gt;
        else&lt;br /&gt;
            return numberData&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---createPageLink&lt;br /&gt;
---@param name string the name of the forest mystery&lt;br /&gt;
---@param iconFilename string the filename, excluding the extension&lt;br /&gt;
---@return string html markup representing the link to the page, with its icon&lt;br /&gt;
local function createMysteryPageLink(name, iconFilename)&lt;br /&gt;
&lt;br /&gt;
    if not iconFilename or iconFilename == &amp;quot;&amp;quot; then&lt;br /&gt;
        iconFilename = REPLACEMENT_FILENAME&lt;br /&gt;
    else&lt;br /&gt;
        iconFilename = iconFilename .. &amp;quot;.png&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    local iconPart = string.format(&amp;quot;[[File:%s|%s|link=%s|alt=%s|%s]] &amp;quot;, iconFilename, MYSTERY_LINK_ICON_SIZE, name, name, name)&lt;br /&gt;
    local pagePart = &amp;quot;[[&amp;quot; .. name .. &amp;quot;]]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
    return mw.getCurrentFrame():preprocess(iconPart .. pagePart)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
--region Public methods&lt;br /&gt;
&lt;br /&gt;
---startTable&lt;br /&gt;
---@param caption string the desired caption&lt;br /&gt;
---@param viewParameters table a ViewParameters initialized with its constructor&lt;br /&gt;
---@return table an html node, the htmlTable member variable&lt;br /&gt;
function MysteriesView.startTable(caption, viewParameters)&lt;br /&gt;
&lt;br /&gt;
    openTable(caption)&lt;br /&gt;
    addHeaderRow(viewParameters)&lt;br /&gt;
&lt;br /&gt;
    return htmlTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---addRow&lt;br /&gt;
---@param id string the ID of the mystery&lt;br /&gt;
---@param name string the name of the mystery&lt;br /&gt;
---@param icon string the icon filename of the mystery, excluding the extension&lt;br /&gt;
---@param description string the description of the mystery&lt;br /&gt;
---@param severity string the severity of the mystery, for example, &amp;quot;Beneficial&amp;quot; or &amp;quot;Harsh&amp;quot;&lt;br /&gt;
---@param hostilityLevel number the hostility when the mystery kicks in&lt;br /&gt;
---@param season number the season when the mystery kicks in every year&lt;br /&gt;
---@param isConditionHousing boolean whether the mystery requires housing need to be satisfied to avoid&lt;br /&gt;
---@param isConditionFood boolean whether the mystery requires complex food need to be satisfied to avoid&lt;br /&gt;
---@param isConditionServices boolean whether the mystery requires services need to be satisfied to avoid&lt;br /&gt;
function MysteriesView.addRow(id, name, icon, description, severity, hostilityLevel, season, isConditionHousing, isConditionFood, isConditionServices, viewParameters)&lt;br /&gt;
&lt;br /&gt;
    local data = {&lt;br /&gt;
        [HEADER_ID] = validateStringData(id),&lt;br /&gt;
        [HEADER_NAME] = createMysteryPageLink(name, icon),&lt;br /&gt;
        [HEADER_DESCRIPTION] = validateStringData(description),&lt;br /&gt;
        [HEADER_SEVERITY] = validateStringData(severity),&lt;br /&gt;
        [HEADER_HOSTILITY_LEVEL] = validateNumberData(hostilityLevel),&lt;br /&gt;
        [HEADER_SEASON] = validateNumberData(season),&lt;br /&gt;
        [HEADER_ABBR_HOUSING] = validateBooleanData(isConditionHousing),&lt;br /&gt;
        [HEADER_ABBR_FOOD] = validateBooleanData(isConditionFood),&lt;br /&gt;
        [HEADER_ABBR_SERVICES] = validateBooleanData(isConditionServices)&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    addDataRow(data, viewParameters)&lt;br /&gt;
&lt;br /&gt;
    return htmlTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
---finalize&lt;br /&gt;
---@return table the complete html table node, fully rendered&lt;br /&gt;
function MysteriesView.finalize()&lt;br /&gt;
&lt;br /&gt;
    return htmlTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--endregion&lt;br /&gt;
&lt;br /&gt;
return MysteriesView&lt;/div&gt;</summary>
		<author><name>76561197969631737</name></author>
	</entry>
</feed>