Модуль:Список серий

Из Википедии, бесплатной энциклопедии

Документация

Документацию смотри на странице шаблонов {{Таблица эпизодов}} и {{Список серий}}

require('strict')  local p = {}  local tableEmptyCellModule = require('Модуль:Пустая ячейка таблицы') local colorContrastModule = require('Модуль:Color contrast') local htmlColor = mw.loadData('Модуль:Color contrast/colors') local delinkModule = require('Модуль:Delink') local yesNoModule = require('Модуль:Yesno') local mathModule = require('Модуль:Math')  local lower = mw.ustring.lower local match = mw.ustring.match local find = mw.ustring.find local gsub = mw.ustring.gsub local sub = mw.ustring.sub local len = mw.ustring.len   local row local nonNilParams = 0 local cellValueTBA = false local trackingCategories = '[[Категория:Википедия:Статьи использующие шаблон cписок серий]]'  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']='[[Категория:Википедия:Списки эпизодов с неоформленными сюжетом или телесценарием]]' }  local cellNameList = { 	'Aux1', 'Столбец1', 	'DirectedBy', 'Режиссёр', 'Режиссер', 	'WrittenBy', 'Сценарист', 	'Aux2', 'Столбец2', 	'Aux3', 'Столбец3', 	'OriginalAirDate', 'ДатаПоказа', 	'AltDate', 'ДатаПоказа2', 	'Guests', 'Гости', 	'MusicalGuests', 'Персоны', 	'ProdCode', 'ПродКод', 	'Viewers', 'Зрителей', 	'Aux4', 'Столбец4' }  -- Список взаимоисключающих параметров local excludeList = { 	['Guests']='Aux1', ['Гости']='Столбец2', 	['MusicalGuests']='Aux2', ['Персоны']='Столбец3' }  -- Список ячеек, имеющих группы параметров local parameterGroupCells = {} local firstParameterGroupCell local parameterGroupCellsAny = false  -- Список параметров заголовка для списков с несколькими названиями. local titleList = { 	'Title', 'Название', 	'RTitle', 'НазваниеПрим', 	'AltTitle', 'Название2', 	'RAltTitle', 'Название2Прим', 	'NativeTitle', 'НазваниеОригинал', 	'TranslitTitle', 'НазваниеТранслит' }  -- Локальная функция для получения номера эпизода или производственного кода, -- без какого-либо дополнительного текста. local function idTrim(val, search) 	local valFind = find(val, search) 	if not valFind then 		return val 	end 	return sub(val, 0, valFind - 1) end  -- Локальная функция для проверки того, что параметр имеет значение. local function hasValue(param) 	if param and param:find('%S') then 		return true 	end end  -- Локальная функция для создания транслитерации. local function langSpan(translitNativeTitle, langCode) 	local line = mw.html.create('span') 	if hasValue(langCode) then 		line:attr('lang', langCode) 			:attr('xml:lang', langCode) 			:css('font-style', 'normal') 			:wikitext(translitNativeTitle) 	end 	return tostring(line) end  -- Локальная функция для создания аббревиатуры local function abbr(text, title) 	local abbr = mw.html.create('abbr') 	abbr:attr('title', title) 		:wikitext(text) 	return tostring(abbr) end  -- Локальная функция для создания ячейки Таблицы эпизодов. local function createTableData(text, rowSpan, textAlign) 	if rowSpan 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 addTrackingCategory(category) 	trackingCategories = trackingCategories .. category end  -- Локальная функция для создания Краткого Содержания. local function createShortSummaryRow(args, lineColor, linetop) 	local shortSummary = args.ShortSummary or args['КраткоеСодержание']  	-- исправление Краткого Содержания 	if hasValue(shortSummary) and (match(shortSummary, '^[*:;#]') or match(shortSummary, '^{|')) then 		shortSummary = '<span></span>\n' .. shortSummary 	end  	if hasValue(shortSummary) and match(shortSummary, '\n[*:;#]') then 		shortSummary = shortSummary .. '\n<span></span>' 	end  	if linetop then 		shortSummary = nil 	end  	local shortSummaryCell = mw.html.create('td') 	shortSummaryCell 		:addClass('description') 		:css('border-bottom', 'solid 3px ' .. lineColor) 		:attr('colspan', nonNilParams) 		:newline() 		:wikitext(shortSummary)  	local cell = mw.html.create('tr') 	cell:addClass('expand-child') 		:node(shortSummaryCell)  	if linetop then 		local lencell = len(cell) 		cell = sub(cell, 1, lencell - 10) 	end  	return tostring(cell) end  -- Локальная функция для добавления отслеживающей категории при проблемах с цветом. local function addTopColorTrackingCategories(args) 	local topColor = args.TopColor or args['ЦветСтроки'] 	if hasValue(topColor) or '#eaecf0' then 		addTrackingCategory(trackingCategoryList['row_deviations'])  		-- Проверка контрастности в соответствии с [[ВП:ЦВЕТ]]. 		local textContrastRatio = colorContrastModule._ratio{topColor, 'black', ['error']=0} 		local linkContrastRatio = colorContrastModule._ratio{'#0B0080', topColor, ['error']=0} 		local visitedLinkContrastRatio = colorContrastModule._ratio{topColor, '#0645AD', ['error']=0}  		if textContrastRatio < 4.5 or linkContrastRatio < 3 or visitedLinkContrastRatio < 3 then 			addTrackingCategory(trackingCategoryList['invalid_top_colors']) 		end 	end end  -- Локальная функция для добавления отслеживающей категории при проблемах с цветом границы. local function addLineColorTrackingCategories(args) 	local lineColor = args.LineColor or args['ЦветГраницы']  --  or '#a2a9b1' 	if hasValue(lineColor) then 		local blackContrastRatio = colorContrastModule._ratio{lineColor, 'black', ['error']=0} 		local whiteContrastRatio = colorContrastModule._ratio{'white', lineColor, ['error']=0}  		if lineColor == '' then 			addTrackingCategory(trackingCategoryList['faulty_line_colors']) 		elseif blackContrastRatio < 3 and whiteContrastRatio < 3 then 			addTrackingCategory(trackingCategoryList['non_compliant_line_colors']) 		end 	else 		addTrackingCategory(trackingCategoryList['default_line_colors']) 	end end  -- Локальная функция для удаления вики-ссылок из повторяющейся информации в ячейках. local function removeWikilinks(args, v) 	return delinkModule._delink({args[v]}) end  -- Локальная функция для установки текста пустой ячейки либо 'TBD' - to be announced, либо  'н/д' - нет данных. local function setTBDStatus(args, awaitingText, expiredText) 	local year, day, month, seconds 	local originalAirDate = args.OriginalAirDate or args['ДатаПоказа']   	if not hasValue(originalAirDate) then 		return tableEmptyCellModule._main({alt_text=awaitingText, title_text=awaitingText}) 	end  	originalAirDate = gsub(originalAirDate, '&nbsp;', ' ') 	originalAirDate = gsub(originalAirDate, '[%[%]]', '')  	if match(originalAirDate, '{{[Ss]tart date') or match(originalAirDate, '-') then 		originalAirDate = gsub(originalAirDate, '[-%s|]+', ' ') 		year, month, day = match(originalAirDate, '(%d+) (%d+) (%d+)') 	elseif match(originalAirDate, ',') then 		month, day, year = match(originalAirDate, '([%a%A]+) (%d+), (%d+)') 	elseif match(originalAirDate, '[./]') then 		originalAirDate = gsub(originalAirDate, '[%s./|]+', ' ') 		day, month, year = match(originalAirDate, '(%d+) (%d+) (%d+)') 	else 		originalAirDate = gsub(originalAirDate, '[-%s.,/|]+', ' ') 		day, month, year = match(originalAirDate, '(%d+) ([%a%A]+) (%d+)') 	end  	if not day then 		return tableEmptyCellModule._main({alt_text='TBD'}) 	end  	-- Список месяцев. 	local monthList = { 		['января']=1, ['январь']=1, ['january']=1, ['1']=1, ['01']=1, 1, 		['февраля']=2, ['февраль']=2, ['february']=2, ['2']=2, ['02']=2, 2, 		['марта']=3, ['март']=3, ['march']=3, ['3']=3, ['03']=3, 3, 		['апреля']=4, ['апрель']=4, ['april']=4, ['4']=4, ['04']=4, 4, 		['мая']=5, ['май']=5, ['may']=5, ['5']=5, ['05']=5, 5, 		['июня']=6, ['июнь']=6, ['june']=6, ['6']=6, ['06']=6, 6, 		['июля']=7, ['июль']=7, ['july']=7, ['7']=7, ['07']=7, 7, 		['августа']=8, ['август']=8, ['august']=8, ['8']=8, ['08']=8, 8, 		['сентября']=9, ['сентябрь']=9, ['september']=9, ['9']=9, ['09']=9, 9, 		['октября']=10, ['октябрь']=10, ['october']=10, ['10']=10, 10, 		['ноября']=11, ['ноябрь']=11, ['november']=11, ['11']=11, 11, 		['декабря']=12, ['декабрь']=12, ['december']=12, ['12']=12, 12 	}  	local monthnumber = monthList[lower(month)] 	if not monthnumber then 		error('Неверный месяц ' .. month) 	end  	seconds = os.time() - os.time({year=year, month=monthnumber, day=day, hour=0, min=0, sec=0})  	-- 60 * 60 * 24 * 7 * 4 = 2419200 секунд в 4-х неделях 	if seconds >= 2419200 then 		return tableEmptyCellModule._main({alt_text=expiredText, title_text=expiredText}) 	end  	return tableEmptyCellModule._main({alt_text=awaitingText, title_text=awaitingText}) end  -- Local function which is used to create an empty cell. local function createEmptyCell(args, v, unsetParameterGroup) 	local originalAirDate = args.OriginalAirDate or args['ДатаПоказа'] 	local directedByAll = 'DirectedBy Режиссёр Режиссер' 	local writtenByAll = 'WrittenBy Сценарист' 	local viewersAll = 'Viewers Зрителей' 	if unsetParameterGroup then 		args[v] = tableEmptyCellModule._main({alt_text='н/д'}) 	elseif find(viewersAll, v) and hasValue(originalAirDate) then 		args[v] = setTBDStatus(args, 'TBD', 'н/д') 	elseif find(directedByAll, v) or find(writtenByAll, v) then 		args[v] = setTBDStatus(args, 'TBA', 'Неизвестно') 	else 		args[v] = tableEmptyCellModule._main({}) 	end end  -- Air dates that don't use {{Start date}} local function checkUsageOfDateTemplates(args, v, onInitialPage, title) 	local originalAirDateAll = 'OriginalAirDate ДатаПоказа' 	local altDateAll = 'AltDate ДатаПоказа2'  	if find(originalAirDateAll, v) 		and hasValue(args[v]) 		and match(args[v], '%d%d%d%d') 		and not match(args[v], '2C2C2C') 		and not find(args[v], 'dtstart') 		and onInitialPage 		and title.namespace == 0 	then 		addTrackingCategory(trackingCategoryList['air_dates']) 	end  	-- Alternate air dates that do use {{Start date}} 	if find(altDateAll, v) 			and hasValue(args[v]) 			and find(args[v], 'dtstart') 			and onInitialPage 			and title.namespace == 0 	then 		addTrackingCategory(trackingCategoryList['alt_air_dates']) 	end 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) 	local prodCodeAll = 'ProdCode ПродКод'  	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 find(prodCodeAll, v) 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 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) 	local prodCodeAll = 'ProdCode ПродКод' 	local writtenByAll = 'WrittenBy Сценарист' 	local aux1All = 'Aux1 Столбец1'  	for k, v in ipairs(cellNameList) do 		if args[v] then 			-- Set empty cells to TBA/TBD 			if args[v] == '' then 				createEmptyCell(args, v, false) 			elseif find(writtenByAll, v) and title.namespace == 0 then 				if (find(args[v], "''Сюжет") 						or find(args[v], "''Сценарий") 						or find(args[v], "''Телесценарий") 						or find(args[v], "''Телепередача") 						or find(args[v], "''Передача")) 						and not find(args[v], '8202') 				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 find(aux1All, v) and isSerial then 				textAlign = 'left' 			end  			local thisRowspan 			if firstParameterGroupCell and k < firstParameterGroupCell then 				thisRowspan = numberOfParameterGroups 			else 				thisRowspan = 1 			end  			if currentRow == 1 or (currentRow > 1 and k >= (firstParameterGroupCell or 0)) then 				createTableData(args[v], thisRowspan, textAlign) 			end 			nonNilParams = nonNilParams + 1 			checkUsageOfDateTemplates(args, v, onInitialPage, title) 		end  		if args[v] == 'TBA' 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: <Title><RTitle> (with no space between) 	Line 2: <AltTitle><RAltTitle> (with no space between) OR 	Line 2: Transcription: <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 title = args.Title or args['Название'] 	local rTitle = args.RTitle or args['НазваниеПрим'] 	local altTitle = args.AltTitle or args['Название2'] 	local rAltTitle = args.RAltTitle or args['Название2Прим'] 	local nativeTitle = args.NativeTitle or args['НазваниеОригинал'] 	local translitTitle = args.TranslitTitle or args['НазваниеТранслит'] 	local nativeTitleLangCode = args.NativeTitleLangCode or args['КодЯзыка'] 	local titleString = '' 	local isCellPresent = false 	local useSecondLine = false 	local lineBreakUsed = false  	-- «Заголовок в кавычках». Если пустой, то без кавычек. 	if title then 		isCellPresent = true 		if hasValue(title) then 			-- Название 			titleString = "&#171;'''" .. title .. "'''&#187;" 			useSecondLine = true 		end 	end  	if rTitle then 		isCellPresent = true 		if hasValue(rTitle) then 			titleString = titleString .. rTitle 			useSecondLine = true 		end 	end  	-- Surround the AltTitle/TranslitTitle with quotes; No quotes if empty. 	if altTitle or translitTitle then 		isCellPresent = true 		if useSecondLine then 			titleString = titleString .. '<br>' 			lineBreakUsed = true 		end  		if hasValue(altTitle) then 			-- Название2 			titleString = titleString .. "&#171;''" .. altTitle .. "''&#187;" 		elseif hasValue(translitTitle) then 			titleString = titleString .. abbr('транслит.', 'транслитерация названия') .. '.: &#171;' 			if hasValue(nativeTitleLangCode) then 				titleString = titleString .. langSpan(translitTitle, nativeTitleLangCode) .. '&#187;' 			else 				titleString = titleString .. translitTitle .. '&#187;' 			end 		end 	end  	if nativeTitle then 		isCellPresent = true 		if hasValue(nativeTitle) then 			if useSecondLine and not lineBreakUsed then 				titleString = titleString .. '<br>' 			end  			titleString = titleString .. ' (' .. abbr('ориг.', 'оригинальное название') .. '.: ' 			if hasValue(nativeTitleLangCode) then 				titleString = titleString .. langSpan(nativeTitle, nativeTitleLangCode) .. ')' 			else 				titleString = titleString .. nativeTitle .. ')' 			end 		end 	end  	if rAltTitle then 		isCellPresent = true 		if hasValue(rAltTitle) then 			if useSecondLine and not lineBreakUsed then 				titleString = titleString .. '<br>' 			end 			titleString = titleString .. 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 .. '_' .. i 		args[parameter] = args[numberedParameter]  		if not nextGroupValid 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 or args['КодЯзыка'] 	local titleText = '' 	local isCellPresent = false 	local isFirstTitleGroup = true -- Ячейка заголовка создана хотя бы один раз и не создается повторно, если другие заголовки пусты.  	for i=1, numberOfParameterGroups do 		local args, nextGroupValid = extractDataFromNumberedTitleArgs(args, i) 		if nextGroupValid then 			if not isFirstTitleGroup then 				titleText = titleText .. '<hr>' 			end  			local 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, isCellPresent  	if multiTitleListEnabled then 		titleText, isCellPresent = processMultiTitleList(args, numberOfParameterGroups) 	else 		titleText, isCellPresent = createTitleText(args) 	end  	if not isCellPresent then 		return nil 	end  	local textAlign = 'left'  	-- If Title is blank, then set Raw Title to TBA 	if not hasValue(titleText) 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 a table row header for either the -- EpisodeNumber or EpisodeNumber2 column cells. local function createTableRowEpisodeNumberHeader(episodeNumber, numberOfParameterGroups, episodeText) 	local epID = match(episodeNumber, '^%w+') 	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: '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, 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 match(episodeNumber, '^(%w+)%s*[%s%-–/&]%s*(%w+)$') then 			episodeNumber1, episodeNumber2 = match(episodeNumber, '^(%w+)%s*[%s%-–/&]%s*(%w+)$') 			divider = '–' 		else 			episodeNumber1, episodeNumber2 = match(episodeNumber, '^(%w+)%s*[%s%-–/&].-[%s%-–/&]%s*(%w+)$') -- 3 or more elements 			divider = '–' 		end  		if not episodeNumber1 then 			return episodeNumber 		elseif not episodeNumber2 then 			return 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, 'center') 		end  		nonNilParams = nonNilParams + 1  	end end  -- Local function which is used to create seconday episode number cells. local function createEpisodeNumberCellSecondary(args, numberOfParameterGroups) 	local episodeNumber2 = args.EpisodeNumber2 or args['НомерЭпизода2'] 	local episodeNumber3 = args.EpisodeNumber3 or args['НомерЭпизода3'] 	_createEpisodeNumberCellSecondary(episodeNumber2, numberOfParameterGroups) 	_createEpisodeNumberCellSecondary(episodeNumber3, numberOfParameterGroups) end  -- Local function which is used to create an EpisodeNumber cell. local function createEpisodeNumberCell(args, numberOfParameterGroups) 	local episodeNumber = args.EpisodeNumber or args['НомерЭпизода'] 	if episodeNumber then 		local episodeText = getEpisodeText(episodeNumber) 		createTableRowEpisodeNumberHeader(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) 	local numParts = args.NumParts or args['КоличествоЧастей'] 	for k, v in ipairs(cellNameList) do 		local numberedParameter = v .. '_' .. 1 		if args[numberedParameter] then 			parameterGroupCells[v] = true 			if not firstParameterGroupCell then 				firstParameterGroupCell = k 			end 		end 	end  	if hasValue(numParts) then 		return numParts, true 	else 		return 1, false 	end end  -- Local function which is used to retrieve the remainder value after x has been divided by y local function mathMod(x, y) 	local ret = x % y 	if not (0 <= ret and ret < y) then 		ret = 0 	end 	return ret end  -- Local function which is used to retrieve the Top Color value. local function getTopColor(args, rowColorEnabled, onInitialPage) 	local episodeNumber = args.EpisodeNumber or args['НомерЭпизода'] 	local topColor = args.TopColor or args['ЦветСтроки'] 	local shortSummary = args.ShortSummary or args['КраткоеСодержание'] 	local episodeNumber = mathModule._cleanNumber(episodeNumber) or 1 	if topColor then 		if find(topColor, '#') then 			return topColor 		else 			return '#' .. topColor 		end 	elseif (rowColorEnabled and onInitialPage and mathMod(episodeNumber, 2) == 0) then 		return 'var(--background-color-neutral, #eaecf0)' 	elseif onInitialPage and shortSummary then 		return 'var(--background-color-neutral-subtle, #f8f9fa)' 	else 		return 'inherit' 	end end  -- Локальная функция, которая используется для включения чередования цвета строк. local function isRowColorEnabled(args) 	local rowColor = args.RowColor or args['ЧередованиеЦвета'] 	return yesNoModule(rowColor, nil) 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 args['ЦветГраницы'] or 'CCCCFF'  	-- Add # to color if necessary, and set to default color if invalid 	if not htmlColor[lineColor] then 		lineColor = '#' .. (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 = false  	-- Only sublist had anything about hiding, so only it needs to even check 	if sublist then 		local pageTitleMatch = match(pageTitle, 'Список эпизодов', 1) -- Результат: Список эпизодов или nil  		if not initiallistTitle and pageTitleMatch == 'Список эпизодов' then -- Автоопределение страницы 'Список эпизодов ...' 			onInitialPage = true 		elseif hasValue(initiallistTitle) then 			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, linetop) 	local title = mw.title.getCurrentTitle() 	local title2 = args.Title_2 or args['Название_2'] 	local pageTitle = title.text 	local initiallistTitle = args['1'] or '' 	local shortSummary = args.ShortSummary or args['КраткоеСодержание']  	-- 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 not hasValue(shortSummary) then 		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) 			:css('color', 'inherit')  	local numberOfParameterGroups, multiTitleListEnabled = getnumberOfParameterGroups(args)  	if multiTitleListEnabled and not title2 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 and title.namespace == 0 then 		addTrackingCategory(trackingCategoryList['tba_values']) 	end  	local pageTitleMatch = match(pageTitle, 'Список эпизодов', 1) 	if pageTitleMatch == 'Список эпизодов' or title.namespace ~= 0 then 		trackingCategories = '' 	end  	-- Do not show the summary if this is being transcluded on the initial list page 	-- Do include it on all other lists 	if not onInitialPage and shortSummary then 		local bottomWrapper = createShortSummaryRow(args, lineColor) 		return tostring(root) .. tostring(bottomWrapper) .. trackingCategories 	elseif linetop then 		local bottomWrapper = createShortSummaryRow(args, lineColor) 		local bottomWrapperLen = len(bottomWrapper) 		local bottomWrapperEnd = sub(bottomWrapper, 1, bottomWrapperLen - 10) 		return tostring(root) .. tostring(bottomWrapperEnd) .. trackingCategories 	else 		return tostring(root) .. trackingCategories 	end end  -- Local function which handles both module entry points. local function main(frame, sublist, linetop) 	local getArgs = require('Модуль: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='Шаблон:Список серий/sublist'}) 	elseif linetop then 		args = getArgs(frame, {removeBlanks=false, wrappers='Шаблон:Список серий/шапка'}) 	else 		args = getArgs(frame, {removeBlanks=false, wrappers='Шаблон:Список серий'}) 	end  	return _main(args, sublist, linetop) end  -------------------------------------------------------------------------------- -- Экспорт --------------------------------------------------------------------------------  function p.sublist(frame) 	return main(frame, true, false) end  function p.linetop(frame) 	return main(frame, false, true) end  function p.list(frame) 	return main(frame, false, false) end  return p