Module:Episode list
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 delinkModule = require('Module:Delink')
 local langModule = require(&quot;Module:Lang&quot;)
 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 = &quot;&quot;
 
 -- List of tracking categories.
 local trackingCategoryList = {
 	[&quot;air_dates&quot;] = &quot;[[Category:Episode lists with unformatted air dates]]&quot;,
 	[&quot;alt_air_dates&quot;] = &quot;[[Category:Episode lists with incorrectly formatted alternate air dates]]&quot;,
 	[&quot;faulty_line_colors&quot;] = &quot;[[Category:Episode lists with faulty line colors]]&quot;,
 	[&quot;non_compliant_line_colors&quot;] = &quot;[[Category:Episode lists with non-compliant line colors]]&quot;,
 	[&quot;default_line_colors&quot;] = &quot;[[Category:Episode list using the default LineColor]]&quot;,
 	[&quot;row_deviations&quot;] = &quot;[[Category:Episode lists with row deviations]]&quot;,
 	[&quot;invalid_top_colors&quot;] = &quot;[[Category:Episode lists with invalid top colors]]&quot;,
 	[&quot;tba_values&quot;] = &quot;[[Category:Episode lists with TBA values]]&quot;,
 	[&quot;nonmatching_numbered_parameters&quot;] = &quot;[[Category:Episode lists with a non-matching set of numbered parameters]]&quot;,
 	[&quot;raw_unformatted_storyteleplay&quot;] = &quot;[[Category:Episode lists with unformatted story or teleplay credits]]&quot;
 }
 
 -- List of parameter names in this order.
 local cellNameList = {
 	'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
 
 -- 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 ~= &quot;&quot;) 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) &gt; 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 = '&lt;span&gt;&lt;/span&gt;\n' .. shortSummaryText
 	end
 
 	if (shortSummaryText:match('\n[*:;#]')) then
 		shortSummaryText = shortSummaryText .. '\n&lt;span&gt;&lt;/span&gt;'
 	end
 
 	local shortSummaryCell = mw.html.create('td')
 				:addClass('description')
 				:css('border-bottom', 'solid 3px ' .. lineColor)
 				:attr('colspan', nonNilParams)
 				:newline()
 				:wikitext(shortSummaryText)
 
 	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[&quot;row_deviations&quot;])
 
 		-- 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 &lt; 7 or linkContrastRatio &lt; 7 or visitedLinkContrastRatio &lt; 7) then
 			addTrackingCategory(trackingCategoryList[&quot;invalid_top_colors&quot;])
 		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[&quot;faulty_line_colors&quot;])
 		elseif (blackContrastRatio &lt; 7 and whiteContrastRatio &lt; 7) then
 			addTrackingCategory(trackingCategoryList[&quot;non_compliant_line_colors&quot;])
 		end
 	else
 		addTrackingCategory(trackingCategoryList[&quot;default_line_colors&quot;])
 	end
 end
 
 -- Local function which is used to remove wiki-links from repated information in rowspans.
 -- Used for Doctor Who serials, where the director and writer are the same for each part of serial.
 local function removeWikilinks(args, v)
 	return delinkModule._delink{args[v]}
 end
 
 -- Local function which is used to set the text of an empty cell
 -- with either &quot;TBD&quot; or &quot;N/A&quot;.
 -- Set to N/A if viewers haven't been available for four weeks, else set it as TBD.
 local function setTBDStatus(args)
 	local month, day, year = args.OriginalAirDate:gsub(&quot;&amp;nbsp;&quot;, &quot; &quot;):match(&quot;(%a+) (%d+), (%d+)&quot;)
 
 	if (month == nil) then
 		day, month, year = args.OriginalAirDate:gsub(&quot;&amp;nbsp;&quot;, &quot; &quot;):match(&quot;(%d+) (%a+) (%d+)&quot;)
 	end
 
 	if (day == nil) then
 		return tableEmptyCellModule._main({alt_text = &quot;TBD&quot;})
 	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 &gt;= 60 * 60 * 24 * 7 * 4) then
 			return tableEmptyCellModule._main({alt_text = &quot;N/A&quot;})
 		else
 			return tableEmptyCellModule._main({alt_text = &quot;TBD&quot;})
 		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 = &quot;N/A&quot;})
 	elseif (v == 'Viewers' and hasValue(args.OriginalAirDate)) then
 		args[v] = setTBDStatus(args)
 	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') ~= nil
 		and string.match(args[v], '2C2C2C') == nil
 		and string.find(args[v], 'dtstart') == nil
 		and onInitialPage
 		and title.namespace == 0)
 	then
 		addTrackingCategory(trackingCategoryList[&quot;air_dates&quot;])
 	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[&quot;alt_air_dates&quot;])
 	end
 end
 
 -- Local function which is used to create a Production Code cell.
 local function createProductionCodeCell(args, v)
 	if (hasValue(args.ProdCode) and string.find(args.ProdCode, 'TBA') == nil) then
 		row:tag('td')
 			:attr('id', 'pc' .. idTrim(idTrim(args.ProdCode, ' ----'), '&lt;'))
 			: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, 1, &quot;center&quot;)
 	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 .. &quot;_&quot; .. i
 		local excludeParameter = excludeList[parameter] or 'NULL' .. parameter
 		local excludeNumberParameter = (excludeList[numberedParameter] or 'NULL' .. parameter) .. &quot;_&quot; .. 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[&quot;nonmatching_numbered_parameters&quot;])
 			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 column cells.
 
 EpisodeNumber, EpisodeNumber2 and Title are created in different functions
 as they require some various if checks.
 
 See:
 	-- createEpisodeNumberCell()
 	-- createEpisodeNumberCellSecondary()
 	-- createTitleCell()
 ]]--
 local function createCells(args, isSerial, currentRow, onInitialPage, title, numberOfParameterGroups)
 	for k, v in ipairs(cellNameList) do
 		if (v == 'ProdCode') then
 			createProductionCodeCell(args, v)
 		elseif (args[v]) 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], &quot;''Story&quot;) ~= nil or string.find(args[v], &quot;''Teleplay&quot;) ~= nil) and string.find(args[v], &quot;8202&quot;) == nil) then
 					-- &amp;#8202; is the hairspace added through {{StoryTeleplay}}
 					addTrackingCategory(trackingCategoryList[&quot;raw_unformatted_storyteleplay&quot;])
 				end
 			end
 
 			-- If serial titles need to be centered and not left, then this should be removed.
 			local textAlign = &quot;center&quot;
 			if (v == 'Aux1' and isSerial) then
 				textAlign = &quot;left&quot;
 			end
 
 			-- Remove wikilinks from links in serial rowspans rows after the first.
 			-- if (currentRow &gt; 1) then
 				-- args[v] = removeWikilinks(args, v)
 			-- end
 
 			local thisRowspan
 			if (firstParameterGroupCell and k &lt; firstParameterGroupCell) then
 				thisRowspan = numberOfParameterGroups
 			else
 				thisRowspan = 1
 			end
 
 			if (currentRow == 1 or (currentRow &gt; 1 and k &gt;= (firstParameterGroupCell or 0))) then
 				createTableData(args[v], thisRowspan, textAlign)
 			end
 			nonNilParams = nonNilParams + 1
 			checkUsageOfDateTemplates(args, v, onInitialPage, title)
 		end
 
 		if (args[v] == &quot;TBA&quot;) then
 			cellValueTBA = true
 		end
 	end
 end
 
 --[[
 Local function which is used to create the Title cell text.
 
 The title text will be handled in the following way:
 	Line 1: &lt;Title&gt;&lt;RTitle&gt; (with no space between)
 	Line 2: &lt;AltTitle&gt;&lt;RAltTitle&gt; (with no space between) OR
 	Line 2: Transcription: &lt;TranslitTitle&gt; (&lt;Language&gt;: &lt;NativeTitle&gt;)&lt;RAltTitle&gt; (with space between first two parameters)
 
 	If &lt;Title&gt; or &lt;RTitle&gt; 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 == &quot;&quot;) then
 			isCellPresent = true
 		else
 			titleString = '&quot;' .. args.Title .. '&quot;'
 			useSecondLine = true
 			isCellPresent = true
 		end
 	end
 
 	if (args.RTitle ~= nil) then
 		if (args.RTitle == &quot;&quot;) then
 			isCellPresent = true
 		else
 			titleString = titleString .. args.RTitle
 			useSecondLine = true
 			isCellPresent = true
 		end
 	end
 
 	-- Surround the AltTitle/TranslitTitle with quotes; No quotes if empty.
 	if (args.AltTitle or args.TranslitTitle) then
 
 		isCellPresent = true
 
 		if (useSecondLine) then
 			titleString = titleString .. &quot;&lt;br /&gt;&quot;
 			lineBreakUsed = true
 		end
 
 		if (hasValue(args.AltTitle)) then
 			titleString = titleString .. '&quot;' .. args.AltTitle .. '&quot;'
 		elseif (hasValue(args.TranslitTitle)) then
 			if (hasValue(args.NativeTitleLangCode)) then
 				titleString = titleString .. 'Transcription: &quot;' .. langModule._transl({args.NativeTitleLangCode, args.TranslitTitle, italic = 'no'})  .. '&quot;'
 			else
 				titleString = titleString .. 'Transcription: &quot;' .. args.TranslitTitle .. '&quot;'
 			end
 		end
 	end
 
 	if (args.NativeTitle ~= nil) then
 		if (args.NativeTitle == &quot;&quot;) then
 			isCellPresent = true
 		else
 			isCellPresent = true
 
 			if (useSecondLine and lineBreakUsed == false) then
 				titleString = titleString .. &quot;&lt;br /&gt;&quot;
 			end
 
 			if (hasValue(args.NativeTitleLangCode)) then
 				local languageCode = &quot;Lang-&quot; .. args.NativeTitleLangCode
 				titleString = titleString .. &quot; (&quot; .. langModule._lang_xx_inherit({code = args.NativeTitleLangCode, args.NativeTitle}) .. &quot;)&quot;
 			else
 				titleString = titleString .. &quot; (&quot; .. args.NativeTitle .. &quot;)&quot;
 			end
 		end
 	end
 
 	if (args.RAltTitle ~= nil) then
 		if (args.RAltTitle == &quot;&quot;) then
 			isCellPresent = true
 		else
 			isCellPresent = true
 
 			if (useSecondLine and lineBreakUsed == false) then
 				titleString = titleString .. &quot;&lt;br /&gt;&quot;
 			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 prameter 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 .. &quot;_&quot; .. 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 process the multi title list.
 local function processMultiTitleList(args, numberOfParameterGroups)
 	local nativeTitleLangCode = args.NativeTitleLangCode
 	local titleText = &quot;&quot;
 	local isCellPresent = false
 	local isFirstTitleGroup = true -- Making sure that the title cell is created at least once and isn't created again if other #N titles are empty.
 
 	for i = 1, numberOfParameterGroups do
 		local args, nextGroupValid = extractDataFromNumberedTitleArgs(args, i)
 		if (nextGroupValid) then
 			if (isFirstTitleGroup == false) then
 				titleText = titleText .. &quot;&lt;hr /&gt;&quot;
 			end
 
 			local titleTextRow
 			titleTextRow = createTitleText(args)
 			titleText = titleText .. titleTextRow
 			isFirstTitleGroup = false
 		else
 			if (isFirstTitleGroup) then
 				titleText, isCellPresent = createTitleText(args)
 			end
 
 			-- Valid titles have to be in succession (#1, #2, #3 and not #1, #4 #5), so exit for loop if next group is empty.
 			return titleText, isCellPresent
 		end
 	end
 
 	return titleText
 end
 
 -- Local function which is used to create a Title cell.
 local function createTitleCell(args, numberOfParameterGroups, multiTitleListEnabled, isSerial)
 	local titleText
 	local isCellPresent
 
 	if (multiTitleListEnabled) then
 		titleText, isCellPresent = processMultiTitleList(args, numberOfParameterGroups)
 	else
 		titleText, isCellPresent = createTitleText(args)
 	end
 
 	if (isCellPresent == false) then
 		return nil
 	end
 
 	local textAlign = &quot;left&quot;
 
 	-- If Title is blank, then set Raw Title to TBA
 	if (hasValue(titleText) == false) then
 		titleText = tableEmptyCellModule._main({})
 		textAlign = &quot;left&quot;
 	end
 
 	-- If title is the first cell, create it with a !scope=&quot;row&quot;
 	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 a table row header for either the
 -- EpisodeNumber or EpisodeNumber2 column cells.
 local function createTableRowEpisodeNumberHeader(episodeNumber, numberOfParameterGroups, episodeText)
 		local epID = string.match(episodeNumber, &quot;^%w+&quot;)
 		row:tag('th')
 			:attr('scope', 'row')
 			:attr('rowspan', numberOfParameterGroups)
 			: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: &quot;1 - 2&quot; -&gt; &quot;1–2&quot;; &quot;1-2-3&quot; -&gt; &quot;1–3&quot;.
 	-- An alphanumeric or letter range, similar to the above.
 		Example: &quot;A - B&quot; -&gt; &quot;A–B&quot;; &quot;A-B-C&quot; -&gt; &quot;A–C&quot;.
 		Example: &quot;A1 - B1&quot; -&gt; &quot;A1–B1&quot;; &quot;A1-B1-C1&quot; -&gt; &quot;A1–C1&quot;.
 	-- A number range of two numbers, indicating the start and end of the range,
 	seperated by a visual &lt;hr /&gt; (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 &quot;–&quot;&quot; or &quot;&lt;hr /&gt;&quot;&quot; added.
 		local divider
 
 		episodeNumber = episodeNumber:gsub('%s*&lt;br%s*/?%s*&gt;%s*', '&lt;hr /&gt;')
 
 		if (episodeNumber:match('^(%w+)%s*&lt;hr */%s*&gt;%s*(%w+)$')) then
 			episodeNumber1, episodeNumber2 = episodeNumber:match('^(%w+)%s*&lt;hr */%s*&gt;%s*(%w+)$')
 			divider = &quot;&lt;hr /&gt;&quot;
 		elseif (episodeNumber:match('^(%w+)%s*&lt;hr */%s*&gt;.-&lt;hr */%s*&gt;%s*(%w+)$')) then  -- 3 or more elements
 			episodeNumber1, episodeNumber2 = episodeNumber:match('^(%w+)%s*&lt;hr */%s*&gt;.-&lt;hr */%s*&gt;%s*(%w+)$')
 			divider = &quot;&lt;hr /&gt;&quot;
 		elseif (mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&amp;]%s*(%w+)$')) then
 			episodeNumber1, episodeNumber2 = mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&amp;]%s*(%w+)$')
 			divider = &quot;–&quot;
 		else
 			episodeNumber1, episodeNumber2 = mw.ustring.match(episodeNumber, '^(%w+)%s*[%s%-–/&amp;].-[%s%-–/&amp;]%s*(%w+)$')  -- 3 or more elements
 			divider = &quot;–&quot;
 		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)
 	if (episodeValue) then
 		local episodeText = getEpisodeText(episodeValue)
 
 		if (nonNilParams == 0) then
 			createTableRowEpisodeNumberHeader(episodeValue, numberOfParameterGroups, episodeText)
 		else
 			createTableData(episodeText, numberOfParameterGroups, &quot;center&quot;)
 		end
 
 		nonNilParams = nonNilParams + 1
 
 	end
 end
 
 -- Local function which is used to create seconday episode number cells.
 local function createEpisodeNumberCellSecondary(args, numberOfParameterGroups)
 	_createEpisodeNumberCellSecondary(args.EpisodeNumber2, numberOfParameterGroups)
 	_createEpisodeNumberCellSecondary(args.EpisodeNumber3, numberOfParameterGroups)
 end
 
 -- Local function which is used to create an EpisodeNumber cell.
 local function createEpisodeNumberCell(args, numberOfParameterGroups)
 	if (args.EpisodeNumber) then
 		local episodeText = getEpisodeText(args.EpisodeNumber)
 		createTableRowEpisodeNumberHeader(args.EpisodeNumber, numberOfParameterGroups, episodeText)
 		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)
 	createEpisodeNumberCellSecondary(args, 1)
 	createTitleCell(args, numberOfParameterGroups, multiTitleListEnabled, 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.
 -- Current use is for Doctor Who serials.
 local function createMultiRowCells(args, numberOfParameterGroups, onInitialPage, title, topColor)
 	createEpisodeNumberCell(args, numberOfParameterGroups)
 	createEpisodeNumberCellSecondary(args, numberOfParameterGroups)
 	createTitleCell(args, numberOfParameterGroups, false, true)
 
 	for i = 1, numberOfParameterGroups do
 		args = extractDataFromNumberedSerialArgs(args, i, numberOfParameterGroups, title)
 		createCells(args, true, i, onInitialPage, title, numberOfParameterGroups)
 		if (i ~= numberOfParameterGroups) then
 			row = row:done()  -- Use done() to close the 'tr' tag in rowspaned rows.
 				:tag('tr')
 				:css('background', topColor)
 		end
 	end
 end
 
 -- Local function which is used to retrieve the NumParts value.
 local function getnumberOfParameterGroups(args)
 	for k, v in ipairs(cellNameList) do
 		local numberedParameter = v .. &quot;_&quot; .. 1
 		if (args[numberedParameter]) then
 			parameterGroupCells[v] = true
 			if not firstParameterGroupCell then
 				firstParameterGroupCell = k
 			end
 		end
 	end
 
 	if (hasValue(args.NumParts)) then
 		return args.NumParts, true
 	else
 		return 1, false
 	end
 end
 
 -- Local function which is used to retrieve the Top Color value.
 local function getTopColor(args, rowColorEnabled, onInitialPage)
 	local episodeNumber = mathModule._cleanNumber(args.EpisodeNumber) or 1
 	if (args.TopColor) then
 		if (string.find(args.TopColor, &quot;#&quot;)) then
 			return args.TopColor
 		else
 			return '#' .. args.TopColor
 		end
 	elseif (rowColorEnabled and onInitialPage and mathModule._mod(episodeNumber, 2) == 0) then
 		return '#E9E9E9'
 	elseif (onInitialPage and args.ShortSummary) then
 		return '#F2F2F2'
 	else
 		return 'inherit'
 	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
 
 	-- Only sublist had anything about hiding, so only it needs to even check
 	if (sublist) then
 		onInitialPage = mw.uri.anchorEncode(pageTitle) == mw.uri.anchorEncode(initiallistTitle)
 		-- avoid processing ghost references
 		if (not onInitialPage) then
 			args.ShortSummary = nil
 		end
 	else
 		if (initiallistTitle == &quot;&quot;) then
 			onInitialPage = true
 		else
 			onInitialPage = mw.uri.anchorEncode(pageTitle) == mw.uri.anchorEncode(initiallistTitle)
 		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
 	row = root:tag('tr')									-- Create the table row and store it globally
 				:addClass('vevent')
 				:css('text-align', 'center')
 				:css('background', topColor)
 
 	local numberOfParameterGroups, multiTitleListEnabled = getnumberOfParameterGroups(args)
 
 	if (multiTitleListEnabled and not args.Title_2) 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[&quot;tba_values&quot;])
 	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 &quot;Title&quot;,
 											or can be used as a &quot;raw title&quot; to replace &quot;Title&quot; 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 &quot;AltTitle&quot;,
 											or can be used as a &quot;raw title&quot; to replace &quot;AltTitle&quot; 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 &quot;pc&quot;; 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 &quot;#CCCCFF&quot;
 											and the article is placed in Category:Episode list using the default LineColor.
 											Use of &quot;#&quot;, 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. &quot;12–13&quot; for two episodes described in one table entry).
 											If the first episode number is even, define pass &quot;on&quot;. If the first episode number is odd, pass &quot;off&quot;.
 --]]
 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