require('strict')
local p = {}
--: https://www.mediawiki.org/wiki/Extension_default_namespaces
local NS_MAIN = 0
local NS_PROJECT = 4
local NS_TEMPLATE = 10
local NS_MODULE = 828
local moduleNamespace = mw.site.namespaces[NS_MODULE].name
local source = require(moduleNamespace .. ':Песочница/D6194c-1cc/WDSourceV2')
local formatter = require(moduleNamespace .. ':Песочница/D6194c-1cc/WDFormatV2')
local f = formatter.f
local wdLang = require(moduleNamespace .. ':WDLang')
local wikidata = require(moduleNamespace .. ':Песочница/D6194c-1cc/WDCommonV2')
local i18n = require(moduleNamespace .. ':Песочница/D6194c-1cc/I18n')
local l10n = i18n.load()
local currentLangObj = mw.getContentLanguage()
local currentLang = currentLangObj:getCode()
local stylesUsed = false
local function commonLangVisible(source, fieldName)
if not source[fieldName] then
return false
end
if table.getn(source[fieldName]) > 0 then
for _, lang in ipairs(source[fieldName]) do
if lang.components and currentLang == lang.components.langCode.value then
return false
end
end
else
if source[fieldName].components and currentLang == source[fieldName].components.langCode.value then
return false
end
end
return true
end
local function langVisible(source)
return commonLangVisible(source, 'lang')
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 langPropHintVisible(source)
if table.getn(source.lang) > 1 then
if source.lang == source.publishedInLang then
return true
end
end
return false
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 then
return false
end
if source.lang.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 formatTitle(source, processedData, result)
if processedData.fieldTable.fromLabel then
if source.workVersion then
result.text = result.text .. '?'
result.wikitext = result.wikitext .. '<sup>[[d:Property:P1476|P1476?]]</sup>'
end
end
end
local function formatUrlStatus(source, processedData, result)
if not result.linked then
return
end
local fieldTable = processedData.fieldTable
if not fieldTable.components then
return
end
local statusTable = fieldTable.components.urlStatus
if not statusTable then
return
end
local classesMap = {
['Q1193907'] = 'cite-dead-url',
['Q910845'] = 'cite-paywall-url',
['Q232932'] = 'cite-open-access-url',
['Q24707952'] = 'cite-free-access-url',
['Q107459441'] = 'cite-reg-required-url',
}
local statusClass = classesMap[statusTable.entity]
if not statusClass then
mw.log('Unknown url status:')
mw.logObject(statusTable)
return
end
stylesUsed = true
local spanTitle
if statusTable.value then
spanTitle = mw.ustring.gsub(statusTable.value, '^%l', mw.ustring.upper)
else
spanTitle = ''
end
result.wikitext = '<span class="' .. statusClass .. '" title="' .. spanTitle .. '">' .. result.wikitext .. '</span>'
end
local function formatTitleUrlStatus(source, processedData, result)
if not result.linked then
return
end
formatUrlStatus(source, { fieldTable = source.url }, result)
end
local function formatPublishedInUrlStatus(source, processedData, result)
if not result.linked then
return
end
formatUrlStatus(source, { fieldTable = source.publishedInUrl }, result)
end
local function formatPublishedInTitle(source, processedData, result)
if processedData.fieldTable.fromLabel then
if source.publishedInVersion and not source.publishedInIsHosting then
result.text = result.text .. '?'
result.wikitext = result.wikitext .. '<sup>[[d:Property:P1476|P1476?]]</sup>'
end
end
end
local function formatArchiveUrl(source, processedData, result)
local t = processedData.fieldTable
local archiveUrl = t.components.archiveUrl
if type(archiveUrl) == 'table' then
archiveUrl = archiveUrl.value
end
local archiveDate = t.components.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="' .. l10n('messages', 'archive-date-hint') .. '">' .. l10n('messages', 'archive-abbreviated') .. '</abbr>] ' .. dateStr
end
local function formatDateMonth(source, processedData, result, state)
local langObj
if processedData.langCode then
langObj = mw.getLanguage(processedData.langCode)
else
langObj = currentLangObj
end
local dateStr
if source.date.value.day then
dateStr = langObj:formatDate('j xg', source.date.value.timestamp)
else
-- Without this hack formatDate() gives previous month for the 00 day
local timestamp = string.gsub(source.date.value.timestamp, '-00T00:00:00Z', '-01T00:00:00Z')
dateStr = langObj:formatDate('F', timestamp)
if (processedData.capitalize and state.groupEmpty) or processedData.forceCapitalize then
dateStr = mw.ustring.gsub(dateStr, '^%l', mw.ustring.upper)
end
end
result.text = dateStr
result.wikitext = '<span class="nowrap">' .. dateStr .. '</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 .. ' '
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 .. ' ' .. initials
result.text = text
result.wikitext = text
end
local function formatTranslatedFrom(source, processedData, result)
local abbrByLang = {
el = 'Μετάφρ. από τα',
ru = 'Пер. с',
uk = 'Пер. з',
en = 'Transl. 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. in ch.',
es = 'ed. jefe',
de = 'ch.-Red.',
uk = 'гол. ред.',
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 .. '">' .. l10n('messages', 'et-al') .. '</abbr>'
return l10n('messages', 'et-al'), othersWikitext
end
local function localTitleDelimiter(source)
if source.issue then
return ' : '
else
return ' '
end
end
local GostProfile = {
tag = {
name = 'span',
classes = { 'citation' },
attr = {
-- ref
id = ruwikiAnchor,
},
},
groups = {
{
{
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, formatTitleUrlStatus, f.wikisource, f.wikiversity, f.wikibooks, f.resolveWikilink, formatTitle },
},
{
field = 'titleIsMissing',
delimiter = ' ',
ensureEnds = '.',
capitalize = true,
prefix='(',
tag = {
name = 'span',
classes = { 'error' },
},
suffix = ')',
},
{
delimiter = ' : ',
field = 'subtitle',
format = { f.dash },
},
{
tag = {
name = 'sup',
classes = { 'error' },
attr = {
title = l10n('errors', 'missing-lang-explanation'),
},
},
field = 'langIsMissing',
delimiter = '',
},
{
delimiter = ' ',
field = 'contentType',
format = { f.squareBrackets },
},
{
delimiter = ' = ',
cond = originLangVisible,
field = 'origin',
urlField = 'originUrl',
format = { f.dash, f.link, f.resolveWikilink },
},
{
delimiter = ' : ',
depends = 'origin',
cond = originLangVisible,
field = 'originSubtitle',
format = { f.dash },
},
},
{
childDelimiter = ' : ',
{
field = 'lang',
prefix = '[',
cond = langVisible,
forceLang = 'default',
capitalize = false,
limits = {
max = 2,
cutTo = 2,
replaceBy = replaceExceeded,
},
format = { f.abbrWithHint },
suffix = ']',
},
{
cond = langPropHintVisible,
delimiter = '',
value = '<sup>[[d:Property:P407|P407?]]</sup>',
},
{
depends = {
{ isPath=true, 'url', 'archiveUrl'},
{ isPath=true, 'url', 'archiveDate'},
},
field = 'url',
forceLang = 'default',
format = { formatArchiveUrl, f.squareBrackets },
},
{
delimiter = ' ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'wrongArchiveDateFormat',
itemsDelimiter = ' ',
suffix = ')'
},
{
conflicts = 'isVolume',
delimiter = ' : ',
field = 'partsCount',
capitalize = false,
format = { f.quantity, formatPartsCount },
},
},
{
{
depends = 'isVolume',
{
ensureEnds = '.',
delimiter = ' ',
field = 'partsCount',
format = { f.quantity, formatPartsCount },
},
{
ensureEnds = '.',
delimiter = ' ',
depends = 'volume',
entity = 'Q1238720',
capitalize = true,
format = { f.abbr },
},
{
delimiter = ' ',
field = 'volume',
format = { formatNonNumber },
},
{
delimiter = ' : ',
ensureEnds = '.',
delimiter = ' ',
depends = 'volume',
field = 'volumeTitle',
format = { f.dash },
},
{
ensureEnds = '.',
delimiter = ' ',
conflicts = 'volume',
depends = 'volumeTitle',
entity = 'Q1238720',
capitalize = true,
format = { f.abbr },
},
{
ensureEnds = '.',
delimiter = ' ',
conflicts = 'volume',
field = 'volumeTitle',
format = { formatNonNumber },
},
},
{
depends = 'isIssue',
{
ensureEnds = '.',
delimiter = ' ',
field = 'partsCount',
format = { f.quantity, formatPartsCount },
},
{
ensureEnds = '.',
delimiter = ' ',
depends = 'issue',
entity = 'Q28869365',
capitalize = true,
format = { f.abbr },
},
{
delimiter = ' ',
field = 'issue',
format = { formatNonNumber },
},
{
delimiter = ' : ',
ensureEnds = '.',
delimiter = ' ',
depends = 'issue',
field = 'issueTitle',
format = { f.dash },
},
{
ensureEnds = '.',
delimiter = ' ',
conflicts = 'issue',
depends = 'issueTitle',
entity = 'Q28869365',
capitalize = true,
format = { f.abbr },
},
{
ensureEnds = '.',
delimiter = ' ',
conflicts = 'issue',
field = 'issueTitle',
format = { formatNonNumber },
},
},
{
delimiter = ' : ',
field = 'workType',
capitalize = false,
},
{
delimiter = ' : ',
field = 'info',
format = { f.dash },
capitalize = false,
},
{
delimiter = ' : ',
prefix = '[',
field = 'detectedInfo',
forceLang = 'default',
format = { f.dash },
suffix = ']',
capitalize = false,
},
{
depends = 'originLang',
cond = originLangVisible,
delimiter = ' : ',
entity = 'Q7553',
format = { f.abbr, formatTranslatedFrom },
},
{
delimiter = ' ',
cond = originLangVisible,
field = 'originLang',
format = { f.abbrWithHint },
},
},
{
delimiter = ' / ',
{
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 = ' // ',
conflicts = { 'isVolume', 'isIssue' },
{
field = 'publishedIn',
urlField = 'publishedInUrl',
capitalize = true,
format = { f.dash, formatPublishedInLink, formatPublishedInUrlStatus, f.wikisource, f.wikiversity, f.wikibooks, f.resolveWikilink, f.wikidata, formatPublishedInTitle },
},
{
field = 'publishedInTitleIsMissing',
delimiter = ' ',
ensureEnds = '.',
capitalize = true,
prefix='(',
tag = {
name = 'span',
classes = { 'error' },
},
suffix = ')',
},
{
delimiter = ' : ',
field = 'publishedInSubtitle',
format = { f.dash },
},
{
delimiter = ' = ',
cond = publishedInOriginLangVisible,
field = 'publishedInOrigin',
format = { f.dash, f.resolveWikilink },
},
{
depends = 'publishedInOriginLang',
cond = publishedInOriginLangVisible,
delimiter = ' : ',
entity = 'Q7553',
format = { f.abbr, formatTranslatedFrom },
},
{
delimiter = ' : ',
field = 'publishedInPartsCount',
capitalize = false,
format = { f.quantity, formatPartsCount },
},
{
delimiter = ' ',
cond = publishedInOriginLangVisible,
field = 'publishedInOriginLang',
format = { f.abbrWithHint },
},
{
delimiter = ' ',
depends = {
{ isPath=true, 'publishedInUrl', 'archiveUrl'},
{ isPath=true, 'publishedInUrl', 'archiveDate'},
},
field = 'publishedInUrl',
forceLang = 'default',
format = { formatArchiveUrl, f.squareBrackets },
},
{
delimiter = ' ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'wrongPublishedInArchiveDateFormat',
itemsDelimiter = ' ',
suffix = ')'
},
{
conflicts = 'isArticle',
delimiter = ' : ',
field = 'publishedInWorkType',
capitalize = false,
},
},
{
delimiter = ' / ',
{
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 },
},
{
conflicts = { 'isArticle', 'publishedInIsPeriodic', 'publishedInIsHosting' },
{
depends = { 'publishedInEditorInChief' },
delimiter = '; ',
entity = 'Q589298',
format = { f.abbr, formatEditorsInChiefLead },
},
{
delimiter = ': ',
field = 'publishedInEditorInChief',
limits = {
max = 2,
cutTo = 1,
replaceBy = replaceAuthors,
},
format = { f.person, f.wikilink, f.wikidata },
},
{
depends = { 'publishedInEditors' },
delimiter = '; ',
entity = 'Q1607826',
format = { f.abbr, formatEditorsLead },
},
{
delimiter = ': ',
field = 'publishedInEditors',
limits = {
max = 2,
cutTo = 1,
replaceBy = replaceAuthors,
},
format = { f.person, f.wikilink, f.wikidata },
},
},
},
{
delimiter = ' — ',
ensureEnds = '.',
field = 'edition',
},
{
delimiter = ' — ',
ensureEnds = '.',
{
conflicts = 'isArticle',
cond = locationVisible,
field = 'location',
itemsDelimiter = '; ',
format = { f.abbr, f.wikilink, f.wikidata },
},
{
conflicts = 'isArticle',
cond = publisherVisible,
field = 'publisher',
delimiter = ' : ',
format = { f.short, f.wikilink, f.wikidata },
},
{
field = { 'date', sub='year' },
delimiter = ', ',
},
{
delimiter = ', ',
field = {'date', sub = 'month'},
format = { formatDateMonth },
},
{
field = { 'startDate', sub='year' },
delimiter = ', ',
},
{
delimiter = '—',
field = { 'endDate', sub='year' },
},
{
delimiter = ', ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'wrongDateFormat',
itemsDelimiter = ' ',
suffix = ')'
},
},
{
delimiter = ' — ',
ensureEnds = '.',
{
conflicts = 'isVolume',
{
depends = { {'volume', --[[ or --]] 'volumeTitle'} },
entity = 'Q1238720',
format = { f.abbr },
},
{
delimiter = ' ',
field = 'volume',
format = { f.numericalRanges },
},
{
delimiter = localTitleDelimiter,
field = 'volumeTitle',
},
},
{
conflicts = 'isIssue',
passthrough = true,
{
depends = { {'issue', --[[ or --]] 'issueTitle'} },
delimiter = ', ',
entity = 'Q28869365',
format = { f.abbr },
},
{
delimiter = ' ',
field = 'issue',
},
{
delimiter = localTitleDelimiter,
field = 'issueTitle',
},
},
},
{
delimiter = ' — ',
ensureEnds = '.',
childDelimiter = ' — ',
childEnsureEnds = '.',
{
depends = 'articleId',
entity = 'Q191067',
format = { f.abbr },
},
{
delimiter = ' ',
field = 'articleId',
},
{
depends = 'pages',
entity = 'Q1069725',
format = { f.unit },
},
{
delimiter = ' ',
field = 'pages',
format = { f.numericalRanges },
},
{
conflicts = 'pages',
field = 'pagesCount',
format = { f.quantity },
},
},
{
delimiter = ' — ',
ensureEnds = '.',
prefix = '(',
{
field = 'series',
},
{
ensureEnds = ';',
delimiter = ' ',
field = 'seriesIssue',
},
suffix = ')',
},
{
delimiter = ' — ',
ensureEnds = '.',
prefix = '(',
field = 'comment',
suffix = ')',
},
{
delimiter = ' — ',
ensureEnds = '.',
childDelimiter = ' — ',
childEnsureEnds = '.',
{
depends = 'dedicatedTo',
suffix = ':',
entity = 'P825',
},
{
delimiter = ' ',
field = 'dedicatedTo',
format = { f.person, f.wikilink },
},
{
depends = 'lastUpdate',
value = l10n('messages', 'last-update'),
},
{
delimiter = ': ',
field = 'lastUpdate',
forceLang = currentLang,
format = { f.date },
},
{
delimiter = ' ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'wrongLastUpdateFormat',
itemsDelimiter = ' ',
suffix = ')'
},
{
depends = 'accessDate',
value = l10n('messages', 'access-date'),
},
{
delimiter = ': ',
field = 'accessDate',
forceLang = currentLang,
format = { f.date },
},
{
delimiter = ' ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'wrongAccessDateFormat',
itemsDelimiter = ' ',
suffix = ')'
},
},
{
delimiter = ' — ',
ensureEnds = '.',
childDelimiter = ' — ',
childEnsureEnds = '.',
{
depends = 'issn',
entity = 'Q131276',
format = { f.abbr , f.wikilink },
},
{
delimiter = ' ',
field = 'issn',
urlMaskProp = 'P236',
format = { f.link },
},
{
depends = 'isbn',
entity = 'Q33057',
value = 'ISBN',
format = { f.wikilink },
},
{
delimiter = ' ',
field = 'isbn',
wikilinkMask = l10n('messages', 'wikilink-mask'),
format = { f.wikilink },
},
{
depends = 'doi',
entity = 'Q25670',
value = 'doi',
capitalize = false,
format = { f.wikilink },
},
{
delimiter = ':',
field = 'doi',
urlMaskProp = 'P356',
format = { f.lowercase, f.link, formatUrlStatus },
},
{
conflicts = { 'isbn', 'pmid' },
depends = 'oclc',
entity = 'Q190593',
value = 'OCLC',
format = { f.wikilink },
},
{
conflicts = { 'isbn', 'pmid' },
delimiter = ' ',
field = 'oclc',
urlMaskProp = 'P243',
format = { f.link },
},
{
depends = 'pmid',
entity = 'Q2082879',
value = 'PMID',
format = { f.wikilink },
},
{
delimiter = ' ',
field = 'pmid',
urlMaskProp = 'P698',
format = { f.link },
},
{
depends = 'pmc',
conflicts = 'workVersion',
entity = 'Q229883',
value = 'PMC',
format = { f.wikilink },
},
{
conflicts = 'workVersion',
delimiter = ' ',
field = 'pmc',
urlMaskProp = 'P932',
format = { f.link },
},
{
depends = 's2sic',
conflicts = 'workVersion',
entity = 'Q22908627',
value = 'S2SIC',
format = { f.wikilink },
},
{
conflicts = 'workVersion',
delimiter = ' ',
field = 's2sic',
urlMaskProp = 'P8299',
format = { f.link },
},
{
childDelimiter = ' — ',
childEnsureEnds = '.',
{
entity = 'Q2013',
value = 'WD',
isStatic = true,
format = { f.wikilink },
},
{
delimiter = ' ',
field = 'workVersion',
format = { f.entity, f.forceWikidataLink },
},
{
conflicts = { 'workVersion' },
delimiter = ' ',
field = 'issueVersion',
format = { f.entity, f.forceWikidataLink },
},
{
conflicts = { 'workVersion', 'issueVersion' },
delimiter = ' ',
field = 'volumeVersion',
format = { f.entity, f.forceWikidataLink },
},
{
conflicts = { 'workVersion', 'issueVersion', 'volumeVersion', 'isArticle', 'publishedInIsPeriodic', 'publishedInIsHosting', 'id' },
delimiter = ' ',
field = 'publishedInVersion',
format = { f.entity, f.forceWikidataLink },
},
},
},
{
ensureEnds = '.',
delimiter = ' — ',
depends = 'unknownFields',
tag = {
name = 'span',
classes = { 'error' },
},
prefix = '(',
{
value = l10n('errors', 'unknown-template-args')
},
{
delimiter = ' ',
field = 'unknownFields',
format = { f.nowiki },
},
suffix = ')'
},
{
ensureEnds = '.',
delimiter = ' — ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'urlIsArchive',
itemsDelimiter = ' ',
suffix = ')'
},
{
ensureEnds = '.',
delimiter = ' — ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'archiveUrlWithoutUrl',
itemsDelimiter = ' ',
suffix = ')'
},
{
ensureEnds = '.',
delimiter = ' — ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'archiveUrlWithoutArchiveDate',
itemsDelimiter = ' ',
suffix = ')'
},
{
ensureEnds = '.',
delimiter = ' — ',
prefix = '(',
tag = {
name = 'span',
classes = { 'error' },
},
field = 'idWithoutPublishedIn',
itemsDelimiter = ' ',
suffix = ')'
},
},
ensureEnds = '.',
}
local function argIsEmpty(s)
return (not s or s == '')
end
local function argIsSet(s)
return (s and s ~= '')
end
local function strToArray(str, t)
if type(str) == 'table' then
if next(str) == nil then
return t
end
return str
end
local array = t
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, t)
local year, month, day = str:match('^(%d+)-(%d+)-(%d+)$')
if not year then
year, month = str:match('^(%d+)-(%d+)$')
if not year then
year = str:match('^(%d+)$')
end
end
if not year then
t.erroneous = true
return t
end
year = tonumber(year)
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
t.value = { year=year, month=month, day=day, timestamp=timestamp }
return t
end
local function strToDateToParentTable(value, parentTable, name)
if not parentTable.components then
parentTable.components = {}
end
parentTable.components[name] = strToDate(value, {})
return parentTable
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, t)
t.value = volumesCount
t.unitEntity = 'Q1238720'
return t
end
local function entityToTable(entity, t)
t.entity = entity
return t
end
local function valueToTable(value, t)
t.value = value
return t
end
local function valueToParentTable(value, parentTable, name)
if not parentTable.components then
parentTable.components = {}
end
local t = {
value = value,
}
parentTable.components[name] = t
return parentTable
end
local function urlStatusToTable(value, parentTable)
if not parentTable.components then
parentTable.components = {}
end
local status = l10n('args', 'reversed', 'urlStatus', value)
local entityMap = {
dead = 'Q1193907',
paywall = 'Q910845',
['open access'] = 'Q232932',
['free access'] = 'Q24707952',
['registration required'] = 'Q107459441',
}
local t = {
value = value,
lang = currentLang,
entity = entityMap[status],
}
parentTable.components.urlStatus = t
return parentTable
end
local function langCodeToTable(langCode, t)
t.entity = wdLang.langEntity(langCode)
t.components = {
langCode = { value = langCode },
}
return t
end
local function pagesToTable(pagesCount, t)
t.value = pagesCount
t.unitEntity = 'Q1069725'
return t
end
local function getTitles(frame)
if not frame then
frame = mw.getCurrentFrame()
end
local template
local parentFrame = frame:getParent()
local selfTitle = frame:getTitle()
if parentFrame then
template = parentFrame:getTitle()
else
template = selfTitle
end
return template, selfTitle
end
local function getTemplateNameFromTitle(template)
local templateNamespace = mw.site.namespaces[NS_TEMPLATE].name
return template:gsub('^' .. templateNamespace .. ':', '')
end
local function argsToSource(frame)
local s = {}
local argsMap = {
entity = { entityToTable, 'workVersion' },
origin = { valueToTable, 'origin' },
originEntity = { entityToTable, 'origin' },
originUrl = { valueToTable, 'originUrl' },
originLang = { langCodeToTable, 'originLang' },
contentTypeEntity = { entityToTable, 'contentType' },
contentType = { valueToTable, 'contentType' },
workTypeEntity = { entityToTable, 'workType' },
workType = { valueToTable, 'workType' },
authors = { strToArray, 'authors' },
illustrators = { strToArray, 'illustrators' },
editorInChief = { strToArray, 'editorInChief' },
editors = { strToArray, 'editors' },
translators = { strToArray, 'translators' },
title = { valueToTable, 'title' },
subtitle = { valueToTable, 'subtitle' },
info = { valueToTable, 'info' },
volumesCount = { volumesCountToPartsCount, 'partsCount' },
publishedInEntity = { entityToTable, 'publishedInVersion' },
publishedIn = { valueToTable, 'publishedIn' },
publishedInSubtitle = { valueToTable, 'publishedInSubtitle' },
publishedInOrigin = { valueToTable, 'publishedInOrigin' },
publishedInOriginLang = { langCodeToTable, 'publishedInOriginLang' },
publishedInLang = { langCodeToTable, 'publishedInLang' },
publishedInAuthors = { strToArray, 'publishedInAuthors' },
publishedInIllustrators = { strToArray, 'publishedInIllustrators' },
publishedInEditors = { strToArray, 'publishedInEditors' },
publishedInEditorInChief = { strToArray, 'publishedInEditorInChief' },
publishedInTranslators = { strToArray, 'publishedInTranslators' },
publishedInUrl = { valueToTable, 'publishedInUrl' },
publishedInUrlStatus = { urlStatusToTable, 'publishedInUrl' },
publishedInVolumes = { volumesCountToPartsCount, 'publishedInPartsCount' },
publishedInWorkType = { valueToTable, 'publishedInWorkType' },
comment = { valueToTable, 'comment' },
edition = { valueToTable, 'edition' },
location = { valueToTable, 'location' },
locationEntity = { entityToTable, 'location' },
publisher = { valueToTable, 'publisher' },
publisherEntity = { entityToTable, 'publisher' },
topic = { valueToTable, 'topic' },
topicEntity = { entityToTable, 'topic' },
id = { valueToTable, 'id' },
date = { strToDate, 'date' },
volume = { valueToTable, 'volume' },
volumeTitle = { valueToTable, 'volumeTitle' },
issue = { valueToTable, 'issue' },
issueTitle = { valueToTable, 'issueTitle' },
articleId = { valueToTable, 'articleId' },
pages = { valueToTable, 'pages' },
pagesCount = { pagesToTable, 'pagesCount' },
series = { valueToTable, 'series' },
seriesEntity = { entityToTable, 'series' },
seriesIssue = { valueToTable, 'seriesIssue' },
lastUpdate = { strToDate, 'lastUpdate' },
accessDate = { strToDate, 'accessDate' },
doi = { valueToTable, 'doi' },
pmid = { valueToTable, 'pmid' },
pmc = { valueToTable, 'pmc' },
s2sic = { valueToTable, 's2sic' },
oclc = { valueToTable, 'oclc' },
issn = { strToArray, 'issn' },
isbn = { strToArray, 'isbn' },
url = { valueToTable, 'url' },
urlStatus = { urlStatusToTable, 'url' },
waybackStamp = { valueToTable, 'waybackStamp' },
archiveUrl = { valueToParentTable, 'url', 'archiveUrl' },
archiveDate = { strToDateToParentTable, 'url', 'archiveDate' },
publishedInArchiveUrl = { valueToParentTable, 'publishedInUrl', 'archiveUrl' },
publishedInArchiveDate = { strToDateToParentTable, 'publishedInUrl', 'archiveDate' },
ref = { valueToTable, 'ref' },
lang = { langCodeToTable, 'lang' },
}
local serviceArgs = {
offline = true,
forceSubst = true,
pureWikitext = true,
}
local unknownFields = {}
local template, selfTitle = getTitles(frame)
local ok, templateInfo = pcall(mw.loadData, selfTitle .. '/args:' .. template)
if ok then
local parentFrame = frame:getParent()
for parentKey, value in pairs(parentFrame.args) do
local key = templateInfo.argsToModule[parentKey]
if key then
value = mw.text.trim(value)
if argIsSet(value) then
local argMap = argsMap[key]
if argMap then
local name = argMap[2]
local childName = argMap[3]
local fieldTable = s[name] or {}
s[name] = argMap[1](value, fieldTable, childName)
elseif not serviceArgs[key] then
error(string.format(l10n('errors', 'unknown-module-arg'), key))
end
end
else
table.insert(unknownFields, { value = parentKey })
end
end
else
for key, value in pairs(frame.args) do
if argIsSet(value) then
local argMap = argsMap[key]
if argMap then
local name = argMap[2]
local childName = argMap[3]
local fieldTable = s[name] or {}
s[name] = argMap[1](value, fieldTable, childName)
elseif not serviceArgs[key] then
error('Unknown module argument: ' .. key)
end
end
end
end
if next(unknownFields) ~= nil then
s.unknownFields = unknownFields
end
if s.publishedInVersion then
s.publishedIn = entityToTable(s.publishedInVersion.entity, s.publishedIn or {})
end
if s.publishedInEditors or s.publishedInEditorInChief then
s.isLikeBook = { entity = 'Q571' }
end
return s
end
local function urlIsArchive(urlTable)
local url = urlTable.value:gsub('^(https?://)www%.', '%1')
local isArchive = false
if url:match('^https?://web%.archive%.org/web/') or url:match('^https?://webcitation%.org/%w+') then
isArchive = true
end
if url:match('^https?://archive%.%a+/%w+') then
if url:match('^https?://archive%.today/') or
url:match('^https?://archive%.ph/') or
url:match('^https?://archive%.is/') or
url:match('^https?://archive%.li/') or
url:match('^https?://archive%.vn/') or
url:match('^https?://archive%.fo/') or
url:match('^https?://archive%.md/') then
isArchive = true
end
end
return isArchive
end
local function appendErrorField(s, fieldName, errorKey)
s[fieldName] = s[fieldName] or {}
table.insert(s[fieldName], {
value = l10n('errors', errorKey),
lang = currentLang,
})
end
local function checkFields(s)
local hasErrors = false
if not s.lang or (table.getn(s.lang) == 0 and s.lang.isDefault) then
appendErrorField(s, 'langIsMissing', 'missing-lang')
hasErrors = true
end
if s.url then
if s.url.value then
if urlIsArchive(s.url) then
appendErrorField(s, 'urlIsArchive', 'url-contains-archiveUrl')
hasErrors = true
end
elseif s.url.components then
if s.url.components.archiveUrl then
appendErrorField(s, 'archiveUrlWithoutUrl', 'archiveUrl-without-url')
hasErrors = true
end
if s.url.components.urlStatus then
appendErrorField(s, 'urlIsMissing', 'urlStatus-without-url')
hasErrors = true
end
end
if s.url.components then
if s.url.components.archiveDate then
if s.url.components.archiveDate.erroneous then
appendErrorField(s, 'wrongArchiveDateFormat', 'wrong-date-format')
hasErrors = true
end
elseif s.url.components.archiveUrl then
appendErrorField(s, 'archiveUrlWithoutArchiveDate', 'archiveUrl-without-archiveDate')
hasErrors = true
end
end
end
if s.publishedInUrl then
if s.publishedInUrl.value then
if urlIsArchive(s.publishedInUrl) then
appendErrorField(s, 'urlIsArchive', 'publishedIn-url-contains-archiveUrl')
hasErrors = true
end
elseif s.publishedInUrl.components then
if s.publishedInUrl.components.archiveUrl then
appendErrorField(s, 'archiveUrlWithoutUrl', 'publishedIn-archiveUrl-without-url')
hasErrors = true
end
if s.publishedInUrl.components.urlStatus then
appendErrorField(s, 'urlIsMissing', 'publishedIn-urlStatus-without-url')
hasErrors = true
end
end
if s.publishedInUrl.components then
if s.publishedInUrl.components.archiveDate then
if s.publishedInUrl.components.archiveDate.erroneous then
appendErrorField(s, 'wrongPublishedInArchiveDateFormat', 'wrong-date-format')
hasErrors = true
end
elseif s.publishedInUrl.components.archiveUrl then
appendErrorField(s, 'archiveUrlWithoutArchiveDate', 'publishedIn-archiveUrl-without-archiveDate')
hasErrors = true
end
end
end
if (not s.url or not s.url.value) and (not s.publishedInUrl or not s.publishedInUrl.value) then
if s.accessDate then
appendErrorField(s, 'urlIsMissing', 'accessDate-without-url')
hasErrors = true
end
end
if not s.title then
appendErrorField(s, 'titleIsMissing', 'missing-title')
hasErrors = true
end
if (s.publishedInUrl or s.publishedInSubtitle) and (not s.publishedIn or not s.publishedIn.value) then
appendErrorField(s, 'publishedInTitleIsMissing', 'missing-publishedIn')
hasErrors = true
end
if s.date and s.date.erroneous then
appendErrorField(s, 'wrongDateFormat', 'wrong-date-format')
hasErrors = true
end
if s.accessDate and s.accessDate.erroneous then
appendErrorField(s, 'wrongAccessDateFormat', 'wrong-date-format')
hasErrors = true
end
if s.lastUpdate and s.lastUpdate.erroneous then
appendErrorField(s, 'wrongLastUpdateFormat', 'wrong-date-format')
hasErrors = true
end
if s.id and not s.id.retrieved and not (s.publishedIn and s.publishedIn.entity) then
appendErrorField(s, 'idWithoutPublishedIn', 'id-without-publishedInEntity')
hasErrors = true
end
return hasErrors
end
local function categoriesAllowed()
if not mw.title.getCurrentTitle():inNamespaces(NS_MAIN, NS_TEMPLATE, NS_PROJECT) then
return false
end
return true
end
local function getErrorCategories(source, categories, templateName)
local categoriesStr = ''
for _, cat in ipairs(categories) do
local found = false
for _, fieldName in ipairs(cat) do
if source[fieldName] then
found = true
break
end
end
if found then
categoriesStr = categoriesStr .. '[[' .. mw.ustring.format(l10n('categories', cat.name), templateName) .. ']]'
end
end
return categoriesStr
end
function p.cite(frame)
local s = argsToSource(frame)
local langCode
if not s.offline or not s.offline.value then
s, langCode = source.fetch(s)
elseif s.lang then
if table.getn(s.lang) > 0 then
for _, lang in ipairs(s.lang) do
if lang.components and s.lang.components.langCode then
langCode = lang.components.langCode.value
break
end
end
else
if s.lang.components and s.lang.components.langCode then
langCode = s.lang.components.langCode.value
end
end
else
langCode = currentLangObj:getCode()
end
if s.url and s.url.value and s.waybackStamp then
if not s.url.components or (not s.url.components.archiveUrl or not s.url.components.archiveUrl.retrieved) then
if not s.url.components then
s.url.components = {}
end
s.url.components.archiveUrl = waybackStampToArchiveUrl(s.waybackStamp.value, s.url)
s.url.components.archiveDate = waybackStampToArchiveDate(s.waybackStamp.value)
end
end
local hasErrors = checkFields(s)
local args = frame.args
if (mw.isSubsting() or argIsSet(args.forceSubst)) and argIsEmpty(args.pureWikitext) then
local template, selfTitle = getTitles(frame)
local argsProfile = require(selfTitle .. '/Subst')
local langCode = s.lang and s.lang.components and s.lang.components.langCode
return '{{' .. getTemplateNameFromTitle(template) .. '|' .. formatter.format(argsProfile, s, langCode) .. '}}'
end
local formattedCitation = formatter.format(GostProfile, s, langCode)
local styles = ''
if stylesUsed then
styles = frame:extensionTag('templatestyles', '', { src = frame:getTitle() .. '/styles.css' })
end
local categoriesStr = ''
if not argIsSet(args.noCat) then
if hasErrors and categoriesAllowed() then
local template, selfTitle = getTitles(frame)
local templateName = getTemplateNameFromTitle(template)
local categories = {
{
'unknownFields',
name = 'Unknown parameters',
},
{
'archiveUrlWithoutUrl',
'archiveUrlWithoutArchiveDate',
'urlIsArchive',
name = 'Wrong archive url parameters',
},
{
name = 'Lang is not specified',
'langIsMissing',
},
{
'urlIsMissing',
name = 'URL is not specified',
},
{
'titleIsMissing',
name = 'Title is not specified',
},
{
'publishedInTitleIsMissing',
name = 'PublishedIn is not specified',
},
{
'wrongDateFormat',
'wrongAccessDateFormat',
'wrongLastUpdateFormat',
'wrongArchiveDate',
'wrongPublishedInArchiveDateFormat',
name = 'Wrong date format',
},
{
'idWithoutPublishedIn',
name = 'Id without publishedIn',
},
}
categoriesStr = getErrorCategories(s, categories, templateName)
end
end
return styles .. formattedCitation .. categoriesStr
end
return p