Модуль:Песочница/Vavilexxx/Таблица эпизодов

Документация
require('strict')

-- Этот модуль реализован шаблонами {{Таблица эпизодов}}, {{Таблица эпизодов/шапка}} и {{Таблица эпизодов/часть}}.

local HTMLcolor = mw.loadData('Модуль:Color contrast/colors')
local contrast_ratio = require('Модуль:Color contrast')._ratio
local plainText = require('Модуль:Таблица эпизодов/Plain text')._main
local anchor = require('Модуль:Якорь').main
local match = mw.ustring.match
local lower = mw.ustring.lower
local gsub = mw.ustring.gsub


--------------------------------------------------------------------------------
-- Local functions
--------------------------------------------------------------------------------

local function isExistNumsCols(args)
	local forceoverall = args.forceoverall or args['ОбщийНомер']
	local overall = args.overall or args['Номер']
	local season = args.season or args['Сезон']
	local series = args.series or args['Серия']

	if forceoverall
		or season and series
		or overall and season and series
	then
		return true
	end

	return false
end

--------------------------------------------------------------------------------
-- EpisodeTable class
-- The main class.
--------------------------------------------------------------------------------

local EpisodeTable = {}

function EpisodeTable.cell(background, width, text, reference, textColor)
	local cell = mw.html.create('th')

	-- Ячейка
	cell:attr('scope', 'col')
		:css('background', background or '#ccccff')
		:css('width', width ~= '' and width .. '%' or nil)
		:css('color', textColor)
		:wikitext(text)

	-- Сноска на примечание
	if reference then
		cell:wikitext(' ' .. EpisodeTable.reference(reference, background))
	end

	return cell
end

function EpisodeTable.reference(reference, background)
	-- контрастность с непосещенной и посещенной ссылкой
	local link1_cr = contrast_ratio{background or '#ccccff', '#0645AD', ['error']=0}
	local link2_cr = contrast_ratio{background or '#ccccff', '#0B0080', ['error']=0}

	local refspan = mw.html.create('span')
			:wikitext(reference)

	if link1_cr < 7 or link2_cr < 7 then
		refspan
			:css('color', link1_cr < 3 and link2_cr < 3 and 'white' or 'black')
			:css('background-color', link1_cr < 3 and link2_cr < 3 and '#f8f9fa' or 'transparent')
			:css('padding', '1px')
			:css('display', 'inline-block')
			:css('line-height', '50%')
	end

	return tostring(refspan)
end

function EpisodeTable.abbr(text, title)
	local abbr = mw.html.create('abbr')
	abbr:attr('title', title)
		:wikitext(text)
	return tostring(abbr)
end

function EpisodeTable.part(args)
	local row = mw.html.create('tr')

	local cellColor = args.c or args['Цвет']  or args['цвет']
	local cellRef = args.r or args['Ссылка']  or args['ссылка']
	local subTitle = args.subtitle or args['Подзаголовок'] or args['подзаголовок'] or args['Текст'] or args['текст']

	-- Цвет по умолчанию
	if not cellColor or cellColor == '' then
		cellColor = '#ccccff'
	end

	-- Добавление # при необходимости
	if cellColor and not HTMLcolor[cellColor] then
		cellColor = '#'..(match(cellColor, '^[%s#]*([a-fA-F0-9]+)%s*$') or 'ccccff')
	end

	local black_cr = contrast_ratio{'black', cellColor or '#ccccff', ['error']=0}
	local white_cr = contrast_ratio{'white', cellColor or '#ccccff', ['error']=0}

	local partTypes = {
		{'act', 'акт', 'Акт'},
		{'chapter', 'глава', 'Глава'},
		{'part', 'часть', 'Часть'},
		{'p', 'ч', 'Часть'},
		{'volume', 'том', 'Том'},
		{'week', 'неделя', 'Неделя'},
	}

	local displaytext = ''
	local isAnyPartSet = false
	local partValue

	for k, v in pairs(partTypes) do
		partValue = args[v[1]] or args[v[2]] or args[v[3]]
		if partValue then
			isAnyPartSet = true
			displaytext = v[3] .. ' ' .. partValue
		end
	end

	if subTitle then
		displaytext = displaytext .. ((isAnyPartSet and ': ' or '') .. subTitle)
	end

	local displayTextAnchor = plainText(displaytext)

	row:tag('td')
		:attr('colspan', 13)
		:attr('id', args.id or displayTextAnchor)
		:css('text-align', 'center')
		:css('background-color', cellColor or '#ccccff')
		:css('color', black_cr > white_cr and 'black' or 'white')
		:wikitext("'''" .. displaytext .. "'''" .. (cellRef and "&#8202;" .. EpisodeTable.reference(cellRef, cellColor) or ''))

	return tostring(row)
