Module:Episode list/sandbox

local p = {}

-- This module requires the use of the following modules:

local colorContrastModule = require('Module:Color contrast')

local htmlColor = mw.loadData('Module:Color contrast/colors')

local langModule = require("Module:Lang")

local mathModule = require('Module:Math')

local tableEmptyCellModule = require('Module:Table empty cell')

local yesNoModule = require('Module:Yesno')

-- mw.html object for the generated row.

local row

-- Variable that will decide the colspan= of the Short Summary cell.

local nonNilParams = 0

-- Variable that will keep track if a TBA value was entered.

local cellValueTBA = false

-- Variable that handles the assigned tracking categories.

local trackingCategories = ""

-- List of tracking categories.

local trackingCategoryList = {

["air_dates"] = "Category:Episode lists with unformatted air dates",

["alt_air_dates"] = "Category:Episode lists with incorrectly formatted alternate air dates",

["faulty_line_colors"] = "Category:Episode lists with faulty line colors",

["non_compliant_line_colors"] = "Category:Episode lists with non-compliant line colors",

["default_line_colors"] = "Category:Episode list using the default LineColor",

["row_deviations"] = "Category:Episode lists with row deviations",

["invalid_top_colors"] = "Category:Episode lists with invalid top colors",

["tba_values"] = "Category:Episode lists with TBA values",

["nonmatching_numbered_parameters"] = "Category:Episode lists with a non-matching set of numbered parameters",

["raw_unformatted_storyteleplay"] = "Category:Episode lists with unformatted story or teleplay credits",

}

-- List of parameter names in this order.

local cellNameList = {

'EpisodeNumber',

'EpisodeNumber2',

'Title',

'Aux1',

'DirectedBy',

'WrittenBy',

'Aux2',

'Aux3',

'OriginalAirDate',

'AltDate',

'Guests',

'MusicalGuests',

'ProdCode',

'Viewers',

'Aux4'

}

-- List of pairs which cannot be used together

local excludeList = {

['Guests'] = 'Aux1',

['MusicalGuests'] = 'Aux2'

}

-- List of cells that have parameter groups

local parameterGroupCells = {}

local firstParameterGroupCell

local parameterGroupCellsAny = false

-- List of title parameter names in this order.

-- List used for multi title lists.

local titleList = {

'Title',

'RTitle',

'AltTitle',

'RAltTitle',

'NativeTitle',

'TranslitTitle',

}

-- Local function which is used to retrieve the episode number or production code number,

-- without any additional text.

local function idTrim(val, search)

local valFind = string.find(val, search)

if (valFind == nil) then

return val

else

return string.sub(val, 0, valFind-1)

end

end

-- Local function which is used to validate that a parameter has an actual value.

local function hasValue(param)

if (param ~= nil and param ~= "") then

return true

else

return false

end

end

-- Local function which is used to create a table data cell.

local function createTableData(text, rowSpan, textAlign)

if (rowSpan ~= nil and tonumber(rowSpan) > 1) then

row:tag('td')

:attr('rowspan', rowSpan)

:wikitext(text)

else

row:tag('td')

:css('text-align', textAlign)

:wikitext(text)

end

end

-- Local function which is used to add a tracking category to the page.

local function addTrackingCategory(category)

trackingCategories = trackingCategories .. category

end

-- Local function which is used to create a Short Summary row.

local function createShortSummaryRow(args, lineColor)

-- fix for lists in the Short Summary

local shortSummaryText = args.ShortSummary

if (shortSummaryText:match('^[*:;#]') or shortSummaryText:match('^{|')) then

shortSummaryText = '\n' .. shortSummaryText

end

if (shortSummaryText:match('\n[*:;#]')) then

shortSummaryText = shortSummaryText .. '\n'

end

local shortSummaryDiv = mw.html.create('div')

:addClass('shortSummaryText')

:css('max-width', '90vw')

:css('position', 'sticky')

:css('left', '0.2em')

:newline()

:wikitext(shortSummaryText)

local shortSummaryCell = mw.html.create('td')

:addClass('description')

:css('border-bottom', 'solid 3px ' .. lineColor)

:attr('colspan', nonNilParams)

:newline()

:node(shortSummaryDiv)

return mw.html.create('tr')

:addClass('expand-child')

:node(shortSummaryCell)

end

-- Local function which is used to add tracking categories for Top Color issues.

local function addTopColorTrackingCategories(args)

if (hasValue(args.TopColor)) then

addTrackingCategory(trackingCategoryList["row_deviations"])

-- Track top colors that have a color contrast rating below AAA with

-- respect to text color, link color, or visited link color. See

-- WP:COLOR for more about color contrast requirements.

local textContrastRatio = colorContrastModule._ratio{args.TopColor, 'black', ['error'] = 0}

local linkContrastRatio = colorContrastModule._ratio{args.TopColor, '#0B0080', ['error'] = 0}

local visitedLinkContrastRatio = colorContrastModule._ratio{args.TopColor, '#0645AD', ['error'] = 0}

