Module:Episode list: Difference between revisions

Content deleted Content added
Fix for when date not set
Comment out per talk page
 
(99 intermediate revisions by 17 users not shown)
Line 1:
local p = {}
 
-- This module requires the use of the following modules:
local getArgs
local yesnocolorContrastModule = require('Module:YesnoColor contrast')
local mmhtmlColor = requiremw.loadData('Module:MathColor contrast/colors')
local contrast_ratiolangModule = require('"Module:Color contrast'Lang")._ratio
local HTMLcolormathModule = mw.loadDatarequire( 'Module:Color contrast/colorsMath' )
local tableEmptyCellModule = require('Module:Table empty cell')
local yesNoModule = require('Module:Yesno')
 
-- mw.html object for the generated row.
function p.sublist(frame)
local row
return main(frame,true)
end
 
-- Variable that will decide the colspan= of the Short Summary cell.
function p.list(frame)
local nonNilParams = 0
return main(frame,false)
end
 
-- Variable that will keep track if a TBA value was entered.
function idtrim(val,search)
local cellValueTBA = false
local valfind = string.find(val, search)
 
if valfind == nil then
-- 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, valfindvalFind-1)
end
end
 
-- Local function which is used to validate that a parameter has an actual value.
function main(frame, sublist)
local function hasValue(param)
if not getArgs then
if (param ~= nil and param ~= "") then
getArgs = require('Module:Arguments').getArgs
return true
else
return false
end
end
local args
 
-- Local function which is used to create a table data cell.
-- Most parameters should still display when blank, so don't remove blanks
local function createTableData(text, rowSpan, textAlign)
if sublist then
if (rowSpan ~= nil and tonumber(rowSpan) > 1) then
args = getArgs(frame, {removeBlanks = false, wrappers = 'Template:Episode list/sublist'})
row:tag('td')
:attr('rowspan', rowSpan)
:wikitext(text)
else
row:tag('td')
args = getArgs(frame, {removeBlanks = false, wrappers = 'Template:Episode list'})
: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 = '<span></span>\n' .. shortSummaryText
end
 
if (shortSummaryText:match('\n[*:;#]')) then
shortSummaryText = shortSummaryText .. '\n<span></span>'
end
local titleshortSummaryDiv = mw.titlehtml.getCurrentTitlecreate('div')
:addClass('shortSummaryText')
local page_title = mw.title.getCurrentTitle().text
:css('max-width', '90vw')
local mainlist = args.MainList or args['1'] or ''
:css('position', 'sticky')
:css('left', '0.2em')
-- Is this list on the same page as the page directly calling the template?
:newline()
local on_main_page
:wikitext(shortSummaryText)
 
-- Only sublist had anything about hiding, so only it needs to even check
local shortSummaryCell = mw.html.create('td')
if sublist then
:addClass('description')
on_main_page = mw.uri.anchorEncode(page_title) == mw.uri.anchorEncode(mainlist)
:css('border-bottom', 'solid 3px ' .. lineColor)
-- avoid processing ghost references
:attr('colspan', nonNilParams)
if on_main_page then
:newline()
args.ShortSummary = nil
: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
else
-- Normal lists can ALWAYS show the summary
on_main_page = false
end
end
 
-- Declare all the possible <td>/<th> tags here, and the return string
-- Local function which is used to add tracking categories for Line Color issues.
local EpisodeNumber,EpisodeNumber2,Title,Aux1,DirectedBy,WrittenBy,
local function addLineColorTrackingCategories(args)
DirectedBy,Aux2,Aux3,OriginalAirDate,AltDate,ProdCode,
if (hasValue(args.LineColor)) then
Viewers,Aux4,return_table
local blackContrastRatio = colorContrastModule._ratio{args.LineColor, 'black', ['error'] = 0}
local whiteContrastRatio = colorContrastModule._ratio{'white', args.LineColor, ['error'] = 0}
-- Need just this parameter removed if blank, no others
 
if args.ShortSummary then
if not args(colorContrastModule.ShortSummary:find_lum(args.LineColor) == '%S') then
addTrackingCategory(trackingCategoryList["faulty_line_colors"])
args.ShortSummary = nil
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
-- Default color to light blue
-- with either "TBD" or "N/A".
local line_color = args.LineColor or 'CCCCFF'
-- 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)
-- Add # to color if necessary, and set to default color if invalid
if HTMLcolor[line_color]args.OriginalAirDate == nil or args.OriginalAirDate == '' then
return tableEmptyCellModule._main({alt_text = awaitingText})
line_color = '#'..(mw.ustring.match(line_color, '^[%s#]*([a-fA-F0-9]*)[%s]*$') or '')
end
if line_color == '#' then
 
