Перейти к содержанию

Модуль:Карточка

Материал из PERSONO.WIKI

Для документации этого модуля может быть создана страница Модуль:Карточка/doc

--[[
	Модуль:Карточка — отрисовка карточки для любого типа сущности, кроме
	персоны. У персоны своя логика (частичные даты, годы до нашей эры,
	склонение месяцев), она осталась в Модуль:Персона.

	Здесь общее для организаций, терминов, произведений, событий и мест:
	собрать пары «подпись — значение», выбросить пустые, разбить списки по
	точке с запятой, обрезать длинные и не рисовать таблицу, если показывать
	нечего. Пустая карточка хуже отсутствующей: она обещает сведения, которых
	нет.

	Шаблон своего типа остаётся тонким: он объявляет поля, зовёт
	{{Сущность}} для общей записи и передаёт сюда готовый список строк.

	Разделитель списков — точка с запятой, как во всём проекте.
]]

local p = {}

--- Сколько значений списка показываем; остальное сворачиваем в «и ещё N».
local LIMIT = 5

local function trim( s )
	return ( tostring( s or '' ):gsub( '^%s*(.-)%s*$', '%1' ) )
end

--- Разбивает список по точке с запятой. Запятая внутри значения
--- («Ростов-на-Дону, Россия») разбор не ломает — это и есть причина выбора.
local function split( value )
	local out = {}
	for part in tostring( value ):gmatch( '[^;]+' ) do
		local item = trim( part )
		if item ~= '' then
			out[#out + 1] = item
		end
	end
	return out
end

--- Дата в записи «1809-08-16» — не то, что читают. Разбор и склонение
--- месяцев живут в Модуль:Персона вместе с правилами о неполных датах и о
--- годах до нашей эры; заводить здесь второй разбор значило бы обречь их
--- разойтись. Организация, событие и произведение показывали сырую запись.
local persona = require( 'Модуль:Персона' )

--- Похоже ли значение на дату ISO. Проверяем перед вызовом, чтобы «1809»
--- в поле «тираж» не превратилось в год: форматируем только то, что
--- записано датой целиком.
local function looksLikeDate( value )
	return value:match( '^%d%d%d%d%-%d%d%-%d%d$' )
		or value:match( '^%d%d%d%d%-%d%d$' )
		or value:match( '^%-%d%d%d%d%-%d%d%-%d%d$' )
end

--- Значение строки: одиночное показываем как есть, список — через запятую с
--- обрезкой. Обрезка нужна, чтобы карточка не превращалась в простыню:
--- полный список всегда есть в тексте статьи.
local function renderValue( value )
	local items = split( value )
	if #items <= 1 then
		local one = trim( value )
		if looksLikeDate( one ) then
			return persona.formatDate( one ) or one
		end
		return one
	end
	if #items <= LIMIT then
		return table.concat( items, ', ' )
	end
	local shown = {}
	for i = 1, LIMIT do
		shown[i] = items[i]
	end
	return table.concat( shown, ', ' ) .. ' и ещё ' .. ( #items - LIMIT )
end

--[[
	Точка входа. Именованные аргументы:
	  заголовок      — подпись карточки
	  подзаголовок   — строка под заголовком
	  изображение    — имя файла без «Файл:»
	  подпись_изображения
	  лицензия_подтверждена — «да» разрешает показ изображения

	Строки задаются парами «строка1_подпись» / «строка1_значение» и так далее,
	до строка20. Порядок задаёт шаблон типа: он знает, что важно для своего
	типа, а модуль не должен об этом догадываться.
]]
p['карточка'] = function ( frame )
	local args = frame.args
	local rows = {}

	for i = 1, 20 do
		local label = trim( args['строка' .. i .. '_подпись'] )
		local value = trim( args['строка' .. i .. '_значение'] )
		if label ~= '' and value ~= '' then
			rows[#rows + 1] = { label = label, value = renderValue( value ) }
		end
	end

	local image = trim( args['изображение'] )
	-- Изображение без подтверждённой лицензии не показывается никогда.
	-- Это правило проекта, а не настройка оформления.
	local licensed = trim( args['лицензия_подтверждена'] ):lower()
	local showImage = image ~= '' and ( licensed == 'да' or licensed == 'yes' )

	if #rows == 0 and not showImage then
		return ''
	end

	local root = mw.html.create( 'table' )
	root:addClass( 'pw-card' )

	local title = trim( args['заголовок'] )
	if title ~= '' then
		local caption = root:tag( 'caption' ):addClass( 'pw-card__title' )
		caption:wikitext( title )
		local sub = trim( args['подзаголовок'] )
		if sub ~= '' then
			caption:tag( 'div' ):addClass( 'pw-card__subtitle' ):wikitext( sub )
		end
	end

	if showImage then
		local cell = root:tag( 'tr' ):tag( 'td' )
			:attr( 'colspan', 2 ):addClass( 'pw-card__image' )
		cell:wikitext( '[[Файл:' .. image .. '|frameless|260px|'
			.. ( trim( args['подпись_изображения'] ) ~= ''
				and trim( args['подпись_изображения'] ) or title ) .. ']]' )
		local caption = trim( args['подпись_изображения'] )
		if caption ~= '' then
			cell:tag( 'div' ):addClass( 'pw-card__caption' ):wikitext( caption )
		end
	end

	for _, row in ipairs( rows ) do
		local tr = root:tag( 'tr' )
		tr:tag( 'th' ):attr( 'scope', 'row' ):wikitext( row.label )
		tr:tag( 'td' ):wikitext( row.value )
	end

	return tostring( root )
end

-- Точки входа названы по-русски, поэтому объявлены через скобки: имена в Lua
-- состоят только из ASCII, и «function p.карточка()» не разбирается вовсе.
return p