Перайсці да зместу

Модуль:Footnotes

модуль пад перманентнай аховай
З Вікіпедыі, свабоднай энцыклапедыі

require('strict');
local getArgs = require ('Module:Arguments').getArgs;


--[[--------------------------< А Р Г У М Е Н Т Ы _ П А _ З М А Ў Ч А Н Н І >------------------------------------------------------

табліца для ўказання пачатковых значэнняў.

]]

local args_default = {
    group = '',
	bracket_left = '',
	bracket_right = '',
	bracket_year_left = '',
	bracket_year_right = '',
	postscript = '',
	page = '',
	pages = '',
	pagebe = '',
	seite = '',
	seitebig = '',
	location = '',
	page_sep = ", p.&nbsp;",
	pages_sep = ", pp.&nbsp;",
	pagebe_sep = ", с.&nbsp;",
	seite_sep = ", s.&nbsp;",
	seitebig_sep = ", S.&nbsp;",
	ref = '',
	template = 'harv',															-- калі назва шаблона не ўказана ў {{#invoke:}}, выкарыстоўваць гэта
	};


--[[--------------------------< П Р А В Е Р К А _ М Э Т Ы >------------------------------------------------------

шукае anchor_id (спіс імёнаў CITEREF і год або тэкст з |ref=) у anchor_id_list

амылка 'няма мэты' (no target) можа быць схаваная з дапамогай |ignore-err=yes, калі мэта не можа быць знойдзена, таму што мэта знаходзіцца ўнутры
шаблона, які абгортвае іншы шаблон; памылка 'некалькі мэт' (multiple targets) не можа быць схаваная

]]

local function target_check (anchor_id, args)
	local namespace = mw.title.getCurrentTitle().namespace;
	local anchor_id_list_module = mw.loadData ('Module:Footnotes/anchor_id_list');
	local anchor_id_list = anchor_id_list_module.anchor_id_list;
	local article_whitelist = anchor_id_list_module.article_whitelist;
	local template_list = anchor_id_list_module.template_list;
    local citeref_patterns = anchor_id_list_module.citeref_patterns or {}
	
	local whitelist_module = mw.loadData ('Module:Footnotes/whitelist');
	local whitelist = whitelist_module.whitelist;

	local tally = anchor_id_list[anchor_id];									-- nil, калі anchor_id няма ў спісе; у адваротным выпадку — падлік
	local msg;
	local category;

	if not tally then
		if args.ignore then
			return '';															-- калі ignore мае значэнне true, то няма ні паведамлення, ні катэгорыі
		end
		
		if article_whitelist and article_whitelist[anchor_id] then				-- калі ёсць лакальны белы спіс артыкула і ідэнтыфікатар якара знаходзіцца ў ім
			return '';															-- гатова
		end
		
		local wl_anchor_id = anchor_id;											-- копія для змянення, каб выкарыстоўваць як індэкс для белага спіса
		
		if args.year then														-- для ідэнтыфікатараў якароў, створаных гэтым шаблонам (не ў |ref=), якія маюць дату
			if args.year:match ('%d%l$') or										-- выкарыстоўвае значэнне даты, каб вызначыць, ці неабходна выдаліць ўдакладняльнік
				args.year:match ('n%.d%.%l$') or
				args.year:match ('nd%l$') then
					wl_anchor_id = wl_anchor_id:gsub ('%l$', '');				-- выдаляе ўдакладняльнік
			end
		end		

		local t_tbl = whitelist[wl_anchor_id];									-- атрымлівае спіс шаблонаў, звязаных з гэтым ідэнтыфікатарам якара

		if t_tbl then															-- калі ідэнтыфікатар якара не ў белым спісе, t_tbl мае значэнне nil
			for _, t in ipairs (t_tbl) do										-- перабірае спіс шаблонаў, звязаных з гэтым ідэнтыфікатарам якара
				if template_list[t] then										-- калі звязаны шаблон знойдзены ў спісе шаблонаў у артыкуле
					return '';													-- ідэнтыфікатар якара знаходзіцца ў белым спісе, і артыкул мае адпаведны шаблон, таму памылкі няма
				end
			end
		end

        for _, pattern in ipairs(citeref_patterns) do                           -- загружае шаблоны пошуку для шаблонаў-абгортак на гэтай старонцы
           if anchor_id:match(pattern) then                                     -- перабірае спецыяльныя шаблоны пошуку і спрабуе знайсці супадзенне
              return ''
           end
        end
  

		msg = 'няма мэты: ' .. anchor_id;										-- anchor_id не знойдзены
mw.log(msg)
        if namespace == 10 and not args.show then                               -- не ствараць паведамленне пра памылку ў прасторы імёнаў шаблонаў
           return ''
        end
		category = '[[Катэгорыя:Вікіпедыя:Памылкі адсутнасці якара для шаблонаў Harv і Sfn]]';

	elseif 1 < tally then
		msg = 'некалькі мэт (' .. tally .. '×): ' .. anchor_id;					-- больш за адзін anchor_id у гэтым артыкуле
mw.log(msg)
        if namespace == 10 and not args.show then                               -- не ствараць паведамленне пра памылку ў прасторы імёнаў шаблонаў
           return ''
        end
		category = 0 == namespace and '[[Катэгорыя:Вікіпедыя:Памылкі шматлікіх якароў у шаблонах Harv і Sfn]]' or '';			-- дадаваць у катэгорыю толькі ў прасторы артыкулаў
		return '<span class="error harv-error" style="display: inline; font-size:100%"> ' .. args.template .. ' памылка: ' .. msg .. ' ([[:Катэгорыя:Вікіпедыя:Памылкі шаблонаў Harv і Sfn|даведка]])</span>' .. category;
	end

--	category = 0 == namespace and '[[Катэгорыя:Вікіпедыя:Памылкі шаблонаў Harv і Sfn]]' or '';	-- дадаваць у катэгорыю толькі ў прасторы артыкулаў
	category = 0 == namespace and category or '';								-- дадаваць у катэгорыю толькі ў прасторы артыкулаў

-- адлюстраванне на аснове args.show (па змаўчанні не адлюстроўваецца)
    local display = args.show and 'inline' or 'none'
    return msg and '<span class="error harv-error" style="display: '..display..'; font-size:100%"> ' .. args.template .. ' error: ' .. msg .. ' ([[:Катэгорыя:Вікіпедыя:Памылкі шаблонаў Harv і Sfn|даведка]])</span>' .. category or '';

end


--[[--------------------------< I S _ Y E A R >----------------------------------------------------------------

ацэньвае параметр, каб праверыць, ці адпавядае ён адной з гэтых формаў з удакладняльнікам у выглядзе літары ніжняга рэгістра або без яго:
	YYYY
	n.d.
	nd	
	c. YYYY
	YYYY–YYYY	(раздзяляльнік — кароткі працяжнік)
	YYYY–YY		(раздзяляльнік — кароткі працяжнік)

вяртае true, калі параметр мае распазнаную форму; у адваротным выпадку — false

]]

local patterns_date= {
	'^%d%d%d%d?%l?$',
	'^n%.d%.%l?$',
	'^nd%l?$',
	'^c%. %d%d%d%d?%l?$',
	'^%d%d%d%d–%d%d%d%d%l?$',
	'^%d%d%d%d–%d%d%l?$',
	}

local function is_year (param, args)
	args.year = '';																-- выкарыстоўваецца для памылкі harv; 
	
	for _, pattern in ipairs (patterns_date) do
		if mw.ustring.match (param, pattern) then
			args.year = param;													-- выкарыстоўваецца для памылкі harv; 
			return true;
		end
	end
end


--[[--------------------------< Я Д Р О >----------------------------------------------------------------------

вяртае якарную спасылку (CITEREF), сфарміраваную з аднаго-чатырох імёнаў аўтараў, года і размяшчэння ў крыніцы (|p=, |pp=, loc=)

]]

local function core( args )
	local result;
	local err_msg = ''

	if args.P5 ~= '' then
		if is_year (args.P5, args) then
			result = table.concat ({args.P1, ' et al. ', args.bracket_year_left, args.P5, args.bracket_year_right});
		else
			args.P5 = '';														-- калі P5 не з’яўляецца годам, не ўключаць у якар
			result = table.concat ({args.P1, ' et al.'});						-- і не адлюстроўваць яго
		end

	elseif args.P4 ~= '' then
		if is_year (args.P4, args) then
			result = table.concat ({args.P1, ', ', args.P2, ' &amp; ', args.P3, ' ', args.bracket_year_left, args.P4, args.bracket_year_right});	-- тры імёны і год
		else
			result = table.concat ({args.P1, ' et al.'});						-- чатыры імёны
		end

	elseif args.P3 ~= '' then
		if is_year (args.P3, args) then
			result = table.concat ({args.P1, ' &amp; ', args.P2, ' ', args.bracket_year_left, args.P3, args.bracket_year_right});	-- два імёны і год
		else
			result = table.concat ({args.P1, ', ', args.P2, ' ', ' &amp; ', args.P3});	-- тры імёны
		end
			
	elseif args.P2 ~= '' then
		if is_year (args.P2, args) then
			result = table.concat ({args.P1, ' ', args.bracket_year_left, args.P2, args.bracket_year_right});	-- адно імя і год
		else
			result = table.concat ({args.P1, ' &amp; ', args.P2});				-- два імёны
		end
		
	else
		result = args.P1;														-- адно імя
	end
																				-- калі вынік "аўтар-дата" заканчваецца кропкай (звычайна калі апошні пазіцыйны параметр утрымлівае 'n.d.')
																				-- і калі няма размяшчэння ў крыніцы (няма |p=, |pp= або |loc=)
																				-- і калі першы або адзіны сімвал у args.postscript — гэта кропка
																				-- выдаляе кропку ў канцы выніку "аўтар-дата"
																				-- кропка ў канцы выніку "аўтар-дата" будзе пазней заменена змесцівам args.postscript (звычайна кропкай)
	if ('.' == result:sub(-1)) and ('.' == args.postscript:sub(1)) and ('' == args.page) and ('' == args.pages) and ('' == args.pagebe) and ('' == args.seite) and ('' == args.seitebig) and ('' == args.location) then
		result = result:gsub ('%.$', '');
	end
	
	if args.ref ~= 'none' then
		local anchor_id;
		if args.ref ~= '' then
			anchor_id = mw.uri.anchorEncode (args.ref);
			err_msg = target_check (anchor_id, args);
			result = table.concat ({'[[#', anchor_id, '|', result, ']]'});
		else
			anchor_id = mw.uri.anchorEncode (table.concat ({'CITEREF', args.P1, args.P2, args.P3, args.P4, args.P5}));
			err_msg = target_check (anchor_id, args);
			result = table.concat ({'[[#', anchor_id, '|', result, ']]'});
		end
	end

	if args.page ~= '' then
		result = table.concat ({result, args.page_sep, args.page});
	elseif args.pages ~= ''then
		result = table.concat ({result, args.pages_sep, args.pages});
	elseif args.pagebe ~= ''then
		result = table.concat ({result, args.pagebe_sep, args.pagebe});
	elseif args.seite ~= ''then
		result = table.concat ({result, args.seite_sep, args.seite});
	elseif args.seitebig ~= ''then
		result = table.concat ({result, args.seitebig_sep, args.seitebig});
	end      

	if args.location ~= '' then
		result = table.concat ({result, ', ', args.location});
	end

	result = table.concat ({args.bracket_left, result, args.bracket_right, args.postscript}):gsub ('%s+', ' ');		-- выдаляе лішнія прабелы
	return result .. err_msg;
end


--[[--------------------------< З _ Д Э Ф І С А _ Ў _ П Р А Ц Я Ж Н І К >--------------------------------------------------

Пераўтварае дэфіс у працяжнік пры пэўных умовах. Дэфіс павінен раздзяляць
падобныя элементы; розныя элементы вяртаюцца без змяненняў. Змяняюцца наступныя формы:
	літара - літара (A - B)
	лічба - лічба (4-5)
	лічба раздзяляльнік лічба - лічба раздзяляльнік лічба (4.1-4.5 або 4-1-4-5)
	літаралічба - літаралічба (A1-A5) (падтрымліваецца неабавязковы раздзяляльнік
		паміж літарай і лічбай – a.1-a.5 або a-1-a-5)
	лічбалітара - лічбалітара (5a - 5d) (падтрымліваецца неабавязковы раздзяляльнік
		паміж літарай і лічбай – 5.a-5.d або 5-a-5-d)

любыя іншыя формы вяртаюцца без змяненняў.

str можа быць спісам, падзеленым коскамі або кропкай з коскай

Гэты код скапіяваны з Module:Citation/CS1. Адзінае змяненне — патрабаванне Module:Citation/CS1/Utilities,
каб ён меў доступ да функцый is_set() і has_accept_as_written()

]]

local function hyphen_to_dash( str )
	local utilities = require ('Module:Citation/CS1/Utilities');				-- адзінае змяненне, каб гэтая функцыя мела доступ да is_set() і has_accept_as_written()

	if not utilities.is_set (str) then
		return str;
	end

	local accept; -- Boolean (лагічнае значэнне)

	str = str:gsub ('&[nm]dash;', {['&ndash;'] = '–', ['&mdash;'] = '—'});		-- замяняе сутнасці &mdash; і &ndash; на іхнія сімвалы; кропка з коскай псуе text.split
	str = str:gsub ('&#45;', '-'); -- замяняе лікавую сутнасць HTML на сімвал дэфіса

	str = str:gsub ('&nbsp;', ' '); -- замяняе сутнасць &nbsp; на звычайны сімвал прабелу з клавіятуры
	
	local out = {};
	local list = mw.text.split (str, '%s*[,;]%s*');								-- разбівае str па косках або кропках з коскай, калі яны ёсць

	for _, item in ipairs (list) do												-- для кожнага элемента ў спісе
		item, accept = utilities.has_accept_as_written (item);					-- выдаляе разметку accept-this-as-written ("прымаць-як-напісана"), калі яна абгортвае ўвесь элемент
		if not accept and mw.ustring.match (item, '^%w*[%.%-]?%w+%s*[%-–—]%s*%w*[%.%-]?%w+$') then	-- калі дыяпазон праз дэфіс або мае як раздзяляльнікі кароткія або доўгія працяжнікі
			if item:match ('^%a+[%.%-]?%d+%s*%-%s*%a+[%.%-]?%d+$') or			-- літаралічба дэфіс літаралічба (неабавязковы раздзяляльнік паміж літарай і лічбай)
				item:match ('^%d+[%.%-]?%a+%s*%-%s*%d+[%.%-]?%a+$') or			-- лічбалітара дэфіс лічбалітара (неабавязковы раздзяляльнік паміж лічбай і літарай)
				item:match ('^%d+[%.%-]%d+%s*%-%s*%d+[%.%-]%d+$') or			-- лічба раздзяляльнік лічба дэфіс лічба раздзяляльнік лічба
				item:match ('^%d+%s*%-%s*%d+$') or								-- лічба дэфіс лічба
				item:match ('^%a+%s*%-%s*%a+$') then							-- літара дэфіс літара
					item = item:gsub ('(%w*[%.%-]?%w+)%s*%-%s*(%w*[%.%-]?%w+)', '%1–%2');	-- замяняе дэфіс, выдаляе лішнія прабелы
			else
				item = mw.ustring.gsub (item, '%s*[–—]%s*', '–');				-- для дыяпазонаў, падзеленых кароткім або доўгім працяжнікам, замяняе доўгі на кароткі, выдаляе лішнія прабелы
			end
		end
		table.insert (out, item);												-- дадае (магчыма, зменены) элемент у выніковую табліцу
	end

	local temp_str = '';														-- аб’ядноўвае выніковую табліцу ў радок, падзелены коскамі
	temp_str, accept = utilities.has_accept_as_written (table.concat (out, ', ')); -- выдаляе разметку accept-this-as-written ("прымаць-як-напісана"), калі яна абгортвае ўвесь аб'яднаны вынік
	if accept then
		temp_str = utilities.has_accept_as_written (str);						-- калі глабальная разметка выдалена, вяртае зыходны str; робіць гэта такім чынам, каб схаваць другое лагічнае значэнне (boolean), якое вяртаецца
		return temp_str;
	else
		return temp_str;														-- у адваротным выпадку вяртае сабраны temp_str
	end
end


--[[--------------------------< А Т Р Ы М А Н Н Е _ А Р Г У М Е Н Т А Ў >---------------------------------------------------------

Паколькі ўсе шаблоны маюць агульны набор параметраў, выкарыстоўваецца адна агульная функцыя для атрымання
гэтых параметраў з фрэйма і бацькоўскага фрэйма.

]]

local function args_fetch (frame, ps)
	local args = args_default;													-- стварае копію табліцы па змаўчанні
	local pframe = frame:getParent();											-- паказвае на табліцу параметраў шаблона

	for k, v in pairs (frame.args) do											-- перавызначае значэнні па змаўчанні значэннямі, указанымі ў #invoke:, калі такія ёсць
		args[k] = v;	   
	end
	
	args.postscript = pframe.args.postscript or pframe.args.ps or ps;
	if 'none' == args.postscript then
		args.postscript = '';
	end
	args.page = pframe.args.p or pframe.args.page or '';
	args.pages = pframe.args.pp or pframe.args.pages or '';
	args.pages = ('' ~= args.pages) and hyphen_to_dash (args.pages) or '';
	args.pagebe = pframe.args['с'] or pframe.args['старонка'] or '';
	args.seite = pframe.args.s or pframe.args.seite or '';
	args.seitebig = pframe.args.S or pframe.args.Seite or '';
	args.location = pframe.args.loc or '';
	args.name = pframe.args.name;
	args.ref = pframe.args.ref or pframe.args.Ref or '';
	args.ignore = ('yes' == pframe.args['ignore-false-positive']) or ('yes' == pframe.args['ignore-err']);

	for i, v in ipairs ({'P1', 'P2', 'P3', 'P4', 'P5'}) do						-- перабірае пяць пазіцыйных параметраў і абразае прабелы, калі яны зададзены, у адваротным выпадку — пусты радок
		args[v] = (pframe.args[i] and mw.text.trim (pframe.args[i])) or '';
	end

	if args.P5 and not is_year (args.P5, args) then
		local i = 6;															-- ініцыялізуе індэксатар шостым пазіцыйным параметрам
		while pframe.args[i] do													-- у выпадку, калі аўтараў занадта шмат, перабірае аўтараў у пошуках года
			local v = mw.text.trim (pframe.args[i]);							-- абразае прабелы
			if is_year (v, args) then											-- калі гэта год
				args.P5 = v;													-- перазапісвае тое, што было ў args.P5, годам
				break;															-- і спыняе пошук
			end
			i = i + 1;															-- павялічвае індэкс
		end
	end
	return args;
end


--[[--------------------------< Г А Р В А Р Д С К А Я _ Ц Ы Т А Т А >----------------------------------------------

агульная кропка ўваходу для:
	{{harvard citation}}, ён жа {{harv}}
	{{Harvard citation no brackets}}, ён жа {{harvnb}}
	{{harvcol}}
	{{harvcolnb}}
	{{harvcoltxt}}
	{{Harvard citation text}}, ён жа {{harvtxt}}
	{{Harvp}}

Адметныя асаблівасці (дужкі і раздзяляльнікі старонак) пазначаюцца ў {{#invoke}} гэтага модуля ў адпаведных шаблонах.

]]

local function harvard_citation (frame)
	local args = args_fetch (frame, '');										-- атрымлівае параметры шаблона і выкліку; пасляслоўе (postscript) па змаўчанні — пусты радок

	return core (args);
end


--[[--------------------------< А Ч Ы С Т К А _ U R L >------------------------------------------------------------

выкарыстоўваецца ў sfn() і sfnm(). Гэтая функцыя выпраўляе праблему з гаджэтам усплывальных падказак для зносак, калі падказка не адлюстроўваецца,
калі лакатар у крыніцы (|p=, |pp=, |loc=) мае вонкавую вікі-спасылку, якая ўтрымлівае сімвал #

выдаляе зарэзерваваныя сімвалы uri з url-адрасоў у параметрах |p=, |pp= і |loc=.  Зарэзерваваныя сімвалы:
	!#$&'()*+,/:;=?@[]
	
]]

local function strip_url (pages)
	local escaped_uri;
	if not pages or ('' == pages) then
		return pages;
	end
	
	for uri in pages:gmatch ('%[(%a[%w%+%.%-]*://%S+)') do						-- для кожнай вонкавай спасылкі атрымлівае uri
		escaped_uri = uri:gsub ("([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" );		-- захоўвае копію з экранаванымі сімваламі шаблонаў lua
		uri = uri:gsub ("[!#%$&'%(%)%*%+,/:;=%?@%[%]%.%%]", '');				-- выдаляе зарэзерваваныя сімвалы і '%', таму што '%20' (сімвал прабелу) з’яўляецца 'няправільным індэксам захопу' ('invalid capture index') у lua
		pages = pages:gsub (escaped_uri, uri, 1);								-- замяняе зыходны uri на вычышчаную версію
	end
	
	return pages;
end


--[[--------------------------< S F N >------------------------------------------------------------------------

кропка ўваходу для {{sfn}} і {{sfnp}}

]]

local function sfn (frame)
	local args = args_fetch (frame, '.');										-- атрымлівае параметры шаблона і выкліку; пасляслоўе па змаўчанні — кропка

	local result = core (args);													-- стварае якар CITEREF
																				-- збірае ўсё разам, а затым выдаляе лішнія прабелы
	local name = args.name or table.concat ({'FOOTNOTE', args.P1, args.P2, args.P3, args.P4, args.P5, strip_url (args.page), strip_url (args.pages), strip_url (args.pagebe), strip_url (args.seite), strip_url (args.seitebig), strip_url (args.location)}):gsub ('%s+', ' ');

	return frame:extensionTag ({name='ref', args={group=args.group, name=name}, content=result});	

	
end


--[[--------------------------< S F N M >----------------------------------------------------------------------

агульная кропка ўваходу для {{sfnm}} і {{sfnmp}}

Адметныя асаблівасці (дужкі) пазначаюцца ў {{#invoke}} гэтага модуля ў адпаведных шаблонах.

]]

local function sfnm (frame)
	local args = args_default;													-- стварае копію табліцы па змаўчанні
	local pframe = frame:getParent();											-- паказвае на табліцу параметраў шаблона
	
	local n = 1;																-- індэкс крыніцы; гэта 'n' у na1, ny і г.д.
	local first_pnum = 1;														-- першы з пары пазіцыйных параметраў
	local second_pnum = 2;														-- другі з пары пазіцыйных параметраў

	local last_ps = 0;															-- індэкс апошняй крыніцы з зададзеным |nps=
	local last_index = 0;														-- індэкс апошняй крыніцы; яны выкарыстоўваюцца, каб вызначыць, што з |ps= або |nps= завершыць увесь рэндэрынг

	local out = {};																-- табліца для захоўвання адрэндэраных крыніц
	local footnote = {'FOOTNOTE'};												-- усе даныя аўтара, даты, размяшчэння ў крыніцы становяцца часткай id зноскі; дадаюцца па ходзе

	for k, v in pairs (frame.args) do											-- перавызначае значэнні па змаўчанні значэннямі, указанымі ў #invoke:, калі такія ёсць
		args[k] = v;	   
	end
	
	while true do
		if not pframe.args[table.concat ({n, 'a1'})] and not pframe.args[first_pnum] then
			break;																-- няма na1 або адпаведнага пазіцыйнага параметра, таму гатова
		end
		
		if pframe.args[table.concat ({n, 'a1'})] then							-- ці выкарыстоўвае гэтая крыніца імянаваныя параметры?
			for _, v in ipairs ({'P1', 'P2', 'P3', 'P4', 'P5'}) do				-- ініцыялізуе для гэтай крыніцы
				args[v] = '';
			end

			for i, v in ipairs ({'P1', 'P2', 'P3', 'P4', 'P5'}) do				-- здабывае параметры аўтара і года для гэтай крыніцы
				args[v] = pframe.args[table.concat ({n, 'a', i})] or '';		-- спроба прызначыць імя аўтара
				if '' == args[v] then											-- калі не было імя аўтара
					args[v] = pframe.args[table.concat ({n, 'y'})] or '';		-- спроба прызначыць год
					break;														-- гатова з аўтарам/датай для гэтай крыніцы
				end
			end

		else																	-- гэтая крыніца выкарыстоўвае пазіцыйныя параметры
			args.P1 = mw.text.trim (pframe.args[first_pnum]);					-- так, падтрымліваецца толькі адзін аўтар
			args.P2 = (pframe.args[second_pnum] and mw.text.trim (pframe.args[second_pnum])) or '';	-- калі аўтар пазіцыйны, год таксама павінен быць пазіцыйным

			for _, v in ipairs ({'P3', 'P4', 'P5'}) do							-- ачышчае астатнія для гэтай крыніцы
				args[v] = '';
			end

			first_pnum = first_pnum + 2;										-- крыніца павінна выкарыстоўваць пазіцыйнага аўтара і пазіцыйны год
			second_pnum = first_pnum + 1;										-- павялічвае іх для магчымай наступнай пазіцыйнай крыніцы
		end
		
		args.postscript = pframe.args[table.concat ({n, 'ps'})] or '';
		if 'none' == args.postscript then										-- гэта для сумяшчальнасці з іншымі шаблонамі зносак; нічога не робіць
			args.postscript = '';
		end
		args.group = pframe.args.group or '';									-- група зносак
		args.ref = pframe.args[table.concat ({n, 'ref'})] or '';				-- альтэрнатыўная зноска для гэтай крыніцы

		args.page = pframe.args[table.concat ({n, 'p'})] or '';					-- размяшчэнні ў крыніцы для гэтай крыніцы
		args.pages = pframe.args[table.concat ({n, 'pp'})] or '';
		args.pages = ('' ~= args.pages) and hyphen_to_dash (args.pages) or '';
		args.pagebe = pframe.args[table.concat ({n, 'с'})] or '';				-- insource locations for this source
		args.seite = pframe.args[table.concat ({n, 's'})] or '';
		args.seitebig = pframe.args[table.concat ({n, 'S'})] or '';
		args.location = pframe.args[table.concat ({n, 'loc'})] or pframe.args[table.concat ({n, 'at'})] or '';
		args.ignore = ('yes' == pframe.args[table.concat ({n, 'ignore-false-positive'})]) or ('yes' == pframe.args[table.concat ({n, 'ignore-err'})]);

		table.insert (out, core (args));										-- захоўвае рэндэрынг гэтай крыніцы
		
		for k, v in ipairs ({'P1', 'P2', 'P3', 'P4', 'P5'}) do					-- стварае ідэнтыфікатар ЗНОСКІ (FOOTNOTE)
			if '' ~= args[v] then
				table.insert (footnote, args[v]);
			end
		end
		for k, v in ipairs ({'page', 'pages', 'pagebe', 'seite', 'seitebig', 'location'}) do					-- гэта робіцца асобна, каб мы маглі выдаліць зарэзерваваныя сімвалы uri з нумароў старонак з вонкавымі спасылкамі 
			if '' ~= args[v] then
				table.insert (footnote, strip_url (args[v]))
			end
		end
		
		last_index = n;															-- сцяжкі, якія выкарыстоўваюцца для выбару канчатковага пасляслоўя (postscript) з nps або з end_ps
		if '' ~= args.postscript then							
			last_ps = n;
		end
		
		n = n+1;																-- павялічвае для наступнага
	end
	
	local name = table.concat (footnote):gsub ('%s+', ' ');						-- збірае зноску разам і выдаляе лішнія прабелы
	
	args.end_ps = pframe.args.postscript or pframe.args.ps or '.';				-- гэта пасляслоўе (postscript) для ўсяго агулам, а не для асобных крыніц
	if 'none' == args.end_ps then												-- не з’яўляецца арыгінальным значэннем параметра sfnm; дададзена для сумяшчальнасці з іншымі шаблонамі зносак
		args.end_ps = '';
	end

	local result = table.concat ({table.concat (out, '; '), (last_index == last_ps) and '' or  args.end_ps});
	return frame:extensionTag ({name='ref', args={group=args.group, name=name}, content=result});
end


--[[--------------------------< S F N R E F >------------------------------------------------------------------

рэалізуе {{sfnref}}

]]

local function sfnref (frame)
	local args = getArgs (frame);
	local out = {};
	
	for i=1, 5 do																-- атрымлівае першыя пяць аргументаў, калі ёсць пяць аргументаў
		if args[i] then
			out[i] = args[i];
		else
			break;																-- менш за 5 аргументаў, выхад
		end
	end
	
	if 5 == #out then															-- калі мы ўбачылі пяць аргументаў, іх можа быць больш
		local i = 6;															-- ініцыялізуе індэксатар шостым пазіцыйным параметрам
		while args[i] do														-- у выпадку, калі аўтараў занадта шмат, перабірае аўтараў у пошуках года
			if is_year (args[i], args) then										-- калі гэта год
				out[5] = args[i];												-- перазапісвае тое, што было ў args[5], годам
				break;															-- і спыняе пошук
			end
			i = i + 1;															-- павялічвае індэкс
		end
	end
	
	return mw.uri.anchorEncode ('CITEREF' .. table.concat (out));
end


--[[--------------------------< Э К С П А Р Т А В А Н Ы Я   Ф У Н К Ц Ы І >------------------------------------------
]]

return {
	harvard_citation = harvard_citation,
	sfn = sfn,
	sfnm = sfnm,
	sfnref = sfnref,
    target_check = target_check,
	};