line_color = '#CCCCFF'
-- 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("&nbsp;", " "):match("(%a+) (%d+), (%d+)")
 
if (month == nil) then
day, month, year = args.OriginalAirDate:gsub("&nbsp;", " "):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})
else
return tableEmptyCellModule._main({alt_text = awaitingText})
end
end
end
 
-- List of parameter names to test
-- Local function which is used to create an empty cell.
-- Keep this order as is
local function createEmptyCell(args, v, unsetParameterGroup)
local cell_names = {
if (unsetParameterGroup) then
'EpisodeNumber2',
args[v] = tableEmptyCellModule._main({alt_text = "N/A"})
'Title',
elseif (v == 'Viewers' and hasValue(args.OriginalAirDate)) then
'Aux1',
args[v] = setTBDStatus(args, "TBD", "N/A", 4)
'DirectedBy',
elseif (v == 'DirectedBy' or v == 'WrittenBy',) then
args[v] = setTBDStatus(args, "TBA", "Unknown", 4)
'Aux2',
else
'Aux3',
args[v] = tableEmptyCellModule._main({})
'OriginalAirDate',
'AltDate',
'ProdCode',
'Viewers',
'Aux4'
}
-- Is there a way to call a variable by its name stored as a string? Doubt it
-- This list matches strings with the table cell variables
local td_tags = {
['EpisodeNumber2'] = EpisodeNumber2,
['Title'] = Title,
['Aux1'] = Aux1,
['DirectedBy'] = DirectedBy,
['WrittenBy'] = WrittenBy,
['DirectedBy'] = DirectedBy,
['Aux2'] = Aux2,
['Aux3'] = Aux3,
['OriginalAirDate'] = OriginalAirDate,
['AltDate'] = AltDate,
['ProdCode'] = ProdCode,
['Viewers'] = Viewers,
['Aux4'] = Aux4,
}
local table_row = mw.html.create('tr')
:addClass('vevent')
:css('text-align','center')
local row_color = yesno(args.RowColor, false)
if args.RowColor and string.lower(args.RowColor) == 'on' then
row_color = true
end
end
 
-- Air dates that don't use {{Start date}}
local top_color
local function checkUsageOfDateTemplates(args, v, onInitialPage, title)
local epn = mm._cleanNumber(args.EpisodeNumber) or 1
if v == 'OriginalAirDate'
if args.TopColor then
and args[v] ~= ''
top_color = '#'..args.TopColor
and string.match(args[v], '%d%d%d%d') -- Ensure it contains a four-digit number (likely a year)
elseif row_color and not on_main_page and mm._mod(epn,2) == 0 then
and string.match(args[v], '2C2C2C') == nil -- Avoids this specific pattern
top_color = '#E9E9E9'
and not string.match(args[v], '^Unaired') -- Exclude anything starting with "Unaired"
elseif not on_main_page and args.ShortSummary then
and string.find(args[v], 'itvstart') == nil -- Avoids a {{Start date}} unique class
top_color = '#F2F2F2'
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
top_color = 'inherit'
end
table_row:css('background',top_color)
if (hasValue(args.ProdCode) and string.find(args.ProdCode, 'TBA') == nil) then
-- This will decide the colspan= of the summary cell
row:tag('td')
-- Start as 1 because EpisodeNumber is always created
:attr('id', 'pc' .. idTrim(idTrim(args.ProdCode, ' ----'), '<'))
local nonnil_params = 1
:attr('rowspan', thisRowspan)
:css('text-align', 'center')
-- Created separately because it is the only <th> tag
:wikitext(args.ProdCode)
elseif (args.ProdCode == '' or string.find(args.ProdCode or '', 'TBA') ~= nil) then
if args.EpisodeNumber then
createEmptyCell(args, v, false)
if (args.EpisodeNumber == '') then args.EpisodeNumber = frame:expandTemplate{title='TableTBA'}; end
createTableData(args.ProdCode, thisRowspan, "center")
EpisodeNumber = mw.html.create('th')
else
:attr('scope','row')
-- ProductionCode parameter not used; Do nothing.
:attr('id','ep'..idtrim(idtrim(args.EpisodeNumber,' ----'),'<'))
:css('text-align','center')
:wikitext(args.EpisodeNumber)
table_row:css('background',top_color)
table_row:node(EpisodeNumber)
end
 
nonNilParams = nonNilParams + 1
-- The wikitext in the Title cell is a little more involved than the others
end
local function add_title()
 
