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("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"] = "",
["alt_air_dates"] = "",
["faulty_line_colors"] = "",
["non_compliant_line_colors"] = "",
["default_line_colors"] = "",
["row_deviations"] = "",
["invalid_top_colors"] = "",
["tba_values"] = "",
["nonmatching_numbered_parameters"] = "",
["raw_unformatted_storyteleplay"] = ""
}  
-- 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 ~= "") 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 = '<span></span>\n' .. shortSummaryText
end  
if (shortSummaryText:match('\n[*:;#]')) then
shortSummaryText = shortSummaryText .. '\n<span></span>'
end  
local shortSummaryCell = mw.html.create('td')
:addClass('description')
:css('-bottom', 'solid ' .. 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["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 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 "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)
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 * 4) then
return tableEmptyCellModule._main({alt_text = "N/A"})
else
return tableEmptyCellModule._main({alt_text = "TBD"})
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)
else
args[v] = tableEmptyCellModule._main({})
end
end  
-- Air dates that don't use
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["air_dates"])
end  
-- Alternate air dates that do use
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)
if (hasValue(args.ProdCode) and string.find(args.ProdCode, 'TBA') == nil) then
row:tag('td')
:attr('id', 'pc' .. idTrim(idTrim(args.ProdCode, ' ----'), '<'))
:css('text-align', '')
:wikitext(args.ProdCode)
elseif (args.ProdCode == * or string.find(args.ProdCode or *, 'TBA') ~= nil) then
createEmptyCell(args, v, false)
createTableData(args.ProdCode, 1, "")
else
-- ProductionCode parameter not used; Do nothing.
end  
nonNilParams = nonNilParams + 1
end  
---
Local function which is used to extract data