if (textContrastRatio < 7 or linkContrastRatio < 7 or visitedLinkContrastRatio < 7) then

addTrackingCategory(trackingCategoryList["invalid_top_colors"])

end

end

end

-- Local function which is used to add tracking categories for Line Color issues.

local function addLineColorTrackingCategories(args)

if (hasValue(args.LineColor)) then

local blackContrastRatio = colorContrastModule._ratio{args.LineColor, 'black', ['error'] = 0}

local whiteContrastRatio = colorContrastModule._ratio{'white', args.LineColor, ['error'] = 0}

if (colorContrastModule._lum(args.LineColor) == '') then

addTrackingCategory(trackingCategoryList["faulty_line_colors"])

elseif (blackContrastRatio < 7 and whiteContrastRatio < 7) then

addTrackingCategory(trackingCategoryList["non_compliant_line_colors"])

end

else

addTrackingCategory(trackingCategoryList["default_line_colors"])

end

end

-- Local function which is used to set the text of an empty cell

-- with either "TBD" or "N/A".

-- Set to N/A if viewers haven't been available for four weeks, else set it as TBD.

local function setTBDStatus(args, awaitingText, expiredText, weeks)

if args.OriginalAirDate == nil or args.OriginalAirDate == '' then

return tableEmptyCellModule._main({alt_text = awaitingText, title_text = awaitingText})

end

-- If it hasn't aired, change to "N/A".

if string.match(args.OriginalAirDate, '^Unaired') then

return tableEmptyCellModule._main({alt_text = "N/A"})

end

local month, day, year = args.OriginalAirDate:gsub(" ", " "):match("(%a+) (%d+), (%d+)")

if (month == nil) then

day, month, year = args.OriginalAirDate:gsub(" ", " "):match("(%d+) (%a+) (%d+)")

end

if (day == nil) then

return tableEmptyCellModule._main({alt_text = "TBD"})

else

-- List of months.

local monthList = {

['January'] = 1,

['February'] = 2,

['March'] = 3,

['April'] = 4,

['May'] = 5,

['June'] = 6,

['July'] = 7,

['August'] = 8,

['September'] = 9,

['October'] = 10,

['November'] = 11,

['December'] = 12

}

if not monthList[month] then

error('Invalid month ' .. month)

end

local seconds = os.time() - os.time({year = year, month = monthList[month], day = day, hour = 0, min = 0, sec = 0})

if (seconds >= 60 * 60 * 24 * 7 * weeks) then

return tableEmptyCellModule._main({alt_text = expiredText, title_text = expiredText})

else

return tableEmptyCellModule._main({alt_text = awaitingText, title_text = awaitingText})

end

end

end

-- Local function which is used to create an empty cell.

local function createEmptyCell(args, v, unsetParameterGroup)

if (unsetParameterGroup) then

args[v] = tableEmptyCellModule._main({alt_text = "N/A"})

elseif (v == 'Viewers' and hasValue(args.OriginalAirDate)) then

args[v] = setTBDStatus(args, "TBD", "N/A", 4)

elseif (v == 'DirectedBy' or v == 'WrittenBy') then

args[v] = setTBDStatus(args, "TBA", "Unknown", 4)

else

args[v] = tableEmptyCellModule._main({})

end

end

-- Air dates that don't use {{Start date}}

local function checkUsageOfDateTemplates(args, v, onInitialPage, title)

if v == 'OriginalAirDate'

and args[v] ~= ''

and string.match(args[v], '%d%d%d%d') -- Ensure it contains a four-digit number (likely a year)

and string.match(args[v], '2C2C2C') == nil -- Avoids this specific pattern

and not string.match(args[v], '^Unaired') -- Exclude anything starting with "Unaired"

and string.find(args[v], 'itvstart') == nil -- Avoids a {{Start date}} unique class

and onInitialPage

and title.namespace == 0

then

addTrackingCategory(trackingCategoryList["air_dates"])

end

-- Alternate air dates that do use {{Start date}}

if (v == 'AltDate' and args[v] ~= '' and string.find(args[v], 'dtstart') ~= nil and onInitialPage and title.namespace == 0) then

addTrackingCategory(trackingCategoryList["alt_air_dates"])

end

end

-- Local function which is used to create a Production Code cell.

local function createProductionCodeCell(args, v, numberOfParameterGroups)

local thisRowspan

if (not parameterGroupCells[v] and parameterGroupCellsAny) then

thisRowspan = numberOfParameterGroups

else

thisRowspan = 1

end

if (hasValue(args.ProdCode) and string.find(args.ProdCode, 'TBA') == nil) then

row:tag('td')

:attr('id', 'pc' .. idTrim(idTrim(args.ProdCode, ' ----'), '<'))

:attr('rowspan', thisRowspan)

:css('text-align', 'center')

:wikitext(args.ProdCode)

elseif (args.ProdCode == or string.find(args.ProdCode or , 'TBA') ~= nil) then

createEmptyCell(args, v, false)

createTableData(args.ProdCode, thisRowspan, "center")

else

-- ProductionCode parameter not used; Do nothing.

end

