Перейти к содержанию
Главное меню
Главное меню
переместить в боковую панель
скрыть
Навигация
Заглавная страница
Указатели
Свежие правки
Случайная страница
Справка по MediaWiki
Народные Сказки
Поиск
Найти
Создать учётную запись
Войти
Персональные инструменты
Создать учётную запись
Войти
Страницы для неавторизованных редакторов
узнать больше
Вклад
Обсуждение
Редактирование:
Модуль:Sources/песочница
Модуль
Обсуждение
English
Читать
Править код
История
Инструменты
Инструменты
переместить в боковую панель
скрыть
Действия
Читать
Править код
История
Общие
Ссылки сюда
Связанные правки
Служебные страницы
Сведения о странице
Внимание:
Вы не вошли в систему. Ваш IP-адрес будет общедоступен, если вы запишете какие-либо изменения. Если вы
войдёте
или
создадите учётную запись
, её имя будет использоваться вместо IP-адреса, наряду с другими преимуществами.
Анти-спам проверка.
Не
заполняйте это!
---@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
Описание изменений:
Пожалуйста, учтите, что любой ваш вклад в проект «Народные Сказки» может быть отредактирован или удалён другими участниками. Если вы не хотите, чтобы кто-либо изменял ваши тексты, не помещайте их сюда.
Вы также подтверждаете, что являетесь автором вносимых дополнений, или скопировали их из источника, допускающего свободное распространение и изменение своего содержимого (см.
РуСказки:Авторские права
).
НЕ РАЗМЕЩАЙТЕ БЕЗ РАЗРЕШЕНИЯ ОХРАНЯЕМЫЕ АВТОРСКИМ ПРАВОМ МАТЕРИАЛЫ!
Отменить
Справка по редактированию
(в новом окне)
Отобразить/Скрыть ограниченную ширину содержимого