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

require('strict')

local p = {}
local source = require('Модуль:WDSource')
local formatter = require('Модуль:WDFormat')
local f = formatter.f
local wdLang = require('Модуль:WDLang')

local wikidata = require('Модуль:WDCommon')

local function langVisible(source)
	if not source.langCode then
		return false
	end

	local currentLangObj = mw.getContentLanguage()
	local currentLang = currentLangObj:getCode()
	if currentLang == source.langCode.value then
		return false
	end
	return true
end

local function mainAuthorVisible(source)
	if not source.authors then
		return false
	end
	if table.getn(source.authors) == 0 or table.getn(source.authors) > 3 then
		return false
	end
	return true
end

local function mainPublishedInAuthorVisible(source)
	if not source.publishedInAuthors then
		return false
	end
	if table.getn(source.publishedInAuthors) == 0 or table.getn(source.publishedInAuthors) > 3 then
		return false
	end
	return true
end

local function authorsVisible(source)
	if not source.authors then
		return false
	end
	if table.getn(source.authors) == 1 then
		return false
	end
	return true
end

local function originLangVisible(source)
	if not source.originLang then
		return false
	end
	if source.lang.entity == source.originLang.entity then
		return false
	end
	return source.originLang.entity ~= nil
end

local function publishedInOriginLangVisible(source)
	if not source.publishedInOriginLang or not source.publishedInLang then
		return false
	end
	if source.publishedInLang.entity == source.publishedInOriginLang.entity then
		return false
	end
	return source.publishedInOriginLang.entity ~= nil
end

local function publisherVisible(source)
	if source.publishedInIsPeriodic and not source.workVersion then
		if source.publisher.retrieved then
			return false
		end
	end
	return true
end

local function locationVisible(source)
	if source.publishedInIsPeriodic and not source.workVersion then
		if source.location.retrieved then
			return false
		end
	end
	return true
end

local function ruwikiAnchor(source)
	if not source.ref then
		return nil
	end

	local anchor = 'CITEREF' .. source.ref.value
    local date = source.date
    if date and date.value and date.value.year then
        anchor = anchor .. date.value.year
    end
    return anchor
end

local function formatArchiveUrl(source, processedData, result)
	local archiveUrl = source.archiveUrl
	if type(archiveUrl) == 'table' then
		archiveUrl = archiveUrl.value
	end
	local archiveDate = source.archiveDate
	if type(archiveDate) == 'table' and archiveDate.value then
		archiveDate = archiveDate.value
	end

	local langObj = mw.getLanguage(processedData.langCode)
	local dateStr = langObj:formatDate('j xg Y', archiveDate.timestamp)

	result.text = 'арх. ' .. dateStr
	result.wikitext = '[' .. archiveUrl .. ' <abbr title="Архивная копия от указанной даты">арх.</abbr>]&nbsp;' .. dateStr
end

local function formatDateMonth(source, processedData, result)
	local langObj
	if processedData.langCode then
		langObj = mw.getLanguage(processedData.langCode)
	else
		langObj = mw.getContentLanguage()
	end

	local date = langObj:formatDate('j xg', source.date.value.timestamp)
	result.text = date
	result.wikitext = '<span class="nowrap">' .. date .. '</span>'
end

local function formatPartsCount(source, processedData, result, state)
	local text
	local prefixByLang = {
		ru = 'в',
		uk = 'у',
		en = 'in',
		be = 'у',
		de = 'in',
		fr = 'en',
		el = 'σε',
		es = 'en',
		ar = 'في',
		no = 'i',
	}

	local prefix = prefixByLang[processedData.langCode]
	if not prefix then
		prefix = ''
	else
		prefix = prefix .. '&nbsp;'
	end
	text = prefix .. tostring(result.text)

	if (processedData.capitalize and state.groupEmpty) or processedData.forceCapitalize then
		text = mw.ustring.gsub(text, '^%l', mw.ustring.upper)
	end

	result.text = text
	result.wikitext = text
end