end

function EpisodeTable.new(args, dontclose)
	args = args or {}
	local categories = ''
	local background = args.background or args['ЦветФона']
	if background == '' or background =='#' then
		background = nil
	end

	-- Добавление # при необходимости
	if background and not HTMLcolor[background] then
		background = '#'..(match(background, '^[%s#]*([a-fA-F0-9]+)%s*$') or '')
	end

	-- Ширина ячеек по умолчанию
	local defaultwidths = {
		overall=5, ['Номер']=5, ['номер']=5,
		overall2=5, ['Номер2']=5, ['номер2']=5,
		season=5, ['Сезон']=5, ['сезон']=5,
		series=5, ['Серия']=5, ['серия']=5,
		airdate=10, ['ДатаПоказа']=10, ['датапоказа']=10,
		altdate=10, ['ДатаПоказа2']=10, ['датапоказа2']=10,
		prodcode=7, ['ПродКод']=7, ['продкод']=7,
		viewers=10, ['Зрителей']=10, ['зрителей']=10
	}

	-- Создание таблицы эпизодов
	local root = mw.html.create('table')
	local totalWidth = args.total_width or args['ОбщаяШирина'] or args['общаяширина']

	root:addClass('wikitable')
		:addClass('plainrowheaders')
		:addClass('wikiepisodetable')
		:css('width', totalWidth and gsub(totalWidth, '%%', '') .. '%' or '100%')

	-- Заголовок
	local caption = args.caption or args['Заголовок'] or args['заголовок']
	if caption then
		root:tag('caption')
			:wikitext(caption)
	end

	-- Цветовой контраст; добавление в категории, если основное пространство имен
	local title = mw.title.getCurrentTitle()
	local black_cr = contrast_ratio{background or '#ccccff', 'black', ['error']=0}
	local white_cr = contrast_ratio{'white', background or '#ccccff', ['error']=0}

	if title.namespace == 0 and background and black_cr < 4.5 and white_cr < 4.5 then
		categories = categories .. '[[Категория:Википедия:Потенциально нечитаемые таблицы эпизодов]]'
	end

	-- Основная строка
	local textColor = background and (black_cr > white_cr and 'black' or 'white') or 'black'
	local mainRow = root:tag('tr')
	mainRow
		:css('color', textColor)
		:css('text-align', 'center')

	-- Ячейки
	do
		local used_season = false
		local numsColsExist = isExistNumsCols(args)
		local country = args.country or args['Страна'] or args['страна'] or ''
		local viewersType = args.viewers_type or args['ЗрителейТ'] or args['зрителейТ']
		if not viewersType or viewersType == '' then
			viewersType = 'млн'
		end

		local overallColName = EpisodeTable.abbr('№', 'Номер') .. ((args.season or args['Сезон'] or args.forceoverall or args['ОбщийНомер']) and '<br>об­щий' or '')
		local overall2ColName = EpisodeTable.abbr('№', 'Номер') .. (args['Номер2ТАббр'] and args['Номер2АббрТ'] and '<br>' .. EpisodeTable.abbr(args['Номер2ТАббр'], args['Номер2АббрТ']) or '')
		local seasonColName = EpisodeTable.abbr('№', 'Номер') .. (numsColsExist and '<br>се­зо­на' or ' в<br>се­зо­не')
		local seriesColName = EpisodeTable.abbr('№', 'Номер') .. (numsColsExist and '<br>се­рии' or ' в<br>се­зо­не')
		local prodcodeColName = EpisodeTable.abbr('Про­изв.', 'Производственный') .. '<br>код'
		local viewersColName = 'Зрители ' .. country .. '<br>(' .. viewersType  .. ')'

		--- {'paramEng', 'paramRus', 'paramRus', 'ColumnName'}
		local cellNames = {
			{'overall', 'Номер', 'номер', overallColName},
			{'overall2', 'Номер2', 'номер2', overall2ColName},
			{'season', 'Сезон', 'сезон', seasonColName},
			{'series', 'Серия', 'серия', seriesColName},
			{'title', 'Название', 'название', 'Наз­ва­ние'},
			{'aux1', 'Столбец1', 'столбец1', ''},
			{'director', 'Режиссер', 'Режиссёр', 'Ре­жис­сёр'},
			{'writer', 'Сценарист', 'сценарист', 'Ав­тор сце­на­рия'},
			{'aux2', 'Столбец2', 'столбец2', ''},
			{'aux3', 'Столбец3', 'столбец3', ''},
			{'airdate', 'ДатаПоказа', 'датапоказа', 'Да­та пре­мье­ры'},
			{'altdate', 'ДатаПоказа2', 'датапоказа2', ''},
			{'prodcode', 'ПродКод', 'продкод', prodcodeColName},
			{'viewers', 'Зрителей', 'зрителей', viewersColName},
			{'aux4', 'Столбец4', 'столбец4', ''}
		}

		for k, v in pairs(cellNames) do
