Модуль:Hatnote

Материал из wikixw
Перейти к навигации Перейти к поиску

Модуль предназначен для реализации hatnote-шаблонов (например, {{основная статья}}, {{см. также}}). Также он содержит несколько функций для работы со ссылками и отслеживания ошибок. См. тесты на странице Шаблон:Основная статья/тесты.

Основные функции[править]

Основные функции содержатся в таблице p, все функции можно вызывать как из других модулей, так и через {{#invoke}}. При вызове из модуля функции принимают первым параметром таблицу с аргументами. Все описанные ниже параметры являются аргументами этой таблицы. Некоторые параметры принимают булевоподобные значения: их перечисление можно найти здесь.

parse_link[править]

Принимает на вход ссылку, возвращает два значения: целевую страницу и видимую часть ссылки. Если ссылку не удалось разобрать, возвращает nil. Наличие квадратных скобок не обязательно.

format_link[править]

Формирует вики-ссылку. Превращает # в ссылках на разделы в знак параграфа, отбитый пробелами, делая их более читабельными (см. {{ссылка на раздел}}).

Параметры:

  • первый параметр: название целевой страницы или вики-ссылка.
  • второй параметр: название раздела. Параметр будет проигнорирован, если раздел уже задан в первом параметре через #.
  • третий параметр: видимый текст ссылки.

remove_precision[править]

Убирает уточнение из первого параметра (см. {{без уточнения}}).

is_disambig[править]

Функция, определяющая, является ли первый параметр страницей разрешения неоднозначности. Список шаблонов неоднозначностей берется со страницы MediaWiki:Disambiguationspage.

list[править]

Формирует список из вики-ссылок.

Параметры:

  • нумерованные параметры — ссылки на страницы.
  • |l1=, |l2= и т. д. — текст ссылок.
  • |list_sep= или |разделитель списка=: разделитель списка, по умолчанию — «, ».
  • |natural_join=: при неложном значении вместо последнего разделителя выводится союз «и».
  • |links_ns= или |ПИ ссылок=: ко всем ссылкам добавляется префикс указанного пространства имен.
  • |bold_links= или |ссылки болдом=: при непустом и неложном значении каждая ссылка выделяется полужирным.

Возвращает таблицу, для неё предусмотрен метод tostring() и конкатенация. Именованные параметры этой таблицы — булевы переменные, истинные при обнаружении проблем. Отслеживаемые проблемы: red_link, bad_format, formatted, unparsable_link. Их описание см. ниже.

hatnote[править]

Формирует код <div class="hatnote">...</div>.

Параметры:

  • первый параметр — текст заметки.
  • |id=: id заметки.
  • |extraclasses=: дополнительные классы заметки.
  • |hide_disambig=: при непустом и неложном значении к тексту добавляется класс dabhide, скрывающий подсветку для пользователей гаджета подсветки неоднозначностей.

main[править]

Генерирует текст-заметку. Позволяет ссылаться на неограниченное число страниц. Синтаксис:

  • вызов из вики-текста:
{{#invoke:Hatnote|main|префикс= префикс |префикс мн. ч.= префикс для нескольких ссылок |разделитель списка= разделитель списка |ПИ ссылок= пространство имен  |extraclasses = дополнительные классы |точка= true / false |hide_disambig= true / false}}
  • вызов из другого модуля:
-- подключение модуля
local hatnote = require('Модуль:Hatnote').main

-- пример использования без изменения категорий по умолчанию
local res1 = hatnote{
    'Какая-то статья',
    l1 = 'Какой-нибудь текст ссылки',
    prefix = 'Основная статья:',
    prefix_plural = 'Основные статьи:',
    dot = true,
    nocat = args.nocat -- следует всегда передавать nocat, если он предусмотрен
}

-- и с изменением
local res2 = hatnote({
    'Еще какая-то статья',
    prefix = 'См. также:',
    list_sep = '; ',
    natural_join = false,
    nocat = args.nocat
}, {
    red_link = 'Википедия:Статьи с шаблоном «См. также» с красными ссылками'
})

Параметры:

  • |prefix= или |префикс=: обязательный параметр, текст перед списком ссылок.
  • |prefix_plural= или |префикс мн. ч.=: если ссылок несколько, для префикса вместо предыдущего параметра будет использован этот.
  • |sep= или |разделитель=: разделитель между префиксом и списком ссылок.
  • |dot= или |точка=: при непустом и неложном значении выводится точка в конце.
  • |nocat=: при непустом и неложном значении отключается категоризация.
  • |empty_list_message=: сообщение об ошибке, выводящееся при пустом списке ссылок. По умолчанию — «Не указано ни одной страницы».
  • |preview_error=: при непустом и неложном значении ошибка при пустом списке ссылок выводится только во время предпросмотра.

Функция также принимает все параметры функций list и hatnote и передает их без изменения в эти функции.

Вторым параметром (при вызове через другой модуль) функция принимает таблицу с переназначенными категориями. Их идентификаторы см. ниже:

Служебные функции[править]

Находятся в таблице tr, предназначены для отслеживания проблем и вывода сообщений об ошибках. Все функции можно вызывать только из других модулей.

define_categories[править]

tr.define_categories(tracking_categories)

Создает объект, представляющий собой таблицу с категориями и функцией для их добавления. Первым параметром передается ассоциированный массив с идентификаторами отслеживающих категорий.

add[править]

categories:add(type, nocat)

или

categories:add(category_name, nocat)

Добавляет в таблицу категории. Первый параметр — либо идентификатор, объявленный в define_categories(), либо название категории. Второй параметр — отключение категоризации.

error[править]

tr.error(msg, categories, preview_only)

Выводит сообщение об ошибке. Если ошибка генерируется шаблоном, то отображается название этого шаблона; если прямым вызовом через {{#invoke}}, то название вызываемого модуля.

Параметры:

  • msg: текст сообщения.
  • categories: либо объект, возвращаемый define_categories(), либо строка с категориями.
  • preview_only: при непустом и неложном значении ошибка выводится только во время предпросмотра.

Отслеживаемые случаи[править]

  • no_prefix — не задан префикс. Также выводится сообщение об ошибке.
    Категория:
  • no_links — не задано ни одной ссылки. Также выводится сообщение об ошибке (либо в тексте статьи, либо только во время предварительного просмотра, подробнее см. #main-Параметры).
    Категория:
  • red_link — параметр содержит ссылку на несуществующую статью.
    Категория:
  • bad_format — параметр содержит разделитель {{!}} вместо параметра |lx= или лишние квадратные скобки.
    Категория:
  • formatted — параметр содержит &lt;span (в таком случае он не парсится, а выдаётся в готовом виде). Нужно для поддержки шаблонов {{не переведено}} в качестве параметров.
    Категория:
  • unparsable_link — параметр содержит нестандартное оформление, которое не может быть разобрано модулем (например, {{см. также|[https://example.com внешние ссылки]}} или {{см. также|посторонний текст помимо [[ссылка|ссылки]]}}).
    Категория:
  • Использование в других модулях[править]

    Некоторые функции используются в следующих модулях (проверить):


    local get_args = require('Module:Arguments').getArgs
    local mError
    local yesno = function (v) return require('Module:Yesno')(v, true) end
    
    local p, tr = {}, {}
    local current_title = mw.title.getCurrentTitle()
    local tracking_categories = {
    	no_prefix = 'Википедия:Страницы с модулем Hatnote без указания префикса',
    	no_links = 'Википедия:Страницы с модулем Hatnote без ссылок',
    	red_link = 'Википедия:Страницы с модулем Hatnote с красной ссылкой',
    	bad_format = 'Википедия:Страницы с модулем Hatnote с некорректно заполненными параметрами',
    	unparsable_link = 'Википедия:Страницы с модулем Hatnote с нечитаемой ссылкой',
    	formatted = 'Википедия:Страницы с модулем Hatnote с готовым форматированием',
    }
    
    local function index(t1, t2)
    	return setmetatable(t1, {__index = t2})
    end
    
    local function concat(e1, e2) 
    	return tostring(e1) .. tostring(e2)
    end
    
    function tr.define_categories(tracked_cases)
    	local categories = setmetatable({}, {
    		__tostring = function (self) return table.concat(self) end, 
    		__concat = concat
    	})
    
    	function categories:add(element, nocat)
    		if not nocat then
    			local cat_name
    			if tracked_cases and tracked_cases[element] then
    				cat_name = tracked_cases[element]
    			else
    				cat_name = element
    			end
    			table.insert(self, string.format('[[Категория:%s]]', cat_name))
    		end
    	end
    	
    	return categories
    end
    
    function tr.error(msg, categories, preview_only)
    	local current_frame = mw.getCurrentFrame()
    	local parent_frame = current_frame:getParent()
    	local res_frame_title = parent_frame and parent_frame:getTitle() ~= current_title.prefixedText and
    		parent_frame:getTitle() or
    		current_frame:getTitle()
    	if not preview_only or current_frame:preprocess('{{REVISIONID}}') == '' then
    		mError = require('Module:Error')
    		return mError.error{
    			tag = 'div',
    			string.format('Ошибка в [[%s]]: %s.' 
    				.. (preview_only and '<br><small>Это сообщение показывается только во время предпросмотра.</small>' or ''), res_frame_title, msg)
    		} .. categories
    	else 
    		return categories
    	end
    end
    
    function p.parse_link(frame)
    	local args = get_args(frame)
    	local link = args[1]:gsub('\n', '')
    	local label
    	
    	link = mw.text.trim(link:match('^%[%[([^%]]+)%]%]$') or link)
    	if link:sub(1, 1) == '/' then
    		label = link
    		link = current_title.prefixedText .. link
    	end
    	link = link:match(':?(.+)')
    	if link:match('|') then
    		link, label = link:match('^([^%|]+)%|(.+)$')
    	end
    	
    	if not mw.title.new(link) then
    		return nil, nil
    	end
    	
    	return link, label
    end
    
    function p.format_link(frame)
    	-- {{ссылка на раздел}}
    	local args = get_args(frame)
    	local link, section, label = args[1], args[2], args[3]
    	
    	if not link then
    		link = current_title.prefixedText
    		if section then
    			link = '#' .. section
    			label = label or '§&nbsp;' .. section
    		end
    	else
    		local parsed_link, parsed_label = p.parse_link{link}
    		if parsed_link then
    			link = parsed_link
    		else
    			return link
    		end
    		if section and not link:match('#') then
    			link = link .. '#' .. section
    			if parsed_label then
    				parsed_label = parsed_label .. '#' .. section
    			end
    		end
    		
    		label = (label or parsed_label or link):gsub('^([^#]-)#(.+)$', '%1 §&nbsp;%2')
    	end
    	
    	if label and label ~= link then
    		return string.format('[[:%s|%s]]', link, label)
    	else
    		return string.format('[[:%s]]', link)
    	end
    end
    
    function p.remove_precision(frame)
    	-- {{без уточнения}}
    	local args = get_args(frame)
    	local title = args[1]
    	
    	return title:match('^(.+)%s+%b()$') or title
    end
    
    function p.is_disambig(frame)
    	local args = get_args(frame)
    	local title = args[1]
    	local page = mw.title.new(title)
    	
    	if not page or not page.exists or mw.title.equals(page, current_title) then
    		return false
    	end
    	
    	local page_content = page:getContent()
    	local mw_list_content = mw.title.new('MediaWiki:Disambiguationspage'):getContent()
    	local lang = mw.language.getContentLanguage()
    	for template in mw.ustring.gmatch(mw_list_content, '%*%s?%[%[Шаблон:([^%]]+)') do
    		if page_content:match('{{' .. template) or page_content:match('{{' .. lang:lcfirst(template)) then 
    			return true
    		end
    	end
    	return false
    end
    
    function p.list(frame)
    	local args = get_args(frame, {trim = false})
    	local list_sep = args.list_sep or args['разделитель списка'] or ', '
    	local last_list_sep = yesno(args.natural_join) ~= false and ' и ' or list_sep
    	local links_ns = args.links_ns or args['ПИ ссылок']
    	local bold_links = yesno(args.bold_links or args['ссылки болдом'])
    
    	local res_list = {}
    	local tracked = {
    		red_link = false,
    		bad_format = false,
    		formatted = false,
    		unparsable_link = false
    	}
    	
    	local i = 1
    	while args[i] do
    		local link = args[i]
    		local label = args['l' .. i]
    		
    		local element = ''
    		if link:match('<span') then -- TODO: переписать
    			tracked.formatted = true
    			element = link -- for {{не переведено}}
    		else
    			local bad_format = (link:match('|') or link:match('[%[%]]')) ~= nil
    			local parsed_link, parsed_label = p.parse_link{link}
    			
    			if parsed_link then
    				tracked.bad_format = tracked.bad_format or bad_format
    				if links_ns then
    					parsed_label = parsed_label or parsed_link
    					parsed_link = mw.site.namespaces[links_ns].name .. ':' .. parsed_link
    				end
    			
    				local title = mw.title.new(parsed_link)
    				tracked.red_link = tracked.red_link or not (title.isExternal or title.exists)
    				element = p.format_link{parsed_link, nil, label or parsed_label}
    			else
    				tracked.unparsable_link = true
    				element = link
    			end
    		end
    		
    		if bold_links then
    			element = string.format('<b>%s</b>', element)
    		end
    		
    		table.insert(res_list, element)
    		i = i + 1
    	end
    	
    	return setmetatable(res_list, {
    		__index = tracked,
    		__tostring = function (self) return mw.text.listToText(self, list_sep, last_list_sep) end,
    		__concat = concat,
    		__pairs = function (self) return pairs(tracked) end
    	})
    end
    
    function p.hatnote(frame)
    	local args = get_args(frame)
    	local text = args[1]
    	local id = args.id
    	local extraclasses = args.extraclasses
    	local hide_disambig = yesno(args.hide_disambig)
    	
    	local res = mw.html.create('div')
    		:attr('id', id)
    		:addClass('hatnote')
    		:addClass('navigation-not-searchable')
    		:addClass(extraclasses)
    		:wikitext(text)
    	
    	if hide_disambig then
    		res:addClass('dabhide')
    	end
    	
    	return res
    end
    
    function p.main(frame, _tracking_categories)
    	local args = get_args(frame, {trim = false})
    	
    	local prefix = args.prefix or args['префикс']
    	local prefix_plural = args.prefix_plural or args['префикс мн. ч.']
    	local sep = args.sep or args['разделитель'] or ' '
    	local dot = yesno(args.dot or args['точка']) and '.' or ''
    	local nocat = yesno(args.nocat)
    	local preview_error = yesno(args.preview_error)
    	local empty_list_message = args.empty_list_message or 'Не указано ни одной страницы'
    	
    	categories = tr.define_categories(index(_tracking_categories or {}, tracking_categories))
    
    	if not prefix then
    		categories:add('no_prefix', nocat)
    		return tr.error('Не указан префикс', categories)
    	end
    	if not args[1] then
    		categories:add('no_links', nocat)
    		return tr.error(empty_list_message, categories, preview_error)
    	end
    	
    	if args[2] and prefix_plural then
    		prefix = prefix_plural
    	end
    	
    	local list = p.list(args)
    	
    	for k, v in pairs(list) do
    		if type(v) == 'boolean' and v then
    			categories:add(k, nocat)
    		end
    	end
    	
    	return p.hatnote(index({prefix .. sep .. list .. dot}, args)) .. categories
    end
    
    return index(p, tr)