local function formatMainAuthorInitialsToEnd(source, processedData, result)
	local text = result.text
	local initials, familyName = mw.ustring.match(text, '^(.+%.) ([^%. ]+)$')
	if not initials or not familyName then
		return false
	end
	text = familyName .. '&nbsp;' .. initials
	result.text = text
	result.wikitext = text
end

local function formatTranslatedFrom(source, processedData, result)
	local abbrByLang = {
		el = 'Μετάφρ. από τα',
		ru = 'Пер.&nbsp;с',
		uk = 'Пер.&nbsp;з',
		en = 'Transl.&nbsp;from',
		de = 'Übers. aus dem',
	}
	local supported = abbrByLang[processedData.langCode]
	if supported then
		result.text = supported
		result.wikitext = result.text
	else
		result.text = result.text .. ':'
		result.wikitext = result.text
	end
end

local function formatEditorsLead(source, processedData, result, state)
	local abbrByLang = {
		el = 'επιμ.',
		en = 'ed.',
		es = 'ed.',
		de = 'hrsg.',
		uk = 'ред.',
		no = 'hrsg.',
	}
	local text = abbrByLang[processedData.langCode]
	if (text and state.groupEmpty and processedData.capitalize) or processedData.forceCapitalize then
		text = mw.ustring.gsub(text, '^%l', mw.ustring.upper)
	end
	if text then
		result.text = text
		result.wikitext = text
	end
end

local function formatEditorsInChiefLead(source, processedData, result, state)
	local abbrByLang = {
		el = 'αρχισυντ.',
		en = 'ed.&nbsp;in&nbsp;ch.',
		es = 'ed.&nbsp;jefe',
		de = 'ch.-Red.',
		uk = 'гол.&nbsp;ред.',
		no = 'ch.-Red.',
	}
	local text = abbrByLang[processedData.langCode]
	if (text and state.groupEmpty and processedData.capitalize) or processedData.forceCapitalize then
		text = mw.ustring.gsub(text, '^%l', mw.ustring.upper)
	end
	if text then
		result.text = text
		result.wikitext = text
	end
end

local function formatTranslatorsLead(source, processedData, result, state)
	local abbrByLang = {
		el = 'μεταφρ.',
		en = 'transl.',
		es = 'trad.',
		de = 'übers.',
	}
	local text = abbrByLang[processedData.langCode]
	if (text and state.groupEmpty and processedData.capitalize) or processedData.forceCapitalize then
		text = mw.ustring.gsub(text, '^%l', mw.ustring.upper)
	end
	if text then
		result.text = text
		result.wikitext = text
	end
end

local function formatIllustratorsLead(source, processedData, result, state)
	local abbrByLang = {
		el = 'εικον.',
		en = 'illus.',
	}
	local text = abbrByLang[processedData.langCode]
	if (text and state.groupEmpty and processedData.capitalize) or processedData.forceCapitalize then
		text = mw.ustring.gsub(text, '^%l', mw.ustring.upper)
	end
	if text then
		result.text = text
		result.wikitext = text
	end
end

local function formatPublishedInLink(source, processedData, result)
	if result.linked then
		return
	end

	if source.publishedInUrl and source.publishedInUrl.retrieved and mw.wikibase.getSitelink(processedData.fieldTable.entity) then
		return
	end
	f.link(source, processedData, result)
end

local function formatNonNumber(source, processedData, result)
	if string.match(result.text, '^[%d%-]+$') then
		f.numericalRanges(source, processedData, result)
		return
	elseif string.match(result.text, '^%d[%.%-%s]') then
		f.dash(source, processedData, result)
		return
	end

	if processedData.langCode == 'ru' then
		result.text = '«' .. result.text .. '»'
		result.wikitext = result.text
	end
end

local function replaceAuthors(source, processedData, others)
	local othersText
	local delimiter
	if processedData.langCode == 'ru' then
		othersText = '[и др.]'
		delimiter = ' '
	else
		-- the delimiter would probably be language-dependent
		othersText = '[' .. wikidata.abbr('Q311624', processedData.langCode) .. ']'
		delimiter = ' '
	end
	others = string.gsub(others, '%[%[d:([^%]]+)|[^%]]+%]%]', '(%1)')
	others = string.gsub(others, '<[^>]+>', '')
	local othersWikitext = delimiter .. '<abbr title="' .. others .. '">' .. othersText .. '</abbr>'
	return othersText, othersWikitext
