Модуль:Sources/песочница: различия между версиями
(Новая страница: «---@alias args table ---@alias frame { args: args, extensionTag: function } ---@alias source table<string, any> ---@alias value: string | { id: string } ---@alias snak { datatype: string, snaktype: string, datavalue: { type: string, value: value } } ---@alias snaks table<string, table<number, snak>> ---@alias statement { mainsnak: snak, rank: string, qualifiers: snaks } ---@alias statements table<string, table<number, statement>> ---@alias map table<string,...») |
Импортёр (обсуждение | вклад) м (1 версия импортирована) |
(нет различий)
|
Текущая версия от 21:41, 30 апреля 2024
Файл:Sandbox.png | Эта страница — песочница для модуля Модуль:Sources (разница с модулем). См. также сопроводительную подстраницу с контрольными примерами. |
Тесты[править код]
7 тестов провалено.
Тест | Ожидаемое значение | Фактическое значение | |
---|---|---|---|
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Ломоносов, Михаил Васильевич }} | Ломоносов М. В. | Ломоносов М. В. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Ломоносов, Михаил }} | Ломоносов М. | Ломоносов М. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Михаил Васильевич Ломоносов }} | Ломоносов М. В. | Ломоносов М. В. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Михаил Ломоносов }} | Ломоносов М. | Ломоносов М. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | М. В. Ломоносов }} | Ломоносов М. В. | Ломоносов М. В. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | М. Ломоносов }} | Ломоносов М. | Ломоносов М. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Ломоносов М. В. }} | Ломоносов М. В. | Ломоносов М. В. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Ломоносов М. }} | Ломоносов М. | Ломоносов М. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Топчибашев, Мустафа Агабек оглы }} | Топчибашев М. А. | Топчибашев М. А. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Гельмонт, Ян Баптиста ван }} | ван Гельмонт Я. Б. | ван Гельмонт Я. Б. |
✔ | {{#invoke:Sources | testPersonNameToAuthorName | Jan Baptista van Helmont }} | van Helmont J. B. | van Helmont J. B. |
Тест | Ожидаемое значение | Фактическое значение | |
---|---|---|---|
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Ломоносов, Михаил Васильевич }} | М. В. Ломоносов | М. В. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Ломоносов, Михаил }} | М. Ломоносов | М. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Михаил Васильевич Ломоносов }} | М. В. Ломоносов | М. В. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Михаил Ломоносов }} | М. Ломоносов | М. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | М. В. Ломоносов }} | М. В. Ломоносов | М. В. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | М. Ломоносов }} | М. Ломоносов | М. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Ломоносов М. В. }} | М. В. Ломоносов | М. В. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Ломоносов М. }} | М. Ломоносов | М. Ломоносов |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Топчибашев, Мустафа Агабек оглы }} | М. А. Топчибашев | М. А. Топчибашев |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Гельмонт, Ян Баптиста ван }} | Я. Б. ван Гельмонт | Я. Б. ван Гельмонт |
✔ | {{#invoke:Sources | testPersonNameToResponsibleName | Jan Baptista van Helmont }} | J. B. van Helmont | J. B. van Helmont |
Тест | Ожидаемое значение | Фактическое значение | |
---|---|---|---|
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q20750516}} | президент Российской Федерации Указ Президента Российской Федерации от 15 января 1992 г. № 23 «О Генеральном директоре Агентства федеральной безопасности Российской Федерации и Министре внутренних дел Российской Федерации» // Собрание законодательства Российской Федерации — 1992. | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#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. Camenisch — Springer, Berlin, Heidelberg, 2004. — 630 p. — ISBN 978-3-540-21935-4 — doi:10.1007/B97182 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q21683981}} | Nguyen P. 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. Camenisch — Springer, Berlin, Heidelberg, 2004. — P. 555—570. — 630 p. — ISBN 978-3-540-21935-4 — doi:10.1007/978-3-540-24676-3_33 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q21725400}} | Eichenauer J., Lehn J. A non-linear congruential pseudo random number generator (англ.) // Statistische Hefte — Springer Berlin Heidelberg, Springer Science+Business Media, 1986. — Vol. 27, Iss. 1. — P. 315—326. — ISSN 0932-5026; 1613-9798 — doi:10.1007/BF02932576 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#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 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Файл:X mark.svg | {{#invoke:Sources | renderSource | Q27450585}} | Введение в криптографию / под ред. В. В. Ященко — М.: МЦНМО, 2000. — 271 с. — ISBN 978-5-900916-26-2 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Тест | Ожидаемое значение | Фактическое значение | |
---|---|---|---|
Файл:X mark.svg | {{Source | 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 | Ошибка Lua в Модуль:Sources на строке 512: attempt to index field 'wikibase' (a nil value). |
Ошибка Lua на строке 450: attempt to index field 'wikibase' (a nil value).
Ошибка Lua на строке 450: attempt to index field 'wikibase' (a nil value).
Ошибка Lua на строке 450: attempt to index field 'wikibase' (a nil value).
[1]Ошибка Lua: not enough memory.
Ошибка Lua: not enough memory.
Генерирует сноски и ссылки на источники.
---@alias args table ---@alias frame { args: args, extensionTag: function } ---@alias source table<string, any> ---@alias value: string | { id: string } ---@alias snak { datatype: string, snaktype: string, datavalue: { type: string, value: value } } ---@alias snaks table<string, table<number, snak>> ---@alias statement { mainsnak: snak, rank: string, qualifiers: snaks } ---@alias statements table<string, table<number, statement>> ---@alias map table<string, string[]> ---@alias mwText { listToText: ( fun( list: table, separator: string, conjunction: string ): string ), trim: ( fun( text: string ): string ) } ---@alias mwWikibase { getAllStatements: ( fun( entityId: string, propertyId: string ): table<number, statement> ), getBestStatements: ( fun( entityId: string, propertyId: string ): table<number, statement> ), getEntityIdForCurrentPage: ( fun(): string ), getLabel: ( fun( entityId: string, language: string | nil ): string | nil ), getReferencedEntityId: ( fun( fromEntityId: string, propertyId: string, toEntityIds: string[] ): string | nil ), getSitelink: ( fun( entityId: string, project: string | nil ): string | nil ) } ---@type { clone: ( fun( in: table ): table ), hash: table, language: table, log: fun( text: string ), logObject: fun( object: any, name: string ), text: mwText, uri: { anchorEncode: function }, ustring: table, wikibase: mwWikibase } --local mw = require( 'mw' ) --mw.ustring = string ---@type table local p = {} ---@type table<string, string> local NORMATIVE_DOCUMENTS = { Q20754888 = 'Закон Российской Федерации', Q20754884 = 'Закон РСФСР', Q20873831 = 'Распоряжение Президента Российской Федерации', Q20873834 = 'Указ исполняющего обязанности Президента Российской Федерации', Q2061228 = 'Указ Президента Российской Федерации', } ---@type table<string, string> local LANG_CACHE = { Q150 = 'fr', Q188 = 'de', Q1321 = 'es', Q1860 = 'en', Q652 = 'it', Q7737 = 'ru', Q8798 = 'uk', } ---@type map local PROPERTY_MAP = { author = { 'P50', 'P2093' }, lang = { 'P407', 'P364' }, part = { 'P958', 'P1810' }, title = { 'P1476' }, subtitle = { 'P1680' }, url = { 'P953', 'P1065', 'P854', 'P973', 'P2699' }, editor = { 'P98' }, translator = { 'P655' }, [ 'publication-id' ] = { 'P1433' }, edition = { 'P393' }, publisher = { 'P123' }, place = { 'P291' }, volume = { 'P478' }, issue = { 'P433' }, dateOfCreation = { 'P571' }, dateOfPublication = { 'P577' }, pages = { 'P304' }, numberOfPages = { 'P1104' }, tirage = { 'P1092' }, isbn = { 'P212', 'P957' }, issn = { 'P236' }, -- accessdate = { 'P813' }, -- disable, creates duplicate references docNumber = { 'P1545' }, type = { 'P31' }, arxiv = { 'P818' }, doi = { 'P356' }, pmid = { 'P698' }, url = { 'P888' }, } ---@type map local PUBLICATION_PROPERTY_MAP = { title = { 'P1433', 'P1160', 'P1476' }, subtitle = { 'P1680' }, } -- table.insert( PROPERTY_MAP.url, 'P856' ) -- only as qualifier ---@type string[] local monthGen = { 'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря' } ---@type string local i18nDefaultLanguage = mw.language.getContentLanguage():getCode() p.i18nDefaultLanguage = i18nDefaultLanguage ---@type string local i18nEtAlDefault = ' et al.' ---@type table<string, string> local i18nEtAl = { ru = ' и др.', uk = ' та ін.', } ---@type table<string, string> local i18nEditors = { fr = '', de = 'Hrsg.: ', es = '', en = '', it = '', ru = 'под ред. ', uk = 'за ред. ', } ---@type table<string, string> local i18nTranslators = { fr = '', de = '', es = '', en = '', it = '', ru = 'пер. ', uk = 'пер. ', } ---@type table<string, string> local i18nVolume = { de = 'Vol.', fr = 'Vol.', es = 'Vol.', en = 'Vol.', it = 'Vol.', ru = 'Т.', uk = 'Т.', } ---@type table<string, string> local i18nIssue = { en = 'Iss.', ru = 'вып.', uk = 'вип.', } ---@type table<string, string> local i18nPages = { fr = 'P.', de = 'S.', es = 'P.', en = 'P.', it = 'P.', ru = 'С.', uk = 'С.', } ---@type table<string, string> local i18nNumberOfPages = { en = 'p.', ru = 'с.', } ---@type table<string, string> local i18nTirage = { en = 'ed. size: %d', ru = '%d экз.', } ---Returns formatted pair {Family name(s), First name(s)} ---@param fullName string ---@return table<number, string> 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, f2, i, i2, o 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 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 ) .. '. ' .. mw.ustring.sub( o, 1, 1 ) .. '.' } end f, f2, i = mw.ustring.match( fullName, start .. surname .. space .. surname .. comma .. name .. finish ) if f then mw.log( 'tokenizeName: «' .. fullName .. '»: have «Fa Fa, Im» match' ) return { f .. ' ' .. f2, mw.ustring.sub( i, 1, 1 ) .. '.' } end 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 ) .. '. ' .. mw.ustring.sub( o, 1, 1 ) .. '.' } end i, 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( i, 1, 1 ) .. '. ' .. 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 ---@type string[] local matched = { mw.ustring.match( fullName, pattern ) } if #matched ~= 0 then mw.log( 'tokenizeName: «' .. fullName .. '»: have «Im (x' .. k .. ') Fa» match' ) for j = 1, k do matched[ j ] = mw.ustring.sub( matched[ j ], 1, 1 ) end return { matched[ k + 1 ], table.concat( matched, '. ', 1, k ) .. '.' } end end mw.log( 'Unmatched any pattern: «' .. fullName .. '»' ) return { fullName } end ---@param fullName string ---@return string 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 ] .. ' ' .. tokenized[ 2 ] end end ---@param fullName string ---@return string 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 ] .. ' ' .. tokenized[ 1 ] end end ---@alias options { separator: string, conjunction: string, format: ( fun( src: string ): string ), nolinks: boolean, preferids: boolean, short: boolean } ---@type options local options_commas = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false, short = false, } ---@type options local options_commas_short = mw.clone( options_commas ) options_commas_short.short = true ---@type options local options_commas_it_short = mw.clone( options_commas_short ) options_commas_it_short.format = function( src ) return "''" .. src .. "''" end ---@type options local options_commas_nolinks = mw.clone( options_commas ) options_commas_nolinks.nolinks = true ---@type options local options_citetypes = { separator = ' ', conjunction = ' ', format = function( src ) return 'citetype_' .. src end, nolinks = true , preferids = true, short = false, } ---@type options local options_commas_authors = mw.clone( options_commas ) options_commas_authors.format = personNameToAuthorName ---@type options local options_commas_responsible = mw.clone( options_commas ) options_commas_responsible.format = personNameToResponsibleName ---@type options local options_ids = { separator = '; ', conjunction = '; ', format = function( id ) return id end, nolinks = true, preferids = false, short = false, } ---@type options local options_arxiv = mw.clone( options_ids ) options_arxiv.format = function( id ) return '[https://arxiv.org/abs/' .. id .. ' arXiv:' .. id .. ']' end ---@type options local options_doi = mw.clone( options_ids ) options_doi.format = function( doi ) return '[https://dx.doi.org/' .. doi .. ' doi:' .. doi .. ']' end ---@type options local options_issn = mw.clone( options_ids ) options_issn.format = function( issn ) return '[https://www.worldcat.org/issn/' .. issn .. ' ' .. issn .. ']' end ---@type options local options_pmid = mw.clone( options_ids ) options_pmid.format = function( pmid ) return '[https://www.ncbi.nlm.nih.gov/pubmed/?term=' .. pmid .. ' PMID:' .. pmid .. ']' end ---@param str string | nil ---@return boolean local function isEmpty( str ) return not str or #str == 0 end ---@param allQualifiers snaks ---@param qualifierPropertyId string ---@return string | nil local function getSingleStringQualifierValue( allQualifiers, qualifierPropertyId ) if not allQualifiers or not allQualifiers[ qualifierPropertyId ] then return nil end ---@type table<number, snak> local propertyQualifiers = allQualifiers[ qualifierPropertyId ] for _, qualifier in pairs( propertyQualifiers ) do if ( qualifier and qualifier.datatype == 'string' and qualifier.datavalue and qualifier.datavalue.type == 'string' and qualifier.datavalue.value ~= '' ) then return qualifier.datavalue.value end end return nil end ---@param result table ---@param resultProperty string ---@return void local function appendImpl_toTable( result, resultProperty ) if not result[ resultProperty ] then result[ resultProperty ] = {} elseif ( type( result[ resultProperty ] ) == 'string' or ( type( result[ resultProperty ] ) == 'table' and type( result[ resultProperty ].id ) == 'string' ) ) then result[ resultProperty ] = { result[ resultProperty ] } end end ---@param datavalue table ---@param qualifiers snaks ---@param result table ---@param propertyName string ---@param options table local function appendImpl( datavalue, qualifiers, result, propertyName, options ) if datavalue.type == 'string' then local value = getSingleStringQualifierValue( qualifiers, 'P1932' ) if not value then value = getSingleStringQualifierValue( qualifiers, 'P1810' ) end if not value then value = datavalue.value if options.format then value = options.format( value ) end end appendImpl_toTable( result, propertyName) local pos = getSingleStringQualifierValue( qualifiers, 'P1545' ) if pos then table.insert( result[ propertyName ], tonumber(pos), value ) else table.insert( result[ propertyName ], value ) end elseif datavalue.type == 'url' then local value = datavalue.value if options.format then value = options.format( value ) end appendImpl_toTable( result, propertyName) table.insert( result[ propertyName ], value ) elseif datavalue.type == 'monolingualtext' then local value = datavalue.value.text if options.format then value = options.format( value ) end appendImpl_toTable( result, propertyName) table.insert( result[ propertyName ], value ) elseif datavalue.type == 'quantity' then local value = datavalue.value.amount if ( mw.ustring.sub( value , 1, 1 ) == '+' ) then value = mw.ustring.sub( value , 2 ) end if options.format then value = options.format( value ) end appendImpl_toTable( result, propertyName) table.insert( result[ propertyName ], value ) elseif datavalue.type == 'wikibase-entityid' then local pos = getSingleStringQualifierValue( qualifiers, 'P1545' ) local value = datavalue.value appendImpl_toTable( result, propertyName) local label = getSingleStringQualifierValue( qualifiers, 'P1932' ) if not label then label = getSingleStringQualifierValue( qualifiers, 'P1810' ) end local toInsert = { id = value.id, label = label } if pos and tonumber( pos ) then table.insert( result[ propertyName ], tonumber( pos ), toInsert ) else table.insert( result[ propertyName ], toInsert ) end elseif datavalue.type == 'time' then local value = datavalue.value if options.format then value = options.format( value ) end appendImpl_toTable( result, propertyName) table.insert( result[ propertyName ], tostring( value.time ) ) end end ---@param entityId string ---@param propertyId string ---@return table<number, statement> local function getAllStatements( entityId, propertyId ) ---@type boolean, table<number, statement> local wdStatus, statements = pcall( mw.wikibase.getAllStatements, entityId, propertyId ) if wdStatus and statements then return statements end return {} end ---@param entityId string ---@param propertyId string ---@return table<number, statement> local function getBestStatements( entityId, propertyId ) ---@type boolean, table<number, statement> local wdStatus, statements = pcall( mw.wikibase.getBestStatements, entityId, propertyId ) if wdStatus and statements then return statements end return {} end ---@param entityId string ---@param projectToCheck string? ---@return string | nil local function getSitelink( entityId, projectToCheck ) ---@type boolean, string local wbStatus, sitelink if projectToCheck then wbStatus, sitelink = pcall( mw.wikibase.getSitelink, entityId, projectToCheck ) else wbStatus, sitelink = pcall( mw.wikibase.getSitelink, entityId ) end if not wbStatus then return nil end return sitelink end ---@param entityId string ---@param propertyId string ---@param result table ---@param propertyName string ---@param options table? ---@return void local function appendEntitySnaks( entityId, propertyId, result, propertyName, options ) options = options or {} -- do not populate twice if result[ propertyName ] and ( propertyName ~= 'author' or result[ propertyId ] ) then return end local statements = getBestStatements( entityId, propertyId ) if propertyName == 'author' then result[ propertyId ] = true end if propertyId == 'P1680' then -- if there is a russian for _, statement in pairs( statements ) do if statement and statement.mainsnak and statement.mainsnak.datavalue and statement.mainsnak.datavalue.value and statement.mainsnak.datavalue.value.language == i18nDefaultLanguage then --found russian string appendImpl( statement.mainsnak.datavalue, statement.qualifiers, result, propertyName, options ) return end end end for _, snak in pairs( statements ) do if snak and snak.mainsnak and snak.mainsnak.datavalue then appendImpl( snak.mainsnak.datavalue, snak.qualifiers, result, propertyName, options ) end end end ---@param claims table<number, statement> ---@param qualifierPropertyId string ---@param result table ---@param resultPropertyId string ---@param options table ---@return void local function appendQualifiers( claims, qualifierPropertyId, result, resultPropertyId, options ) -- do not populate twice if not claims or result[ resultPropertyId ] then return end for _, claim in pairs( claims ) do if claim.qualifiers and claim.qualifiers[ qualifierPropertyId ] then ---@type table<number, snak> local propertyQualifiers = claim.qualifiers[ qualifierPropertyId ] for _, qualifier in pairs( propertyQualifiers ) do if qualifier and qualifier.datavalue then appendImpl( qualifier.datavalue, nil, result, resultPropertyId, options ) end end end end end ---@param argName string ---@param arg any | nil ---@return void local function assertNotNull( argName, arg ) if not arg then error( argName .. ' is not specified' ) end end ---@param args any[] ---@return any | nil local function coalesce( args ) for _, arg in pairs( args ) do if not isEmpty( arg ) then return arg end end return nil end ---@param propertyId string ---@param strValue string ---@return snak local function toStringSnak( propertyId, strValue ) assertNotNull( 'propertyId', strValue ) assertNotNull( 'strValue', strValue ) return { snaktype = "value", property = propertyId, datatype = 'string', datavalue = { value = strValue, type = 'string', }, } end ---@param propertyId string ---@param strValue string ---@return snak local function toUrlSnak( propertyId, strValue ) assertNotNull( 'propertyId', strValue ) assertNotNull( 'strValue', strValue ) return { snaktype = 'value', property = propertyId, datatype = 'string', datavalue = { value = strValue, type = 'url', }, } end ---@param args args ---@param snaks snaks ---@return void local function copyArgsToSnaks( args, snaks ) if not isEmpty( args.part ) then snaks.P958 = { toStringSnak( 'P958', tostring( args.part ) ) } end if not isEmpty( args.pages ) then snaks.P304 = { toStringSnak( 'P304', tostring( args.pages ) ) } end if not isEmpty( args.issue ) then snaks.P433 = { toStringSnak( 'P433', tostring( args.issue ) ) } end if not isEmpty( args.volume ) then snaks.P478 = { toStringSnak( 'P478', tostring( args.volume ) ) } end if not isEmpty( args.url ) then snaks.P953 = { toUrlSnak( 'P953', tostring( args.url ) ) } end if not isEmpty( args.parturl ) then snaks.P953 = { toUrlSnak( 'P953', tostring( args.parturl ) ) } end end ---@param langEntityId string ---@return string | nil local function getLangCode( langEntityId ) if not langEntityId then return nil end local cached = LANG_CACHE[ langEntityId ] if cached then if cached == '' then return nil end return cached end local claims = getBestStatements( langEntityId, 'P424' ) for _, claim in pairs( claims ) do if claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value then LANG_CACHE[ langEntityId ] = claim.mainsnak.datavalue.value return claim.mainsnak.datavalue.value end end LANG_CACHE[ langEntityId ] = '' return nil end ---@param entityId string ---@param propertyId string ---@param value any ---@return table<number, statement> local function findClaimsByValue( entityId, propertyId, value ) local result = {} local claims = getAllStatements( entityId, propertyId ) for _, claim in pairs( claims ) do if ( claim.mainsnak and claim.mainsnak.datavalue ) then local datavalue = claim.mainsnak.datavalue if ( datavalue.type == "string" and datavalue.value == value ) or ( datavalue.type == "wikibase-entityid" and datavalue.value[ "entity-type" ] == "item" and tostring( datavalue.value.id ) == value ) then table.insert( result, claim ) end end end return result end ---@param data source ---@return void local function expandBookSeries( data ) local bookSeries = data.bookSeries if not bookSeries then return end -- use only first one if type( bookSeries ) == 'table' and bookSeries[ 1 ] and bookSeries[ 1 ].id then data.bookSeries = bookSeries[ 1 ] bookSeries = data.bookSeries end if not bookSeries or not bookSeries.id then return end appendEntitySnaks( bookSeries.id, 'P123', data, 'publisher', {} ) appendEntitySnaks( bookSeries.id, 'P291', data, 'place', {} ) appendEntitySnaks( bookSeries.id, 'P236', data, 'issn', {} ) end ---@param entityId string ---@param typeEntityId string ---@return boolean local function isInstanceOf( entityId, typeEntityId ) return findClaimsByValue( entityId, 'P31', typeEntityId )[ 1 ] ~= nil end ---@param entityId string ---@param typeEntityIds string[] ---@return string ---@todo Rewrite local function getFirstType( entityId, typeEntityIds ) for _, typeEntityId in pairs( typeEntityIds ) do if isInstanceOf( entityId, typeEntityId ) then return typeEntityId end end return nil end ---@param snaks snaks ---@param data source ---@param map map ---@return void local function populateDataFromSnaks( snaks, data, map ) for parameterName, propertyIds in pairs( map ) do for _, propertyId in pairs( propertyIds ) do if snaks[ propertyId ] then local options = {} if propertyId == 'P888' then options = { format = function( id ) return 'http://www.jstor.org/stable/' .. id end } end for _, snak in pairs( snaks[ propertyId ] ) do if snak and snak.datavalue then appendImpl( snak.datavalue, {}, data, parameterName, options ) end end end end end end ---@param entityId string | nil ---@param data source ---@param map map ---@return void local function populateDataFromEntity( entityId, data, map ) if not data.title then if not isEmpty( entityId ) then local optionsAsLinks = { format = function( text ) return { id = entityId, label = text } end } appendEntitySnaks( entityId, 'P1476', data, 'title', optionsAsLinks ) else appendEntitySnaks( entityId, 'P1476', data, 'title', {} ) end appendEntitySnaks( entityId, 'P1680', data, 'subtitle', {} ) end if claims and claims.P361 then ---@type table<number, statement> local propertyClaims = claims.P361 for _, claim in pairs( propertyClaims ) do if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.id ) then local possibleBookSeriesEntityId = claim.mainsnak.datavalue.value.id if isInstanceOf( possibleBookSeriesEntityId, 'Q277759' ) then appendImpl_toTable( data, 'bookSeries' ) table.insert( data.bookSeries, { id = possibleBookSeriesEntityId } ) appendQualifiers( { claim }, 'P478', data, 'bookSeriesVolume', {} ) appendQualifiers( { claim }, 'P433', data, 'bookSeriesIssue', {} ) end end end end for parameterName, propertyIds in pairs( map ) do for _, propertyId in pairs( propertyIds ) do local options = {} if propertyId == 'P888' then options = { format = function( id ) return 'http://www.jstor.org/stable/' .. id end } end appendEntitySnaks( entityId, propertyId, data, parameterName, options ) end end end ---@param entityId string ---@return string | nil local function getNormativeTitle( entityId ) local possibleTypeIds = {} for typeId, _ in pairs( NORMATIVE_DOCUMENTS ) do table.insert( possibleTypeIds, typeId ) end local foundTypeId = getFirstType( entityId, possibleTypeIds ) if foundTypeId then return NORMATIVE_DOCUMENTS[ foundTypeId ] end return nil end ---@param value any ---@return string | nil local function getSingle( value ) if type( value ) == 'string' then return tostring( value ) elseif type( value ) == 'table' then if value.id then return tostring( value.id ) end for _, tableValue in pairs( value ) do return getSingle( tableValue ) end end -- return '(unknown)' return nil end ---@param urls table<number, string> | string ---@param text string ---@return string local function wrapInUrl( urls, text ) local url = getSingle( urls ) if ( string.sub( url, 1, 1 ) == ':' ) then return '[[' .. url .. '|' .. text .. ']]' else return '[' .. url .. ' ' .. text .. ']' end end ---@param entityId string ---@param lang string ---@return string local function getElementLink( entityId, lang ) local sitelink = getSitelink( entityId, nil ) if sitelink then return ':' .. sitelink end if lang ~= 'mul' then -- link to entity in source language sitelink = getSitelink( entityId, lang .. 'wiki' ) if sitelink then return ':' .. lang .. ':' .. sitelink end end return ':d:' .. entityId end ---@param entityId string ---@param lang string ---@return string local function getLabel( entityId, lang ) local wbStatus, label = pcall( mw.wikibase.getLabel, entityId, lang ) if not wbStatus then return '' end return label or '' end ---@param lang string ---@param entityId string ---@param customTitle string ---@param options table local function renderLink( lang, entityId, customTitle, options ) if not entityId then error( 'entityId is not specified' ) end if type( entityId ) ~= 'string' then error( 'entityId is not string, but ' .. type( entityId ) ) end if type( customTitle or '' ) ~= 'string' then error( 'customTitle is not string, but ' .. type( customTitle ) ) end local title = customTitle -- ISO 4 if isEmpty( title ) then local propertyStatements = getBestStatements( entityId, 'P1160' ) for _, claim in pairs( propertyStatements ) do if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.language == lang ) then title = claim.mainsnak.datavalue.value.text mw.log( 'Got title of ' .. entityId .. ' from ISO 4 claim: «' .. title .. '»' ) break end end end -- official name P1448 -- short name P1813 if isEmpty( title ) and options.short then local propertyStatements = getBestStatements( entityId, 'P1813' ) for _, claim in pairs( propertyStatements ) do if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value.language == lang ) then title = claim.mainsnak.datavalue.value.text mw.log( 'Got title of ' .. entityId .. ' from short name claim: «' .. title .. '»' ) break end end end -- person name P1559 -- labels if isEmpty( title ) then title = getLabel( entityId, lang ) mw.log( 'Got title of ' .. entityId .. ' from label: «' .. title .. '»' ) end local actualText = title or '\'\'(untranslated)\'\'' local link = getElementLink( entityId, lang ) return wrapInUrl( link, actualText ) end ---@param lang string ---@param value value ---@param options options ---@return string local function toString( lang, value, options ) if type( value ) == 'string' then return options.format( value ) end if type( value ) ~= 'table' then return options.format( '(unknown type)' ) end if value.id then -- this is link if type( value.label or '' ) ~= 'string' then mw.logObject( value, 'value' ) error( 'label of table value is not string but ' .. type( value.label ) ) end local title if options.preferids then title = value.id elseif options.nolinks then title = value.label or getLabel( value.id, i18nDefaultLanguage ) else title = renderLink( lang, value.id, value.label, options ) end if title == '' then title = "''(untranslated title)''" end return options.format( title ) end local resultList = {} for _, tableValue in pairs( value ) do table.insert( resultList, toString( lang, tableValue, options ) ) end return mw.text.listToText( resultList, options.separator, options.conjunction ) end ---@param entityId string ---@param data source ---@return source local function populateSourceDataImpl( entityId, data, map ) local wsLink = getSitelink( entityId, 'ruwikisource' ) if wsLink and not mw.ustring.gmatch( wsLink, 'Категория:' ) then data.url = ":ru:s:" .. wsLink end populateDataFromEntity( entityId, data, PROPERTY_MAP ) local normativeTitle = getNormativeTitle( entityId ) if normativeTitle then local y, m, d = mw.ustring.match( getSingle( data.dateOfCreation ) , "(%-?%d+)%-(%d+)%-(%d+)T" ) y, m, d = tonumber( y ),tonumber( m ), tonumber( d ) local title = toString( 'ru', data.title, options_commas_nolinks ) local docNumber = getSingle( data.docNumber ) data.title = { normativeTitle .. " от " .. tostring( d ) .. " " .. monthGen[ m ] .. " " .. tostring( y ) .. " г." .. ( docNumber and ( " № " .. docNumber ) or '' ) .. ' «' .. title.. '»' } end if not data.title then local label = getLabel( entityId, i18nDefaultLanguage ) if label ~= '' then data.title = { label } end end return data end ---@param entityId string ---@param propertyId string ---@param data source ---@return void local function expandSpecialsQualifiers( entityId, propertyId, data ) local statements = getBestStatements( entityId, propertyId ) for _, statement in pairs( statements ) do populateDataFromSnaks( statement.qualifiers, data, PROPERTY_MAP ) end end ---Expand special types of references when additional data could be found in OTHER entity properties ---@param currentEntityId string ---@param snaks snaks ---@param data source ---@return void local function expandSpecials( currentEntityId, snaks, data ) local sourceId if ( snaks.P805 and snaks.P805[ 1 ] and snaks.P805[ 1 ].datavalue and snaks.P805[ 1 ].datavalue.value.id ) then sourceId = snaks.P805[ 1 ].datavalue.value.id elseif ( snaks.P248 and snaks.P248[ 1 ] and snaks.P248[ 1 ].datavalue and snaks.P248[ 1 ].datavalue.value.id ) then sourceId = snaks.P248[ 1 ].datavalue.value.id end if sourceId then data.sourceId = sourceId -- Gemeinsame Normdatei -- specified by P227 if sourceId == 'Q36578' then appendEntitySnaks( currentEntityId, 'P227', data, 'part', { format = function(gnd ) return 'Record #' .. gnd; end } ) appendEntitySnaks( currentEntityId, 'P227', data, 'url', { format = function(gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } ) data.year = '2012—2016' expandSpecialsQualifiers( currentEntityId, 'P227', data ) -- BNF -- specified by P268 elseif sourceId == 'Q15222191' then appendEntitySnaks( currentEntityId, 'P268', data, 'part', { format = function(id ) return 'Record #' .. id; end } ) appendEntitySnaks( currentEntityId, 'P268', data, 'url', { format = function(id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end } ) expandSpecialsQualifiers( currentEntityId, 'P268', data ) -- VIAF -- specified by P214 elseif sourceId == 'Q54919' then appendEntitySnaks( currentEntityId, 'P214', data, 'part', { format = function(id ) return 'Record #' .. id; end } ) appendEntitySnaks( currentEntityId, 'P214', data, 'url', { format = function(id ) return 'https://viaf.org/viaf/' .. id; end } ) expandSpecialsQualifiers( currentEntityId, 'P214', data ) -- generic property search else for _, sourceClaim in pairs( getBestStatements( sourceId, 'P1687' ) ) do if sourceClaim.mainsnak.snaktype == 'value' then local sourcePropertyId = sourceClaim.mainsnak.datavalue.value.id for _, sourcePropertyClaim in pairs( getBestStatements( sourcePropertyId, 'P1630' ) ) do if ( sourcePropertyClaim.mainsnak.snaktype == 'value' ) then appendEntitySnaks( currentEntityId, sourcePropertyId, data, 'url', { format = function( id ) return mw.ustring.gsub( mw.ustring.gsub( sourcePropertyClaim.mainsnak.datavalue.value, '$1', id ), ' ', '%%20' ) end } ) expandSpecialsQualifiers( currentEntityId, sourcePropertyId, data ) break end end end end end -- do we have appropriate record in P1433 ? local claims = findClaimsByValue( currentEntityId, 'P1343', sourceId ) if claims and #claims ~= 0 then for _, claim in pairs( claims ) do populateDataFromSnaks( claim.qualifiers, data, PROPERTY_MAP ) populateDataFromEntity( sourceId, data, PROPERTY_MAP ) end end end end ---@param text string ---@param tip string ---@return string local function toTextWithTip( text, tip ) return '<span title="' .. tip .. '" style="border-bottom: 1px dotted; cursor: help; white-space: nowrap">' .. text .. '</span>' end ---@param lang string ---@param placeId string ---@return string local function getPlaceName( placeId, lang ) -- ГОСТ Р 7.0.12—2011 if lang == 'ru' then if placeId == 'Q649' then return toTextWithTip( 'М.', 'Москва' ); end if placeId == 'Q656' then return toTextWithTip( 'СПб.', 'Санкт-Петербург' ); end if placeId == 'Q891' then return toTextWithTip( 'Н. Новгород', 'Нижний Новгород' ); end if placeId == 'Q908' then return toTextWithTip( 'Ростов н/Д.', 'Ростов-на-Дону' ); end end return nil end ---@param src source ---@param lang string ---@return void local function preprocessPlace( src, lang ) if not src.place then return end ---@type table<number, string> local newPlace = {} for index, place in pairs( src.place ) do if place.id then local newPlaceStr = getPlaceName( place.id, lang ) if newPlaceStr then newPlace[ index ] = newPlaceStr else newPlace[ index ] = getLabel( place.id, lang ) end else newPlace[ index ] = place end end src.place = newPlace end ---@param entityId string ---@param lang string ---@param providedLabel string | nil ---@param options options ---@return string local function getPersonNameAsLabel( entityId, lang, providedLabel, options ) -- would custom label provided we don't need to check entity at all if not isEmpty( providedLabel ) then mw.log( 'Custom label provided for ' .. entityId ) return options.format( providedLabel ) end if lang == 'mul' then lang = i18nDefaultLanguage end ---@type string | nil local personName = getLabel( entityId, lang ) if personName then mw.log( 'Got person name of ' .. entityId .. ' from label: «' .. personName .. '»' ) end if isEmpty( personName ) then return '\'\'(not translated to ' .. lang .. ')\'\'' end mw.logObject( providedLabel, 'providedLabel' ) mw.logObject( entityId, 'entityId' ) mw.logObject( isInstanceOf( entityId, 'Q5' ), 'isInstanceOf' ) if not isInstanceOf( entityId, 'Q5' ) then mw.log( 'Entity ' .. entityId .. ' is not a person' ) return personName end return options.format( personName ) end ---@param entityId string ---@param lang string ---@param customLabel string | nil ---@param options options ---@return string local function getPersonNameAsWikitext( entityId, lang, customLabel, options ) local personName = getPersonNameAsLabel( entityId, lang, customLabel, options ) local link = getElementLink( entityId, lang ) return wrapInUrl( link, personName ) end ---@param value value ---@param lang string ---@param options options ---@return string local function getPeopleAsWikitext( value, lang, 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 tostring( value.id ) else if options.nolinks then return getPersonNameAsLabel( value.id, lang, value.label, options ) else return getPersonNameAsWikitext( value.id, lang, 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 _, tableValue in pairs( value ) do local nextWikitext = getPeopleAsWikitext( tableValue, lang, options ) if not 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[ 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 ---@param lang string ---@param src source ---@return string local function generateAuthorLinks( lang, src ) local result = '' if src.author then result = getPeopleAsWikitext( src.author, lang, options_commas_authors ) result = '<i class="wef_low_priority_links">' .. result .. '</i> ' end return result end ---@param lang string ---@param src source ---@param conjunction string ---@param propertyName string ---@param urlPropertyName string? ---@return string local function appendProperty( lang, src, conjunction, propertyName, urlPropertyName ) if not src[ propertyName ] then return '' end local out if urlPropertyName and src[ urlPropertyName ] then out = conjunction .. wrapInUrl( src[ urlPropertyName ], toString( lang, src[ propertyName ], options_commas_nolinks ) ) else out = toString( lang, src[ propertyName ], options_commas ) end if not out or out == '' then return '' end return conjunction .. out end ---@param lang string ---@param src source ---@return string local function appendTitle( lang, src ) local conjunction = '' local result = '' if src.part then result = result .. appendProperty( lang, src, '', 'part', 'parturl' ) conjunction = ' // ' end result = result .. appendProperty( lang, src, conjunction, 'title', 'url' ) return result end ---@param lang string ---@return string local function appendLanguage( lang ) if lang == i18nDefaultLanguage then return '' end ---@type { getRefHtml: ( fun( lang: string ): string ) } local langs = require( 'Module:Languages' ) return langs.list_ref( p.currentFrame:newChild{ args = { lang } } ) end ---@param lang string ---@param src source ---@return string local function appendSubtitle( lang, src ) return appendProperty( lang, src, ': ', 'subtitle', nil ) end ---@param lang string ---@param src source ---@return string local function appendOriginalTitle( lang, src ) return appendProperty( lang, src, ' = ', 'originaltitle', nil ) end ---@param lang string ---@param src source ---@return string local function appendPublication( lang, src ) if not src.publication then return '' end local result = ' // ' .. toString( lang, src.publication.title, options_commas_it_short ) if src.publication.subtitle and src.publication.subtitle ~= '' then result = result .. ': ' .. toString( lang, src.publication.subtitle, options_commas_it_short ) end return result end ---@param lang string ---@param src source ---@return string local function appendEditor( lang, src ) if not src.editor and not src.translator then return '' end local result = ' / ' if src.editor then local prefix = i18nEditors[ lang ] or i18nEditors[ i18nDefaultLanguage ] result = result .. prefix .. getPeopleAsWikitext( src.editor, lang, options_commas_responsible ) if src.translator then result = result .. ', ' end end if src.translator then local prefix = i18nTranslators[ lang ] or i18nTranslators[ i18nDefaultLanguage ] result = result .. prefix .. getPeopleAsWikitext( src.translator, lang, options_commas_responsible ) end return result end ---@param lang string ---@param src source local function appendEdition( lang, src ) return appendProperty( lang, src, ' — ', 'edition', nil ) end ---@param lang string ---@param src source ---@return string local function appendPublicationData( lang, src ) if not src.place and not src.publisher and not src.year then return '' end local result = ' — ' if src.place then result = result .. toString( lang, src.place, options_commas_short ) if src.publisher or src.year then result = result .. ': ' end end if src.publisher then result = result .. toString( lang, src.publisher, options_commas_short ) if src.year then result = result .. ', ' end end if src.year then result = result .. toString( lang, src.year, options_commas ) end result = result .. '.' return result end ---@param lang string ---@param src source ---@return string local function appendVolumeAndIssue( lang, src ) if not src.volume and not src.issue then return '' end local result = ' — ' local letter_vol = i18nVolume[ lang ] or i18nVolume[ i18nDefaultLanguage ] local letter_iss = i18nIssue[ lang ] or i18nIssue[ i18nDefaultLanguage ] if src.volume then result = result .. appendProperty( lang, src, letter_vol .. ' ', 'volume', nil ) result = result ..appendProperty( lang, src, ', ' .. letter_iss .. ' ', 'issue', nil ) else result = result .. appendProperty( lang, src, letter_iss .. ' ', 'issue', nil ) end result = result .. '.' return result end ---@param lang string ---@param src source ---@return string local function appendPages( lang, src ) if not src.pages then return '' end local letter = i18nPages[ lang ] or i18nPages[ i18nDefaultLanguage ] local strPages = toString( lang, src.pages, options_commas ) strPages = mw.ustring.gsub( strPages, '[-—]', '—' ) return ' — ' .. letter .. ' ' .. strPages .. '.' end ---@param lang string ---@param src source ---@return string local function appendNumberOfPages( lang, src ) if not src.numberOfPages then return '' end local letter = i18nNumberOfPages[ lang ] or i18nNumberOfPages[ i18nDefaultLanguage ] return appendProperty( lang, src, ' — ', 'numberOfPages', nil ) .. ' ' .. letter end ---@param lang string ---@param src source ---@return string local function appendBookSeries( lang, src ) if not src.bookSeries then return '' end local result = appendProperty( lang, src, ' — (', 'bookSeries', nil ) if src.bookSeriesVolume or src.bookSeriesIssue then result = result .. '; ' local letter_vol = i18nVolume[ lang ] or i18nVolume[ i18nDefaultLanguage ] local letter_iss = i18nIssue[ lang ] or i18nIssue[ i18nDefaultLanguage ] if src.bookSeriesVolume then result = result .. appendProperty( lang, src, letter_vol .. ' ', 'bookSeriesVolume', nil ) result = result .. appendProperty( lang, src, ', ' .. letter_iss .. ' ', 'bookSeriesIssue', nil ) else result = result .. appendProperty( lang, src, letter_iss .. ' ', 'bookSeriesIssue', nil ) end end result = result .. ')' return result end ---@param lang string ---@param src source ---@return string local function appendTirage( lang, src ) if not src.tirage then return '' end local tirageTemplate = i18nTirage[ lang ] or i18nTirage[ i18nDefaultLanguage ] ---@type options local optionsTirage = { separator = '; ', conjunction = '; ', format = function( data ) return tostring( mw.ustring.format( tirageTemplate, data ) ) end, short = false, nolinks = false, preferids = false, } return ' — ' .. toString( lang, src.tirage, optionsTirage ) end ---@param lang string ---@param value string | nil ---@param options options ---@param prefix string? ---@return string local function appendIdentifier( lang, value, options, prefix ) if not value then return '' end return ' — ' .. ( prefix or '' ) .. toString( lang, value, options ) end ---@param result string ---@param lang string ---@param src source ---@return string local function wrapSourceId( result, lang, src ) local citeType = src.type and toString( lang, src.type, options_citetypes ) or 'citetype_unknown' return '<span class="wikidata_cite ' .. citeType .. '" data-entity-id="' .. getSingle( src.sourceId ) .. '">' .. result .. '</span>' end ---@param src source ---@return string local function appendAccessDate( src ) if not src.accessdate then return '' end local date = 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 ) local date_str = ( d > 0 and ' ' .. tostring( d ) or '' ) .. ( m > 0 and ' ' .. monthGen[ m ] or '' ) .. ( y > 0 and ' ' .. tostring( y ) or '' ) return " <small>Проверено" .. date_str .. ".</small>" end ---@param src source ---@param lang string ---@return void local function populateUrl( src, lang ) if src.sourceId and not src.url then local sitelink = getSitelink( src.sourceId, lang .. 'wikisource' ) if sitelink then src.url = ':' .. lang .. ':s:' .. sitelink end end end ---@param src source ---@return void local function populateYear( src ) if not src.year and src.dateOfPublication then local date = getSingle( src.dateOfPublication ) src.year = mw.ustring.sub( date, 2, 5 ) end if not src.year and src.dateOfCreation then local date = getSingle( src.dateOfCreation ) src.year = mw.ustring.sub( date, 2, 5 ) end end ---@param src source ---@return void local function populateTitle( src ) src.title = src.title or getSingle( src.url ) or '\'\'(unspecified title)\'\'' end ---@param src source ---@return string local function renderSource( src ) options_commas_authors.format = personNameToAuthorName options_commas_responsible.format = personNameToResponsibleName local lang = getLangCode( getSingle( src.lang ) ) or i18nDefaultLanguage preprocessPlace( src, lang ) populateUrl( src, lang ) populateTitle( src ) populateYear( src ) local result = generateAuthorLinks( lang, src ) result = result .. appendTitle( lang, src ) result = result .. appendLanguage( lang ) result = result .. appendSubtitle( lang, src ) result = result .. appendOriginalTitle( lang, src ) result = result .. appendPublication( lang, src ) result = result .. '<span class="wef_low_priority_links">' result = result .. appendEditor( lang, src ) -- Might take current editor instead of actual. Use with caution result = result .. appendEdition( lang, src ) result = result .. appendPublicationData( lang, src ) result = result .. appendVolumeAndIssue( lang, src ) result = result .. appendPages( lang, src ) result = result .. appendNumberOfPages( lang, src ) result = result .. appendBookSeries( lang, src ) result = result .. appendTirage( lang, src ) result = result .. appendIdentifier( lang, src.isbn, options_commas, 'ISBN ' ) result = result .. appendIdentifier( lang, src.issn, options_issn, 'ISSN ' ) result = result .. appendIdentifier( lang, src.doi, options_doi, nil ) result = result .. appendIdentifier( lang, src.pmid, options_pmid, nil ) result = result .. appendIdentifier( lang, src.arxiv, options_arxiv, nil ) result = result .. appendAccessDate( src ) result = result .. '</span>' return wrapSourceId( result, lang, src ) end ---@param data source Данные в простом формате, согласованном с модулями формирования библиографического описания ---@param snaks snaks ---@return string | nil local function renderReferenceImpl( data, snaks ) -- забрать данные из reference populateDataFromSnaks( snaks, data, PROPERTY_MAP ) populateDataFromEntity( data.sourceId, data, PROPERTY_MAP ) expandSpecials( data.sourceId, snaks, data ) populateSourceDataImpl( data.sourceId, data, PROPERTY_MAP ) if data[ 'publication-id' ] then local publicationId = getSingle( data[ 'publication-id' ] ) data.publication = { sourceId = publicationId, title = data[ 'publication-title' ], subtitle = data[ 'publication-subtitle' ], } populateDataFromSnaks( snaks, data.publication, PUBLICATION_PROPERTY_MAP ) populateDataFromEntity( publicationId, data.publication, PUBLICATION_PROPERTY_MAP ) if type( data.publication.title ) == 'table' and data.publication.title[ 1 ] then data.publication.title = data.publication.title[ 1 ] end if type( data.publication.subtitle ) == 'table' and data.publication.subtitle[ 1 ] then data.publication.subtitle = data.publication.subtitle[ 1 ] end end expandBookSeries( data ) if next( data ) == nil then return nil end local rendered = renderSource( data ) if mw.ustring.len( rendered ) == 0 then return nil end if data.ref then local anchorValue = 'CITEREF' .. data.ref .. ( coalesce( { data[ 'ref-year' ], data.year } ) or '' ) rendered = '<span class="citation" id="' .. mw.uri.anchorEncode( anchorValue ) .. '">' .. rendered .. '</span>' end return rendered end ---@param args args ---@param snaks snaks ---@return string | nil local function getEntityId( args, snaks ) if args[ 1 ] then return mw.text.trim( args[ 1 ] ) end ---@type table<number, snak> local propertySnaks = snaks.P248 or snaks.P805 or {} for _, snak in pairs( propertySnaks ) do if snak.datavalue.type == 'value' then return tostring( snak.datavalue.value.id ) end end return nil end ---@param frame frame ---@param currentEntityId string ---@param reference table ---@return string | nil function p.renderSource( frame, currentEntityId, reference ) reference = reference or {} p.currentFrame = frame ---@type source local data = mw.clone( frame.args or {} ) data.sourceId = currentEntityId -- template call {{source[-ref]|Q123}} if not data.sourceId or data.sourceId == '' then data.sourceId = getEntityId( data, reference.snaks or {} ) if not data.sourceId then return '' end if reference.snaks then reference.snaks.P248 = nil reference.snaks.P805 = nil end end ---@type string local rendered = renderReferenceImpl( data, reference.snaks or {} ) if not rendered then return '' end return rendered end ---@param frame frame ---@param currentEntityId string ---@param reference table ---@return string function p.renderReference( frame, currentEntityId, reference ) local rendered = p.renderReference( frame, currentEntityId, reference ) if not rendered or rendered == '' then return '' end -- Про выбор алгоритма хеширования см. [[Модуль:Hash]]. Знак подчёркивания в начале позволяет -- исключить ошибку, когда имя сноски — чисто числовое значение, каковыми иногда бывают хеши. return frame:extensionTag( 'ref', rendered, { name = '_' .. mw.hash.hashValue( 'fnv164', rendered ) } ) .. '[[Category:Википедия:Статьи с источниками из Викиданных]]' end return p