Анонимный участник
Модуль:Wikidata: различия между версиями
Новая страница: «Test»
/>Vlsergey (Новая страница: «Test») |
/>Vlsergey (Новая страница: «Test») |
||
Строка 1: | Строка 1: | ||
-- settings, may differ from project to project | -- settings, may differ from project to project | ||
local | local fileDefaultSize = '267x400px'; | ||
local outputReferences = true; | local outputReferences = true; | ||
Строка 40: | Строка 15: | ||
-- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) | -- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) | ||
local moduleSources = require('Module:Sources') | local moduleSources = require( 'Module:Sources' ) | ||
local WDS = require( 'Module:WikidataSelectors' ); | |||
local | -- Константы | ||
local contentLanguageCode = mw.getContentLanguage():getCode(); | |||
local | local p = {}; | ||
local config = nil; | |||
local function copyTo( obj, target ) | local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement, | ||
formatStatementDefault, formatProperty, getSourcingCircumstances, | |||
getPropertyDatatype, getPropertyParams, throwError, toBoolean; | |||
local function copyTo( obj, target, skipEmpty ) | |||
for k, v in pairs( obj ) do | for k, v in pairs( obj ) do | ||
target[k] = v | if skipEmpty ~= true or ( v ~= nil and v ~= '' ) then | ||
target[k] = v; | |||
end | |||
end | end | ||
return target; | return target; | ||
end | end | ||
local function | local function min( prev, next ) | ||
local | if ( prev == nil ) then return next; | ||
if | elseif ( prev > next ) then return next; | ||
return | else return prev; end | ||
end | |||
local function max( prev, next ) | |||
if ( prev == nil ) then return next; | |||
elseif ( prev < next ) then return next; | |||
else return prev; end | |||
end | |||
local function getConfig( section, code ) | |||
if config == nil then | |||
config = require( 'Module:Wikidata/config' ); | |||
end; | |||
if not config then | |||
config = {}; | |||
end | |||
if not section then | |||
return config; | |||
end | |||
if not code then | |||
return config[ section ] or {}; | |||
end | |||
if not config[ section ] then | |||
return nil; | |||
end | |||
return config[ section ][ code ]; | |||
end | |||
local function getCategoryByCode( code ) | |||
local value = getConfig( 'categories', code ); | |||
if not value or value == '' then | |||
return ''; | |||
end | end | ||
return | return '[[Category:' .. value .. ']]'; | ||
end | end | ||
Строка 69: | Строка 86: | ||
end | end | ||
end | end | ||
local Y, M, D = (function(str) | local Y, M, D = (function(str) | ||
local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" | local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" | ||
local Y, M, D = mw.ustring.match( str, pattern ) | local Y, M, D = mw.ustring.match( str, pattern ) | ||
return tonumber(Y), tonumber(M), tonumber(D) | return tonumber(Y), tonumber(M), tonumber(D) | ||
end) (str); | end) (str); | ||
local h, m, s = (function(str) | local h, m, s = (function(str) | ||
local pattern = "T(%d+):(%d+):(%d+)%Z"; | local pattern = "T(%d+):(%d+):(%d+)%Z"; | ||
local H, M, S = mw.ustring.match( str, pattern); | local H, M, S = mw.ustring.match( str, pattern); | ||
Строка 137: | Строка 154: | ||
end | end | ||
--[[ | --[[ | ||
Преобразует строку в булевое значение | Преобразует строку в булевое значение | ||
Строка 144: | Строка 161: | ||
]] | ]] | ||
local function toBoolean( valueToParse, defaultValue ) | local function toBoolean( valueToParse, defaultValue ) | ||
if ( valueToParse ~= nil ) then | |||
if valueToParse == false or valueToParse == '' or valueToParse == 'false' or valueToParse == '0' then | |||
return false | |||
end | |||
return true | |||
end | |||
return defaultValue; | |||
end | end | ||
--[[ | --[[ | ||
Функция для получения сущности (еntity) для текущей страницы | |||
Подробнее о сущностях см. d:Wikidata:Glossary/ru | |||
Принимает: строковый индентификатор (типа P18, Q42) | |||
Возвращает: объект таблицу, элементы которой индексируются с нуля | |||
]] | ]] | ||
local function getEntityFromId( id ) | local function getEntityFromId( id ) | ||
local entity; | |||
local wbStatus; | |||
if id then | |||
wbStatus, entity = pcall( mw.wikibase.getEntityObject, id ) | |||
else | |||
wbStatus, entity = pcall( mw.wikibase.getEntityObject ); | |||
end | end | ||
return entity; | |||
end | end | ||
--[[ | --[[ | ||
Внутрення функция для формирования сообщения об ошибке | |||
Принимает: ключ элемента в таблице config.errors (например entity-not-found) | |||
Возвращает: строку сообщения | |||
]] | ]] | ||
local function throwError( key ) | local function throwError( key ) | ||
error( getConfig( 'errors', key ) ); | |||
end | end | ||
--[[ | --[[ | ||
Функция для получения идентификатора сущностей | |||
Принимает: объект таблицу сущности | |||
Возвращает: строковый индентификатор (типа P18, Q42) | |||
]] | ]] | ||
local function getEntityIdFromValue( value ) | local function getEntityIdFromValue( value ) | ||
local prefix = '' | |||
if value['entity-type'] == 'item' then | |||
prefix = 'Q' | |||
elseif value['entity-type'] == 'property' then | |||
prefix = 'P' | |||
else | |||
throwError( 'unknown-entity-type' ) | |||
end | |||
return prefix .. value['numeric-id'] | |||
end | end | ||
-- проверка на наличие специилизированной функции в опциях | -- проверка на наличие специилизированной функции в опциях | ||
local function getUserFunction( options, prefix, defaultFunction ) | local function getUserFunction( options, prefix, defaultFunction ) | ||
-- проверка на указание специализированных обработчиков в параметрах, | |||
-- переданных при вызове | |||
if options[ prefix .. '-module' ] or options[ prefix .. '-function' ] then | |||
-- проверка на пустые строки в параметрах или их отсутствие | |||
if not options[ prefix .. '-module' ] or not options[ prefix .. '-function' ] then | |||
throwError( 'unknown-' .. prefix .. '-module' ); | |||
end | |||
-- динамическая загруза модуля с обработчиком указанным в параметре | |||
local formatter = require( 'Module:' .. options[ prefix .. '-module' ] ); | |||
if formatter == nil then | |||
throwError( prefix .. '-module-not-found' ) | |||
end | |||
local fun = formatter[ options[ prefix .. '-function' ] ] | |||
if fun == nil then | |||
throwError( prefix .. '-function-not-found' ) | |||
end | |||
return fun; | |||
end | |||
return defaultFunction; | |||
end | end | ||
-- Выбирает свойства по property id, дополнительно фильтруя их по рангу | -- Выбирает свойства по property id, дополнительно фильтруя их по рангу | ||
local function selectClaims( context, options, propertySelector ) | local function selectClaims( context, options, propertySelector ) | ||
if ( not context ) then error( 'context not specified'); end; | if ( not context ) then error( 'context not specified' ); end; | ||
if ( not options ) then error( 'options not specified'); end; | if ( not options ) then error( 'options not specified' ); end; | ||
if ( not options.entity ) then error( 'options.entity is missing'); end; | if ( not options.entity ) then error( 'options.entity is missing' ); end; | ||
if ( not propertySelector ) then error( 'propertySelector not specified'); end; | if ( not propertySelector ) then error( 'propertySelector not specified' ); end; | ||
result = WDS.filter( options.entity.claims, propertySelector ); | |||
if ( not result or #result == 0 ) then | |||
return nil; | |||
end | |||
if options.limit and options.limit ~= '' and options.limit ~= '-' then | |||
local limit = tonumber( options.limit, 10 ); | |||
while #result > limit do | |||
table.remove( result ); | |||
end | |||
end | |||
return result; | |||
end | |||
--[[ | |||
Функция для получения значения свойства элемента в заданный момент времени. | |||
Принимает: контекст, элемент, временные границы, таблица ID свойства | |||
Возвращает: таблицу соответствующих значений свойства | |||
]] | |||
local function getPropertyInBoundaries( context, entity, boundaries, propertyIds ) | |||
local results = {}; | |||
if not propertyIds or #propertyIds == 0 then | |||
return results; | |||
end | |||
if entity.claims then | |||
for _, propertyId in ipairs( propertyIds ) do | |||
local filteredClaims = WDS.filter( entity.claims, propertyId .. '[rank:preferred, rank:normal]' ); | |||
if filteredClaims then | |||
for _, claim in pairs( filteredClaims ) do | |||
if not boundaries or not propertyIds or #propertyIds == 0 then | |||
table.insert( results, claim.mainsnak ); | |||
else | |||
local startBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P580' ); | |||
local endBoundaries = p.getTimeBoundariesFromQualifier( context.frame, context, claim, 'P582' ); | |||
if ( (startBoundaries == nil or ( startBoundaries[2] <= boundaries[1])) | |||
and (endBoundaries == nil or ( endBoundaries[1] >= boundaries[2]))) then | |||
table.insert( results, claim.mainsnak ); | |||
end | |||
end | |||
end | |||
end | |||
if #results > 0 then | |||
break; | |||
end | |||
end | |||
end | |||
return results; | |||
end | |||
--[[ | |||
TODO | |||
]] | |||
function p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ) | |||
-- only support exact date so far, but need improvment | |||
local left = nil; | |||
local right = nil; | |||
if ( statement.qualifiers and statement.qualifiers[qualifierId] ) then | |||
for _, qualifier in pairs( statement.qualifiers[qualifierId] ) do | |||
local boundaries = context.parseTimeBoundariesFromSnak( qualifier ); | |||
if ( not boundaries ) then return nil; end | |||
left = min( left, boundaries[1] ); | |||
right = max( right, boundaries[2] ); | |||
end | |||
end | |||
if ( not left or not right ) then | |||
return nil; | |||
end | |||
return { left, right }; | |||
end | |||
--[[ | |||
TODO | |||
]] | |||
function p.getTimeBoundariesFromQualifiers( frame, context, statement, qualifierIds ) | |||
if not qualifierIds then | |||
qualifierIds = { 'P582', 'P580', 'P585' }; | |||
end | |||
for _, qualifierId in ipairs( qualifierIds ) do | |||
local result = p.getTimeBoundariesFromQualifier( frame, context, statement, qualifierId ); | |||
if result then | |||
return result; | |||
end | |||
end | |||
return nil; | |||
end | |||
--[[ | |||
Функция для получения метки элемента в заданный момент времени. | |||
Принимает: контекст, элемент, временные границы | |||
Возвращает: текстовую метку элемента, язык метки | |||
]] | |||
function getLabelWithLang( context, options, entity, boundaries, propertyIds ) | |||
if not entity then | |||
return nil; | |||
end | |||
local lang = mw.language.getContentLanguage(); | |||
local langCode = lang:getCode(); | |||
-- name from label | |||
local label = nil; | |||
if ( options.text and options.text ~= '' ) then | |||
label = options.text; | |||
else | |||
label, langCode = entity:getLabelWithLang(); | |||
if not langCode then | |||
return nil; | |||
end | |||
if not propertyIds then | |||
propertyIds = { | |||
'P1813[language:' .. langCode .. ']', | |||
'P1448[language:' .. langCode .. ']', | |||
'P1705[language:' .. langCode .. ']' | |||
}; | |||
end | |||
-- name from properties | |||
local results = getPropertyInBoundaries( context, entity, boundaries, propertyIds ); | |||
for _, result in pairs( results ) do | |||
if result.datavalue and result.datavalue.value then | |||
if result.datavalue.type == 'monolingualtext' and result.datavalue.value.text then | |||
label = result.datavalue.value.text; | |||
lang = result.datavalue.value.language; | |||
break; | |||
elseif result.datavalue.type == 'string' then | |||
label = result.datavalue.value; | |||
break; | |||
end | |||
end | |||
end | |||
end | |||
return label, langCode; | |||
end | end | ||
--[[ | --[[ | ||
Функция для оформления утверждений (statement) | |||
Подробнее о утверждениях см. d:Wikidata:Glossary/ru | |||
Принимает: таблицу параметров | |||
Возвращает: строку оформленного текста, предназначенного для отображения в статье | |||
]] | ]] | ||
local function formatProperty( options ) | local function formatProperty( options ) | ||
-- Получение сущности по идентификатору | |||
local entity = getEntityFromId( options.entityId ) | |||
if not entity then | |||
return -- throwError( 'entity-not-found' ) | |||
end | |||
-- проверка на присутсвие у сущности заявлений (claim) | -- проверка на присутсвие у сущности заявлений (claim) | ||
-- подробнее о заявлениях см. d:Викиданные:Глоссарий | -- подробнее о заявлениях см. d:Викиданные:Глоссарий | ||
if (entity.claims == nil) then | |||
return '' --TODO error? | |||
end | |||
-- improve options | -- improve options | ||
Строка 274: | Строка 427: | ||
if ( options.i18n ) then | if ( options.i18n ) then | ||
options.i18n = copyTo( options.i18n, copyTo( i18n, {} ) ); | options.i18n = copyTo( options.i18n, copyTo( getConfig( 'i18n' ), {} ) ); | ||
else | else | ||
options.i18n = i18n; | options.i18n = getConfig( 'i18n' ); | ||
end | end | ||
Строка 285: | Строка 438: | ||
formatPropertyDefault = formatPropertyDefault, | formatPropertyDefault = formatPropertyDefault, | ||
formatStatementDefault = formatStatementDefault } | formatStatementDefault = formatStatementDefault } | ||
context.formatProperty = function( options ) | context.cloneOptions = function( options ) | ||
local entity = options.entity; | |||
options.entity = nil; | |||
newOptions = mw.clone( options ); | |||
options.entity = entity; | |||
newOptions.entity = entity; | |||
newOptions.frame = options.frame; -- На склонированном фрейме frame:expandTemplate() | |||
return newOptions; | |||
end; | |||
context.formatProperty = function( options ) | |||
local func = getUserFunction( options, 'property', context.formatPropertyDefault ); | local func = getUserFunction( options, 'property', context.formatPropertyDefault ); | ||
return func( context, options ) | return func( context, options ) | ||
Строка 292: | Строка 456: | ||
context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; | context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; | ||
context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; | context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; | ||
context.parseTimeFromSnak = function( snak ) | context.parseTimeFromSnak = function( snak ) | ||
if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then | if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then | ||
Строка 316: | Строка 480: | ||
if ( not options.entity ) then error( 'options.entity missing' ); end; | if ( not options.entity ) then error( 'options.entity missing' ); end; | ||
local claims; | |||
if options.property then -- TODO: Почему тут может не быть property? | |||
claims = context.selectClaims( options, options.property ); | |||
end | |||
if claims == nil then | |||
return '' --TODO error? | |||
end | |||
-- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных | |||
-- заявлений в таблице | |||
local formattedClaims = {} | |||
for i, claim in ipairs(claims) do | |||
local formattedStatement = context.formatStatement( options, claim ) | |||
-- здесь может вернуться либо оформленный текст заявления, либо строка ошибки, либо nil | |||
if ( formattedStatement and formattedStatement ~= '' ) then | |||
formattedStatement = '<span class="wikidata-claim" data-wikidata-property-id="' .. string.upper( options.property ) .. '" data-wikidata-claim-id="' .. claim.id .. '">' .. formattedStatement .. '</span>' | |||
table.insert( formattedClaims, formattedStatement ) | |||
end | |||
end | |||
-- создание текстовой строки со списком оформленых заявлений из таблицы | |||
local out = mw.text.listToText( formattedClaims, options.separator, options.conjunction ) | |||
if out ~= '' then | |||
if options.before then | |||
out = options.before .. out | |||
end | |||
if options.after then | |||
out = out .. options.after | |||
end | |||
end | |||
return out | |||
end | end | ||
--[[ | --[[ | ||
Функция для оформления одного утверждения (statement) | |||
Принимает: объект-таблицу утверждение и таблицу параметров | |||
Возвращает: строку оформленного текста с заявлением (claim) | |||
]] | ]] | ||
function formatStatement( context, options, statement ) | function formatStatement( context, options, statement ) | ||
Строка 349: | Строка 525: | ||
error( 'statement is not specified or nil' ); | error( 'statement is not specified or nil' ); | ||
end | end | ||
if not statement.type or statement.type ~= 'statement' then | |||
throwError( 'unknown-claim-type' ) | |||
end | |||
local functionToCall = getUserFunction( options, 'claim', context.formatStatementDefault ); | |||
return functionToCall( context, options, statement ); | |||
end | end | ||
Строка 368: | Строка 544: | ||
and qualifier.datavalue.type == 'wikibase-entityid' | and qualifier.datavalue.type == 'wikibase-entityid' | ||
and qualifier.datavalue.value | and qualifier.datavalue.value | ||
and qualifier.datavalue.value[ | and qualifier.datavalue.value['entity-type'] == 'item' ) then | ||
local circumstance = | local circumstance = qualifier.datavalue.value.id; | ||
if ( 'Q5727902' == circumstance ) then | if ( 'Q5727902' == circumstance ) then | ||
circumstances.circa = true; | circumstances.circa = true; | ||
Строка 382: | Строка 558: | ||
end | end | ||
--[[ | --[[ | ||
Функция для оформления одного утверждения (statement) | |||
Принимает: объект-таблицу утверждение, таблицу параметров, | |||
объект-функцию оформления внутренних структур утверждения (snak) и | |||
объект-функцию оформления ссылки на источники (reference) | |||
Возвращает: строку оформленного текста с заявлением (claim) | |||
]] | ]] | ||
function formatStatementDefault( context, options, statement ) | function formatStatementDefault( context, options, statement ) | ||
Строка 397: | Строка 573: | ||
local circumstances = context.getSourcingCircumstances( statement ); | local circumstances = context.getSourcingCircumstances( statement ); | ||
options.qualifiers = statement.qualifiers; | |||
local result = context.formatSnak( options, statement.mainsnak, circumstances ); | |||
if ( result and result ~= '' and options.references ) then | |||
result = result .. context.formatRefs( options, statement ); | |||
end | |||
return result; | |||
end | end | ||
--[[ | --[[ | ||
Функция для оформления части утверждения (snak) | |||
Подробнее о snak см. d:Викиданные:Глоссарий | |||
Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций | |||
Возвращает: строку оформленного викитекста | |||
]] | ]] | ||
function formatSnak( context, options, snak, circumstances ) | function formatSnak( context, options, snak, circumstances ) | ||
Строка 424: | Строка 603: | ||
local after = '</span>' | local after = '</span>' | ||
if snak.snaktype == 'somevalue' then | |||
if ( options['somevalue'] and options['somevalue'] ~= '' ) then | |||
result = options['somevalue']; | |||
else | |||
result = options.i18n['somevalue']; | |||
end | |||
elseif snak.snaktype == 'novalue' then | |||
if ( options['novalue'] and options['novalue'] ~= '' ) then | |||
result = options['novalue']; | |||
else | |||
result = options.i18n['novalue']; | |||
end | |||
elseif snak.snaktype == 'value' then | |||
result = formatDatavalue( context, options, snak.datavalue, snak.datatype ); | |||
if ( circumstances.presumably ) then | if ( circumstances.presumably ) then | ||
result = options.i18n.presumably .. result; | |||
end | end | ||
if ( circumstances.circa ) then | if ( circumstances.circa ) then | ||
result = options.i18n.circa .. result; | |||
end | end | ||
else | |||
throwError( 'unknown-snak-type' ); | |||
end | |||
if ( not result or result == '' ) then | |||
return nil; | |||
end | |||
return before .. result .. after; | |||
end | end | ||
--[[ | --[[ | ||
Функция для оформления объектов-значений с географическими координатами | |||
Принимает: объект-значение и таблицу параметров, | |||
Возвращает: строку оформленного текста | |||
]] | ]] | ||
function formatGlobeCoordinate( value, options ) | function formatGlobeCoordinate( value, options ) | ||
-- проверка на требование в параметрах вызова на возврат сырого значения | -- проверка на требование в параметрах вызова на возврат сырого значения | ||
if options['subvalue'] == 'latitude' then -- широты | |||
return value['latitude'] | |||
elseif options['subvalue'] == 'longitude' then -- долготы | |||
return value['longitude'] | |||
elseif options['nocoord'] and options['nocoord'] ~= '' then | |||
-- если передан параметр nocoord, то не выводить координаты | |||
-- обычно это делается при использовании нескольких карточек на странице | |||
return '' | |||
else | |||
-- в противном случае формируются параметры для вызова шаблона {{coord}} | |||
-- нужно дописать в документации шаблона, что он отсюда вызывается, и что | |||
-- любое изменние его парамеров должно быть согласовано с кодом тут | |||
local eps = 0.0000001 -- < 1/360000 | |||
local globe = options.globe or '' -- TODO | |||
local lat = {} | |||
lat['abs'] = math.abs(value['latitude']) | |||
lat['ns'] = value['latitude'] >= 0 and 'N' or 'S' | |||
lat['d'] = math.floor(lat['abs'] + eps) | |||
lat['m'] = math.floor((lat['abs'] - lat['d']) * 60 + eps) | |||
lat['s'] = math.max(0, ((lat['abs'] - lat['d']) * 60 - lat['m']) * 60 + eps) | |||
local lon = {} | |||
lon['abs'] = math.abs(value['longitude']) | |||
lon['ew'] = value['longitude'] >= 0 and 'E' or 'W' | |||
lon['d'] = math.floor(lon['abs'] + eps) | |||
lon['m'] = math.floor((lon['abs'] - lon['d']) * 60 + eps) | |||
lon['s'] = math.max(0, ((lon['abs'] - lon['d']) * 60 - lon['m']) * 60 + eps) | |||
-- TODO: round seconds with precision | |||
local coord = '{{coord' | |||
if (value['precision'] == nil) or (value['precision'] < 1/60) then -- по умолчанию с точностью до секунды | |||
coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['s'] .. '|' .. lat['ns'] | |||
coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['s'] .. '|' .. lon['ew'] | |||
elseif value['precision'] < 1 then | |||
coord = coord .. '|' .. lat['d'] .. '|' .. lat['m'] .. '|' .. lat['ns'] | |||
coord = coord .. '|' .. lon['d'] .. '|' .. lon['m'] .. '|' .. lon['ew'] | |||
else | |||
coord = coord .. '|' .. lat['d'] .. '|' .. lat['ns'] | |||
coord = coord .. '|' .. lon['d'] .. '|' .. lon['ew'] | |||
end | |||
coord = coord .. '|globe:' .. globe | |||
if options['type'] and options['type'] ~= '' then | |||
coord = coord .. '|type=' .. options.type | |||
end | |||
if options['display'] and options['display'] ~= '' then | |||
coord = coord .. '|display=' .. options.display | |||
else | |||
coord = coord .. '|display=title' | |||
end | |||
coord = coord .. '}}' | |||
return g_frame:preprocess(coord) | |||
end | |||
end | |||
--[[ | |||
Функция для оформления объектов-значений с файлами с Викисклада | |||
Принимает: объект-значение и таблицу параметров, | |||
Возвращает: строку оформленного текста | |||
]] | |||
function formatCommonsMedia( value, options ) | |||
local image = value; | |||
local caption = ''; | |||
if options[ 'caption' ] and options[ 'caption' ] ~= '' then | |||
caption = options[ 'caption' ]; | |||
elseif options[ 'description' ] and options[ 'description' ] ~= '' then | |||
caption = options[ 'description' ]; | |||
end | |||
if caption ~= '' then | |||
caption = '<span data-wikidata-qualifier-id="P2096" style="display:block">' .. caption .. '</span>'; | |||
end | |||
if not string.find( value, '[%[%]%{%}]' ) then | |||
image = '[[File:' .. value .. '|frameless'; | |||
if options[ 'border' ] and options[ 'border' ] ~= '' then | |||
image = image .. '|border'; | |||
end | |||
local size = options[ 'size' ]; | |||
if size and size ~= '' then | |||
if not string.match( size, 'px$' ) | |||
and not string.match( size, 'пкс$' ) -- TODO: использовать перевод для языка вики | |||
then | |||
size = size .. 'px' | |||
end | |||
else | |||
size = fileDefaultSize; | |||
end | |||
image = image .. '|' .. size; | |||
if options[ 'alt' ] and options[ 'alt' ] ~= '' then | |||
image = image .. '|' .. options[ 'alt' ]; | |||
end | |||
image = image .. ']]'; | |||
if caption ~= '' then | |||
image = image .. '<br>' .. caption; | |||
end | |||
else | |||
image = image .. caption .. getCategoryByCode( 'media-contains-markup' ); | |||
end | |||
return image | |||
end | |||
--[[ | |||
Fonction for render math formulas | |||
@param string Value. | |||
@param table Parameters. | |||
@return string Formatted string. | |||
]] | |||
function formatMath( value, options ) | |||
return options.frame:extensionTag{ name = 'math', content = value }; | |||
end | |||
--[[ | |||
Функция для оформления внешних идентификаторов | |||
Принимает: объект-значение и таблицу параметров, | |||
Возвращает: строку оформленного текста | |||
]] | |||
local function formatExternalId( value, options ) | |||
local formatter = options.formatter; | |||
if not formatter or formatter == '' then | |||
local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, options.property:upper() ) | |||
if wbStatus == true and propertyEntity then | |||
local isGoodFormat = false; | |||
local statements = propertyEntity:getBestStatements( 'P1793' ); | |||
for _, statement in pairs( statements ) do | |||
if statement.mainsnak.snaktype == 'value' then | |||
local pattern = mw.ustring.gsub( statement.mainsnak.datavalue.value, '\\', '%' ); | |||
pattern = mw.ustring.gsub( pattern, '{%d+,?%d*}', '+' ); | |||
if ( string.find( pattern, '|' ) or string.find( pattern, '%)%?' ) | |||
or mw.ustring.match( value, '^' .. pattern .. '$' ) ~= nil ) then | |||
isGoodFormat = true; | |||
break; | |||
end | |||
end | |||
end | |||
if ( isGoodFormat == true ) then | |||
statements = propertyEntity:getBestStatements( 'P1630' ); | |||
for _, statement in pairs( statements ) do | |||
if statement.mainsnak.snaktype == 'value' then | |||
formatter = statement.mainsnak.datavalue.value; | |||
break | |||
end | |||
end | |||
end | |||
end | |||
end | |||
if formatter and formatter ~= '' then | |||
local link = mw.ustring.gsub( mw.ustring.gsub( formatter, '$1', value ), ' ', '%%20' ) | |||
local title = options.title | |||
if not title or title == '' then | |||
title = '$1' | |||
end | |||
title = mw.ustring.gsub( title, '$1', value ) | |||
return '[' .. link .. ' ' .. title .. ']' | |||
end | |||
return value | |||
end | |||
--[[ | |||
Функция для оформления числовых значений | |||
Принимает: объект-значение и таблицу параметров, | |||
Возвращает: строку оформленного текста | |||
]] | |||
local function formatQuantity( value, options ) | |||
-- диапазон значений | |||
local amount = string.gsub( value['amount'], '^%+', '' ); | |||
local lang = mw.language.getContentLanguage(); | |||
local langCode = lang:getCode(); | |||
local function formatNum( number, sigfig ) | |||
sigfig = sigfig or 12 -- округление до 12 знаков после запятой, на 13-м возникает ошибка в точности | |||
local mult = 10^sigfig; | |||
number = math.floor( number * mult + 0.5 ) / mult; | |||
return string.gsub( lang:formatNum( number ), '^-', '−' ); | |||
end | |||
local out = formatNum( tonumber( amount ) ); | |||
if value.upperBound then | |||
local diff = tonumber( value.upperBound ) - tonumber( amount ) | |||
if diff > 0 then -- временная провека, пока у большинства значений не будет убрано ±0 | |||
out = out .. '±' .. formatNum( diff ) | |||
end | |||
end | |||
if options.unit and options.unit ~= '' then | |||
if options.unit ~= '-' then | |||
out = out .. ' ' .. options.unit | |||
end | |||
elseif value.unit and string.match( value.unit, 'http://www.wikidata.org/entity/' ) then | |||
local unitEntityId = string.gsub( value.unit, 'http://www.wikidata.org/entity/', '' ); | |||
local wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); | |||
if wbStatus == true and unitEntity then | |||
if unitEntity.claims.P2370 and | |||
unitEntity.claims.P2370[1].mainsnak.snaktype == 'value' and | |||
not value.upperBound and | |||
options.siConversion | |||
then | |||
conversionToSIunit = string.gsub( unitEntity.claims.P2370[1].mainsnak.datavalue.value.amount, '^%+', '' ); | |||
if math.floor( math.log10( conversionToSIunit )) ~= math.log10( conversionToSIunit ) then | |||
-- Если не степени десятки (переводить сантиметры в метры не надо!) | |||
outValue = tonumber( amount ) * conversionToSIunit | |||
if ( outValue > 0 ) then | |||
-- Пробуем понять до какого знака округлять | |||
local integer, dot, decimals, expstr = amount:match( '^(%d*)(%.?)(%d*)(.*)' ) | |||
local prec | |||
if dot == '' then | |||
prec = -integer:match('0*$'):len() | |||
else | |||
prec = #decimals | |||
end | |||
local adjust = math.log10( math.abs( conversionToSIunit )) + math.log10( 2 ) | |||
local minprec = 1 - math.floor( math.log10( outValue ) + 2e-14 ); | |||
out = formatNum( outValue, math.max( math.floor( prec + adjust ), minprec )); | |||
else | |||
out = formatNum( outValue, 0 ) | |||
end | |||
unitEntityId = string.gsub( unitEntity.claims.P2370[1].mainsnak.datavalue.value.unit, 'http://www.wikidata.org/entity/', '' ); | |||
wbStatus, unitEntity = pcall( mw.wikibase.getEntity, unitEntityId ); | |||
end | |||
end | |||
local writingSystemElementId = 'Q8209'; | |||
local langElementId = 'Q7737'; | |||
local label = getLabelWithLang( context, options, unitEntity, nil, { | |||
'P5061[language:' .. langCode .. ']', | |||
'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']', | |||
'P558[!P282][!P407]' | |||
} ); | |||
out = out .. ' ' .. label; | |||
end | |||
end | |||
return out; | |||
end | |||
local DATATYPE_CACHE = {} | |||
--[[ | |||
Get property datatype by ID. | |||
@param string Property ID, e.g. 'P123'. | |||
@return string Property datatype, e.g. 'commonsMedia', 'time' or 'url'. | |||
]] | |||
local function getPropertyDatatype( propertyId ) | |||
if not propertyId or not string.match( propertyId, '^P%d+$' ) then | |||
return nil; | |||
end | |||
local cached = DATATYPE_CACHE[propertyId]; | |||
if (cached ~= nil) then return cached; end | |||
local wbStatus, propertyEntity = pcall( mw.wikibase.getEntity, propertyId ); | |||
if wbStatus ~= true or not propertyEntity then | |||
return nil; | |||
end | |||
mw.log("Loaded datatype " .. propertyEntity.datatype .. " of " .. propertyId .. ' from wikidata, consider passing datatype argument to formatProperty call or to Wikidata/config' ) | |||
DATATYPE_CACHE[propertyId] = propertyEntity.datatype; | |||
return propertyEntity.datatype; | |||
end | |||
local function formatLangRefs( options ) | |||
local langRefs = '' | |||
if ( options.qualifiers and options.qualifiers.P407 ) then | |||
for i, qualifier in pairs( options.qualifiers.P407 ) do | |||
if ( qualifier | |||
and qualifier.datavalue | |||
and qualifier.datavalue.type == 'wikibase-entityid' ) then | |||
local langRefEntity = getEntityFromId( qualifier.datavalue.value.id ) | |||
if ( langRefEntity and langRefEntity.claims ) then | |||
local langRefCodeClaims = WDS.filter( langRefEntity.claims, 'P218' ) | |||
if langRefCodeClaims then | |||
for _, claim in pairs( langRefCodeClaims ) do | |||
if ( claim.mainsnak | |||
and claim.mainsnak | |||
and claim.mainsnak.datavalue | |||
and claim.mainsnak.datavalue.type == 'string' ) then | |||
local langRefCode = claim.mainsnak.datavalue.value | |||
langRefs = langRefs .. '​' .. options.frame:expandTemplate{ title = 'ref-' ..langRefCode } | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
return langRefs | |||
end | end | ||
local function getDefaultValueFunction( datavalue ) | local function getDefaultValueFunction( datavalue, datatype ) | ||
-- вызов обработчиков по умолчанию для известных типов значений | |||
if datavalue.type == 'wikibase-entityid' then | |||
-- Entity ID | |||
return function( context, options, value ) return formatEntityId( context, options, getEntityIdFromValue( value ) ) end; | |||
elseif datavalue.type == 'string' then | |||
-- String | |||
if datatype and datatype == 'commonsMedia' then | |||
-- Media | |||
return function( context, options, value ) | |||
if ( not options.caption or options.caption == '' ) | |||
and ( not options.description or options.description == '' ) | |||
and options.qualifiers and options.qualifiers.P2096 then | |||
for i, qualifier in pairs( options.qualifiers.P2096 ) do | |||
if ( qualifier | |||
and qualifier.datavalue | |||
and qualifier.datavalue.type == 'monolingualtext' | |||
and qualifier.datavalue.value | |||
and qualifier.datavalue.value.language == contentLanguageCode ) then | |||
options.caption = qualifier.datavalue.value.text | |||
options.description = qualifier.datavalue.value.text | |||
break | |||
end | |||
end | |||
end | |||
if options['appendTimestamp'] and options.qualifiers and options.qualifiers.P585 and options.qualifiers.P585[1] then | |||
local moment = formatDatavalue (context, options, options.qualifiers.P585[1].datavalue, 'time') | |||
if not options.caption or options.caption == '' then | |||
options.caption = moment | |||
options.description = moment | |||
else | |||
local moduleDate = require('Module:Wikidata/date') | options.caption = options.caption .. ', ' .. moment | ||
options.description = options.description .. ', ' .. moment | |||
end | |||
end | |||
return formatCommonsMedia( value, options ) | |||
end; | |||
elseif datatype and datatype == 'external-id' then | |||
-- External ID | |||
return function( context, options, value ) | |||
return formatExternalId( value, options ) | |||
end | |||
elseif datatype and datatype == 'math' then | |||
-- Math formula | |||
return function( context, options, value ) | |||
return formatMath( value, options ) | |||
end | |||
elseif datatype and datatype == 'url' then | |||
-- URL | |||
return function( context, options, value ) | |||
local moduleUrl = require( 'Module:URL' ) | |||
local langRefs = formatLangRefs( options ) | |||
if not options.length or options.length == '' then | |||
options.length = math.max( 18, 25 - #langRefs ) | |||
end | |||
return moduleUrl.formatUrlSingle( context, options, value ) .. langRefs | |||
end | |||
end | |||
return function( context, options, value ) return value end; | |||
elseif datavalue.type == 'monolingualtext' then | |||
-- моноязычный текст (строка с указанием языка) | |||
return function( context, options, value ) | |||
if ( options.monolingualLangTemplate == 'lang' ) then | |||
if ( value.language == contentLanguageCode ) then | |||
return value.text; | |||
end | |||
return options.frame:expandTemplate{ title = 'lang-' .. value.language, args = { value.text } }; | |||
elseif ( options.monolingualLangTemplate == 'ref' ) then | |||
return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>' .. options.frame:expandTemplate{ title = 'ref-' .. value.language }; | |||
else | |||
return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>'; | |||
end | |||
end; | |||
elseif datavalue.type == 'globecoordinate' then | |||
-- географические координаты | |||
return function( context, options, value ) return formatGlobeCoordinate( value, options ) end; | |||
elseif datavalue.type == 'quantity' then | |||
return function( context, options, value ) return formatQuantity( value, options ) end; | |||
elseif datavalue.type == 'time' then | |||
return function( context, options, value ) | |||
local moduleDate = require( 'Module:Wikidata/date' ) | |||
return moduleDate.formatDate( context, options, value ); | |||
end; | |||
else | |||
-- во всех стальных случаях возвращаем ошибку | |||
throwError( 'unknown-datavalue-type' ) | |||
end | |||
end | end | ||
--[[ | --[[ | ||
Функция для оформления значений (value) | |||
Подробнее о значениях см. d:Wikidata:Glossary/ru | |||
Принимает: объект-значение и таблицу параметров, | |||
Возвращает: строку оформленного текста | |||
]] | ]] | ||
function formatDatavalue( context, options, datavalue ) | function formatDatavalue( context, options, datavalue, datatype ) | ||
if ( not context ) then error( 'context not specified' ); end; | if ( not context ) then error( 'context not specified' ); end; | ||
if ( not options ) then error( 'options not specified' ); end; | if ( not options ) then error( 'options not specified' ); end; | ||
Строка 554: | Строка 1050: | ||
if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; | if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; | ||
-- проверка на указание специализированных обработчиков в параметрах, | |||
-- переданных при вызове | |||
context.formatValueDefault = getDefaultValueFunction( datavalue, datatype ); | |||
local functionToCall = getUserFunction( options, 'value', context.formatValueDefault ); | |||
return functionToCall( context, options, datavalue.value ); | |||
end | end | ||
-- | --[[ | ||
Функция для оформления идентификатора сущности | |||
Принимает: строку индентификатора (типа Q42) и таблицу параметров, | |||
Возвращает: строку оформленного текста | |||
]] | |||
function formatEntityId( context, options, entityId ) | |||
-- получение локализованного названия | |||
local wbStatus, entity = pcall( mw.wikibase.getEntity, entityId ) | |||
if wbStatus ~= true then | |||
return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="color:#b32424; border-bottom: 1px dotted #b32424; cursor: help; white-space: nowrap" title="Ошибка получения элемента из Викиданных.">×</span>' .. getCategoryByCode( 'links-to-entities-with-wikibase-error' ); | |||
end | |||
local boundaries = nil | |||
if options.qualifiers then | |||
boundaries = p.getTimeBoundariesFromQualifiers( frame, context, { qualifiers = options.qualifiers } ) | |||
end | |||
local label, labelLanguageCode = getLabelWithLang( context, options, entity, boundaries ) | |||
-- определение соответствующей показываемому элементу категории | |||
local category = p.extractCategory( context, options, { id = entityId } ) | |||
-- получение ссылки по идентификатору | |||
local link = mw.wikibase.sitelink( entityId ) | |||
if link then | |||
-- ссылка на категорию, а не добавление страницы в неё | |||
if mw.ustring.match( link, '^' .. mw.site.namespaces[ 14 ].name .. ':' ) then | |||
link = ':' .. link | |||
end | |||
if label then | |||
if ( contentLanguageCode ~= labelLanguageCode ) then | |||
return '[[' .. link .. '|' .. label .. ']]' .. getCategoryByCode( 'links-to-entities-with-missing-local-language-label' ) .. category; | |||
else | |||
return '[[' .. link .. '|' .. label .. ']]' .. category; | |||
end | |||
else | |||
return '[[' .. link .. ']]' .. category; | |||
end | |||
end | |||
--[[ | if label then | ||
-- красная ссылка | |||
-- TODO: разобраться, почему не всегда есть options.frame | |||
local title = mw.title.new( label ); | |||
if title and not title.exists and options.frame then | |||
local templateText = "{{Универсальная карточка|" .. entityId .. "}}%0A'''" .. label .. "''' — %0A%0A== Примечания ==%0A{{примечания}}%0A"; | |||
local templateText = templateText .. "[[Категория:ЭАНМ:Связать с элементом Викиданных|" .. entityId .. "]]"; | |||
local preloadUrl = tostring( mw.uri.canonicalUrl( label, 'action=edit&preload=Ш:Preload/Викиданные&preloadparams[]=' .. templateText )); | |||
local redLink = options.frame:expandTemplate{ title='цветная ссылка', args = { '#ba0000', preloadUrl, label }}; | |||
return '<span class="plainlinks">' .. redLink .. '</span><sup>[[:d:' .. entityId .. '|[d]]]</sup>' .. category; | |||
end | |||
-- TODO: перенести до проверки на существование статьи | |||
local sup = ''; | |||
if ( not options.format or options.format ~= 'text' ) | |||
and entityId ~= 'Q6581072' and entityId ~= 'Q6581097' -- TODO: переписать на format=text | |||
then | |||
sup = '<sup class="plainlinks noprint">[//www.wikidata.org/wiki/' .. entityId .. '?uselang=' .. contentLanguageCode .. ' [d]]</sup>' | |||
end | end | ||
-- одноимённая статья уже существует - выводится текст и ссылка на ВД | |||
return '<span class="iw" data-title="' .. label .. '">' .. label | |||
.. sup | |||
.. '</span>' .. category | |||
end | |||
-- сообщение об отсутвии локализованного названия | |||
-- not good, but better than nothing | |||
return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. getCategoryByCode( 'links-to-entities-with-missing-label' ) .. category; | |||
end | |||
--[[ | |||
Функция для формирования категории на основе wikidata/config | |||
]] | |||
function p.extractCategory( context, options, value ) | |||
local wbStatus, entity = pcall( mw.wikibase.getEntity, value.id ) | |||
local category = '' | |||
if ( options.category ) then | |||
local claims = WDS.filter( entity.claims, options.category ); | |||
if ( claims ) then | |||
for _, claim in pairs( claims ) do | |||
if ( claim.mainsnak | |||
and claim.mainsnak | |||
and claim.mainsnak.datavalue | |||
and claim.mainsnak.datavalue.type == 'wikibase-entityid' ) then | |||
local catEntityId = claim.mainsnak.datavalue.value.id; | |||
local wbStatus, catEntity = pcall( mw.wikibase.getEntity, catEntityId ); | |||
if ( wbStatus == true and catEntity ) then | |||
if catEntity:getSitelink() then | |||
category = '[[' .. catEntity:getSitelink() .. ']]'; | |||
end | |||
end | |||
end | |||
end | |||
end | |||
end | |||
return category; | |||
end | end | ||
--[[ | --[[ | ||
Функция для оформления утверждений (statement) | |||
Подробнее о утверждениях см. d:Wikidata:Glossary/ru | |||
Принимает: таблицу параметров | |||
Возвращает: строку оформленного текста, предназначенного для отображения в статье | |||
]] | ]] | ||
-- устаревшее имя, не использовать | -- устаревшее имя, не использовать | ||
function p.formatStatements( frame ) | function p.formatStatements( frame ) | ||
return p.formatProperty( frame ); | return p.formatProperty( frame ); | ||
end | |||
--[[ | |||
Получение параметров, которые обычно используются для вывода свойства. | |||
]] | |||
function getPropertyParams( propertyId, datatype, params ) | |||
local config = getConfig(); | |||
-- Различные уровни настройки параметров, по убыванию приоритета | |||
local propertyParams = {}; | |||
-- 1. Параметры, указанные явно при вызове | |||
if params then | |||
for key, value in pairs( params ) do | |||
if value ~= '' then | |||
propertyParams[ key ] = value; | |||
end | |||
end | |||
end | |||
-- 2. Настройки конкретного параметра | |||
if config[ 'properties' ] and config[ 'properties' ][ propertyId ] then | |||
for key, value in pairs( config[ 'properties' ][ propertyId ] ) do | |||
if propertyParams[ key ] == nil then | |||
propertyParams[ key ] = value; | |||
end | |||
end | |||
end | |||
-- 3. Указанный пресет настроек | |||
if propertyParams[ 'preset' ] and config[ 'presets' ] and | |||
config[ 'presets' ][ propertyParams[ 'preset' ] ] | |||
then | |||
for key, value in pairs( config[ 'presets' ][ propertyParams[ 'preset' ] ] ) do | |||
if propertyParams[ key ] == nil then | |||
propertyParams[ key ] = value; | |||
end | |||
end | |||
end | |||
local datatype = datatype or params.datatype or propertyParams.datatype or getPropertyDatatype( propertyId ); | |||
if propertyParams.datatype == nil then | |||
propertyParams.datatype = datatype; | |||
end | |||
-- 4. Настройки для типа данных | |||
if datatype and config[ 'datatypes' ] and config[ 'datatypes' ][ datatype ] then | |||
for key, value in pairs( config[ 'datatypes' ][ datatype ] ) do | |||
if propertyParams[ key ] == nil then | |||
propertyParams[ key ] = value; | |||
end | |||
end | |||
end | |||
-- 5. Общие настройки для всех свойств | |||
if config[ 'global' ] then | |||
for key, value in pairs( config[ 'global' ] ) do | |||
if propertyParams[ key ] == nil then | |||
propertyParams[ key ] = value; | |||
end | |||
end | |||
end | |||
return propertyParams; | |||
end | end | ||
function p.formatProperty( frame ) | function p.formatProperty( frame ) | ||
local args = frame.args | |||
-- проверка на отсутствие обязательного параметра property | |||
if not args.property then | |||
throwError( 'property-param-not-provided' ) | |||
end | |||
local propertyId = mw.language.getContentLanguage():ucfirst( string.gsub( args.property, '%[.*$', '' ) ) | |||
args = getPropertyParams( propertyId, nil, args ); | |||
local datatype = args.datatype; | |||
-- проброс всех параметров из шаблона {wikidata} и параметра from откуда угодно | |||
p_frame = frame | |||
while p_frame do | |||
if p_frame:getTitle() == mw.site.namespaces[10].name .. ':Wikidata' then | |||
copyTo( p_frame.args, args, true ); | |||
end | |||
if p_frame.args and p_frame.args.from and p_frame.args.from ~= '' then | |||
args.entityId = p_frame.args.from; | |||
end | |||
p_frame = p_frame:getParent(); | |||
end | |||
args.plain = toBoolean( args.plain, false ); | |||
args.nocat = toBoolean( args.nocat, false ); | |||
args.references = toBoolean( args.references, true ); | |||
-- если значение передано в параметрах вызова то выводим только его | |||
if args.value and args.value ~= '' then | |||
-- специальное значение для скрытия Викиданных | |||
if args.value == '-' then | |||
return '' | |||
end | |||
local value = args.value | |||
-- опция, запрещающая оформление значения, поэтому никак не трогаем | |||
if args.plain then | |||
return value | |||
end | |||
-- обработчики по типу значения | |||
local wrapperExtraArgs = '' | |||
if args['value-module'] and args['value-function'] and not string.find( value, '[%[%]%{%}]' ) then | |||
local func = getUserFunction( args, 'value' ); | |||
value = func( {}, args, value ); | |||
elseif datatype == 'commonsMedia' then | |||
value = formatCommonsMedia( value, args ); | |||
elseif datatype == 'external-id' and not string.find( value, '[%[%]%{%}]' ) then | |||
wrapperExtraArgs = wrapperExtraArgs .. ' data-wikidata-external-id="' .. mw.text.encode( value ).. '"'; | |||
value = formatExternalId( value, args ); | |||
elseif datatype == 'math' then | |||
value = formatMath( value, args ); | |||
elseif datatype == 'url' then | |||
local moduleUrl = require( 'Module:URL' ); | |||
if not args.length or args.length == '' then | |||
args.length = 25 | |||
end | |||
value = moduleUrl.formatUrlSingle( nil, args, value ); | |||
end | |||
-- оборачиваем в тег для JS-функций | |||
if string.match( propertyId, '^P%d+$' ) then | |||
value = mw.text.trim( value ) | |||
-- | -- временная штрафная категория для исправления табличных вставок | ||
if ( propertyId ~= 'P166' | |||
and string.match( value, '<t[dr][ >]' ) | |||
and not string.match( value, '<table >]' ) | |||
and not string.match( value, '^%{%|' ) ) then | |||
value = value .. getCategoryByCode( 'value-contains-table' ) | |||
else | |||
-- значений с блочными тегами остаются блоком, текст встраиваем в строку | |||
if ( string.match( value, '\n' ) | |||
or string.match( value, '<t[dhr][ >]' ) | |||
or string.match( value, '<div[ >]' ) ) then | |||
value = '<div class="no-wikidata"' .. wrapperExtraArgs | |||
.. ' data-wikidata-property-id="' .. propertyId .. '">\n' | |||
.. value .. '</div>' | |||
else | |||
value = '<span class="no-wikidata"' .. wrapperExtraArgs | |||
.. ' data-wikidata-property-id="' .. propertyId .. '">' | |||
.. value .. '</span>' | |||
end | |||
end | |||
end | |||
-- добавляем категорию-маркер | |||
if not args.nocat then | |||
local pageTitle = mw.title.getCurrentTitle(); | |||
if pageTitle.namespace == 0 then | |||
value = value .. getCategoryByCode( 'local-value-present' ); | |||
end | |||
end | |||
return value | |||
end | |||
if ( args.plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain | |||
local callArgs = { propertyId }; | |||
if args.entityId then | |||
callArgs.from = args.entityId; | |||
end | |||
return frame:callParserFunction( '#property', callArgs ); | |||
end | |||
g_frame = frame | g_frame = frame | ||
-- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) | -- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) | ||
return formatProperty( args ) | |||
end | end | ||
--[[ | --[[ | ||
Функция оформления ссылок на источники (reference) | |||
Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru | |||
Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context | |||
Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства). | |||
Принимает: объект-таблицу утверждение | |||
Возвращает: строку оформленных ссылок для отображения в статье | |||
]] | ]] | ||
function formatRefs( context, options, statement ) | function formatRefs( context, options, statement ) | ||
Строка 693: | Строка 1361: | ||
end | end | ||
local | local references = {}; | ||
if ( statement.references ) then | if ( statement.references ) then | ||
local allReferences = statement.references; | local allReferences = statement.references; | ||
local hasPreferred = false; | local hasPreferred = false; | ||
local displayCount = 0; | |||
for _, reference in pairs( statement.references ) do | for _, reference in pairs( statement.references ) do | ||
if ( reference.snaks | if ( reference.snaks | ||
Строка 703: | Строка 1372: | ||
and reference.snaks.P248[1] | and reference.snaks.P248[1] | ||
and reference.snaks.P248[1].datavalue | and reference.snaks.P248[1].datavalue | ||
and reference.snaks.P248[1].datavalue.value | and reference.snaks.P248[1].datavalue.value.id ) then | ||
local entityId = | local entityId = reference.snaks.P248[1].datavalue.value.id; | ||
if ( preferredSources[entityId] ) then | if ( preferredSources[entityId] ) then | ||
hasPreferred = true; | hasPreferred = true; | ||
Строка 718: | Строка 1387: | ||
and reference.snaks.P248[1] | and reference.snaks.P248[1] | ||
and reference.snaks.P248[1].datavalue | and reference.snaks.P248[1].datavalue | ||
and reference.snaks.P248[1].datavalue.value | and reference.snaks.P248[1].datavalue.value.id ) then | ||
local entityId = | local entityId = reference.snaks.P248[1].datavalue.value.id; | ||
if ( deprecatedSources[entityId] ) then | if ( deprecatedSources[entityId] ) then | ||
display = false; | display = false; | ||
Строка 725: | Строка 1394: | ||
end | end | ||
end | end | ||
if ( display ) then | if ( display == true ) then | ||
if ( displayCount > 2 ) then | |||
if ( options.entity and options.property ) then | |||
table.remove( references ); | |||
local moreReferences = '<sup>[[d:' .. options.entity.id .. '#' .. string.upper( options.property ) .. '|[…]]]</sup>'; | |||
table.insert( references, moreReferences ); | |||
end | |||
break; | |||
end; | |||
local refText = moduleSources.renderReference( g_frame, options.entity, reference ); | |||
if ( refText ~= '' ) then | |||
table.insert( references, refText ); | |||
displayCount = displayCount + 1; | |||
end | |||
end | end | ||
end | end | ||
end | end | ||
return | return table.concat( references ); | ||
end | end | ||
return p | return p |