local title_string = ''
--[[
Local function which is used to extract data
-- Surround the Title with quotes; no quotes if empty
from the numbered serial parameters (Title1, Aux1, etc.), and then convert them to
if args.Title and args.Title:find('%S') then
use the non-numbered parameter names (Title, Aux).
title_string = title_string..'"'..args.Title..'"'
 
The function returns the args as non-numbered parameter 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
 
if args.RTitle then
return args
title_string = title_string..args.RTitle
end
 
--[[
Local function which is used to create the Title cell text.
 
The title text will be handled in the following way:
Line 1: <Title><RTitle> (with no space between)
Line 2: <AltTitle><RAltTitle> (with no space between) OR
Line 2: Transliteration: <TranslitTitle> (<Language>: <NativeTitle>)<RAltTitle> (with space between first two parameters)
 
If <Title> or <RTitle> are empty,
then the values of line 2 will be placed on line 1 instead.
 
--]]
local function createTitleText(args)
local titleString = ''
local isCellPresent = false
local useSecondLine = false
local lineBreakUsed = false
 
-- Surround the Title with quotes; No quotes if empty.
if (args.Title ~= nil) then
if (args.Title == "") then
isCellPresent = true
else
titleString = '"' .. args.Title .. '"'
useSecondLine = true
isCellPresent = true
end
end
 
-- Surround the AltTitle with quotes; no quotes if empty
if (args.AltTitleRTitle and~= args.AltTitle:find('%S'nil) then
if (args.RTitle == "") then
title_string = title_string..'<br>"'..args.AltTitle..'"'
isCellPresent = true
else
titleString = titleString .. args.RTitle
useSecondLine = true
isCellPresent = true
end
end
 
if args.RAltTitle then
-- Surround the AltTitle/TranslitTitle with quotes; No quotes if empty.
title_string = title_string..args.RAltTitle
if (args.AltTitle or args.TranslitTitle) then
 
isCellPresent = true
 
if (useSecondLine) then
titleString = titleString .. "<br />"
lineBreakUsed = true
end
 
if (hasValue(args.AltTitle)) then
titleString = titleString .. '"' .. args.AltTitle .. '"'
elseif (hasValue(args.TranslitTitle)) then
if (hasValue(args.NativeTitleLangCode)) then
titleString = titleString .. 'Transliteration: "' .. langModule._xlit({args.NativeTitleLangCode, args.TranslitTitle, italic = 'no'}) .. '"'
else
titleString = titleString .. 'Transliteration: "' .. args.TranslitTitle .. '"'
end
end
return title_string
end
local categories = ''
 
if (args.NativeTitle ~= nil) then
for _,v in ipairs(cell_names) do
if (args.NativeTitle == "") then
-- Title is in the middle, so it's probably better to just switch back and again instead of doing 2 nodes
isCellPresent = true
-- and then title, and then the rest in a loop
else
if v == 'Title' then
isCellPresent = true
-- If Title is blank, then set Raw Title to TBA
 
if args.Title == '' and (args.RTitle == '' or not args.RTitle) then args.RTitle = frame:expandTemplate{title='TableTBA'}; end
if (useSecondLine and lineBreakUsed == false) then
nonnil_params = nonnil_params + 1
titleString = titleString .. "<br />"
local title_text = add_title()
td_tags[v] = mw.html.create('td')
td_tags[v]:wikitext(title_text)
:addClass('summary')
:css('text-align','left')
table_row:node(td_tags[v])
elseif args[v] then
-- Air dates that don't use {{Start date}}
if v == 'OriginalAirDate' and args[v] ~= '' and string.match(args[v], '%d%d%d%d') ~= nil and string.find(args[v],'dtstart') == nil and not on_main_page and title.namespace == 0 then
categories = categories..'[[Category:Episode lists with unformatted air dates]]'
end
 
-- Set empty cells to TBA/TBD
if (hasValue(args[v] == ''.NativeTitleLangCode)) then
local languageCode = "Lang-" .. args.NativeTitleLangCode
-- Set to N/A if viewers haven't been available for four weeks, else set it as TBD
titleString = titleString .. " (" .. langModule._langx({code = args.NativeTitleLangCode, text=args.NativeTitle}) .. ")"
if v == 'Viewers' and args.OriginalAirDate and args.OriginalAirDate ~= '' then
else
local month, day, year = args.OriginalAirDate:gsub("&nbsp;"," "):match("(%a+) (%d+), (%d+)")
titleString = titleString .. " (" .. args.NativeTitle .. ")"
if month == nil then
day, month, year = args.OriginalAirDate:gsub("&nbsp;"," "):match("(%d+) (%a+) (%d+)")
end
if day == nil then
args[v] = frame:expandTemplate{title='TableTBA',args={'TBD'}};
else
local MONTHS = {January=1, February=2, March=3, April=4, May=5, June=6, July=7, August=8, September=9, October=10, November=11, December=12}
local seconds = os.time()-os.time({year=year,month=MONTHS[month],day=day,hour=0,min=0,sec=0})
if seconds >= 60*60*24*7*4 then args[v] = frame:expandTemplate{title='TableTBA',args={'N/A'}};
else args[v] = frame:expandTemplate{title='TableTBA',args={'TBD'}}; end
end
else args[v] = frame:expandTemplate{title='TableTBA'}; end
end
nonnil_params = nonnil_params + 1
td_tags[v] = mw.html.create('td')
td_tags[v]:wikitext(args[v])
table_row:node(td_tags[v])
end
end
 
if (args.RAltTitle ~= nil) then
-- Production code also has an additional attribute, so add it separately here
if td_tags.ProdCode and args.ProdCode and (string.find(args.ProdCode,'TBA')RAltTitle == nil"") then
isCellPresent = true
td_tags.ProdCode:attr('id','pc'..idtrim(idtrim(args.ProdCode,' ----'),'<'))
else
isCellPresent = true
 
if (useSecondLine and lineBreakUsed == false) then
titleString = titleString .. "<br />"
end
 
titleString = titleString .. args.RAltTitle
end
end
 
return titleString, isCellPresent
end
 
--[[
Local function which is used to extract data
from the numbered title parameters (Title1, RTitle2, etc.), and then convert them to
use the non-numbered parameter names (Title, RTitle).
 
The function returns two results:
-- The args parameter table.
-- A boolean indicating if the title group has data.
]]--
local function extractDataFromNumberedTitleArgs(args, i)
local nextGroupValid = false
 
for _, v in ipairs(titleList) do
local parameter = v
local numberedParameter = v .. "_" .. i
args[parameter] = args[numberedParameter]
 
if (nextGroupValid == false and hasValue(args[numberedParameter])) then
nextGroupValid = true
end
end
 
return args, nextGroupValid
end
 
-- Local function which is used to create a Title cell.
local function createTitleCell(args, numberOfParameterGroups, currentRow, isSerial)
local titleText
local isCellPresent
if (isSerial and args.Title and currentRow > 1) then
-- add these categories only in the mainspace and only if they are on the page where the template is used
return nil
if not on_main_page and title.namespace == 0 then
end
if args.LineColor and args.LineColor ~= '' then
if (args.Title_2) then
local black_cr = contrast_ratio{args.LineColor, 'black', ['error'] = 0}
local args, nextGroupValid = extractDataFromNumberedTitleArgs(args, currentRow)
local white_cr = contrast_ratio{'white', args.LineColor, ['error'] = 0}
end
if frame:expandTemplate{title='ColorToLum',args={args.LineColor}} == '' then
titleText, isCellPresent = createTitleText(args)
categories = categories..'[[Category:Episode lists with faulty line colors]]'
 
elseif black_cr < 7 and white_cr < 7 then
if (isCellPresent == false) then
categories = categories..'[[Category:Episode lists with non-compliant line colors]]'
return nil
end
 
local textAlign = "left"
 
-- If Title is blank, then set Raw Title to TBA
if (hasValue(titleText) == false) then
titleText = tableEmptyCellModule._main({})
textAlign = "left"
end
 
-- If title is the first cell, create it with a !scope="row"
if (nonNilParams == 0) then
if (isSerial) then
row:tag('th')
:addClass('summary')
:attr('scope', 'row')
:attr('rowspan', numberOfParameterGroups)
:css('text-align', textAlign)
:wikitext(titleText)
else
row:tag('th')
:addClass('summary')
:attr('scope', 'row')
:css('text-align', textAlign)
:wikitext(titleText)
end
else
if (isSerial) then
row:tag('td')
:addClass('summary')
:attr('rowspan', numberOfParameterGroups)
:css('text-align', textAlign)
:wikitext(titleText)
else
row:tag('td')
:addClass('summary')
:css('text-align', textAlign)
:wikitext(titleText)
end
end
 
nonNilParams = nonNilParams + 1
end
 
--[[
Local function which is used to create column cells.
 
EpisodeNumber, EpisodeNumber2 are created in different functions
as they require some various if checks.
 
See:
-- createEpisodeNumberCell()
-- createEpisodeNumberCellSecondary()
]]--
local function createCells(args, isSerial, currentRow, onInitialPage, title, numberOfParameterGroups)
for k, v in ipairs(cellNameList) do
if (v == 'ProdCode') then
if (currentRow == 1 or (currentRow > 1 and parameterGroupCells[v])) then
createProductionCodeCell(args, v, numberOfParameterGroups)
end
elseif (v == 'Title') then
if (currentRow == 1 or (currentRow > 1 and parameterGroupCells[v])) then
local isSerial = not args.Title_2 and true or false
createTitleCell(args, numberOfParameterGroups, currentRow, isSerial)
end
elseif excludeList[v] and args[excludeList[v]] then
-- Ignore this parameter set as multiple conflicting parameters were used
elseif (args[v] and (v ~= 'EpisodeNumber' and v ~= 'EpisodeNumber2')) then
-- Set empty cells to TBA/TBD
if (args[v] == '') then
createEmptyCell(args, v, false)
elseif (v == 'WrittenBy' and title.namespace == 0) then
if ((string.find(args[v], "''Story") ~= nil or string.find(args[v], "''Teleplay") ~= nil) and string.find(args[v], "8202") == nil) then
-- &#8202; is the hairspace added through {{StoryTeleplay}}
addTrackingCategory(trackingCategoryList["raw_unformatted_storyteleplay"])
end
end
 
-- If serial titles need to be centered and not left, then this should be removed.
local textAlign = "center"
-- if (v == 'Aux1' and isSerial) then
-- textAlign = "left"
-- end
 
local thisRowspan
if (not parameterGroupCells[v] and parameterGroupCellsAny) then
thisRowspan = numberOfParameterGroups
else
thisRowspan = 1
end
 
if (currentRow == 1 or (currentRow > 1 and parameterGroupCells[v])) then
createTableData(args[v], thisRowspan, textAlign)
end
nonNilParams = nonNilParams + 1
checkUsageOfDateTemplates(args, v, onInitialPage, title)
end
-- Usages of TBA via [[Template:TableTBA]] can be found with the "tv-tba" class.
if args[v] and (args[v] == "TBA" or string.find(args[v], "tv%-tba")) then
cellValueTBA = true
end
end
end
 
-- Local function which is used to create a table row header for either the
-- EpisodeNumber or EpisodeNumber2 column cells.
local function createTableRowEpisodeNumberHeader(episodeNumber, numberOfParameterGroups, episodeText, separateEpisodeNumbers)
local epID = string.match(episodeNumber, "^%w+")
row:tag('th')
:attr('scope', 'row')
:attr('rowspan', not separateEpisodeNumbers and numberOfParameterGroups or 1)
:attr('id', epID and 'ep' .. epID or '')
:css('text-align', 'center')
:wikitext(episodeText)
end
 
--[[
Local function which is used to extract the text from the EpisodeNumber or EpisodeNumber2
parameters and format them into a correct MoS compliant version.
 
Styles supported:
-- A number range of two numbers, indicating the start and end of the range,
seperated by an en-dash (–) with no spaces in between.
Example: "1 - 2" -> "1–2"; "1-2-3" -> "1–3".
-- An alphanumeric or letter range, similar to the above.
Example: "A - B" -> "A–B"; "A-B-C" -> "A–C".
Example: "A1 - B1" -> "A1–B1"; "A1-B1-C1" -> "A1–C1".
-- A number range of two numbers, indicating the start and end of the range,
seperated by a visual <hr /> (divider line).
-- An alphanumeric or letter range, similar to the above.
]]--
local function getEpisodeText(episodeNumber)
if (episodeNumber == '') then
return tableEmptyCellModule._main({})
else
 
local episodeNumber1
local episodeNumber2
 
-- Used for double episodes that need a visual "–"" or "<hr />"" added.
local divider
 
episodeNumber = episodeNumber:gsub('%s*<br%s*/?%s*>%s*', '<hr />')
 
if (episodeNumber:match('^(%w+)%s*<hr */%s*>%s*(%w+)$')) then
episodeNumber1, episodeNumber2 = episodeNumber:match('^(%w+)%s*<hr */%s*>%s*(%w+)$')
divider = "<hr />"
elseif (episodeNumber:match('^(%w+)%s*<hr */%s*>.-<hr */%s*>%s*(%w+)$')) then -- 3 or more elements
episodeNumber1, episodeNumber2 = episodeNumber:match('^(%w+)%s*<hr */%s*>.-<hr */%s*>%s*(%w+)$')
divider = "<hr />"
elseif (mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&]%s*(%w+)$')) then
episodeNumber1, episodeNumber2 = mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&]%s*(%w+)$')
divider = "–"
else
episodeNumber1, episodeNumber2 = mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&].-[%s%-–/&]%s*(%w+)$') -- 3 or more elements
categories = categories..'[[Category:Episode list using the default LineColor]]'
divider = "–"
end
 
if (not episodeNumber1) then
return episodeNumber
elseif (not episodeNumber2) then
return string.match(episodeNumber, '%w+')
else
return episodeNumber1 .. divider .. episodeNumber2
end
end
end
 
-- Local function which is used to create EpisodeNumber2 and EpisodeNumber3 cells.
local function _createEpisodeNumberCellSecondary(episodeValue, numberOfParameterGroups, separateEpisodeNumbers)
if (episodeValue) then
local episodeText = getEpisodeText(episodeValue)
 
if (nonNilParams == 0) then
createTableRowEpisodeNumberHeader(episodeValue, numberOfParameterGroups, episodeText, separateEpisodeNumbers)
else
createTableData(episodeText, not separateEpisodeNumbers and numberOfParameterGroups or 1, "center")
end
 
nonNilParams = nonNilParams + 1
 
end
end
 
-- Local function which is used to create seconday episode number cells.
local function createEpisodeNumberCellSecondary(args, numberOfParameterGroups, separateEpisodeNumbers)
_createEpisodeNumberCellSecondary(args.EpisodeNumber2, numberOfParameterGroups, separateEpisodeNumbers)
_createEpisodeNumberCellSecondary(args.EpisodeNumber3, numberOfParameterGroups, separateEpisodeNumbers)
end
 
-- Local function which is used to create an EpisodeNumber cell.
local function createEpisodeNumberCell(args, numberOfParameterGroups, separateEpisodeNumbers)
if (args.EpisodeNumber) then
local episodeText = getEpisodeText(args.EpisodeNumber)
createTableRowEpisodeNumberHeader(args.EpisodeNumber, numberOfParameterGroups, episodeText, separateEpisodeNumbers)
nonNilParams = nonNilParams + 1
end
end
 
-- Local function which is used to create a single row of cells.
-- This is the standard function called.
local function createSingleRowCells(args, numberOfParameterGroups, multiTitleListEnabled, onInitialPage, title)
createEpisodeNumberCell(args, 1, false)
createEpisodeNumberCellSecondary(args, 1, false)
createCells(args, false, 1, onInitialPage, title, numberOfParameterGroups)
end
 
-- Local function which is used to create a multiple row of cells.
-- This function is called when part of the row is rowspaned.
local function createMultiRowCells(args, numberOfParameterGroups, onInitialPage, title, topColor)
local EpisodeNumberSplit = (args.EpisodeNumber_1 and true or false)
for i = 1, numberOfParameterGroups do
args = extractDataFromNumberedSerialArgs(args, i, numberOfParameterGroups, title)
if args.TopColor(EpisodeNumberSplit or (not EpisodeNumberSplit and args.TopColori ~== ''1)) then
createEpisodeNumberCell(args, numberOfParameterGroups, EpisodeNumberSplit)
categories = categories..'[[Category:Episode lists with row deviations]]'
createEpisodeNumberCellSecondary(args, numberOfParameterGroups, EpisodeNumberSplit)
end
createCells(args, true, i, onInitialPage, title, numberOfParameterGroups)
if (i ~= numberOfParameterGroups) then
local textColor = '#333'
if topColor == 'inherit' then
textColor = 'inherit'
end
row = row:done() -- Use done() to close the 'tr' tag in rowspaned rows.
:tag('tr')
:addClass('vevent')
:addClass('module-episode-list-row')
:css('text-align', 'center')
:css('background', topColor)
:css('color', textColor)
end
end
end
 
-- Local function which is used to retrieve the NumParts value.
-- Track top colors that have a color contrast rating below AAA with
local function getnumberOfParameterGroups(args)
-- respect to text color, link color, or visited link color. See
for k, v in ipairs(cellNameList) do
-- [[WP:COLOR]] for more about color contrast requirements.
local numberedParameter = v .. "_" .. 1
local text_cr = contrast_ratio{args.TopColor, 'black', ['error'] = 0}
if (args[numberedParameter]) then
local link_cr = contrast_ratio{args.TopColor, '#0B0080', ['error'] = 0}
parameterGroupCells[v] = true
local visited_link_cr = contrast_ratio{args.TopColor, '#0645AD', ['error'] = 0}
parameterGroupCellsAny = true
if text_cr < 7 or link_cr < 7 or visited_link_cr < 7 then
if not firstParameterGroupCell then
categories = categories..'[[Category:Episode lists with invalid top colors]]'
firstParameterGroupCell = k
end
end
end
 
if (hasValue(args.NumParts)) then
-- Do not show the summary if this is being transcluded on the mainlist page
return args.NumParts, true
-- Do include it on all other lists
else
if not on_main_page and args.ShortSummary then
return 1, false
local bottom_wrapper = mw.html.create('tr')
end
-- fix for lists in the Short Summary
end
local ss = args.ShortSummary
 
if ss:match('^[*:;#]') or ss:match('^{|') then
-- Local function which is used to retrieve the Top Color value.
ss = '<span></span>\n' .. ss
local function getTopColor(args, rowColorEnabled, onInitialPage)
local episodeNumber = mathModule._cleanNumber(args.EpisodeNumber) or 1
if (args.TopColor) then
if (string.find(args.TopColor, "#")) then
return args.TopColor
else
return '#' .. args.TopColor
end
else
if ss:match('\n[*:;#]') then
return 'inherit'
ss = ss .. '\n<span></span>'
end
end
 
-- Local function which is used to retrieve the Row Color value.
local function isRowColorEnabled(args)
local rowColorEnabled = yesNoModule(args.RowColor, false)
 
if (args.RowColor and string.lower(args.RowColor) == 'on') then
rowColorEnabled = true
end
 
return rowColorEnabled
end
 
-- Local function which is used to retrieve the Line Color value.
local function getLineColor(args)
-- Default color to light blue
local lineColor = args.LineColor or 'CCCCFF'
 
-- Add # to color if necessary, and set to default color if invalid
if (htmlColor[lineColor] == nil) then
lineColor = '#' .. (mw.ustring.match(lineColor, '^[%s#]*([a-fA-F0-9]*)[%s]*$') or '')
if (lineColor == '#') then
lineColor = '#CCCCFF'
end
end
 
return lineColor
end
 
-- Local function which is used to check if the table is located on the page
-- currently viewed, or on a transcluded page instead.
-- If it is on a transcluded page, the episode summary should not be shown.
local function isOnInitialPage(args, sublist, pageTitle, initiallistTitle)
-- This should be the only check needed, however, it was previously implemented with two templates
-- with one of them not requiring an article name, so for backward compatability, the whole sequence is kept.
local onInitialPage
local onInitialPageCheck = (mw.uri.anchorEncode(pageTitle) == mw.uri.anchorEncode(initiallistTitle))
 
-- Only sublist had anything about hiding, so only it needs to even check
if (sublist) then
onInitialPage = onInitialPageCheck
-- avoid processing ghost references
if (not onInitialPage) then
args.ShortSummary = nil
end
local ShortSummary = mw.html.create('td')
:addClass('description')
:css('border-bottom','solid 3px '..line_color)
:attr('colspan',nonnil_params)
:newline()
:wikitext(ss)
bottom_wrapper:node(ShortSummary)
return tostring(table_row)..tostring(bottom_wrapper)..categories
else
if (initiallistTitle == "") then
return tostring(table_row)..categories
onInitialPage = true
else
onInitialPage = onInitialPageCheck
end
end
 
return onInitialPage
end
 
-- Local function which does the actual main process.
local function _main(args, sublist)
local title = mw.title.getCurrentTitle()
local pageTitle = title.text
local initiallistTitle = args['1'] or ''
 
-- Is this list on the same page as the page directly calling the template?
local onInitialPage = isOnInitialPage(args, sublist, pageTitle, initiallistTitle)
 
-- Need just this parameter removed if blank, no others
if (hasValue(args.ShortSummary) == false) then
args.ShortSummary = nil
end
 
local lineColor = getLineColor(args)
local rowColorEnabled = isRowColorEnabled(args)
local topColor = getTopColor(args, rowColorEnabled, onInitialPage)
 
local root = mw.html.create() -- Create the root mw.html object to return
local textColor = '#333'
if topColor == 'inherit' then
textColor = 'inherit'
end
 
row = root:tag('tr') -- Create the table row and store it globally
:addClass('vevent')
:addClass('module-episode-list-row')
:css('text-align', 'center')
:css('background', topColor)
:css('color', textColor)
 
local numberOfParameterGroups, multiTitleListEnabled = getnumberOfParameterGroups(args)
 
if (multiTitleListEnabled) then
createMultiRowCells(args, numberOfParameterGroups, onInitialPage, title, topColor)
else
createSingleRowCells(args, numberOfParameterGroups, multiTitleListEnabled, onInitialPage, title)
end
 
-- add these categories only in the mainspace and only if they are on the page where the template is used
if (onInitialPage and title.namespace == 0) then
addLineColorTrackingCategories(args)
addTopColorTrackingCategories(args)
end
 
if (cellValueTBA == true and title.namespace == 0) then
addTrackingCategory(trackingCategoryList["tba_values"])
end
 
-- Do not show the summary if this is being transcluded on the initial list page
-- Do include it on all other lists
if (onInitialPage and args.ShortSummary) then
local bottomWrapper = createShortSummaryRow(args, lineColor)
return tostring(root) .. tostring(bottomWrapper) .. trackingCategories
else
return tostring(root) .. trackingCategories
end
end
 
-- Local function which handles both module entry points.
local function main(frame, sublist)
local getArgs = require('Module:Arguments').getArgs
local args
 
-- Most parameters should still display when blank, so don't remove blanks
if (sublist) then
args = getArgs(frame, {removeBlanks = false, wrappers = 'Template:Episode list/sublist'})
else
args = getArgs(frame, {removeBlanks = false, wrappers = 'Template:Episode list'})
end
 
-- args['1'] = mw.getCurrentFrame():getParent():getTitle()
return _main(args, sublist, frame)
end
 
--[[
Public function which is used to create an Episode row
for an Episode Table used for lists of episodes where each table is on a different page,
usually placed on individual season articles.
 
For tables which are all on the same page see p.list().
 
Parameters:
-- |1= — required; The title of the article where the Episode Table is located at.
-- |EpisodeNumber= — suggested; The overall episode number in the series.
-- |EpisodeNumber2= — suggested; The episode number in the season.
-- |Title= — suggested; The English title of the episode.
-- |RTitle= — optional; Unformatted parameter that can be used to add a reference after "Title",
or can be used as a "raw title" to replace "Title" completely.
-- |AltTitle= — optional; An alternative title, such as the title of a foreign show's episode in its native language,
or a title that was originally changed.
-- |TranslitTitle= — optional; The title of the episode transliteration (Romanization) to Latin characters.
-- |RAltTitle= — optional; Unformatted parameter that can be used to add a reference after "AltTitle",
or can be used as a "raw title" to replace "AltTitle" completely.
-- |NativeTitle= — optional; The title of the episode in the native language.
-- |NativeTitleLangCode — optional; The language code of the native title language.
-- |Aux1= — optional; General purpose parameter. The meaning is specified by the column header.
This parameter is also used for Serial episode titles, such as those used in Doctor Who.
-- |DirectedBy= — optional; Name of the episode's director. May contain links.
-- |WrittenBy= — optional; Primary writer(s) of the episode. May include links.
-- |Aux2= — optional; General purpose parameter. The meaning is specified by the column header.
-- |Aux3= — optional; General purpose parameter. The meaning is specified by the column header.
-- |OriginalAirDate= — optional; This is the date the episode first aired on TV, or is scheduled to air.
-- |AltDate= — optional; The next notable air date, such as the first air date of an anime in English.
-- |Guests= — optional; List of Guests for talk shows. Cannot be used simultaneously with Aux1.
-- |MusicalGuests= — optional; List of MusicalGuests for talk shows. Cannot be used simultaneously with Aux2.
-- |ProdCode= — optional; The production code in the series. When defined, this parameter also creates a link anchor,
prefixed by "pc"; for example, List of episodes#pc01.
-- |Viewers= — optional; Number of viewers who watched the episode. Should include a reference.
-- |Aux4= — optional; General purpose parameter. The meaning is specified by the column header.
-- |ShortSummary= — optional; A short 100–200 word plot summary of the episode.
-- |LineColor= — optional; Colors the separator line between episode entries. If not defined the color defaults to "#CCCCFF"
and the article is placed in Category:Episode list using the default LineColor.
Use of "#", or anything but a valid hex code will result in an invalid syntax.
-- |TopColor= — discouraged; Colors the main row of information (that is, not the ShortSummary row).
Articles using this parameter are placed in Category:Episode lists with row deviations.
-- |RowColor= — optional; Switch parameter that must only be defined when the EpisodeNumber= entry is not a regular number
(e.g. "12–13" for two episodes described in one table entry).
If the first episode number is even, define pass "on". If the first episode number is odd, pass "off".
--]]
function p.sublist(frame)
return main(frame, true)
end
 
--[[
Public function which is used to create an Episode row
for an Episode Table used for lists of episodes where all tables are on the same page.
 
For tables which are on different pages see p.sublist().
 
For complete parameter documentation, see the documentation at p.sublist().
--]]
function p.list(frame)
return main(frame, false)
end