nonNilParams = nonNilParams + 1

end

--[[

Local function which is used to extract data

from the numbered serial parameters (Title1, Aux1, etc.), and then convert them to

use the non-numbered parameter names (Title, Aux).

The function returns the args as non-numbered prameter names.

]]--

local function extractDataFromNumberedSerialArgs(args, i, numberOfParameterGroups, title)

for _, v in ipairs(cellNameList) do

local parameter = v

local numberedParameter = v .. "_" .. i

local excludeParameter = excludeList[parameter] or 'NULL' .. parameter

local excludeNumberParameter = (excludeList[numberedParameter] or 'NULL' .. parameter) .. "_" .. i

if (not hasValue(args[numberedParameter]) and not hasValue(args[excludeNumberParameter])

and hasValue(parameterGroupCells[parameter]) and not hasValue(args[excludeParameter])) then

if (v ~= 'ProdCode') then

createEmptyCell(args, parameter, true)

else

args[parameter] = ''

end

if (title.namespace == 0) then

addTrackingCategory(trackingCategoryList["nonmatching_numbered_parameters"])

end

elseif (hasValue(args[numberedParameter]) and not hasValue(args[excludeNumberParameter])) then

args[parameter] = args[numberedParameter]

end

end

return args

end

--[[

Local function which is used to create the Title cell text.

The title text will be handled in the following way:

Line 1: <RTitle> (with no space between)</p> <p>Line 2: <AltTitle><RAltTitle> (with no space between) OR</p> <p>Line 2: Transliteration: <TranslitTitle> (<Language>: <NativeTitle>)<RAltTitle> (with space between first two parameters)</p> <p>If <Title> or <RTitle> are empty,</p> <p>then the values of line 2 will be placed on line 1 instead.</p> <p>--]]</p> <p>local function createTitleText(args)</p> <p>local titleString = ''</p> <p>local isCellPresent = false</p> <p>local useSecondLine = false</p> <p>local lineBreakUsed = false</p> <p>-- Surround the Title with quotes; No quotes if empty.</p> <p>if (args.Title ~= nil) then</p> <p>if (args.Title == "") then</p> <p>isCellPresent = true</p> <p>else</p> <p>titleString = '"' .. args.Title .. '"'</p> <p>useSecondLine = true</p> <p>isCellPresent = true</p> <p>end</p> <p>end</p> <p>if (args.RTitle ~= nil) then</p> <p>if (args.RTitle == "") then</p> <p>isCellPresent = true</p> <p>else</p> <p>titleString = titleString .. args.RTitle</p> <p>useSecondLine = true</p> <p>isCellPresent = true</p> <p>end</p> <p>end</p> <p>-- Surround the AltTitle/TranslitTitle with quotes; No quotes if empty.</p> <p>if (args.AltTitle or args.TranslitTitle) then</p> <p>isCellPresent = true</p> <p>if (useSecondLine) then</p> <p>titleString = titleString .. "<br />"</p> <p>lineBreakUsed = true</p> <p>end</p> <p>if (hasValue(args.AltTitle)) then</p> <p>titleString = titleString .. '"' .. args.AltTitle .. '"'</p> <p>elseif (hasValue(args.TranslitTitle)) then</p> <p>if (hasValue(args.NativeTitleLangCode)) then</p> <p>titleString = titleString .. 'Transliteration: "' .. langModule._xlit({args.NativeTitleLangCode, args.TranslitTitle, italic = 'no'}) .. '"'</p> <p>else</p> <p>titleString = titleString .. 'Transliteration: "' .. args.TranslitTitle .. '"'</p> <p>end</p> <p>end</p> <p>end</p> <p>if (args.NativeTitle ~= nil) then</p> <p>if (args.NativeTitle == "") then</p> <p>isCellPresent = true</p> <p>else</p> <p>isCellPresent = true</p> <p>if (useSecondLine and lineBreakUsed == false) then</p> <p>titleString = titleString .. "<br />"</p> <p>end</p> <p>if (hasValue(args.NativeTitleLangCode)) then</p> <p>local languageCode = "Lang-" .. args.NativeTitleLangCode</p> <p>titleString = titleString .. " (" .. langModule._langx({code = args.NativeTitleLangCode, text=args.NativeTitle}) .. ")"</p> <p>else</p> <p>titleString = titleString .. " (" .. args.NativeTitle .. ")"</p> <p>end</p> <p>end</p> <p>end</p> <p>if (args.RAltTitle ~= nil) then</p> <p>if (args.RAltTitle == "") then</p> <p>isCellPresent = true</p> <p>else</p> <p>isCellPresent = true</p> <p>if (useSecondLine and lineBreakUsed == false) then</p> <p>titleString = titleString .. "<br />"</p> <p>end</p> <p>titleString = titleString .. args.RAltTitle</p> <p>end</p> <p>end</p> <p>return titleString, isCellPresent</p> <p>end</p> <p>--[[</p> <p>Local function which is used to extract data</p> <p>from the numbered title parameters (Title1, RTitle2, etc.), and then convert them to</p> <p>use the non-numbered prameter names (Title, RTitle).</p> <p>The function returns two results:</p> <p>-- The args parameter table.</p> <p>-- A boolean indicating if the title group has data.</p> <p>]]--</p> <p>local function extractDataFromNumberedTitleArgs(args, i)</p> <p>local nextGroupValid = false</p> <p>for _, v in ipairs(titleList) do</p> <p>local parameter = v</p> <p>local numberedParameter = v .. "_" .. i</p> <p>args[parameter] = args[numberedParameter]</p> <p>if (nextGroupValid == false and hasValue(args[numberedParameter])) then</p> <p>nextGroupValid = true</p> <p>end</p> <p>end</p> <p>return args, nextGroupValid</p> <p>end</p> <p>-- Local function which is used to create a Title cell.</p> <p>local function createTitleCell(args, numberOfParameterGroups, currentRow, isSerial)</p> <p>local titleText</p> <p>local isCellPresent</p> <p>if (isSerial and args.Title and currentRow > 1) then</p> <p>return nil</p> <p>end</p> <p>if (args.Title_2) then</p> <p>local args, nextGroupValid = extractDataFromNumberedTitleArgs(args, currentRow)</p> <p>end</p> <p>titleText, isCellPresent = createTitleText(args)</p> <p>if (isCellPresent == false) then</p> <p>return nil</p> <p>end</p> <p>local textAlign = "left"</p> <p>-- If Title is blank, then set Raw Title to TBA</p> <p>if (hasValue(titleText) == false) then</p> <p>titleText = tableEmptyCellModule._main({})</p> <p>textAlign = "left"</p> <p>end</p> <p>-- If title is the first cell, create it with a !scope="row"</p> <p>if (nonNilParams == 0) then</p> <p>if (isSerial) then</p> <p>row:tag('th')</p> <p>:addClass('summary')</p> <p>:attr('scope', 'row')</p> <p>:attr('rowspan', numberOfParameterGroups)</p> <p>:css('text-align', textAlign)</p> <p>:wikitext(titleText)</p> <p>else</p> <p>row:tag('th')</p> <p>:addClass('summary')</p> <p>:attr('scope', 'row')</p> <p>:css('text-align', textAlign)</p> <p>:wikitext(titleText)</p> <p>end</p> <p>else</p> <p>if (isSerial) then</p> <p>row:tag('td')</p> <p>:addClass('summary')</p> <p>:attr('rowspan', numberOfParameterGroups)</p> <p>:css('text-align', textAlign)</p> <p>:wikitext(titleText)</p> <p>else</p> <p>row:tag('td')</p> <p>:addClass('summary')</p> <p>:css('text-align', textAlign)</p> <p>:wikitext(titleText)</p> <p>end</p> <p>end</p> <p>nonNilParams = nonNilParams + 1</p> <p>end</p> <p>--[[</p> <p>Local function which is used to create column cells.</p> <p>EpisodeNumber, EpisodeNumber2 are created in different functions</p> <p>as they require some various if checks.</p> <p>See:</p> <p>-- createEpisodeNumberCell()</p> <p>-- createEpisodeNumberCellSecondary()</p> <p>]]--</p> <p>local function createCells(args, isSerial, currentRow, onInitialPage, title, numberOfParameterGroups)</p> <p>for k, v in ipairs(cellNameList) do</p> <p>if (v == 'ProdCode') then</p> <p>if (currentRow == 1 or (currentRow > 1 and parameterGroupCells[v])) then</p> <p>createProductionCodeCell(args, v, numberOfParameterGroups)</p> <p>end</p> <p>elseif (v == 'Title') then</p> <p>if (currentRow == 1 or (currentRow > 1 and parameterGroupCells[v])) then</p> <p>local isSerial = not args.Title_2 and true or false</p> <p>createTitleCell(args, numberOfParameterGroups, currentRow, isSerial)</p> <p>end</p> <p>elseif excludeList[v] and args[excludeList[v]] then</p> <p>-- Ignore this parameter set as multiple conflicting parameters were used</p> <p>elseif (args[v] and (v ~= 'EpisodeNumber' and v ~= 'EpisodeNumber2')) then</p> <p>-- Set empty cells to TBA/TBD</p> <p>if (args[v] == '') then</p> <p>createEmptyCell(args, v, false)</p> <p>elseif (v == 'WrittenBy' and title.namespace == 0) then</p> <p>if ((string.find(args[v], "<em>Story") ~= nil or string.find(args[v], "</em>Teleplay") ~= nil) and string.find(args[v], "8202") == nil) then</p> <p>--   is the hairspace added through {{StoryTeleplay}}</p> <p>addTrackingCategory(trackingCategoryList["raw_unformatted_storyteleplay"])</p> <p>end</p> <p>end</p> <p>-- If serial titles need to be centered and not left, then this should be removed.</p> <p>local textAlign = "center"</p> <p>if (v == 'Aux1' and isSerial) then</p> <p>textAlign = "left"</p> <p>end</p> <p>local thisRowspan</p> <p>if (not parameterGroupCells[v] and parameterGroupCellsAny) then</p> <p>thisRowspan = numberOfParameterGroups</p> <p>else</p> <p>thisRowspan = 1</p> <p>end</p> <p>if (currentRow == 1 or (currentRow > 1 and parameterGroupCells[v])) then</p> <p>createTableData(args[v], thisRowspan, textAlign)</p> <p>end</p> <p>nonNilParams = nonNilParams + 1</p> <p>checkUsageOfDateTemplates(args, v, onInitialPage, title)</p> <p>end</p> <p>-- Usages of TBA via <a href='?title=Template%3ATableTBA'>Template:TableTBA</a> can be found with the "tv-tba" class.</p> <p>if args[v] and (args[v] == "TBA" or string.find(args[v], "tv%-tba")) then</p> <p>cellValueTBA = true</p> <p>end</p> <p>end</p> <p>end</p> <p>-- Local function which is used to create a table row header for either the</p> <p>-- EpisodeNumber or EpisodeNumber2 column cells.</p> <p>local function createTableRowEpisodeNumberHeader(episodeNumber, numberOfParameterGroups, episodeText, separateEpisodeNumbers)</p> <p>local epID = string.match(episodeNumber, "^%w+")</p> <p>row:tag('th')</p> <p>:attr('scope', 'row')</p> <p>:attr('rowspan', not separateEpisodeNumbers and numberOfParameterGroups or 1)</p> <p>:attr('id', epID and 'ep' .. epID or '')</p> <p>:css('text-align', 'center')</p> <p>:wikitext(episodeText)</p> <p>end</p> <p>--[[</p> <p>Local function which is used to extract the text from the EpisodeNumber or EpisodeNumber2</p> <p>parameters and format them into a correct MoS compliant version.</p> <p>Styles supported:</p> <p>-- A number range of two numbers, indicating the start and end of the range,</p> <p>seperated by an en-dash (–) with no spaces in between.</p> <p>Example: "1 - 2" -> "1–2"; "1-2-3" -> "1–3".</p> <p>-- An alphanumeric or letter range, similar to the above.</p> <p>Example: "A - B" -> "A–B"; "A-B-C" -> "A–C".</p> <p>Example: "A1 - B1" -> "A1–B1"; "A1-B1-C1" -> "A1–C1".</p> <p>-- A number range of two numbers, indicating the start and end of the range,</p> <p>seperated by a visual <hr /> (divider line).</p> <p>-- An alphanumeric or letter range, similar to the above.</p> <p>]]--</p> <p>local function getEpisodeText(episodeNumber)</p> <p>if (episodeNumber == '') then</p> <p>return tableEmptyCellModule._main({})</p> <p>else</p> <p>local episodeNumber1</p> <p>local episodeNumber2</p> <p>-- Used for double episodes that need a visual "–"" or "<hr />"" added.</p> <p>local divider</p> <p>episodeNumber = episodeNumber:gsub('%s*<br%s*/?%s*>%s*', '<hr />')</p> <p>if (episodeNumber:match('^(%w+)%s*<hr */%s*>%s*(%w+)$')) then</p> <p>episodeNumber1, episodeNumber2 = episodeNumber:match('^(%w+)%s*<hr */%s*>%s*(%w+)$')</p> <p>divider = "<hr />"</p> <p>elseif (episodeNumber:match('^(%w+)%s*<hr */%s*>.-<hr */%s*>%s*(%w+)$')) then -- 3 or more elements</p> <p>episodeNumber1, episodeNumber2 = episodeNumber:match('^(%w+)%s*<hr */%s*>.-<hr */%s*>%s*(%w+)$')</p> <p>divider = "<hr />"</p> <p>elseif (mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&]%s*(%w+)$')) then</p> <p>episodeNumber1, episodeNumber2 = mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&]%s*(%w+)$')</p> <p>divider = "–"</p> <p>else</p> <p>episodeNumber1, episodeNumber2 = mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&].-[%s%-–/&]%s*(%w+)$') -- 3 or more elements</p> <p>divider = "–"</p> <p>end</p> <p>if (not episodeNumber1) then</p> <p>return episodeNumber</p> <p>elseif (not episodeNumber2) then</p> <p>return string.match(episodeNumber, '%w+')</p> <p>else</p> <p>return episodeNumber1 .. divider .. episodeNumber2</p> <p>end</p> <p>end</p> <p>end</p> <p>-- Local function which is used to create EpisodeNumber2 and EpisodeNumber3 cells.</p> <p>local function _createEpisodeNumberCellSecondary(episodeValue, numberOfParameterGroups, separateEpisodeNumbers)</p> <p>if (episodeValue) then</p> <p>local episodeText = getEpisodeText(episodeValue)</p> <p>if (nonNilParams == 0) then</p> <p>createTableRowEpisodeNumberHeader(episodeValue, numberOfParameterGroups, episodeText, separateEpisodeNumbers)</p> <p>else</p> <p>createTableData(episodeText, not separateEpisodeNumbers and numberOfParameterGroups or 1, "center")</p> <p>end</p> <p>nonNilParams = nonNilParams + 1</p> <p>end</p> <p>end</p> <p>-- Local function which is used to create seconday episode number cells.</p> <p>local function createEpisodeNumberCellSecondary(args, numberOfParameterGroups, separateEpisodeNumbers)</p> <p>_createEpisodeNumberCellSecondary(args.EpisodeNumber2, numberOfParameterGroups, separateEpisodeNumbers)</p> <p>_createEpisodeNumberCellSecondary(args.EpisodeNumber3, numberOfParameterGroups, separateEpisodeNumbers)</p> <p>end</p> <p>-- Local function which is used to create an EpisodeNumber cell.</p> <p>local function createEpisodeNumberCell(args, numberOfParameterGroups, separateEpisodeNumbers)</p> <p>if (args.EpisodeNumber) then</p> <p>local episodeText = getEpisodeText(args.EpisodeNumber)</p> <p>createTableRowEpisodeNumberHeader(args.EpisodeNumber, numberOfParameterGroups, episodeText, separateEpisodeNumbers)</p> <p>nonNilParams = nonNilParams + 1</p> <p>end</p> <p>end</p> <p>-- Local function which is used to create a single row of cells.</p> <p>-- This is the standard function called.</p> <p>local function createSingleRowCells(args, numberOfParameterGroups, multiTitleListEnabled, onInitialPage, title)</p> <p>createEpisodeNumberCell(args, 1, false)</p> <p>createEpisodeNumberCellSecondary(args, 1, false)</p> <p>createCells(args, false, 1, onInitialPage, title, numberOfParameterGroups)</p> <p>end</p> <p>-- Local function which is used to create a multiple row of cells.</p> <p>-- This function is called when part of the row is rowspaned.</p> <p>local function createMultiRowCells(args, numberOfParameterGroups, onInitialPage, title, topColor)</p> <p>local EpisodeNumberSplit = (args.EpisodeNumber_1 and true or false)</p> <p>for i = 1, numberOfParameterGroups do</p> <p>args = extractDataFromNumberedSerialArgs(args, i, numberOfParameterGroups, title)</p> <p>if (EpisodeNumberSplit or (not EpisodeNumberSplit and i == 1)) then</p> <p>createEpisodeNumberCell(args, numberOfParameterGroups, EpisodeNumberSplit)</p> <p>createEpisodeNumberCellSecondary(args, numberOfParameterGroups, EpisodeNumberSplit)</p> <p>end</p> <p>createCells(args, true, i, onInitialPage, title, numberOfParameterGroups)</p> <p>if (i ~= numberOfParameterGroups) then</p> <p>local textColor = '#333'</p> <p>if topColor == 'inherit' then</p> <p>textColor = 'inherit'</p> <p>end</p> <p>row = row:done() -- Use done() to close the 'tr' tag in rowspaned rows.</p> <p>:tag('tr')</p> <p>:addClass('vevent')</p> <p>:addClass('module-episode-list-row')</p> <p>:css('text-align', 'center')</p> <p>:css('background', topColor)</p> <p>:css('color', textColor)</p> <p>end</p> <p>end</p> <p>end</p> <p>-- Local function which is used to retrieve the NumParts value.</p> <p>local function getnumberOfParameterGroups(args)</p> <p>for k, v in ipairs(cellNameList) do</p> <p>local numberedParameter = v .. "_" .. 1</p> <p>if (args[numberedParameter]) then</p> <p>parameterGroupCells[v] = true</p> <p>parameterGroupCellsAny = true</p> <p>if not firstParameterGroupCell then</p> <p>firstParameterGroupCell = k</p> <p>end</p> <p>end</p> <p>end</p> <p>if (hasValue(args.NumParts)) then</p> <p>return args.NumParts, true</p> <p>else</p> <p>return 1, false</p> <p>end</p> <p>end</p> <p>-- Local function which is used to retrieve the Top Color value.</p> <p>local function getTopColor(args, rowColorEnabled, onInitialPage)</p> <p>local episodeNumber = mathModule._cleanNumber(args.EpisodeNumber) or 1</p> <p>if (args.TopColor) then</p> <p>if (string.find(args.TopColor, "#")) then</p> <p>return args.TopColor</p> <p>else</p> <p>return '#' .. args.TopColor</p> <p>end</p> <p>else</p> <p>return 'inherit'</p> <p>end</p> <p>end</p> <p>-- Local function which is used to retrieve the Row Color value.</p> <p>local function isRowColorEnabled(args)</p> <p>local rowColorEnabled = yesNoModule(args.RowColor, false)</p> <p>if (args.RowColor and string.lower(args.RowColor) == 'on') then</p> <p>rowColorEnabled = true</p> <p>end</p> <p>return rowColorEnabled</p> <p>end</p> <p>-- Local function which is used to retrieve the Line Color value.</p> <p>local function getLineColor(args)</p> <p>-- Default color to light blue</p> <p>local lineColor = args.LineColor or 'CCCCFF'</p> <p>-- Add # to color if necessary, and set to default color if invalid</p> <p>if (htmlColor[lineColor] == nil) then</p> <p>lineColor = '#' .. (mw.ustring.match(lineColor, '^[%s#]*([a-fA-F0-9]*)[%s]*$') or '')</p> <p>if (lineColor == '#') then</p> <p>lineColor = '#CCCCFF'</p> <p>end</p> <p>end</p> <p>return lineColor</p> <p>end</p> <p>-- Local function which is used to check if the table is located on the page</p> <p>-- currently viewed, or on a transcluded page instead.</p> <p>-- If it is on a transcluded page, the episode summary should not be shown.</p> <p>local function isOnInitialPage(args, sublist, pageTitle, initiallistTitle)</p> <p>-- This should be the only check needed, however, it was previously implemented with two templates</p> <p>-- with one of them not requiring an article name, so for backward compatability, the whole sequence is kept.</p> <p>local onInitialPage</p> <p>local onInitialPageCheck = (mw.uri.anchorEncode(pageTitle) == mw.uri.anchorEncode(initiallistTitle))</p> <p>-- Only sublist had anything about hiding, so only it needs to even check</p> <p>if (sublist) then</p> <p>onInitialPage = onInitialPageCheck</p> <p>-- avoid processing ghost references</p> <p>if (not onInitialPage) then</p> <p>args.ShortSummary = nil</p> <p>end</p> <p>else</p> <p>if (initiallistTitle == "") then</p> <p>onInitialPage = true</p> <p>else</p> <p>onInitialPage = onInitialPageCheck</p> <p>end</p> <p>end</p> <p>return onInitialPage</p> <p>end</p> <p>-- Local function which does the actual main process.</p> <p>local function _main(args, sublist)</p> <p>local title = mw.title.getCurrentTitle()</p> <p>local pageTitle = title.text</p> <p>local initiallistTitle = args['1'] or ''</p> <p>-- Is this list on the same page as the page directly calling the template?</p> <p>local onInitialPage = isOnInitialPage(args, sublist, pageTitle, initiallistTitle)</p> <p>-- Need just this parameter removed if blank, no others</p> <p>if (hasValue(args.ShortSummary) == false) then</p> <p>args.ShortSummary = nil</p> <p>end</p> <p>local lineColor = getLineColor(args)</p> <p>local rowColorEnabled = isRowColorEnabled(args)</p> <p>local topColor = getTopColor(args, rowColorEnabled, onInitialPage)</p> <p>local root = mw.html.create() -- Create the root mw.html object to return</p> <p>local textColor = '#333'</p> <p>if topColor == 'inherit' then</p> <p>textColor = 'inherit'</p> <p>end</p> <p>row = root:tag('tr') -- Create the table row and store it globally</p> <p>:addClass('vevent')</p> <p>:addClass('module-episode-list-row')</p> <p>:css('text-align', 'center')</p> <p>:css('background', topColor)</p> <p>:css('color', textColor)</p> <p>local numberOfParameterGroups, multiTitleListEnabled = getnumberOfParameterGroups(args)</p> <p>if (multiTitleListEnabled) then</p> <p>createMultiRowCells(args, numberOfParameterGroups, onInitialPage, title, topColor)</p> <p>else</p> <p>createSingleRowCells(args, numberOfParameterGroups, multiTitleListEnabled, onInitialPage, title)</p> <p>end</p> <p>-- add these categories only in the mainspace and only if they are on the page where the template is used</p> <p>if (onInitialPage and title.namespace == 0) then</p> <p>addLineColorTrackingCategories(args)</p> <p>addTopColorTrackingCategories(args)</p> <p>end</p> <p>if (cellValueTBA == true and title.namespace == 0) then</p> <p>addTrackingCategory(trackingCategoryList["tba_values"])</p> <p>end</p> <p>-- Do not show the summary if this is being transcluded on the initial list page</p> <p>-- Do include it on all other lists</p> <p>if (onInitialPage and args.ShortSummary) then</p> <p>local bottomWrapper = createShortSummaryRow(args, lineColor)</p> <p>return tostring(root) .. tostring(bottomWrapper) .. trackingCategories</p> <p>else</p> <p>return tostring(root) .. trackingCategories</p> <p>end</p> <p>end</p> <p>-- Local function which handles both module entry points.</p> <p>local function main(frame, sublist)</p> <p>local getArgs = require('Module:Arguments').getArgs</p> <p>local args</p> <p>-- Most parameters should still display when blank, so don't remove blanks</p> <p>if (sublist) then</p> <p>args = getArgs(frame, {removeBlanks = false, wrappers = 'Template:Episode list/sublist'})</p> <p>else</p> <p>args = getArgs(frame, {removeBlanks = false, wrappers = 'Template:Episode list'})</p> <p>end</p> <p>-- args['1'] = mw.getCurrentFrame():getParent():getTitle()</p> <p>return _main(args, sublist, frame)</p> <p>end</p> <p>--[[</p> <p>Public function which is used to create an Episode row</p> <p>for an Episode Table used for lists of episodes where each table is on a different page,</p> <p>usually placed on individual season articles.</p> <p>For tables which are all on the same page see p.list().</p> <p>Parameters:</p> <p>-- |1= — required; The title of the article where the Episode Table is located at.</p> <p>-- |EpisodeNumber= — suggested; The overall episode number in the series.</p> <p>-- |EpisodeNumber2= — suggested; The episode number in the season.</p> <p>-- |Title= — suggested; The English title of the episode.</p> <p>-- |RTitle= — optional; Unformatted parameter that can be used to add a reference after "Title",</p> <p>or can be used as a "raw title" to replace "Title" completely.</p> <p>-- |AltTitle= — optional; An alternative title, such as the title of a foreign show's episode in its native language,</p> <p>or a title that was originally changed.</p> <p>-- |TranslitTitle= — optional; The title of the episode transliteration (Romanization) to Latin characters.</p> <p>-- |RAltTitle= — optional; Unformatted parameter that can be used to add a reference after "AltTitle",</p> <p>or can be used as a "raw title" to replace "AltTitle" completely.</p> <p>-- |NativeTitle= — optional; The title of the episode in the native language.</p> <p>-- |NativeTitleLangCode — optional; The language code of the native title language.</p> <p>-- |Aux1= — optional; General purpose parameter. The meaning is specified by the column header.</p> <p>This parameter is also used for Serial episode titles, such as those used in Doctor Who.</p> <p>-- |DirectedBy= — optional; Name of the episode's director. May contain links.</p> <p>-- |WrittenBy= — optional; Primary writer(s) of the episode. May include links.</p> <p>-- |Aux2= — optional; General purpose parameter. The meaning is specified by the column header.</p> <p>-- |Aux3= — optional; General purpose parameter. The meaning is specified by the column header.</p> <p>-- |OriginalAirDate= — optional; This is the date the episode first aired on TV, or is scheduled to air.</p> <p>-- |AltDate= — optional; The next notable air date, such as the first air date of an anime in English.</p> <p>-- |Guests= — optional; List of Guests for talk shows. Cannot be used simultaneously with Aux1.</p> <p>-- |MusicalGuests= — optional; List of MusicalGuests for talk shows. Cannot be used simultaneously with Aux2.</p> <p>-- |ProdCode= — optional; The production code in the series. When defined, this parameter also creates a link anchor,</p> <p>prefixed by "pc"; for example, List of episodes#pc01.</p> <p>-- |Viewers= — optional; Number of viewers who watched the episode. Should include a reference.</p> <p>-- |Aux4= — optional; General purpose parameter. The meaning is specified by the column header.</p> <p>-- |ShortSummary= — optional; A short 100–200 word plot summary of the episode.</p> <p>-- |LineColor= — optional; Colors the separator line between episode entries. If not defined the color defaults to "#CCCCFF"</p> <p>and the article is placed in Category:Episode list using the default LineColor.</p> <p>Use of "#", or anything but a valid hex code will result in an invalid syntax.</p> <p>-- |TopColor= — discouraged; Colors the main row of information (that is, not the ShortSummary row).</p> <p>Articles using this parameter are placed in Category:Episode lists with row deviations.</p> <p>-- |RowColor= — optional; Switch parameter that must only be defined when the EpisodeNumber= entry is not a regular number</p> <p>(e.g. "12–13" for two episodes described in one table entry).</p> <p>If the first episode number is even, define pass "on". If the first episode number is odd, pass "off".</p> <p>--]]</p> <p>function p.sublist(frame)</p> <p>return main(frame, true)</p> <p>end</p> <p>--[[</p> <p>Public function which is used to create an Episode row</p> <p>for an Episode Table used for lists of episodes where all tables are on the same page.</p> <p>For tables which are on different pages see p.sublist().</p> <p>For complete parameter documentation, see the documentation at p.sublist().</p> <p>--]]</p> <p>function p.list(frame)</p> <p>return main(frame, false)</p> <p>end</p> <p>return p</p></div></section></div></main> <footer class="site-footer"> <div class="footer-container"> <div class="footer-links"> <a href="/about.php">About</a> <a href="/help.php">Help</a> <a href="/updates.php">Updates</a> <a href="/contact.php">Contact</a> <a href="/privacy.php">Privacy</a> <a href="/terms.php">Terms</a> <a href="https://github.com/yourusername/friendly-wiki" target="_blank" rel="noopener">GitHub</a> </div> <div class="footer-copy"> © 2025 Friendly Wiki. All rights reserved. </div> </div> </footer> <script> const toggle = document.getElementById('mobileMenuToggle'); const menu = document.getElementById('mobileMenu'); toggle.addEventListener('click', () => { menu.classList.toggle('active'); }); </script> <!-- Collapsible toggle --> <script> document.addEventListener("DOMContentLoaded", function () { const toggles = document.querySelectorAll('.section-toggle'); toggles.forEach(toggle => { toggle.addEventListener('click', function () { const section = toggle.closest('.collapsible'); const body = section.querySelector('.wiki-body'); body.classList.toggle('collapsed'); }); }); }); </script>