-- This module implements .  
require('Module:No globals')
local yesno = require('Module:Yesno')
local HTMLcolor = mw.loadData( 'Module:Color contrast/colors' )  
---
-- SeriesOverview class
-- The main class.
---  
local SeriesOverview = {}  
function SeriesOverview.cellspan(SeasonEntries, SeasonEntries_ordered, key, cell, multipart)
local spanlength = 1  
local firstEntry = SeasonEntries[SeasonEntries_ordered[cell]]
if key == 'network' and firstEntry.networkA and not firstEntry.networkB then spanlength = 2 end  
for i = cell+1, #SeasonEntries_ordered do
local entry = SeasonEntries[SeasonEntries_ordered[i]]
-- Split season, then regular season
if entry.startA then
if not entry[key..'A'] then spanlength = spanlength + 1
else break end
if not entry[key..'B'] then spanlength = spanlength + 1
else break end
else
if not entry[key] and (key == 'network' or (string.sub(key,0,3) == 'aux' and (not entry.special or entry.episodes)) or (string.sub(key,0,4) == 'info') and multipart) then
spanlength = spanlength + 1
else break end
end
end
return spanlength
end  
-- Sorting function
function SeriesOverview.series_sort(op1, op2)
local n1,s1 = string.match(op1,"(%d+)(%a*)")
local n2,s2 = string.match(op2,"(%d+)(%a*)")
local n1N,n2N = tonumber(n1),tonumber(n2)  
if n1N == n2N then
return s1 < s2
else
return n1N < n2N
end
end  
-- Function to add either text or to cell
function SeriesOverview.season_cell(text, )
local cell  
if string.find(text or '', 'table-na', 0, true) ~= nil then
local findpipe = string.find(text, ' | ', 0, true)
if findpipe ~= nil then
cell = SeriesOverview.series_attributes( :expandTemplate{title='N/A',args={string.sub(text,findpipe+3)}} )
else
cell = SeriesOverview.series_attributes( :expandTemplate{title='N/A'} )
end
else
cell = mw.html.create('td'):wikitext(text)
end  
return cell
end  
-- Allow usages of cells
function SeriesOverview.series_attributes(infoParam)
local entries = {}
local infoCell = mw.html.create('td')
local attrMatch = '([%a-]*)="([^"]*)"'  
while true do
local a,b = string.match(infoParam,attrMatch)
if a == nil or b == nil then break end
infoCell:attr(a,b)
infoParam = string.gsub(infoParam,attrMatch,'',1)
end  
infoParam = string.gsub(infoParam,'%s*|%s*','',1)
infoCell:wikitext(infoParam)  
return infoCell
end  
function SeriesOverview.new(, args)
args = args or {}  
local initialArticle = args['1'] or ''
local categories = ''
local title = mw.title.getCurrentTitle
local allReleased = yesno(args.allreleased)  
-- Create series overview table
local root = mw.html.create((args.multiseries or not args.series) and 'table' or '')
local cellPadding = '0 '
local basePadding = '0.2em 0.4em'  
root
:addClass('wikitable')
:addClass('plainrowheaders')
:css('text-align', '')  
-- Sortable
if args.sortable then
root:addClass('sortable');
end  
-- Width
if args.width then
root:css('width', args.width)
end  
-- Summary
if args.summary then
root:attr('summary'):wikitext(args.summary)
end
---
-- Caption
if args.caption then
root:tag('caption'):wikitext(args.caption)
end
---
-- Extract seasons info and place into a 3D array
local SeasonEntries = {}
for k,v in pairs(args) do
local str, num, str2 = string.match(k, '([^%d]*)(%d*)(%a*)')
if num ~= '' then
-- Special
local special = false
if string.sub(str2,1,1) == 'S' then
special = true
num = num .. str2
str2 = ''
end
-- Add to entries, create if necessary
if not SeasonEntries[num] then
SeasonEntries[num] = {}
end
SeasonEntries[num][str .. str2] = v
if special then
SeasonEntries[num]['special'] = 'y'
end
end
end  
-- Order table by season number
local SeasonEntries_ordered = {}
for k in pairs(SeasonEntries) do
table.insert(SeasonEntries_ordered, k)
end
table.sort(SeasonEntries_ordered,SeriesOverview.series_sort)  
local firstRow = args.multiseries and {} or SeasonEntries[SeasonEntries_ordered[1]]  
-- Colspan calculation for information cells (0 = no info set)
local numAuxCells = 0
local numInfoCells = 0
for i = string.byte('A'), string.byte('Z') do
local param = 'info' .. string.char(i)
if args[param] then numInfoCells = numInfoCells + 1 end
end  
-- Top info cell
-- @ = string.char(64), A = string.char(65)
local topInfoCell = numInfoCells > 0 and string.char(numInfoCells + (string.byte('A') - 1)) or '@'  
-- Networks are included if the very first entry sets the first network
local setNetwork = (args.multiseries and args.network) or firstRow.network or firstRow.networkA
local networkTransclude = args.network_transclude
if (networkTransclude == 'onlyinclude' and title.fullText == initialArticle) or (networkTransclude == 'noinclude' and title.fullText ~= initialArticle) then
setNetwork = false
end  
-- Headers
do
if args.multiseries or not args.series then
local headerRow = root:tag('tr')
headerRow
:css('text-align', '')  
local releasedBlurb = args.released and 'released' or 'aired'  
-- Base series/season content on the format of the first date; Series = D M Y, Season = M D, Y
local matchDMY = false
local thisStart = firstRow.start or firstRow.startA
if thisStart then
if string.match(thisStart:gsub("&nbsp;"," "), '(%d+)%s(%a+)%s(%d+)') then
matchDMY = true
end
end  
-- Multiple series header
if args.multiseries then
headerRow:tag('th')
:attr('scope', 'col')
:css('padding', cellPadding)
:attr('rowspan', allReleased and 1 or 2)
:wikitext('Series')
end  
-- Season header
headerRow:tag('th')
:attr('scope', 'col')
:attr('rowspan', allReleased and 1 or 2)
:attr('colspan', 2)
:css('min-width', '')
:css('padding', cellPadding)
:wikitext(args.seriesT or args.seasonT or (matchDMY and 'Series') or 'Season')  
-- Aux headers
for i = string.byte('A'), string.byte('Z') do
local param = 'aux' .. string.char(i)
if args[param] then
numAuxCells = numAuxCells + 1
headerRow:tag('th')
:attr('scope', 'col')
:css('padding', cellPadding)
:attr('rowspan', allReleased and 1 or 2)
:wikitext(args[param])
end
end  
-- Episodes header
headerRow:tag('th')
:attr('scope', 'col')
:attr('rowspan', allReleased and 1 or 2)
:attr('colspan', 2)
:css('padding', cellPadding)
:wikitext('Episodes')  
-- Originally aired header
local OriginallyColspan = (not allReleased and setNetwork) and 3 or 2
headerRow:tag('th')
:attr('scope', 'col')
:attr('colspan', OriginallyColspan)
:wikitext('Originally ' .. releasedBlurb)  
-- Network subheader for released series
if setNetwork and allReleased then
headerRow:tag('th')
:attr('scope', 'col')
:attr('rowspan', allReleased and 1 or 2)
:css('padding', cellPadding)
:wikitext('Network')
end  
-- Information headers
if topInfoCell ~= '@' then
for i = string.byte('A'), string.byte(topInfoCell) do
local param = 'info' .. string.char(i)
local infoTransclude = args[param .. '_transclude']
if (infoTransclude == 'onlyinclude' and title.fullText == initialArticle) or (infoTransclude == 'noinclude' and title.fullText ~= initialArticle) then else
headerRow:tag('th')
:attr('scope', 'col')
:attr('rowspan', allReleased and 1 or 2)
:css('padding', cellPadding)
:wikitext(args[param])
end
end
end  
-- Subheader row
local subheaderRow = mw.html.create('tr')  
if not allReleased then
-- First aired subheader
subheaderRow:tag('th')
:attr('scope', 'col')
:wikitext('First ' .. releasedBlurb)  
-- Last aired subheader
subheaderRow:tag('th')
:attr('scope', 'col')
:wikitext('Last ' .. releasedBlurb)  
-- Network subheader for aired series
if setNetwork then
subheaderRow:tag('th')
:attr('scope', 'col')
:css('padding', cellPadding)
:wikitext('Network')
end
end  
-- Check for scenarios with an empty subheaderRow
if not allReleased or numInfoCells > 0 then
root:node(subheaderRow)
end
end
end  
-- Season rows
do
if args.multiseries then
-- Multi series individual entries
if args.multiseries ~= "y" then
root:node(args.multiseries)
end
else
-- One row entries, only categorized in the mainspace
if title.namespace == 0 and #SeasonEntries == 1 then
categories = categories .. ''
end  
-- Determine number of rows in the whole overview
local SeasonEntriesRows = 0
for X = 1, #SeasonEntries_ordered do