Модуль:Sources

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
Документация

Принцип работы модуля

Данный модуль генерирует текст, используемый в сносках, ссылающихся на элемент викиданных.

Функции

Внешние

Внешние функции принимают объекты типа фрейм и предназначены для вызова из других модулей или через функцию парсера {{#invoke:}}.

Прямое обращение к функциям модулей в статьях крайне нежелательно! Используйте для этих целей подходящие шаблоны.

p.renderSource(frame)

Выдаёт вики-текст ссылки на заданный источник для подстановки внутрь сноски или списка литературы. См. шаблоны {{source}} и {{ВД-Источник}}, использующие данную функцию. Поддерживает следующие аргументы:

  • frame.args[1] — анонимный аргумент, задающий идентификатор объекта на викиданных, по которому нужно сгенерировать ссылку. Например, Q20750516.
  • frame.args['ref'] — задаёт метку ref, которую в дальнейшем можно будет использовать в шаблонах типа {{sfn}}.
  • frame.args['ref-year'] — задаёт метку ref-year, которая используется аналогично метке ref.
  • frame.args['part'] — дополнительный аргумент, позволяющий уточнить часть источника, на которую идёт ссылка (например, главу в книге).
  • frame.args['parturl'] — ссылка, которую следует поставить на часть, описанную предыдущим аргументом.
  • frame.args['pages'] — конкретные страницы в источнике, на которые ведётся ссылка.
  • frame.args['url'] — позволяет явно указать, какую ссылку нужно будет проставить на источник.
  • frame.args['volume'] — позволяет явно указать том источника, на который идёт ссылка.
  • frame.args['issue'] — позволяет явно указать выпуск источника, на который идёт ссылка.

Пробрасывание большей части аргументов происходит в utils.copyArgsToSnaks. Сам переданный фрейм сохраняется в p.currentFrame для дальнейшего использования, а на основе переданных аргументов функцией artificialSnaks создаются искусственные снеки, которые ссылаются на источник, указанный в frame.args[1], через свойства P248 (stated in) и P805 (statement is subject of). Затем данные передаются в renderReferenceImpl для дальнейшей обработки.

p.renderReference(frame, currentEntity, reference)

Выдаёт вики-текст готовой сноски на заданный источник. Поддерживает те же аргументы, что и p.renderSource, кроме ref и ref-year. См. шаблоны {{source-ref}} и {{ВД-Сноска}}, использующие данную функцию. Также используется в Модуль:Wikidata для отображения ссылок, указанных возле утверждений на викиданных. Если currentEntity и reference отсутствуют, создаются искусственные снеки с помощью функции artificialSnaks, после чего они передаются в renderReferenceImpl. Если вики-текст для сноски был успешно сгенерирован, он оборачивается в тэг <ref> с помощью frame:extensionTag, при этом имя для сноски генерируется путём хеширования её вики-текста через mw.hash.hashValue. Статьи, с такими сносками помещаются в Категория:Википедия:Статьи с источниками из Викиданных.

Внутренние

tokenizeName(fullName)

Преобразует полное имя в пару {фамилии через пробел, инициалы имён через пробел}. Реализована в виде разбора случаев, которые можно встретить на викиданных:

  1. Фамилия, Имя
  2. Фамилия, Имя Имя
  3. Фамилия Фамилия, Имя
  4. Имя Имя оглы Фамилия
  5. Имя Имя де Фамилия
  6. Имя … Имя Фамилия (хотя бы одно и не более четырёх единичных имён)

Здесь имя, в отличие от фамилии, может являться инициалом. Если ни один из форматов выше не выполнен, возвращает полное имя без изменений.

personNameToAuthorName(fullName)

Преобразует полное имя в формат Фамилия И. О. с помощью tokenizeName.

personNameToResponsibleName(fullName)

Преобразует полное имя в формат И. О. Фамилия с помощью tokenizeName.

getPeopleAsWikitext(context, value, options)

Преобразует список имён value в викитекст в соответствии со списком опций options. В опциях должны быть проставлены следующие поля:

  1. separator — разделитель в списке;
  2. conjunction — разделитель перед последним элементом списка;
  3. format — функция, преобразующая имена к некоторому нормализованному виду (например, personNameToAuthorName);
  4. nolinks — логическое значение, должно быть истинным если проставление ссылок нежелательно;
  5. preferids — логическое значение, должно быть истинным если нужно вернуть id с викиданных, а не имена.

Если в списке больше maxAuthors (на текущий момент 10) людей, заменяет остальных на и др. или его аналоги (если в контексте указан язык, то используется i18nEtAl[context.lang], иначе используется i18nEtAlDefault).

appendProperty(result, context, src, conjunctor, property, url)

Приписывает src[property] к result, разделяя их строкой, записанной в conjunctor. Если возможно, оформляет его ссылкой на src[url].

generateAuthorLinks(context, src)

Возвращает список авторов src.author, оформленный через getPeopleAsWikitext и обрамлённый в <i class="wef_low_priority_links"></i>.

appendTitle(result, context, src)

Дописывает к result строку src.part // src.title либо только src.title если src.part не указан. Если возможно, обрамляет src.part (или src.title если src.part не указан) в src.url.

appendLanguage(result, context, src)

Если context.lang отличается от i18nDefaultLanguage (в нашем разделе русский), то указание об этом приписывается к result с помощью Модуль:Languages в формате {{ref-lang}}.

appendSubtitle(result, context, src)

Дописывает к result строку : src.subtitle если src.subtitle определён.

appendOriginalTitle(result, context, src)

Дописывает к result строку  = src.originaltitle если src.originaltitle определён.

appendPublication(result, context, src)

Дописывает к result строку  // src.publication: src.publication.subtitle если определён src.publication.subtitle, либо  // src.publication если определён только src.publication.

appendEditor(result, context, src)

Дописывает к result строку  / prefix src.editor если определён src.editor, где prefix определяется по context.lang (по умолчанию, под ред.).

appendEdition(result, context, src)

Дописывает к result строку  — src.edition если src.edition определён.

appendPublicationData(result, context, src)

Добавляет к result строку вида  — src.place: src.publisher, src.year. если хотя бы один из указанных параметров определён. Неуказанная часть опускается вместе с соответствующей пунктуацией. В частности, двоеточие ставится только если указано src.place и хотя бы что-то из src.publisher и src.year, запятая ставится только если указаны и src.publisher, и src.year. Тире и точка ставятся если указан хотя бы один из параметров.

appendVolumeAndIssue(result, context, src)

Добавляет к result строку виду  — letter_vol src.volume, letter_iss src.issue. если хотя бы один из указанных параметров определён. Запятая ставится если указаны оба параметра. letter_vol и letter_iss определяются по context.lang (например, Т. и вып. для русских текстов, Vol. и Iss. для английских).

appendPages(result, context, src)

Добавляет к result строку вида  — letter src.pages. если src.pages определён, при этом в качестве разделителя в src.pages, если это диапозон страниц, используется символ «—», а letter определяется исходя из context.lang (например, P. для английского и С. для русского).

appendNumberOfPages(result, context, src)

Добавляет к result строку вида  — src.numberOfPages letter если src.numberOfPages определён. При этом letter определяется из context.lang (p. для английского и с. для русского).

appendBookSeries(result, context, src)

Добавляет к result строку вида  — (src.bookSeries; letter_vol src.bookSeriesVolume, letter_iss src.bookSeriesIssue) если src.bookSeries определено. Точка с запятой ставится только если определено src.bookSeriesVolume или src.bookSeriesIssue, запятая ставится если определены оба параметра. letter_vol и letter_iss определяются из context.lang, аналогично тому, как это делается в appendVolumeAndIssue.

appendBookSeries(result, context, src)

Добавляет к result информацию из src.tirage если тот определён. Формат определяется из context.lang, для английского это  — ed. size: src.tirage, а для русского  — src.tirage экз..

appendIdentifiers(result, context, src)

Добавляет к result идентификаторы ISBN, ISSN, DOI, PMID и arXiv если те определены. Идентификаторы приписываются через тире, более точный формат определён в таблицах options_commas, options_issn, options_doi, options_pmid и options_arxiv.

appendSourceId(result, context, src)

Оборачивает result в <span class="wikidata_cite citetype" data-entity-id="src.sourceId"></span>, где citetyle это src.type если это поле определено и citetype_unknown в противном случае.

appendAccessDate(result, context, src)

Добавляет к result строку виду Проверено dd month yyyy., где dd, month и yyyy берутся из src.accessdate если данное поле определено.

populateUrl(context, src)

Если src.url не определено, но src.sourceId известен, пытается присвоить в src.url ссылку на викитеку.

populateYear(src)

Если src.year не определён, пытается заполнить его из src.dateOfPublication и src.dateOfCreation.

populateTitle(src)

Если src.title не определён, пытается присвоить ему src.url, если и это не получается, то присваивает (unspecified title).

renderSource(context, src)

Внутренняя функция, генерирующая текст, который будет отображаться в сноске. Действует следующим образом:

  1. Записывает src.lang в context.lang (или i18nDefaultLanguage если src.lang записать не получилось).
  2. Вызывает populateUrl, populateTitle и populateYear.
  3. Заводит переменную result, изначально равную generateAuthorLinks(context, src).
  4. .Последовательно применяет к result функции appendTitle—appendAccessDate, при этом блок appendEditor—appendAccessDate дополнительно обрамляется в <span class="wef_low_priority_links"></span>

artificialSnaks(args)

Создаёт искусственные снеки, которые ссылаются на источник с идентификатором frame.args[1] через свойства P248 (stated in) и P805 (statement is subject of), а также пробрасывает в них аргументы (том, выпуск и т. д.).

Служебные подмодули

Используемые параметры Викиданных

Свойство Комментарий
автор (P50)
имя автора (строка) (P2093)
язык произведения или названия (P407)
язык оригинала фильма или телешоу (P364)
раздел, стих или параграф (P958) используется для указания названия статьи в энциклопедии
название (P1476) если требуется переопределить название из метки элемента
подзаголовок (P1680)
доступен по URL (P953)
архивный URL (P1065)
URL-ссылка на источник (P854)
опубликовано в (P1433)
номер издания (P393)
издатель (P123)
место публикации (P291)
редактор (P98)
страницы (P304)
количество страниц (P1104)
том (P478)
выпуск (P433)
дата основания/создания/возникновения (P571)
дата публикации (P577)
ISBN-13 (P212)
ISBN-10 (P957)
ISSN (P236)
порядковый номер (P1545)
идентификатор arXiv (P818)
JSTOR (P888)

Тесты [ править ]

All tests passed.

test_Sources:

Text Expected Actual
✔ {{#invoke:Sources | renderSource | Q20750516}} Президент Российской Федерации Указ Президента Российской Федерации от 15 января 1992 г. № 23 «О Генеральном директоре Агентства федеральной безопасности Российской Федерации и Министре внутренних дел Российской Федерации» // Собрание законодательства Российской Федерации — 1992. Президент Российской Федерации Указ Президента Российской Федерации от 15 января 1992 г. № 23 «О Генеральном директоре Агентства федеральной безопасности Российской Федерации и Министре внутренних дел Российской Федерации» // Собрание законодательства Российской Федерации — 1992.
✔ {{#invoke:Sources | renderSource | Q21683979}} Advances in Cryptology — EUROCRYPT 2004 (англ.): International Conference on the Theory and Applications of Cryptographic Techniques, Interlaken, Switzerland, May 2-6, 2004. Proceedings / C. Cachin, J. L. CamenischSpringer, Berlin, Heidelberg, 2004. — 630 p. — ISBN 978-3-540-21935-4doi:10.1007/B97182 Advances in Cryptology — EUROCRYPT 2004 (англ.): International Conference on the Theory and Applications of Cryptographic Techniques, Interlaken, Switzerland, May 2-6, 2004. Proceedings / C. Cachin, J. L. CamenischSpringer, Berlin, Heidelberg, 2004. — 630 p. — ISBN 978-3-540-21935-4doi:10.1007/B97182
✔ {{#invoke:Sources | renderSource | Q21683981}} Nguyen P. Q. Can We Trust Cryptographic Software? Cryptographic Flaws in GNU Privacy Guard v1.2.3 (англ.) // Advances in Cryptology — EUROCRYPT 2004: International Conference on the Theory and Applications of Cryptographic Techniques, Interlaken, Switzerland, May 2-6, 2004. Proceedings / C. Cachin, J. L. CamenischSpringer, Berlin, Heidelberg, 2004. — P. 555—570. — 630 p. — ISBN 978-3-540-21935-4doi:10.1007/978-3-540-24676-3_33 Nguyen P. Q. Can We Trust Cryptographic Software? Cryptographic Flaws in GNU Privacy Guard v1.2.3 (англ.) // Advances in Cryptology — EUROCRYPT 2004: International Conference on the Theory and Applications of Cryptographic Techniques, Interlaken, Switzerland, May 2-6, 2004. Proceedings / C. Cachin, J. L. CamenischSpringer, Berlin, Heidelberg, 2004. — P. 555—570. — 630 p. — ISBN 978-3-540-21935-4doi:10.1007/978-3-540-24676-3_33
✔ {{#invoke:Sources | renderSource | Q21725400}} Eichenauer J., Lehn J. A non-linear congruential pseudo random number generator (англ.) // Statistische HefteSpringer Berlin Heidelberg, 1986. — Vol. 27, Iss. 1. — P. 315—326. — ISSN 0932-5026doi:10.1007/BF02932576 Eichenauer J., Lehn J. A non-linear congruential pseudo random number generator (англ.) // Statistische HefteSpringer Berlin Heidelberg, 1986. — Vol. 27, Iss. 1. — P. 315—326. — ISSN 0932-5026doi:10.1007/BF02932576
✔ {{#invoke:Sources | renderSource | Q21725116}} Menezes A. J., Oorschot P. v., Vanstone S. A. Handbook of Applied Cryptography (англ.)CRC Press, 1996. — 816 p. — (Discrete Mathematics and Its Applications) — ISBN 978-0-8493-8523-0 Menezes A. J., Oorschot P. v., Vanstone S. A. Handbook of Applied Cryptography (англ.)CRC Press, 1996. — 816 p. — (Discrete Mathematics and Its Applications) — ISBN 978-0-8493-8523-0
✔ {{#invoke:Sources | renderSource | Q27450585}} Введение в криптографию / под ред. В. В. ЯщенкоМ.: МЦНМО, 2000. — 271 с. — ISBN 978-5-900916-26-2 Введение в криптографию / под ред. В. В. ЯщенкоМ.: МЦНМО, 2000. — 271 с. — ISBN 978-5-900916-26-2


local p = {}

local langs = require('Module:Languages')
local utils = require('Module:Sources/utils')

local i18nDefaultLanguage = 'ru'

local i18nEtAlDefault = ' et al.'
local i18nEtAl = {
	ru	= ' и др.',
}

local i18nEditors = {
	fr	= '',
	de	= 'Hrsg.: ',
	es	= '',
	en	= '',
	it	= '',
	ru	= 'под ред. ',
}

local i18nTranslators = {
	fr	= '',
	de	= '',
	es	= '',
	en	= '',
	it	= '',
	ru	= 'пер. ',
}

local i18nVolume = {
    de  = 'Vol.',
	fr	= 'Vol.',
	es	= 'Vol.',
	en	= 'Vol.',
	it	= 'Vol.',
	ru	= 'Т.',
}

local i18nIssue = {
	en	= 'Iss.',
	ru	= 'вып.',
}

local i18nPages = {
	fr = 'P.',
	de = 'S.',
	es = 'P.',
	en = 'P.',
	it = 'P.',
	ru = 'С.',
}

local i18nNumberOfPages = {
	en = 'p.',
	ru = 'с.',
}

local i18nTirage = {
	en	= 'ed. size: %d',
	ru	= '%d экз.',
}


local monthg = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', "сентября", "октября", "ноября", "декабря"}

local PREFIX_CITEREF = "CITEREF_";

-- Returns formatted pair {Family name(s), First name(s)}
local function tokenizeName( fullName )
	local start = '^%s*' -- matches beginning of the string + arbitrary number of spaces
	local finish = '%s*$' -- matches end of the string + arbitrary number of spaces
	local comma = '\,%s+' -- matches comma + single or more spacing character
	local space = '%s+' -- matches single or more spacing character
	local name = '(%a[%a\-\']*)\.?' -- matches single name, have to start with letter, can contain apostrophe and hyphen, may end with dot
	local surname = '(%a[%a\-\']*)' -- same as name, but can't end with dot
	
	local f, i = mw.ustring.match(fullName, start .. surname .. comma .. name .. finish)
	if f then
		mw.log('tokenizeName: «' .. fullName .. '»: have «Fa, Im» match')
		return {f, mw.ustring.sub( i, 1, 1 ) .. '.'}
	end
	
	local f, i, o = mw.ustring.match(fullName, start .. surname .. comma .. name .. space .. name .. finish)
	if f then
		mw.log( 'tokenizeName: «' .. fullName .. '»: have «Fa, Im Ot» match')
		return {f, mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;'
				.. mw.ustring.sub( o, 1, 1 ) .. '.'}
	end

	local f1, f2, i = mw.ustring.match(fullName, start .. surname .. space .. surname .. comma .. name .. finish)
	if f1 then
		mw.log('tokenizeName: «' .. fullName .. '»: have «Fa Fa, Im» match')
		return {f1 .. '&nbsp;' .. f2, mw.ustring.sub( i, 1, 1 ) .. '.'}
	end
	
	local i, o, f = mw.ustring.match(fullName, start .. name .. space .. name .. space .. 'оглы' .. space .. surname .. finish)
	if f then
		mw.log('tokenizeName: «' .. fullName .. '»: have «Im Ot оглы Fa» match')
		return {f, mw.ustring.sub(i, 1, 1) .. '.&nbsp;' .. mw.ustring.sub(o, 1, 1) .. '.'}
	end

	local i1, i2, f = mw.ustring.match(fullName, start .. name .. space .. name .. space .. 'de' .. space .. surname .. finish)
	if f then
		mw.log('tokenizeName: «' .. fullName .. '»: have «Im Im de Fa» match')
		return {f, mw.ustring.sub( i1, 1, 1 ) .. '.&nbsp;' .. mw.ustring.sub( i2, 1, 1 ) .. '.'}
	end
	
	-- Try matching k names + surname
	for k = 1, 4 do
		pattern = start .. string.rep(name .. space, k) .. surname .. finish
		matched = {mw.ustring.match(fullName, pattern)}
		if #matched ~= 0 then
			mw.log('tokenizeName: «' .. fullName .. '»: have «Im (x' .. k .. ') Fa» match')
			for i = 1, k do
				matched[i] = mw.ustring.sub(matched[i], 1, 1)
			end
			return {matched[k + 1], table.concat(matched, '.&nbsp;', 1, k) .. '.'}
		end
	end
	
	mw.log('Unmatched any pattern: «' .. fullName .. '»')
	return {fullName}
end

local function personNameToAuthorName( fullName )
	if not fullName then return fullName end
	local tokenized = tokenizeName(fullName)
	if #tokenized == 1 then
		return tokenized[1]
	else
		return tokenized[1] .. '&nbsp;' .. tokenized[2]
	end
end

local function personNameToResponsibleName( fullName )
	if not fullName then return fullName end
	local tokenized = tokenizeName(fullName)
	if #tokenized == 1 then
		return tokenized[1]
	else
		return tokenized[2] .. '&nbsp;' .. tokenized[1]
	end
end


local options_commas = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false };
local options_commas_short = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false, short = true };
local options_commas_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = true, preferids = false };
local options_commas_it = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false };
local options_commas_it_short = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false, short = true };
local options_commas_it_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = true , preferids = false };
local options_citetypes = { separator = ' ', conjunction = ' ', format = function( src ) return 'citetype_' .. src end, nolinks = true , preferids = true };

local options_commas_authors = { separator = ', ', conjunction = ', ', format = personNameToAuthorName, nolinks = false, preferids = false };
local options_commas_responsible = { separator = ', ', conjunction = ', ', format = personNameToResponsibleName, nolinks = false, preferids = false };

local options_arxiv = { separator = '; ', conjunction = '; ', format = function( id ) return '[http://arxiv.org/abs/' .. id .. ' arXiv:' .. id .. ']' end, nolinks = true, preferids = false };
local options_doi = { separator = '; ', conjunction = '; ', format = function( doi ) return '[http://dx.doi.org/' .. doi .. ' doi:' .. doi .. ']' end, nolinks = true, preferids = false };
local options_issn = { separator = '; ', conjunction = '; ', format = function( issn ) return '[https://www.worldcat.org/issn/' .. issn .. ' ' .. issn .. ']' end, nolinks = true, preferids = false };
local options_pmid = { separator = '; ', conjunction = '; ', format = function( pmid ) return '[https://www.ncbi.nlm.nih.gov/pubmed/?term=' .. pmid .. ' PMID:' .. pmid .. ']' end, nolinks = true, preferids = false };

local function getPersonNameAsLabel( context, entityId, providedLabel, options )
	-- would custom label provided we don't need to check entity at all
	if ( not utils.isEmpty( providedLabel ) ) then
		mw.log( 'Custom label provided for ' .. entityId );
		return options.format( providedLabel );
	end

	local entity = utils.getEntity( context, entityId );
	if ( not entity ) then return '\'\'(entity ' .. entityId .. ' is missing)\'\'' end;

	local personName = nil;
	-- support only labels so far
	if ( entity.labels[ context.lang ] ) then
		personName = entity.labels[ context.lang ].value;
		mw.log('Got person name of ' .. entityId .. ' from label: «' .. personName .. '»' )
	end

	if ( not utils.isInstanceOf( entity, 'Q5' ) ) then
		mw.log( 'Entity ' .. entityId .. ' is not a person' );
		return personName;
	end

	if ( utils.isEmpty( personName ) ) then
		return '\'\'(not translated to ' .. context.lang .. ')\'\'';
	else
		return options.format( personName );
	end
end

local function getPersonNameAsWikitext( context, entityId, customLabel, options )
	local personName = getPersonNameAsLabel( context, entityId, customLabel, options);
	if ( personName == nil ) then
		return nil;
	end

	local link = utils.getElementLink( context, entityId, nil );
	return utils.wrapInUrl( link, personName );
end

local function getPeopleAsWikitext( context, value, options )
	if type( value ) == 'string' then
		return options.format( value )
	elseif type( value ) == 'table' then
		if value.id then
			-- this is link
			if options.preferids then
				return value.id
			else
				if options.nolinks then
					return getPersonNameAsLabel( context, value.id, value.label, options )
				else
					return getPersonNameAsWikitext( context, value.id, value.label, options )
				end
			end
		end
		
		local maxAuthors = 10 -- need some restrictions, as some publications have enormous amount of authors (e.g. 115 authors of Q68951544)
		local resultList = {}
		for i, tableValue in pairs( value ) do
			local nextWikitext = getPeopleAsWikitext( context, tableValue, options )
			if not utils.isEmpty( nextWikitext ) then
				table.insert( resultList, nextWikitext )
				if #resultList == maxAuthors + 1 then
					-- keep one more to indicate that there are too many
					break
				end
			end
		end

		local resultWikitext = ''
		for i, wikitext in pairs( resultList ) do
			if i == maxAuthors + 1 then
				resultWikitext = resultWikitext .. ( i18nEtAl[ context.lang ] or i18nEtAlDefault )
				break;
			end
			if i ~= 1 then
				resultWikitext = resultWikitext .. ', '
			end
			resultWikitext = resultWikitext .. wikitext
		end

		return resultWikitext
	end

	return options.format( '(unknown type)' )
end

local function generateAuthorLinks(context, src)
	local result = ''
	if src.author then
		result = getPeopleAsWikitext( context, src.author, options_commas_authors )
		result = '<i class="wef_low_priority_links">' .. result .. '</i> '
	end
	return result
end

local function appendProperty(result, context, src, conjunctor, property, url)
	if src[property] then
		if url and src[url] then
			result = result .. conjunctor .. utils.wrapInUrl( src[url], utils.toString( context, src[property], options_commas_nolinks ) )
		else
			result = result .. conjunctor .. utils.toString( context, src[property], options_commas )
		end
	end
	return result
end

local function appendTitle(result, context, src)
	conjunctor = ''
 	if src.part then
 		result = appendProperty(result, context, src, '', 'part', 'parturl')
 		conjunctor = ' // '
 	end
 	result = appendProperty(result, context, src, conjunctor, 'title', 'url')
 	return result
end

local function appendLanguage(result, context, src)
	if context.lang ~= i18nDefaultLanguage then
		result = result .. langs.list_ref(p.currentFrame:newChild{ args = {context.lang} })
	end
	return result
end

local function appendSubtitle(result, context, src)
	return appendProperty(result, context, src, ': ', 'subtitle')
end

local function appendOriginalTitle(result, context, src)
	return appendProperty(result, context, src, ' = ', 'originaltitle')
end

local function appendPublication(result, context, src)
	if src.publication then
		if type( src.publication.title or '') ~= 'string' then
			error('type of src.publication.title is not string but ' .. type( src.publication.title ) )
		end
		
		result = result .. ' // ' .. utils.toString( context, src.publication, options_commas_it_short )
		if src.publication.subtitle then
			result = result .. ': ' .. utils.toString( context, src.publication.subtitle, options_commas_it_short )
		end
	end
	return result
end

local function appendEditor(result, context, src)
	if src.editor or src.translator then
		result = result .. ' / '
		if src.editor then
			local prefix = i18nEditors[ context.lang ] or i18nEditors[ i18nDefaultLanguage ]
			result = result .. prefix .. getPeopleAsWikitext( context, src.editor, options_commas_responsible )
			if src.translator then
				result = result .. ', '
			end
		end
		if src.translator then
			local prefix = i18nTranslators[ context.lang ] or i18nTranslators[ i18nDefaultLanguage ]
			result = result .. prefix .. getPeopleAsWikitext( context, src.translator, options_commas_responsible )
		end
	end
	return result
end

local function appendEdition(result, context, src)
	return appendProperty(result, context, src, ' — ', 'edition')
end

local function appendPublicationData(result, context, src)
	if src.place or src.publisher or src.year then
		result = result .. ' — '
		if src.place then
			result = result .. utils.toString( context, src.place, options_commas_short )
			if src.publisher or src.year then
				result = result .. ': '
			end
		end
		if src.publisher then
			result = result .. utils.toString( context, src.publisher, options_commas_short )
			if src.year then
				result = result .. ', '
			end
		end
		if src.year then
			result = result .. utils.toString( context, src.year, options_commas )
		end
		result = result .. '.';
	end
	return result
end

local function appendVolumeAndIssue(result, context, src)
 	if src.volume or src.issue then
 		result = result .. ' — '
		local letter_vol = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]
		local letter_iss = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ]
		if src.volume then
			result = appendProperty(result, context, src, letter_vol .. '&nbsp;', 'volume')
			result = appendProperty(result, context, src, ', ' .. letter_iss .. '&nbsp;', 'issue')
		else
			result = appendProperty(result, context, src, letter_iss .. '&nbsp;', 'issue')
		end
		result = result .. '.'
 	end
 	return result
end

local function appendPages(result, context, src)
	if src.pages then
		local letter = i18nPages[ context.lang ] or i18nPages[ i18nDefaultLanguage ]
		local strPages = utils.toString( context, src.pages, options_commas )
		strPages = mw.ustring.gsub( strPages, '[-—]', '—' );
		result = result .. ' — ' .. letter .. '&nbsp;' .. strPages .. '.'
	end
	return result
end

local function appendNumberOfPages(result, context, src)
	if src.numberOfPages then
		local letter = i18nNumberOfPages[ context.lang ] or i18nNumberOfPages[ i18nDefaultLanguage ]
		result = appendProperty(result, context, src, ' — ', 'numberOfPages') .. '&nbsp;' .. letter
	end
	return result
end

local function appendBookSeries(result, context, src)
	if src.bookSeries then
		result = appendProperty(result, context, src, ' — (', 'bookSeries')
	 	if src.bookSeriesVolume or src.bookSeriesIssue then
	 		result = result .. '; '
	 		local letter_vol = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]
	 		local letter_iss = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ]
			if ( src.bookSeriesVolume ) then
				result = appendProperty(result, context, src, letter_vol .. '&nbsp;', 'bookSeriesVolume')
				result = appendProperty(result, context, src, ', ' .. letter_iss .. '&nbsp;', 'bookSeriesIssue')
			else
				result = appendProperty(result, context, src, letter_iss .. '&nbsp;', 'bookSeriesIssue')
			end
	 	end
		result = result .. ')'
	end
	return result
end

local function appendTirage(result, context, src)
	if src.tirage then
		local tirageTemplate = i18nTirage[ context.lang ] or i18nTirage[ i18nDefaultLanguage ]
		result = result .. ' — ' .. utils.toString( context, src.tirage, { separator = '; ', conjunction = '; ', format = function( data ) return mw.ustring.format(tirageTemplate, data) end } )
	end
	return result
end

local function appendIdentifiers(result, context, src)
	if src.isbn  then result = result .. ' — ISBN ' .. utils.toString( context, src.isbn, options_commas )	end
	if src.issn  then result = result .. ' — ISSN ' .. utils.toString( context, src.issn, options_issn )	end
	if src.doi   then result = result .. ' — ' .. utils.toString( context, src.doi, options_doi )			end
	if src.pmid  then result = result .. ' — ' .. utils.toString( context, src.pmid, options_pmid )			end
	if src.arxiv then result = result .. ' — ' .. utils.toString( context, src.arxiv, options_arxiv )		end
	return result
end

local function appendSourceId(result, context, src)
	if src.sourceId then
		local citetype = src.type and utils.toString(context, src.type, options_citetypes) or 'citetype_unknown'
		result = '<span class="wikidata_cite ' .. citetype .. '" data-entity-id="' .. utils.getSingle(src.sourceId) .. '">' .. result .. '</span>'
	end
	return result
end

local function appendAccessDate(result, context, src)
	if src.accessdate then
			local date = utils.getSingle(src.accessdate)
			local pattern = "(%-?%d+)%-(%d+)%-(%d+)T";
			local y, m, d = mw.ustring.match(date, pattern)
			y, m, d = tonumber(y), tonumber(m), tonumber(d)
			result = result .. " <small>Проверено " .. tostring(d) .. " " .. monthg[m]  .. " " .. tostring(y) .. ".</small>"
	end
	return result
end

local function populateUrl(context, src)
	if src.sourceId and not src.url then
		local entity = utils.getEntity(context, src.sourceId)
		if entity.sitelinks and entity.sitelinks[context.lang .. 'wikisource'] then
			src.url = ':' .. context.lang .. ':s:' .. entity.sitelinks[context.lang .. 'wikisource'].title
		end
	end
end

local function populateYear(src)
	if not src.year and src.dateOfPublication then
		local date = utils.getSingle(src.dateOfPublication)
		src.year = mw.ustring.sub(date, 2, 5)
	end
	if not src.year and src.dateOfCreation then
		local date = utils.getSingle(src.dateOfCreation)
		src.year = mw.ustring.sub(date, 2, 5)
	end
end

local function populateTitle(src)
	src.title = src.title or utils.getSingle(src.url) or '\'\'(unspecified title)\'\''
end

local function renderSource(context, src)
	options_commas_authors.format = personNameToAuthorName
	options_commas_responsible.format = personNameToResponsibleName

	context.lang = utils.getLangCode(utils.getSingle(src.lang)) or i18nDefaultLanguage

	utils.preprocessPlaces(src, context.lang)

	populateUrl(context, src)
	populateTitle(src)
	populateYear(src)

	local result = generateAuthorLinks(context, src)
	result = appendTitle(result, context, src)
	result = appendLanguage(result, context, src)
	result = appendSubtitle(result, context, src)
	result = appendOriginalTitle(result, context, src)
	result = appendPublication(result, context, src)
	
	result = result .. '<span class="wef_low_priority_links">'
	result = appendEditor(result, context, src) -- Might take current editor instead of actual. Use with caution
	result = appendEdition(result, context, src)
	result = appendPublicationData(result, context, src)
	result = appendVolumeAndIssue(result, context, src)
	result = appendPages(result, context, src)
	result = appendNumberOfPages(result, context, src)
	result = appendBookSeries(result, context, src)
	result = appendTirage(result, context, src)
	result = appendIdentifiers(result, context, src)
	result = appendSourceId(result, context, src)
	result = appendAccessDate(result, context, src)
	result = result .. '</span>'

	return result
end

local function renderReferenceImpl(currentEntity, reference, refAnchor, refAnchorYear)
	if not reference.snaks then
		return nil
	end

	-- контекст, содержит также кеш элементов
	local context = {
		cache = {}
	}

	-- данные в простом формате, согласованном с модулями формирования библиографического описания
	local data = {}

    -- забрать данные из reference
    utils.populateDataFromClaims(context, nil, reference.snaks, data)

	utils.expandSpecials(context, currentEntity, reference, data)

	local sourceEntity = nil
	if data.sourceId then
		sourceEntity = utils.getEntity(context, data.sourceId)
		if sourceEntity then
			utils.populateSourceDataImpl(context, sourceEntity, data)
		end
	end

	if data.publication then
		utils.expandPublication(context, sourceEntity, data)
	end

	utils.expandBookSeries(context, data)

	if next(data) == nil then
		return nil
	end

	local rendered = renderSource(context, data)
	if mw.ustring.len(rendered) == 0 then
		return nil
	end

	if refAnchor then
		local anchorValue = 'CITEREF' .. refAnchor .. (utils.coalesce(refAnchorYear, data.year) or '')
		rendered = '<span class="citation" id="' .. mw.uri.anchorEncode(anchorValue) .. '">' .. rendered .. '</span>'
	end

	return rendered
end

local function artificialSnaks(args)
	local snaks = {}
	if args[1] then
		entityId = mw.text.trim(args[1])
		snaks.P248 = {utils.toWikibaseEntityIdSnak("P248", entityId)}
		snaks.P805 = {utils.toWikibaseEntityIdSnak("P805", entityId)}
	end
	utils.copyArgsToSnaks(args, snaks)
	return mw.wikibase.getEntity(), {snaks = snaks}
end

function p.renderReference(frame, currentEntity, reference)
	p.currentFrame = frame

	-- template call
	if frame and not currentEntity and not reference then
		currentEntity, reference = artificialSnaks(frame.args)
	end

	local rendered = renderReferenceImpl(currentEntity, reference)
	if not rendered then
		return ''
	end
	-- Про выбор алгоритма хеширования см. [[Модуль:Hash]]. Знак подчёркивания в начале позволяет
	-- исключить ошибку, когда имя сноски — чисто числовое значение, каковыми иногда бывают хеши.
	return frame:extensionTag('ref', rendered, {name = '_' .. mw.hash.hashValue('fnv164', rendered)}) .. '[[Category:Википедия:Статьи с источниками из Викиданных]]'
end

function p.renderSource(frame)
	p.currentFrame = frame
	currentEntity, reference = artificialSnaks(frame.args)
	return renderReferenceImpl(currentEntity, reference, frame.args['ref'], frame.args['ref-year'])
end

return p;