end

local function replaceExceeded(source, processedData, others)
	local othersWikitext = ' и <abbr title="' .. others .. '">др.</abbr>'
	return 'и др.', othersWikitext
end

local function localTitleDelimiter(source)
	if source.issue then
		return ' :&nbsp;'
	else
		return '&nbsp;'
	end
end

local GostProfile = {
	tag = {
		name = 'span',
		classes = { 'citation' },
		attr = {
			-- ref
			id = ruwikiAnchor,
		},
	},
	groups = {
		{
			parts = {
				{
					field = { 'authors', 1 },
					cond = mainAuthorVisible,
					format = { formatMainAuthorInitialsToEnd, f.personReversedNoComma, f.wikilink, f.wikidata },
					tag = {
						name = 'i',
					},
				},
				{
					conflicts = { 'authors', 'publishedInIsHosting' },
					field = { 'publishedInAuthors', 1 },
					cond = mainPublishedInAuthorVisible,
					format = { formatMainAuthorInitialsToEnd, f.personReversedNoComma, f.wikilink, f.wikidata },
					tag = {
						name = 'i',
					},
				},
				{
					delimiter = ' ',
					ensureEnds = '.',
					field = 'title',
					urlField = 'url',
					capitalize = true,
					format = { f.dash, f.link, f.wikisource, f.resolveWikilink },
				},
				{
					conflicts = 'title',
					delimiter = ' ',
					ensureEnds = '.',
					capitalize = true,
					value = '(заглавие?)',
					tag = {
						name = 'span',
						classes = { 'error' },
					},
				},
				{
					delimiter = ' :&nbsp;',
					field = 'subtitle',
					format = { f.dash },
				},
				{
					delimiter = ' ',
					field = 'contentType',
					format = { f.squareBrackets },
				},
				{
					delimiter = ' = ',
					cond = originLangVisible,
					field = 'origin',
					urlField = 'originUrl',
					format = { f.dash, f.link, f.resolveWikilink },
				},
				{
					delimiter = ' :&nbsp;',
					depends = 'origin',
					cond = originLangVisible,
					field = 'originSubtitle',
					format = { f.dash },
				},
			}
		},
		{
			delimiter = ' :&nbsp;',
			parts = {
				{
					field = 'lang',
					prefix = '&lsqb;',
					cond = langVisible,
					forceLang = 'default',
					capitalize = false,
					limits = {
						max = 2,
						cutTo = 2,
						replaceBy = replaceExceeded,
					},
					format = { f.abbrWithHint },
					suffix = '&rsqb;',
				},
				{
					depends = 'archiveDate',
					field = 'archiveUrl',
					forceLang = 'default',
					format = { formatArchiveUrl, f.squareBrackets },
				},
				{
					conflicts = 'isVolume',
					delimiter = ' : ',
					field = 'partsCount',
					capitalize = false,
					format = { f.quantity, formatPartsCount },
				},
			},
		},
		{
			parts = {
				{
					depends = 'isVolume',
					ensureEnds = '.',
					delimiter = ' ',
					field = 'partsCount',
					format = { f.quantity, formatPartsCount },
				},
				{
					ensureEnds = '.',
					delimiter = ' ',
					depends = { 'isVolume', 'volume' },
					entity = 'Q1238720',
					capitalize = true,
					format = { f.abbr },
				},
				{
					delimiter = '&nbsp;',
					depends = 'isVolume',
					field = 'volume',
					format = { formatNonNumber },
				},
				{
					delimiter = ' :&nbsp;',
					ensureEnds = '.',
					delimiter = ' ',
					depends = { 'isVolume', 'volume' },
					field = 'volumeTitle',
					format = { f.dash },
				},
				{
					ensureEnds = '.',
					delimiter = ' ',
					conflicts = 'volume',
					depends = { 'isVolume', 'volumeTitle' },
					entity = 'Q1238720',
					capitalize = true,
					format = { f.abbr },
				},
				{
					ensureEnds = '.',
					delimiter = '&nbsp;',
					conflicts = 'volume',
					depends = 'isVolume',
					field = 'volumeTitle',
					format = { formatNonNumber },
				},
				{
					delimiter = ' :&nbsp;',
					field = 'workType',
					capitalize = false,
				},
				{
					delimiter = ' :&nbsp;',
					field = 'info',
					format = { f.dash },
					capitalize = false,
				},
				{
					depends = 'originLang',
					cond = originLangVisible,
					delimiter = ' :&nbsp;',
					entity = 'Q7553',
					format = { f.abbr, formatTranslatedFrom },
				},
				{
					delimiter = ' ',
					cond = originLangVisible,
					field = 'originLang',
					format = { f.abbrWithHint },
				},
			},
		},
		{
			delimiter = ' /&nbsp;',
			parts = {
				{
					field = 'authors',
					cond = authorsVisible,
					limits = {
						max = 4,
						cutTo = 3,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
				{
					depends = 'illustrators',
					delimiter = '; ',
					entity = 'Q644687',
					format = { f.abbr, formatIllustratorsLead },
				},
				{
					delimiter = ': ',
					field = 'illustrators',
					limits = {
						max = 2,
						cutTo = 1,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
				{
					depends = 'translators',
					delimiter = '; ',
					entity = 'Q333634',
					format = { f.abbr, formatTranslatorsLead },
				},
				{
					delimiter = ': ',
					field = 'translators',
					limits = {
						max = 2,
						cutTo = 1,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
				{
					depends = 'editorInChief',
					delimiter = '; ',
					entity = 'Q589298',
					format = { f.abbr, formatEditorsInChiefLead },
				},
				{
					delimiter = ': ',
					field = 'editorInChief',
					limits = {
						max = 2,
						cutTo = 1,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
				{
					depends = 'editors',
					delimiter = '; ',
					entity = 'Q1607826',
					format = { f.abbr, formatEditorsLead },
				},
				{
					delimiter = ': ',
					field = 'editors',
					limits = {
						max = 2,
						cutTo = 1,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
			},
		},
		{
			delimiter = ' //&nbsp;',
			parts = {
				{
					conflicts = 'isVolume',
					field = 'publishedIn',
					urlField = 'publishedInUrl',
					capitalize = true,
					format = { f.dash, formatPublishedInLink, f.wikisource, f.resolveWikilink, f.wikidata },
				},
				{
					conflicts = 'isVolume',
					delimiter = ' :&nbsp;',
					field = 'publishedInSubtitle',
					format = { f.dash },
				},
				{
					conflicts = 'isVolume',
					delimiter = ' = ',
					cond = publishedInOriginLangVisible,
					field = 'publishedInOrigin',
					format = { f.dash, f.resolveWikilink },
				},
				{
					conflicts = 'isVolume',
					depends = 'publishedInOriginLang',
					cond = publishedInOriginLangVisible,
					delimiter = ' :&nbsp;',
					entity = 'Q7553',
					format = { f.abbr, formatTranslatedFrom },
				},
				{
					conflicts = 'isVolume',
					delimiter = ' :&nbsp;',
					field = 'publishedInPartsCount',
					capitalize = false,
					format = { f.quantity, formatPartsCount },
				},
				{
					conflicts = 'isVolume',
					delimiter = ' ',
					cond = publishedInOriginLangVisible,
					field = 'publishedInOriginLang',
					format = { f.abbrWithHint },
				},
				{
					conflicts = { 'isArticle', 'isVolume' },
					delimiter = ' :&nbsp;',
					field = 'publishedInWorkType',
					capitalize = false,
				},
			},
		},
		{
			delimiter = ' /&nbsp;',
			parts = {
				{
					conflicts = { 'isArticle', 'publishedInIsPeriodic' },
					depends = 'publishedInEditionType',
					field = 'publishedInAuthors',
					limits = {
						max = 4,
						cutTo = 3,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
				{
					depends = { 'publishedInIllustrators', 'publishedInIsPeriodic' },
					delimiter = '; ',
					entity = 'Q644687',
					format = { f.abbr, formatIllustratorsLead },
				},
				{
					delimiter = ': ',
					field = { 'publishedInIllustrators', 'publishedInIsPeriodic' },
					limits = {
						max = 2,
						cutTo = 1,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
				{
					depends = { 'publishedInTranslators', 'publishedInIsPeriodic' },
					delimiter = '; ',
					entity = 'Q333634',
					format = { f.abbr, formatTranslatorsLead },
				},
				{
					delimiter = ': ',
					field = { 'publishedInTranslators', 'publishedInIsPeriodic' },
					limits = {
						max = 2,
						cutTo = 1,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
				{
					depends = { 'publishedInEditorInChief' },
					conflicts = { 'isArticle', 'publishedInIsPeriodic', 'publishedInIsHosting' },
					delimiter = '; ',
					entity = 'Q589298',
					format = { f.abbr, formatEditorsInChiefLead },
				},
				{
					conflicts = { 'isArticle', 'publishedInIsPeriodic', 'publishedInIsHosting' },
					delimiter = ': ',
					field = 'publishedInEditorInChief',
					limits = {
						max = 2,
						cutTo = 1,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
				{
					depends = { 'publishedInEditors' },
					conflicts = { 'isArticle', 'publishedInIsPeriodic', 'publishedInIsHosting' },
					delimiter = '; ',
					entity = 'Q1607826',
					format = { f.abbr, formatEditorsLead },
				},
				{
					conflicts = { 'isArticle', 'publishedInIsPeriodic', 'publishedInIsHosting' },
					delimiter = ': ',
					field = 'publishedInEditors',
					limits = {
						max = 2,
						cutTo = 1,
						replaceBy = replaceAuthors,
					},
					format = { f.person, f.wikilink, f.wikidata },
				},
			},
		},
		{
			delimiter = ' — ',
			ensureEnds = '.',
			parts = {
				{
					field = 'edition',
				},
			},
		},
		{
			delimiter = ' — ',
			ensureEnds = '.',
			parts = {
				{
					conflicts = 'isArticle',
					cond = locationVisible,
					field = 'location',
					itemsDelimiter = '; ',
					format = { f.abbr, f.wikilink, f.wikidata },
				},
				{
					conflicts = 'isArticle',
					cond = publisherVisible,
					field = 'publisher',
					delimiter = ' :&nbsp;',
					format = { f.short, f.wikilink, f.wikidata },
				},
				{
					field = { 'date', sub='year' },
					delimiter = ', ',
				},
				{
					field = { 'startDate', sub='year' },
					delimiter = ', ',
				},
				{
					delimiter = '—',
					field = { 'endDate', sub='year' },
				},
			},
		},
		{
			delimiter = ' — ',
			ensureEnds = '.',
			parts = {
				{
					conflicts = 'isVolume',
					depends = { {'volume', --[[ or --]] 'volumeTitle'} },
					entity = 'Q1238720',
					format = { f.abbr },
				},
				{
					conflicts = 'isVolume',
					delimiter = '&nbsp;',
					field = 'volume',
					format = { f.numericalRanges },
				},
				{
					conflicts = 'isVolume',
					delimiter = localTitleDelimiter,
					field = 'volumeTitle',
				},
				{
					depends = { {'issue', --[[ or --]] 'issueTitle'} },
					delimiter = ', ',
					entity = 'Q28869365',
					format = { f.abbr },
				},
				{
					delimiter = '&nbsp;',
					field = 'issue',
				},
				{
					delimiter = localTitleDelimiter,
					field = 'issueTitle',
				},
				{
					field = {'date', sub = 'month'},
					format = { formatDateMonth },
				},
			},
		},
		{
			delimiter = ' — ',
			ensureEnds = '.',
			parts = {
				{
					depends = 'articleId',
					entity = 'Q191067',
					format = { f.abbr },
				},
				{
					delimiter = ' ',
					field = 'articleId',
				},
				{
					depends = 'pages',
					entity = 'Q1069725',
					format = { f.unit },
				},
				{
					delimiter = '&nbsp;',
					field = 'pages',
					format = { f.numericalRanges },
				},
				{
					conflicts = 'pages',
					field = 'pagesCount',
					format = { f.quantity },
				},
			},
		},
		{
			delimiter = ' — ',
			ensureEnds = '.',
			prefix = '(',
			parts = {
				{
					field = 'series',
				},
				{
					ensureEnds = ';',
					delimiter = ' ',
					field = 'seriesIssue',
				},
			},
			suffix = ')',
		},
		{
			delimiter = ' — ',
			ensureEnds = '.',
			parts = {
				{
					prefix = '(',
					field = 'comment',
					suffix = ')',
				},
			},
		},
		{
			delimiter = ' — ',
			ensureEnds = '.',
			parts = {
				{
					depends = 'dedicatedTo',
					suffix = ':',
					entity = 'P825',
				},
				{
					delimiter = ' ',
					field = 'dedicatedTo',
					format = { f.person, f.wikilink },
				},
				{
					depends = 'lastUpdate',
					value = 'Дата обновления',
				},
				{
					delimiter = ': ',
					field = 'lastUpdate',
					forceLang = 'ru',
					format = { f.date },
				},
				{
					depends = 'accessDate',
					value = 'Дата обращения',
				},
				{
					delimiter = ': ',
					field = 'accessDate',
					forceLang = 'ru',
					format = { f.date },
				},
			},
		},
		{
			delimiter = ' — ',
			ensureEnds = '.',
			parts = {
				{
					depends = 'issn',
					entity = 'Q131276',
					format = { f.abbr , f.wikilink },
				},
				{
					delimiter = '&nbsp;',
					field = 'issn',
					urlMaskProp = 'P236',
					format = { f.link },
				},
				{
					depends = 'isbn',
					entity = 'Q33057',
					value = 'ISBN',
					format = { f.wikilink },
				},
				{
					delimiter = '&nbsp;',
					field = 'isbn',
					wikilinkMask = 'Служебная:Источники_книг/$1',
					format = { f.wikilink },
				},
				{
					depends = 'doi',
					entity = 'Q25670',
					value = 'doi',
					capitalize = false,
					format = { f.wikilink },
				},
				{
					delimiter = ':',
					field = 'doi',
					urlMaskProp = 'P356',
					format = { f.lowercase, f.link },
				},
				{
					conflicts = { 'isbn', 'pmid' },
					depends = 'oclc',
					entity = 'Q190593',
					value = 'OCLC',
					format = { f.wikilink },
				},
				{
					conflicts = { 'isbn', 'pmid' },
					delimiter = '&nbsp;',
					field = 'oclc',
					urlMaskProp = 'P243',
					format = { f.link },
				},
				{
					depends = 'pmid',
					entity = 'Q2082879',
					value = 'PMID',
					format = { f.wikilink },
				},
				{
					delimiter = '&nbsp;',
					field = 'pmid',
					urlMaskProp = 'P698',
					format = { f.link },
				},
				{
					depends = 'pmc',
					conflicts = 'workVersion',
					entity = 'Q229883',
					value = 'PMC',
					format = { f.wikilink },
				},
				{
					conflicts = 'workVersion',
					delimiter = '&nbsp;',
					field = 'pmc',
					urlMaskProp = 'P932',
					format = { f.link },
				},
				{
					depends = 's2sic',
					conflicts = 'workVersion',
					entity = 'Q22908627',
					value = 'S2SIC',
					format = { f.wikilink },
				},
				{
					conflicts = 'workVersion',
					delimiter = '&nbsp;',
					field = 's2sic',
					urlMaskProp = 'P8299',
					format = { f.link },
				},
				{
					depends = 'workVersion',
					entity = 'Q2013',
					value = 'WD',
					format = { f.wikilink },
				},
				{
					delimiter = '&nbsp;',
					field = 'workVersion',
					format = { f.entity, f.forceWikidataLink },
				},
				{
					depends = 'publishedInVersion',
					conflicts = { 'workVersion', 'isArticle', 'publishedInIsPeriodic', 'publishedInIsHosting', 'id' },
					entity = 'Q2013',
					value = 'WD',
					format = { f.wikilink },
				},
				{
					conflicts = { 'workVersion', 'isArticle', 'publishedInIsPeriodic', 'publishedInIsHosting', 'id' },
					delimiter = '&nbsp;',
					field = 'publishedInVersion',
					format = { f.entity, f.forceWikidataLink },
				},
			},
		},
	},
	ensureEnds = '.',
}

local function argIsEmpty(s)
	return (not s or s == '')
end

local function argIsSet(s)
	return (s and s ~= '')
end

local function strToArray(str)
	if argIsEmpty(str) then
		return nil
	elseif type(str) == 'table' then
		if next(str) == nil then
			return nil
		end
		return str
	end

	local array = {}
	for item in str:gmatch('[^,]+') do
		item = item:gsub('^%s+', ''):gsub('%s+$', '')
		table.insert(array, { value=item })
	end
	return array
end

local function strToDate(str)
	if argIsEmpty(str) then
		return nil
	end

	local year, month, day = str:match('(%d+)-(%d+)-(%d+)')
	if not year then
		year = str:match('(%d+)-(%d+)')
		if not year then
			year = str:match('(%d+)')
		end
	end
	if year then
		year = tonumber(year)
	else
		return nil
	end
	if month then
		month = tonumber(month)
		if month == 0 then
			month = nil
		end
	end
	if day then
		day = tonumber(day)
		if day == 0 then
			day = nil
		end
	end

	local timestamp
	if day then
		timestamp = string.format("%04d-%02d-%02d", year, month, day)
	elseif month then
		timestamp = string.format("%04d-%02d", year, month)
	else
		timestamp = string.format("%04d", year)
	end
	return { value = { year=year, month=month, day=day, timestamp=timestamp } }
end

local function waybackStampToArchiveUrl(stamp, url)
	if argIsEmpty(stamp) then
		return nil
	end

	return { value='https://web.archive.org/web/' .. stamp .. '/' .. url.value }
end

local function waybackStampToArchiveDate(stamp)
	if argIsEmpty(stamp) then
		return nil
	end

	local year, month, day = string.match(stamp, '(%d%d%d%d)(%d%d)(%d%d)')
	local timestamp = string.format("%04d-%02d-%02d", year, month, day)
	return { value={ year=year, month=month, day=day, timestamp=timestamp } }
end

local function volumesCountToPartsCount(volumesCount)
	if argIsEmpty(volumesCount) then
		return nil
	end

	return { value = volumesCount, unitEntity = 'Q1238720' }
end

local function valueAndEntityToTable(str, entity)
	if str == '' then
		str = nil
	end
	if entity == '' then
		entity = nil
	end

	local t = {}
	if str then
		t.value = str
	end
	if entity then
		t.entity = entity
	end

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

local function valueToTable(value)
	if argIsEmpty(value)then
		return nil
	end
	return { value = value }
end

local function langCodeToLangTable(langCode)
	if argIsEmpty(langCode) then
		return
	end
	return { entity = wdLang.langEntity(langCode) }
end

local function quantityToTable(quantity, defaultUnit)
	if argIsEmpty(quantity) then
		return nil
	end
	return { value = quantity, unitEntity = defaultUnit }
end

local function argsToSource(args)
	local s = {
		workVersion=valueAndEntityToTable(nil, args.entity),
		origin=valueAndEntityToTable(args.origin, args.originEntity),
		originUrl=valueToTable(args.originUrl),
		originLang=langCodeToLangTable(args.originLang),
		originLangCode=valueAndEntityToTable(args.originLang),
		contentType=valueAndEntityToTable(args.contentType, args.contentTypeEntity),
		workType=valueAndEntityToTable(args.workType, args.workTypeEntity),
		authors=strToArray(args.authors),
		illustrators=strToArray(args.illustrators),
		editorInChief=strToArray(args.editorInChief),
		editors=strToArray(args.editors),
		translators=strToArray(args.translators),
		title=valueToTable(args.title),
		subtitle=valueToTable(args.subtitle),
		contentType=valueAndEntityToTable(args.contentType, args.contentTypeEntity),
		info=valueToTable(args.info),
		partsCount=volumesCountToPartsCount(args.volumesCount),
		publishedInVersion=valueAndEntityToTable(nil, args.publishedInEntity),
		publishedIn=valueAndEntityToTable(args.publishedIn, args.publishedInEntity),
		publishedInSubtitle=valueToTable(args.publishedInSubtitle),
		publishedInOrigin=valueAndEntityToTable(args.publishedInOrigin),
		publishedInOriginLangCode=valueAndEntityToTable(args.publishedInOriginLang),
		publishedInOriginLang=langCodeToLangTable(args.publishedInOriginLang),
		publishedInLangCode=valueAndEntityToTable(args.publishedInLang),
		publishedInLang=langCodeToLangTable(args.publishedInLang),
		publishedInAuthors=strToArray(args.publishedInAuthors),
		publishedInIllustrators=strToArray(args.publishedInIllustrators),
		publishedInEditors=strToArray(args.publishedInEditors),
		publishedInEditorInChief=strToArray(args.publishedInEditorInChief),
		publishedInTranslators=strToArray(args.publishedInTranslators),
		publishedInUrl=valueToTable(args.publishedInUrl),
		publishedInPartsCount=volumesCountToPartsCount(args.publishedInVolumes),
		comment=valueToTable(args.comment),
		edition=valueToTable(args.edition),
		location=valueAndEntityToTable(args.location, args.locationEntity),
		publisher=valueAndEntityToTable(args.publisher, args.publisherEntity),
		topic = valueAndEntityToTable(args.topic, args.topicEntity),
		id=valueToTable(args.id),
		date=strToDate(args.date),
		volume=valueToTable(args.volume),
		volumeTitle=valueToTable(args.volumeTitle),
		issue=valueToTable(args.issue),
		issueTitle=valueToTable(args.issueTitle),
		articleId=valueToTable(args.articleId),
		pages=valueToTable(args.pages),
		pagesCount=quantityToTable(args.pagesCount, 'Q1069725'),
		series=valueAndEntityToTable(args.series, args.seriesEntity),
		lastUpdate=strToDate(args.lastUpdate),
		accessDate=strToDate(args.accessDate),
		doi=valueToTable(args.doi),
		pmid=valueToTable(args.pmid),
		pmc=valueToTable(args.pmc),
		s2sic=valueToTable(args.s2sic),
		oclc=valueToTable(args.oclc),
		issn=strToArray(args.issn),
		isbn=strToArray(args.isbn),
		url=valueToTable(args.url),
		archiveUrl=valueToTable(args.archiveUrl),
		archiveDate=strToDate(args.archiveDate),
		ref=valueToTable(args.ref),
		lang=langCodeToLangTable(args.lang),
		langCode=valueAndEntityToTable(args.lang),
	}
	if (args.publishedInEditors and args.publishedInEditors ~= '') or (args.publishedInEditorInChief and args.publishedInEditorInChief ~= '') then
		s.isLikeBook = { entity = 'Q571' }
	end
	return s
end

function p.cite(frame)
	local args = frame.args

	local s = argsToSource(args)
	if argIsEmpty(args.offline) then
		s = source.fetch(s)
	end

	if argIsEmpty(args.archiveUrl) then
		if not argIsEmpty(args.waybackStamp) and s.url then
			s.archiveUrl = waybackStampToArchiveUrl(args.waybackStamp, s.url)
			s.archiveDate = waybackStampToArchiveDate(args.waybackStamp)
		end
	end

	if (mw.isSubsting() or argIsSet(args.forceSubst)) and argIsEmpty(args.pureWikitext) then
		local template
		local parentFrame = frame:getParent()
		local selfTitle = frame:getTitle()
		if parentFrame then
			template = parentFrame:getTitle()
		else
			template = selfTitle
		end
		local argsProfile = require(selfTitle .. '/map:' .. template)
		
		return '{{' .. template:gsub('Шаблон:', '') .. '|' .. formatter.format(argsProfile, s) .. '}}'
	end

	return formatter.format(GostProfile, s)
end

return p