Перейти к содержанию
Главное меню
Главное меню
переместить в боковую панель
скрыть
Навигация
Заглавная страница
Указатели
Свежие правки
Случайная страница
Справка по MediaWiki
Народные Сказки
Поиск
Найти
Создать учётную запись
Войти
Персональные инструменты
Создать учётную запись
Войти
Страницы для неавторизованных редакторов
узнать больше
Вклад
Обсуждение
Редактирование:
Модуль:Sources
Модуль
Обсуждение
English
Читать
Править код
История
Инструменты
Инструменты
переместить в боковую панель
скрыть
Действия
Читать
Править код
История
Общие
Ссылки сюда
Связанные правки
Служебные страницы
Сведения о странице
Внимание:
Вы не вошли в систему. Ваш IP-адрес будет общедоступен, если вы запишете какие-либо изменения. Если вы
войдёте
или
создадите учётную запись
, её имя будет использоваться вместо IP-адреса, наряду с другими преимуществами.
Анти-спам проверка.
Не
заполняйте это!
---@alias args table ---@alias frame { args: args, extensionTag: function, newChild: ( fun( args: args ): frame ) } ---@alias source { publication: source, [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 { name: string, ids: 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 = { { name = 'sourceId', ids = { 'P248', 'P805' } }, { name = 'lang', ids = { 'P407', 'P364' } }, { name = 'author', ids = { 'P50', 'P2093' } }, { name = 'part', ids = { 'P958', 'P1810' } }, { name = 'title', ids = { 'P1476' } }, { name = 'subtitle', ids = { 'P1680' } }, { name = 'url', ids = { 'P953', 'P1065', 'P854', 'P973', 'P2699', 'P888' } }, { name = 'editor', ids = { 'P98' } }, { name = 'translator', ids = { 'P655' } }, { name = 'publication-id', ids = { 'P1433' } }, { name = 'edition', ids = { 'P393' } }, { name = 'publisher', ids = { 'P123' } }, { name = 'place', ids = { 'P291' } }, { name = 'volume', ids = { 'P478' } }, { name = 'issue', ids = { 'P433' } }, { name = 'dateOfCreation', ids = { 'P571' } }, { name = 'dateOfPublication', ids = { 'P577' } }, { name = 'pages', ids = { 'P304' } }, { name = 'numberOfPages', ids = { 'P1104' } }, { name = 'tirage', ids = { 'P1092' } }, { name = 'isbn', ids = { 'P212', 'P957' } }, { name = 'issn', ids = { 'P236' } }, -- { name = 'accessdate', ids = { 'P813' } }, -- disable, creates duplicate references { name = 'docNumber', ids = { 'P1545' } }, { name = 'type', ids = { 'P31' } }, { name = 'arxiv', ids = { 'P818' } }, { name = 'doi', ids = { 'P356' } }, { name = 'pmid', ids = { 'P698' } }, } -- table.insert( PROPERTY_MAP.url, 'P856' ) -- only as qualifier ---@type map local PUBLICATION_PROPERTY_MAP = mw.clone( PROPERTY_MAP ) ---@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 экз.', } ---@param args args ---@return source local function getFilledArgs( args ) ---@type source local data = {} for key, value in pairs( args ) do if mw.text.trim( value ) ~= '' then if key == 1 then key = 'sourceId' end data[ key ] = mw.text.trim( value ) end end return data end ---Returns formatted pair {Family name(s), First name(s)} ---@param fullName string ---@return table<number, string> local function tokenizeName( fullName ) 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 surnamePrefixes = { 'ван', 'van', 'де', 'de' } local nm, nm2, srn, srn2, pref fullName = ' ' .. fullName .. ' ' fullName = mw.ustring.gsub( fullName, ' оглы ', ' ' ) fullName = mw.text.trim( fullName ) -- Surname, Name srn, nm = mw.ustring.match( fullName, '^' .. surname .. ',' .. space .. name .. '$' ) if srn then return { srn, mw.ustring.sub( nm, 1, 1 ) .. '.' } end -- Surname, Name prefix for _, surnamePrefix in pairs( surnamePrefixes ) do srn, nm, pref = mw.ustring.match( fullName, '^' .. surname .. ',' .. space .. name .. space .. '(' .. surnamePrefix .. ')' .. '$' ) if srn then return { mw.ustring.sub(pref) .. ' ' .. srn, mw.ustring.sub(nm, 1, 1 ) .. '. ' .. mw.ustring.sub(nm2, 1, 1 ) .. '.' } end end -- Surname, Name Name srn, nm, nm2 = mw.ustring.match( fullName, '^' .. surname .. ',' .. space .. name .. space .. name .. '$' ) if srn then return { srn, mw.ustring.sub( nm, 1, 1 ) .. '. ' .. mw.ustring.sub( nm2, 1, 1 ) .. '.' } end -- Surname Surname, Name srn, srn2, nm = mw.ustring.match( fullName, '^' .. surname .. space .. surname .. ',' .. space .. name .. '$' ) if srn then return { srn .. ' ' .. srn2, mw.ustring.sub( nm, 1, 1 ) .. '.' } end -- Name Name Surname nm, nm2, srn = mw.ustring.match( fullName, '^' .. name .. space .. name .. space .. surname .. '$' ) if srn then return { srn, mw.ustring.sub( nm, 1, 1 ) .. '. ' .. mw.ustring.sub( nm2, 1, 1 ) .. '.' } end -- Name Name prefix Surname for _, surnamePrefix in pairs( surnamePrefixes ) do nm, nm2, pref, srn = mw.ustring.match( fullName, '^' .. name .. space .. name .. space .. '(' .. surnamePrefix .. ')' .. space .. surname .. '$' ) if srn then return { mw.ustring.sub( pref ) .. ' ' .. srn, mw.ustring.sub( nm, 1, 1 ) .. '. ' .. mw.ustring.sub( nm2, 1, 1 ) .. '.' } end end -- Surname, Name Name prefix for _, surnamePrefix in pairs( surnamePrefixes ) do srn, nm, nm2, pref = mw.ustring.match( fullName, '^' .. surname .. ',' .. space .. name .. space .. name .. space .. '(' .. surnamePrefix .. ')' .. '$' ) if srn then return { mw.ustring.sub(pref) .. ' ' .. srn, mw.ustring.sub(nm, 1, 1 ) .. '. ' .. mw.ustring.sub(nm2, 1, 1 ) .. '.' } end end -- Name{1,4} Surname for k = 1, 4 do local pattern = '^' .. string.rep( name .. space, k ) .. surname .. '$' ---@type string[] local matched = { mw.ustring.match( fullName, pattern ) } if #matched ~= 0 then 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 -- Surname Name{1,4} for k = 1, 4 do local pattern = '^' .. surname .. string.rep( space .. name, k ) .. '$' ---@type string[] local matched = { mw.ustring.match( fullName, pattern ) } if #matched ~= 0 then for j = 2, k + 1 do matched[ j ] = mw.ustring.sub( matched[ j ], 1, 1 ) end return { matched[ 1 ], table.concat( matched, '. ', 2, k + 1 ) .. '.' } end end return { fullName } end ---@param fullName string | nil ---@return string | nil local function personNameToAuthorName( fullName ) if not fullName then return nil end local tokenized = tokenizeName( fullName ) if #tokenized == 1 then return tokenized[ 1 ] end return tokenized[ 1 ] .. ' ' .. tokenized[ 2 ] end ---@param fullName string | nil ---@return string | nil local function personNameToResponsibleName( fullName ) if not fullName then return nil end local tokenized = tokenizeName( fullName ) if #tokenized == 1 then return tokenized[ 1 ] end return tokenized[ 2 ] .. ' ' .. tokenized[ 1 ] end ---@alias options { separator: string, conjunction: string, format: ( fun( data: string ): string ), nolinks: boolean, preferids: boolean, short: boolean } ---@type options local options_commas = { separator = ', ', conjunction = ', ', format = function( data ) return data 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( data ) return "''" .. data .. "''" 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( data ) return 'citetype_' .. data 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 data table ---@param resultProperty string ---@return void local function appendImpl_toTable( data, resultProperty ) if not data[ resultProperty ] then data[ resultProperty ] = {} elseif ( type( data[ resultProperty ] ) == 'string' or ( type( data[ resultProperty ] ) == 'table' and type( data[ resultProperty ].id ) == 'string' ) ) then data[ resultProperty ] = { data[ resultProperty ] } end end ---@param datavalue table ---@param qualifiers snaks ---@param data table ---@param propertyName string ---@param options table local function appendImpl( datavalue, qualifiers, data, propertyName, options ) data[ propertyName ] = data[ propertyName ] or {} if propertyName == 'issn' then table.insert( data[ propertyName ], datavalue.value ) elseif propertyName == 'url' or datavalue.type == 'url' then local value = datavalue.value if options.format then value = options.format( value ) end appendImpl_toTable( data, propertyName ) table.insert( data[ propertyName ], value ) elseif 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(data, propertyName) local pos = getSingleStringQualifierValue( qualifiers, 'P1545' ) if pos then table.insert( data[ propertyName ], tonumber(pos), value ) else table.insert( data[ propertyName ], value ) end elseif datavalue.type == 'monolingualtext' then local value = datavalue.value.text if options.format then value = options.format( value ) end appendImpl_toTable( data, propertyName ) table.insert( data[ 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( data, propertyName ) table.insert( data[ propertyName ], value ) elseif datavalue.type == 'wikibase-entityid' then local pos = getSingleStringQualifierValue( qualifiers, 'P1545' ) local value = datavalue.value appendImpl_toTable(data, 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( data[ propertyName ], tonumber( pos ), toInsert ) else table.insert( data[ propertyName ], toInsert ) end elseif datavalue.type == 'time' then local value = datavalue.value if options.format then value = options.format( value ) end appendImpl_toTable( data, propertyName ) table.insert( data[ 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 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 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 langEntityId string ---@return string | nil local function getLangCode( langEntityId ) if not langEntityId then return nil end langEntityId = getSingle( langEntityId ) if not string.match( langEntityId, '^Q%d+$' ) then return langEntityId 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 data source ---@param propertyName string ---@param options table? ---@return void local function appendEntitySnaks( entityId, propertyId, data, propertyName, options ) options = options or {} -- do not populate twice if data[ propertyName ] and ( propertyName ~= 'author' or data[ propertyId ] ) then return end local statements = getBestStatements( entityId, propertyId ) if propertyName == 'author' then data[ propertyId ] = true end local lang = getLangCode( data.lang ) or i18nDefaultLanguage if propertyId == 'P1680' then -- if there is a default language 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 == lang then --found default language string appendImpl( statement.mainsnak.datavalue, statement.qualifiers, data, propertyName, options ) return end end end for _, statement in pairs( statements ) do if statement and statement.mainsnak and statement.mainsnak.datavalue then appendImpl( statement.mainsnak.datavalue, statement.qualifiers or {}, data, propertyName, options ) if propertyName == 'publication-id' and statement.qualifiers then data[ 'publication-qualifiers' ] = statement.qualifiers end 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 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 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 _, row in ipairs( map ) do local parameterName, propertyIds = row.name, row.ids 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 local bookSeriesStatements = getBestStatements( entityId, 'P361' ) for _, statement in pairs( bookSeriesStatements ) do if statement and statement.mainsnak and statement.mainsnak.datavalue and statement.mainsnak.datavalue.value and statement.mainsnak.datavalue.value.id then local possibleBookSeriesEntityId = statement.mainsnak.datavalue.value.id if isInstanceOf( possibleBookSeriesEntityId, 'Q277759' ) then appendImpl_toTable( data, 'bookSeries' ) table.insert( data.bookSeries, { id = possibleBookSeriesEntityId } ) appendQualifiers( { statement }, 'P478', data, 'bookSeriesVolume', {} ) appendQualifiers( { statement }, 'P433', data, 'bookSeriesIssue', {} ) end end end for _, row in ipairs( map ) do local parameterName, propertyIds = row.name, row.ids 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 data source ---@return void local function expandPublication( data ) if not data[ 'publication-id' ] then return end local publicationId = getSingle( data[ 'publication-id' ] ) data.publication = {} for key, value in pairs( data ) do if not string.match( key, '^publication-' ) then data.publication[ key ] = value end end data.publication.sourceId = publicationId data.publication.title = data[ 'publication-title' ] data.publication.subtitle = data[ 'publication-subtitle' ] if data[ 'publication-qualifiers' ] then populateDataFromSnaks( data[ 'publication-qualifiers' ], data.publication, PUBLICATION_PROPERTY_MAP ) end 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 for key, value in pairs( data.publication ) do if key ~= 'sourceId' and key ~= 'title' and key ~= 'subtitle' and key ~= 'url' and not data[ key ] then data[ key ] = value end end 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 ---@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 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.getLabelByLang, entityId, lang ) if not wbStatus then return '' end if label and label ~= '' then return label end wbStatus, label = pcall( mw.wikibase.getLabel, entityId ) 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 .. '» (' .. lang .. ')' ) 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 .. '» (' .. lang .. ')' ) 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 asString( 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, 'error 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, lang ) 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, asString( 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, 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 = asString( '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 lang = getLangCode( data.lang ) or i18nDefaultLanguage local label = getLabel( entityId, lang ) 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 or {}, data, PROPERTY_MAP ) end end ---Expand special types of references when additional data could be found in OTHER entity properties ---@param data source ---@return void local function expandSpecials( data ) if not data.entityId then return end -- Gemeinsame Normdatei -- specified by P227 if data.sourceId == 'Q36578' then appendEntitySnaks( data.entityId, 'P227', data, 'part', { format = function(gnd ) return 'Record #' .. gnd; end } ) appendEntitySnaks( data.entityId, 'P227', data, 'url', { format = function(gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } ) data.year = '2012—2016' expandSpecialsQualifiers( data.entityId, 'P227', data ) -- BNF -- specified by P268 elseif data.sourceId == 'Q15222191' then appendEntitySnaks( data.entityId, 'P268', data, 'part', { format = function(id ) return 'Record #' .. id; end } ) appendEntitySnaks( data.entityId, 'P268', data, 'url', { format = function(id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end } ) expandSpecialsQualifiers( data.entityId, 'P268', data ) -- VIAF -- specified by P214 elseif data.sourceId == 'Q54919' then appendEntitySnaks( data.entityId, 'P214', data, 'part', { format = function(id ) return 'Record #' .. id; end } ) appendEntitySnaks( data.entityId, 'P214', data, 'url', { format = function(id ) return 'https://viaf.org/viaf/' .. id; end } ) expandSpecialsQualifiers( data.entityId, 'P214', data ) -- generic property search else for _, sourceClaim in pairs( getBestStatements( data.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( data.entityId, sourcePropertyId, data, 'url', { format = function( id ) return mw.ustring.gsub( mw.ustring.gsub( sourcePropertyClaim.mainsnak.datavalue.value, '$1', id ), ' ', '%%20' ) end } ) expandSpecialsQualifiers( data.entityId, sourcePropertyId, data ) break end end end end end -- do we have appropriate record in P1433 ? local claims = findClaimsByValue( currentEntityId, 'P1343', data.sourceId ) if claims and #claims ~= 0 then for _, claim in pairs( claims ) do populateDataFromSnaks( claim.qualifiers, data, PROPERTY_MAP ) populateDataFromEntity( data.sourceId, data, PROPERTY_MAP ) 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 data source ---@param lang string ---@return void local function preprocessPlace( data, lang ) if not data.place then return end ---@type table<number, string> local newPlace = {} for index, place in pairs( data.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 data.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 return options.format( providedLabel ) end if lang == 'mul' then lang = i18nDefaultLanguage end ---@type string | nil local personName = getLabel( entityId, lang ) if isEmpty( personName ) then return '\'\'(not translated to ' .. lang .. ')\'\'' end if not isInstanceOf( entityId, 'Q5' ) then 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 data source ---@return string local function generateAuthorLinks( lang, data ) local result = '' if data.author then result = getPeopleAsWikitext( data.author, lang, options_commas_authors ) result = '<i class="wef_low_priority_links">' .. result .. '</i> ' end return result end ---@param lang string ---@param data source ---@param conjunction string ---@param propertyName string ---@param urlPropertyName string? ---@return string local function appendProperty( lang, data, conjunction, propertyName, urlPropertyName ) if not data[ propertyName ] then return '' end local out if urlPropertyName and data[ urlPropertyName ] then out = conjunction .. wrapInUrl( data[ urlPropertyName ], asString( lang, data[ propertyName ], options_commas_nolinks ) ) else out = asString( lang, data[ propertyName ], options_commas ) end if not out or out == '' then return '' end return conjunction .. out end ---@param lang string ---@param data source ---@return string local function appendTitle( lang, data ) local conjunction = '' local result = '' if data.part then result = result .. appendProperty( lang, data, '', 'part', 'parturl' ) conjunction = ' // ' end return result .. appendProperty( lang, data, conjunction, 'title', 'url' ) end ---@param lang string ---@return string local function appendLanguage( lang ) if lang == i18nDefaultLanguage then return '' end ---@type { getRefHtml: ( fun( lang: string ): string ), list_ref: ( fun( frame: frame ): string ) } local langs = require( 'Module:Languages' ) return langs.list_ref( p.currentFrame:newChild{ args = { lang } } ) end ---@param lang string ---@param data source ---@return string local function appendSubtitle( lang, data ) return appendProperty( lang, data, ': ', 'subtitle', nil ) end ---@param lang string ---@param data source ---@return string local function appendOriginalTitle( lang, data ) return appendProperty( lang, data, ' = ', 'originaltitle', nil ) end ---@param lang string ---@param data source ---@return string local function appendPublication( lang, data ) if not data.publication then return '' end local result = ' // ' .. asString( lang, data.publication.title, options_commas_it_short ) if data.publication.subtitle and data.publication.subtitle ~= '' then result = result .. ': ' .. asString( lang, data.publication.subtitle, options_commas_it_short ) end return result end ---@param lang string ---@param data source ---@return string local function appendEditor( lang, data ) if not data.editor and not data.translator then return '' end local result = ' / ' if data.editor then local prefix = i18nEditors[ lang ] or i18nEditors[ i18nDefaultLanguage ] result = result .. prefix .. getPeopleAsWikitext( data.editor, lang, options_commas_responsible ) if data.translator then result = result .. ', ' end end if data.translator then local prefix = i18nTranslators[ lang ] or i18nTranslators[ i18nDefaultLanguage ] result = result .. prefix .. getPeopleAsWikitext( data.translator, lang, options_commas_responsible ) end return result end ---@param lang string ---@param data source local function appendEdition( lang, data ) return appendProperty( lang, data, ' — ', 'edition', nil ) end ---@param lang string ---@param data source ---@return string local function appendPublicationData( lang, data ) if not data.place and not data.publisher and not data.year then return '' end local result = ' — ' if data.place then result = result .. asString( lang, data.place, options_commas_short ) if data.publisher or data.year then result = result .. ': ' end end if data.publisher then result = result .. asString( lang, data.publisher, options_commas_short ) if data.year then result = result .. ', ' end end if data.year then result = result .. asString( lang, data.year, options_commas ) end result = result .. '.' return result end ---@param lang string ---@param data source ---@return string local function appendVolumeAndIssue( lang, data ) if not data.volume and not data.issue then return '' end local result = ' — ' local letter_vol = i18nVolume[ lang ] or i18nVolume[ i18nDefaultLanguage ] local letter_iss = i18nIssue[ lang ] or i18nIssue[ i18nDefaultLanguage ] if data.volume then result = result .. appendProperty( lang, data, letter_vol .. ' ', 'volume', nil ) result = result ..appendProperty( lang, data, ', ' .. letter_iss .. ' ', 'issue', nil ) else result = result .. appendProperty( lang, data, letter_iss .. ' ', 'issue', nil ) end result = result .. '.' return result end ---@param lang string ---@param data source ---@return string local function appendPages( lang, data ) if not data.pages then return '' end local letter = i18nPages[ lang ] or i18nPages[ i18nDefaultLanguage ] local strPages = asString( lang, data.pages, options_commas ) strPages = mw.ustring.gsub( strPages, '[-—]', '—' ) return ' — ' .. letter .. ' ' .. strPages .. '.' end ---@param lang string ---@param data source ---@return string local function appendNumberOfPages( lang, data ) if not data.numberOfPages then return '' end local letter = i18nNumberOfPages[ lang ] or i18nNumberOfPages[ i18nDefaultLanguage ] return appendProperty( lang, data, ' — ', 'numberOfPages', nil ) .. ' ' .. letter end ---@param lang string ---@param data source ---@return string local function appendBookSeries( lang, data ) if not data.bookSeries then return '' end local result = appendProperty( lang, data, ' — (', 'bookSeries', nil ) if data.bookSeriesVolume or data.bookSeriesIssue then result = result .. '; ' local letter_vol = i18nVolume[ lang ] or i18nVolume[ i18nDefaultLanguage ] local letter_iss = i18nIssue[ lang ] or i18nIssue[ i18nDefaultLanguage ] if data.bookSeriesVolume then result = result .. appendProperty( lang, data, letter_vol .. ' ', 'bookSeriesVolume', nil ) result = result .. appendProperty( lang, data, ', ' .. letter_iss .. ' ', 'bookSeriesIssue', nil ) else result = result .. appendProperty( lang, data, letter_iss .. ' ', 'bookSeriesIssue', nil ) end end result = result .. ')' return result end ---@param lang string ---@param data source ---@return string local function appendTirage( lang, data ) if not data.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 ' — ' .. asString( lang, data.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 '' ) .. asString( lang, value, options ) end ---@param result string ---@param lang string ---@param data source ---@return string local function wrapSourceId( result, lang, data ) if not data.sourceId then return result end local citeType = data.type and asString( lang, data.type, options_citetypes ) or 'citetype_unknown' return '<span class="wikidata_cite ' .. citeType .. '" data-entity-id="' .. data.sourceId .. '">' .. result .. '</span>' end ---@param data source ---@return string local function appendAccessDate( data ) if not data.accessdate then return '' end local date = getSingle( data.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 data source ---@param lang string ---@return void local function populateUrl( data, lang ) if data.sourceId and not data.url then local sitelink = getSitelink( data.sourceId, lang .. 'wikisource' ) if sitelink then data.url = ':' .. lang .. ':s:' .. sitelink end end end ---@param data source ---@return void local function populateYear( data ) if not data.year and data.dateOfPublication then local date = getSingle( data.dateOfPublication ) data.year = mw.ustring.sub( date, 2, 5 ) end if not data.year and data.dateOfCreation then local date = getSingle( data.dateOfCreation ) data.year = mw.ustring.sub( date, 2, 5 ) end end ---@param data source ---@return void local function populateTitle( data ) data.title = data.title or getSingle( data.url ) end ---@param data source ---@return string local function renderSource( data ) local lang = getLangCode( data.lang ) or i18nDefaultLanguage preprocessPlace( data, lang ) populateUrl( data, lang ) populateTitle( data ) if not data.title then return '' end populateYear( data ) local result = generateAuthorLinks( lang, data ) result = result .. appendTitle( lang, data ) result = result .. appendLanguage( lang ) result = result .. appendSubtitle( lang, data ) result = result .. appendOriginalTitle( lang, data ) result = result .. appendPublication( lang, data ) result = result .. '<span class="wef_low_priority_links">' result = result .. appendEditor( lang, data ) -- Might take current editor instead of actual. Use with caution result = result .. appendEdition( lang, data ) result = result .. appendPublicationData( lang, data ) result = result .. appendVolumeAndIssue( lang, data ) result = result .. appendPages( lang, data ) result = result .. appendNumberOfPages( lang, data ) result = result .. appendBookSeries( lang, data ) result = result .. appendTirage( lang, data ) result = result .. appendIdentifier( lang, data.isbn, options_commas, 'ISBN ' ) result = result .. appendIdentifier( lang, data.issn, options_issn, 'ISSN ' ) result = result .. appendIdentifier( lang, data.doi, options_doi, nil ) result = result .. appendIdentifier( lang, data.pmid, options_pmid, nil ) result = result .. appendIdentifier( lang, data.arxiv, options_arxiv, nil ) result = result .. appendAccessDate( data ) result = result .. '</span>' return wrapSourceId( result, lang, data ) end ---@param data source Данные в простом формате, согласованном с модулями формирования библиографического описания ---@param snaks snaks ---@return string | nil local function renderReferenceImpl( data, snaks ) -- не показывать источники с "импортировано из" if snaks.P143 then return nil end -- забрать данные из reference populateDataFromSnaks( snaks or {}, data, PROPERTY_MAP ) data.sourceId = getSingle( data.sourceId ) populateDataFromEntity( data.sourceId, data, PROPERTY_MAP ) expandSpecials( data ) populateSourceDataImpl( data.sourceId, data, PROPERTY_MAP ) expandPublication( data ) 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 frame frame ---@param currentEntityId string | { id: string } ---@param reference table{ snaks: snaks } ---@return string | nil function p.renderSource( frame, currentEntityId, reference ) reference = reference or { snaks = {} } p.currentFrame = frame local data = getFilledArgs( frame.args or {} ) populateDataFromSnaks( reference.snaks, data, PROPERTY_MAP ) data.sourceId = getSingle( data.sourceId ) if type( currentEntityId ) == 'string' then data.entityId = currentEntityId elseif type( currentEntityId ) == 'table' and currentEntityId.id then data.entityId = currentEntityId.id 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.renderSource( 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 ---@param frame frame ---@return string | nil function p.testPersonNameToAuthorName( frame ) return personNameToAuthorName( frame.args[ 1 ] ) end ---@param frame frame ---@return string | nil function p.testPersonNameToResponsibleName( frame ) return personNameToResponsibleName( frame.args[ 1 ] ) end return p
Описание изменений:
Пожалуйста, учтите, что любой ваш вклад в проект «Народные Сказки» может быть отредактирован или удалён другими участниками. Если вы не хотите, чтобы кто-либо изменял ваши тексты, не помещайте их сюда.
Вы также подтверждаете, что являетесь автором вносимых дополнений, или скопировали их из источника, допускающего свободное распространение и изменение своего содержимого (см.
РуСказки:Авторские права
).
НЕ РАЗМЕЩАЙТЕ БЕЗ РАЗРЕШЕНИЯ ОХРАНЯЕМЫЕ АВТОРСКИМ ПРАВОМ МАТЕРИАЛЫ!
Отменить
Справка по редактированию
(в новом окне)
Отобразить/Скрыть ограниченную ширину содержимого