--			local cellWidth = args[v[1]] or args[v[2]] or args[v[3]]
			local cellWidth, argName
			argName = args[v[1]] and v[1] or args[v[2]] and v[2] or args[v[3]] and v[3]
			if argName then
				cellWidth = args[argName]
				argName = lower(argName)
			end
			if cellWidth and ((argName ~= 'series' or argName ~= 'серия') 
				or (argName == 'series' or argName == 'серия' and used_season == false)) 
			then
				if argName == 'season' or argName == 'сезон' then used_season = true end
				if (k <= 3 and cellWidth == '') then cellWidth = '5' end
				if (cellWidth == '' and defaultwidths[argName]) then cellWidth = defaultwidths[argName] end

				local cellText = args[argName .. 'T'] or args[argName .. 'Т']
				local cellRef = args[argName .. 'R'] or args[argName .. 'С']

				mainRow:node(EpisodeTable.cell(background, cellWidth, cellText or v[4], cellRef, textColor))
			end
		end

		-- Эпизоды (серии)
		local episodes = args.episodes or args['Эпизоды'] or args['эпизоды']
		local anchor = args.anchor or args['Якорь'] or args['якорь']

		if episodes then
			if anchor then
				episodes = gsub(episodes, '(id=")(ep%w+")', '%1' .. anchor .. '%2')
			end

			root:node(episodes)
		end
	end

	if title.namespace == 0 then
		categories = categories .. '[[Категория:Википедия:Статьи использующие шаблон таблица эпизодов]]'
	end

	root = tostring(root)
	return (dontclose and gsub(root, '</table>', '') or root) .. categories
end

--------------------------------------------------------------------------------
-- Экспорт
--------------------------------------------------------------------------------

local p = {}

function p.main(frame)
	local args = require('Модуль:Arguments').getArgs(frame, {
			removeBlanks = false,
			wrappers = {
				'Шаблон:Таблица эпизодов',
				'Шаблон:Таблица эпизодов/песочница'
			},
		})
	return EpisodeTable.new(args)
end

function p.maint(frame)
	local args = require('Модуль:Arguments').getArgs(frame, {
			removeBlanks = false,
			wrappers = {
				'Шаблон:Таблица эпизодов/шапка',
				'Шаблон:Таблица эпизодов/песочница/шапка'
			}
		})
	return EpisodeTable.new(args, true)
end

function p.part(frame)
	local args = require('Модуль:Arguments').getArgs(frame, {
			removeBlanks = false,
			wrappers = {
				'Шаблон:Таблица эпизодов/часть',
				'Шаблон:Таблица эпизодов/песочница/часть'
			}
		})
	return EpisodeTable.part(args)
end

function p.ref(frame)
	local args = require('Модуль:Arguments').getArgs(frame, {
			removeBlanks = false,
			-- wrappers = 'Шаблон:Таблица эпизодов/примечание'
		})
	return EpisodeTable.reference(args.r, args.b)
end

return p