Модуль:Wikidata2: юрамалар арасында аерма
Контент бетерелгән Контент өстәлгән
к clean up using AWB |
Marat-avgust (бәхәс | кертем) к IanraBot үзгәртүләре (бәхәс) Marat-avgust юрамасына кадәр кире кайтарылды |
||
Юл номеры - 1: | Юл номеры - 1: | ||
local i18n = { |
|||
-- vim: set noexpandtab ft=lua ts=4 sw=4: |
|||
["errors"] = { |
|||
require('Module:No globals') |
|||
["property-param-not-provided"] = "Не дан параметр свойства", |
|||
["entity-not-found"] = "Сущность не найдена.", |
|||
["unknown-claim-type"] = "Неизвестный тип заявления.", |
|||
["unknown-snak-type"] = "Неизвестный тип снэка.", |
|||
["unknown-datavalue-type"] = "Неизвестный тип значения данных.", |
|||
["unknown-entity-type"] = "Неизвестный тип сущности.", |
|||
["unknown-property-module"] = "Вы должны установить и property-module, и property-function.", |
|||
["unknown-claim-module"] = "Вы должны установить и claim-module, и claim-function.", |
|||
["unknown-value-module"] = "Вы должны установить и value-module, и value-function.", |
|||
["property-module-not-found"] = "Модуль для отображения свойства не найден", |
|||
["property-function-not-found"] = "Функция для отображения свойства не найдена", |
|||
["claim-module-not-found"] = "Модуль для отображения утверждения не найден.", |
|||
["claim-function-not-found"] = "Функция для отображения утверждения не найдена.", |
|||
["value-module-not-found"] = "Модуль для отображения значения не найден.", |
|||
["value-function-not-found"] = "Функция для отображения значения не найдена." |
|||
}, |
|||
["somevalue"] = "''неизвестно''", |
|||
["novalue"] = "", |
|||
["circa"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="якынча">як. </span>', |
|||
["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="предположительно">предп. </span>', |
|||
} |
|||
-- settings, may differ from project to project |
|||
local p = {} |
|||
local categoryLinksToEntitiesWithMissingLabel = '[[Категория:Википедия:Статьи со ссылками на элементы Викиданных без подписи]]'; |
|||
local debug = false |
|||
local categoryLinksToEntitiesWithMissingLocalLanguageLabel = '[[Категория:Википедия:Статьи со ссылками на элементы Викиданных без русской подписи]]'; |
|||
local categoryLocalValuePresent = '[[Категория:Википедия:Статьи с переопределением значения из Викиданных]]'; |
|||
local fileDefaultSize = '267x400px'; |
|||
local outputReferences = true; |
|||
-- sources that shall be omitted if any preffered sources exists |
|||
local deprecatedSources = { |
|||
Q36578 = true, -- Gemeinsame Normdatei |
|||
Q63056 = true, -- Find a Grave |
|||
Q15222191 = true, -- BNF |
|||
}; |
|||
local preferredSources = { |
|||
Q5375741 = true, -- Encyclopædia Britannica Online |
|||
Q17378135 = true, -- Great Soviet Encyclopedia (1969—1978) |
|||
}; |
|||
-- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании) |
|||
------------------------------------------------------------------------------ |
|||
local moduleSources = require( 'Module:Sources' ) |
|||
-- module local variables and functions |
|||
local WDS = require( 'Module:WikidataSelectors' ); |
|||
-- Константы |
|||
local wiki = |
|||
local contentLanguageCode = mw.getContentLanguage():getCode(); |
|||
{ |
|||
langcode = mw.language.getContentLanguage().code |
|||
} |
|||
local p = {} |
|||
-- internationalisation |
|||
local i18n = |
|||
{ |
|||
["errors"] = |
|||
{ |
|||
["property-not-found"] = "Property not found.", |
|||
["entity-not-found"] = "Wikidata entity not found.", |
|||
["unknown-claim-type"] = "Unknown claim type.", |
|||
["unknown-entity-type"] = "Unknown entity type.", |
|||
["qualifier-not-found"] = "Qualifier not found.", |
|||
["site-not-found"] = "Wikimedia project not found.", |
|||
["unknown-datetime-format"] = "Unknown datetime format.", |
|||
["local-article-not-found"] = "Article is not yet available in this wiki." |
|||
}, |
|||
["datetime"] = |
|||
{ |
|||
-- $1 is a placeholder for the actual number |
|||
[0] = "$1 billion years", -- precision: billion years |
|||
[1] = "$100 million years", -- precision: hundred million years |
|||
[2] = "$10 million years", -- precision: ten million years |
|||
[3] = "$1 million years", -- precision: million years |
|||
[4] = "$100,000 years", -- precision: hundred thousand years |
|||
[5] = "$10,000 years", -- precision: ten thousand years |
|||
[6] = "$1 millennium", -- precision: millennium |
|||
[7] = "$1 century", -- precision: century |
|||
[8] = "$1s", -- precision: decade |
|||
-- the following use the format of #time parser function |
|||
[9] = "Y", -- precision: year, |
|||
[10] = "F Y", -- precision: month |
|||
[11] = "F j, Y", -- precision: day |
|||
[12] = "F j, Y ga", -- precision: hour |
|||
[13] = "F j, Y g:ia", -- precision: minute |
|||
[14] = "F j, Y g:i:sa", -- precision: second |
|||
["beforenow"] = "$1 BCE", -- how to format negative numbers for precisions 0 to 5 |
|||
["afternow"] = "$1 CE", -- how to format positive numbers for precisions 0 to 5 |
|||
["bc"] = '$1 "BCE"', -- how print negative years |
|||
["ad"] = "$1", -- how print positive years |
|||
-- the following are for function getDateValue() and getQualifierDateValue() |
|||
["default-format"] = "dmy", -- default value of the #3 (getDateValue) or |
|||
-- #4 (getQualifierDateValue) argument |
|||
["default-addon"] = "BC", -- default value of the #4 (getDateValue) or |
|||
-- #5 (getQualifierDateValue) argument |
|||
["prefix-addon"] = false, -- set to true for languages put "BC" in front of the |
|||
-- datetime string; or the addon will be suffixed |
|||
["addon-sep"] = " ", -- separator between datetime string and addon (or inverse) |
|||
["format"] = -- options of the 3rd argument |
|||
{ |
|||
["mdy"] = "F j, Y", |
|||
["my"] = "F Y", |
|||
["y"] = "Y", |
|||
["dmy"] = "j F Y", |
|||
["ymd"] = "Y-m-d", |
|||
["ym"] = "Y-m" |
|||
} |
|||
}, |
|||
["monolingualtext"] = '<span lang="%language">%text</span>', |
|||
["warnDump"] = "[[Category:Called function 'Dump' from module Wikidata]]", |
|||
["ordinal"] = |
|||
{ |
|||
[1] = "st", |
|||
[2] = "nd", |
|||
[3] = "rd", |
|||
["default"] = "th" |
|||
} |
|||
} |
|||
local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement, |
|||
-- Credit to http://stackoverflow.com/a/1283608/2644759 |
|||
formatStatementDefault, formatProperty, getSourcingCircumstances, |
|||
-- cc-by-sa 3.0 |
|||
getPropertyDatatype, getPropertyParams, throwError, toBoolean; |
|||
local function tableMerge(t1, t2) |
|||
for k,v in pairs(t2) do |
|||
if type(v) == "table" then |
|||
if type(t1[k] or false) == "table" then |
|||
tableMerge(t1[k] or {}, t2[k] or {}) |
|||
else |
|||
t1[k] = v |
|||
end |
|||
else |
|||
t1[k] = v |
|||
end |
|||
end |
|||
return t1 |
|||
end |
|||
local function |
local function copyTo( obj, target ) |
||
for k, v in pairs( obj ) do |
|||
local exist, res = pcall(require, "Module:Wikidata/i18n") |
|||
target[k] = v |
|||
if exist and next(res) ~= nil then |
|||
tableMerge(i18n, res.i18n) |
|||
end |
end |
||
return target; |
|||
end |
end |
||
local function splitISO8601(str) |
|||
loadI18n() |
|||
if 'table' == type(str) then |
|||
if str.args and str.args[1] then |
|||
-- this function needs to be internationalised along with the above: |
|||
str = '' .. str.args[1] |
|||
-- takes cardinal numer as a numeric and returns the ordinal as a string |
|||
-- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc. |
|||
local function makeOrdinal (cardinal) |
|||
local ordsuffix = i18n.ordinal.default |
|||
if cardinal % 10 == 1 then |
|||
ordsuffix = i18n.ordinal[1] |
|||
elseif cardinal % 10 == 2 then |
|||
ordsuffix = i18n.ordinal[2] |
|||
elseif cardinal % 10 == 3 then |
|||
ordsuffix = i18n.ordinal[3] |
|||
end |
|||
-- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th' |
|||
-- similarly for 12 and 13, etc. |
|||
if (cardinal % 100 == 11) or (cardinal % 100 == 12) or (cardinal % 100 == 13) then |
|||
ordsuffix = i18n.ordinal.default |
|||
end |
|||
return tostring(cardinal) .. ordsuffix |
|||
end |
|||
local function printError(code) |
|||
return '<span class="error">' .. (i18n.errors[code] or code) .. '</span>' |
|||
end |
|||
local function parseDateValue(timestamp, date_format, date_addon) |
|||
local prefix_addon = i18n["datetime"]["prefix-addon"] |
|||
local addon_sep = i18n["datetime"]["addon-sep"] |
|||
local addon = "" |
|||
-- check for negative date |
|||
if string.sub(timestamp, 1, 1) == '-' then |
|||
timestamp = '+' .. string.sub(timestamp, 2) |
|||
addon = date_addon |
|||
end |
|||
local function d(f) |
|||
local year_suffix |
|||
local tstr = "" |
|||
local lang_obj = mw.language.new(wiki.langcode) |
|||
local f_parts = mw.text.split(f, 'Y', true) |
|||
for idx, f_part in pairs(f_parts) do |
|||
year_suffix = '' |
|||
if string.match(f_part, "x[mijkot]$") then |
|||
-- for non-Gregorian year |
|||
f_part = f_part .. 'Y' |
|||
elseif idx < #f_parts then |
|||
-- supress leading zeros in year |
|||
year_suffix = lang_obj:formatDate('Y', timestamp) |
|||
year_suffix = string.gsub(year_suffix, '^0+', '', 1) |
|||
end |
|||
tstr = tstr .. lang_obj:formatDate(f_part, timestamp) .. year_suffix |
|||
end |
|||
if addon ~= "" and prefix_addon then |
|||
return addon .. addon_sep .. tstr |
|||
elseif addon ~= "" then |
|||
return tstr .. addon_sep .. addon |
|||
else |
else |
||
return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str ) |
|||
return tstr |
|||
end |
end |
||
end |
end |
||
local Y, M, D = (function(str) |
|||
local _date_format = i18n["datetime"]["format"][date_format] |
|||
local pattern = "(%-?%d+)%-(%d+)%-(%d+)T" |
|||
if _date_format ~= nil then |
|||
local Y, M, D = mw.ustring.match( str, pattern ) |
|||
return d(_date_format) |
|||
return tonumber(Y), tonumber(M), tonumber(D) |
|||
else |
|||
end) (str); |
|||
return printError("unknown-datetime-format") |
|||
local h, m, s = (function(str) |
|||
end |
|||
local pattern = "T(%d+):(%d+):(%d+)%Z"; |
|||
local H, M, S = mw.ustring.match( str, pattern); |
|||
return tonumber(H), tonumber(M), tonumber(S); |
|||
end) (str); |
|||
local oh,om = ( function(str) |
|||
if str:sub(-1)=="Z" then return 0,0 end; -- ends with Z, Zulu time |
|||
-- matches ±hh:mm, ±hhmm or ±hh; else returns nils |
|||
local pattern = "([-+])(%d%d):?(%d?%d?)$"; |
|||
local sign, oh, om = mw.ustring.match( str, pattern); |
|||
sign, oh, om = sign or "+", oh or "00", om or "00"; |
|||
return tonumber(sign .. oh), tonumber(sign .. om); |
|||
end )(str) |
|||
return {year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s}; |
|||
end |
end |
||
local function parseTimeBoundaries( time, precision ) |
|||
-- This local function combines the year/month/day/BC/BCE handling of parseDateValue{} |
|||
local s = splitISO8601( time ); |
|||
-- with the millennium/century/decade handling of formatDate() |
|||
if (not s) then return nil; end |
|||
local function parseDateFull(timestamp, precision, date_format, date_addon) |
|||
local prefix_addon = i18n["datetime"]["prefix-addon"] |
|||
local addon_sep = i18n["datetime"]["addon-sep"] |
|||
local addon = "" |
|||
if ( precision >= 0 and precision <= 8 ) then |
|||
-- check for negative date |
|||
local powers = { 1000000000 , 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 } |
|||
if string.sub(timestamp, 1, 1) == '-' then |
|||
local power = powers[ precision + 1 ]; |
|||
timestamp = '+' .. string.sub(timestamp, 2) |
|||
local left = s.year - ( s.year % power ); |
|||
addon = date_addon |
|||
return { tonumber(os.time( {year=left, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, |
|||
tonumber(os.time( {year=left + power - 1, month=12, day=31, hour=29, min=59, sec=58} )) * 1000 + 1999 }; |
|||
end |
end |
||
if ( precision == 9 ) then |
|||
-- get the next four characters after the + (should be the year now in all cases) |
|||
return { tonumber(os.time( {year=s.year, month=1, day=1, hour=0, min=0, sec=0} )) * 1000, |
|||
-- ok, so this is dirty, but let's get it working first |
|||
tonumber(os.time( {year=s.year, month=12, day=31, hour=23, min=59, sec=58} )) * 1000 + 1999 }; |
|||
local intyear = tonumber(string.sub(timestamp, 2, 5)) |
|||
if intyear == 0 and precision <= 9 then |
|||
return "" |
|||
end |
end |
||
if ( precision == 10 ) then |
|||
-- precision is 10000 years or more |
|||
local lastDays = {31, 28.25, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; |
|||
if precision <= 5 then |
|||
local |
local lastDay = lastDays[s.month]; |
||
return { tonumber(os.time( {year=s.year, month=s.month, day=1, hour=0, min=0, sec=0} )) * 1000, |
|||
local y2 = math.ceil(math.abs(intyear) / factor) |
|||
tonumber(os.time( {year=s.year, month=s.month, day=lastDay, hour=23, min=59, sec=58} )) * 1000 + 1999 }; |
|||
local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2)) |
|||
if addon ~= "" then |
|||
-- negative date |
|||
relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative) |
|||
else |
|||
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative) |
|||
end |
|||
return relative |
|||
end |
end |
||
if ( precision == 11 ) then |
|||
return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=0, min=0, sec=0} )) * 1000, |
|||
local era, card |
|||
tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=23, min=59, sec=58} )) * 1000 + 1999 }; |
|||
if precision == 6 then |
|||
card = math.floor((intyear - 1) / 1000) + 1 |
|||
era = mw.ustring.gsub(i18n.datetime[6], "$1", makeOrdinal(card)) |
|||
end |
end |
||
if precision == 7 then |
|||
if ( precision == 12 ) then |
|||
card = math.floor((intyear - 1) / 100) + 1 |
|||
return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=0, sec=0} )) * 1000, |
|||
era = mw.ustring.gsub(i18n.datetime[7], "$1", makeOrdinal(card)) |
|||
tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=59, sec=58} )) * 1000 + 19991999 }; |
|||
end |
end |
||
if precision == 8 then |
|||
if ( precision == 13 ) then |
|||
era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(intyear) / 10) * 10)) |
|||
return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} )) * 1000, |
|||
tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=58} )) * 1000 + 1999 }; |
|||
end |
end |
||
if era then |
|||
if ( precision == 14 ) then |
|||
local t = tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} ) ); |
|||
era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era) |
|||
return { t * 1000, t * 1000 + 999 }; |
|||
else |
|||
era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) |
|||
end |
|||
return era |
|||
end |
end |
||
error('Unsupported precision: ' .. precision ); |
|||
local _date_format = i18n["datetime"]["format"][date_format] |
|||
end |
|||
if _date_format ~= nil then |
|||
-- check for precision is year and override supplied date_format |
|||
--[[ |
|||
if precision == 9 then |
|||
Преобразует строку в булевое значение |
|||
_date_format = i18n["datetime"][9] |
|||
end |
|||
local year_suffix |
|||
local tstr = "" |
|||
local lang_obj = mw.language.new(wiki.langcode) |
|||
local f_parts = mw.text.split(_date_format, 'Y', true) |
|||
for idx, f_part in pairs(f_parts) do |
|||
year_suffix = '' |
|||
if string.match(f_part, "x[mijkot]$") then |
|||
-- for non-Gregorian year |
|||
f_part = f_part .. 'Y' |
|||
elseif idx < #f_parts then |
|||
-- supress leading zeros in year |
|||
year_suffix = lang_obj:formatDate('Y', timestamp) |
|||
year_suffix = string.gsub(year_suffix, '^0+', '', 1) |
|||
end |
|||
tstr = tstr .. lang_obj:formatDate(f_part, timestamp) .. year_suffix |
|||
end |
|||
local fdate |
|||
if addon ~= "" and prefix_addon then |
|||
fdate = addon .. addon_sep .. tstr |
|||
elseif addon ~= "" then |
|||
fdate = tstr .. addon_sep .. addon |
|||
else |
|||
fdate = tstr |
|||
end |
|||
Принимает: строковое значение (может отсутствовать) |
|||
return fdate |
|||
Возвращает: булевое значение true или false, если получается распознать значение, или defaultValue во всех остальных случаях |
|||
else |
|||
]] |
|||
return printError("unknown-datetime-format") |
|||
local function toBoolean( valueToParse, defaultValue ) |
|||
end |
|||
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 |
||
--[[ |
|||
-- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field |
|||
Функция для получения сущности (еntity) для текущей страницы |
|||
-- use these as the second parameter and this function instead of the built-in "pairs" function |
|||
Подробнее о сущностях см. d:Wikidata:Glossary/ru |
|||
-- to iterate over all qualifiers and snaks in the intended order. |
|||
local function orderedpairs(array, order) |
|||
if not order then return pairs(array) end |
|||
Принимает: строковый индентификатор (типа P18, Q42) |
|||
-- return iterator function |
|||
Возвращает: объект таблицу, элементы которой индексируются с нуля |
|||
local i = 0 |
|||
]] |
|||
return function() |
|||
local function getEntityFromId( id ) |
|||
i = i + 1 |
|||
if id then |
|||
return mw.wikibase.getEntityObject( id ) |
|||
return order[i], array[order[i]] |
|||
end |
|||
return mw.wikibase.getEntityObject(); |
|||
end |
|||
end |
end |
||
--[[ |
|||
-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second |
|||
Внутрення функция для формирования сообщения об ошибке |
|||
local function normalizeDate(date) |
|||
date = mw.text.trim(date, "+") |
|||
Принимает: ключ элемента в таблице i18n (например entity-not-found) |
|||
-- extract year |
|||
Возвращает: строку сообщения |
|||
local yearstr = mw.ustring.match(date, "^\-?%d+") |
|||
]] |
|||
local year = tonumber(yearstr) |
|||
local function throwError( key ) |
|||
-- remove leading zeros of year |
|||
error( i18n.errors[key] ); |
|||
return year .. mw.ustring.sub(date, #yearstr + 1), year |
|||
end |
end |
||
--[[ |
|||
local function formatDate(date, precision, timezone) |
|||
Функция для получения идентификатора сущностей |
|||
precision = precision or 11 |
|||
local date, year = normalizeDate(date) |
|||
if year == 0 and precision <= 9 then return "" end |
|||
Принимает: объект таблицу сущности |
|||
-- precision is 10000 years or more |
|||
Возвращает: строковый индентификатор (типа P18, Q42) |
|||
if precision <= 5 then |
|||
]] |
|||
local factor = 10 ^ ((5 - precision) + 4) |
|||
local function getEntityIdFromValue( value ) |
|||
local y2 = math.ceil(math.abs(year) / factor) |
|||
local prefix = '' |
|||
local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2)) |
|||
if value['entity-type'] == 'item' then |
|||
prefix = 'Q' |
|||
relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative) |
|||
elseif value['entity-type'] == 'property' then |
|||
else |
|||
prefix = 'P' |
|||
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative) |
|||
else |
|||
end |
|||
throwError( 'unknown-entity-type' ) |
|||
return relative |
|||
end |
|||
return prefix .. value['numeric-id'] |
|||
end |
|||
-- проверка на наличие специилизированной функции в опциях |
|||
-- precision is decades, centuries and millennia |
|||
local function getUserFunction( options, prefix, defaultFunction ) |
|||
local era |
|||
-- проверка на указание специализированных обработчиков в параметрах, |
|||
if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end |
|||
-- переданных при вызове |
|||
if precision == 7 then era = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(math.floor((math.abs(year) - 1) / 100) + 1)) end |
|||
if options[ prefix .. '-module' ] or options[ prefix .. '-function' ] then |
|||
if precision == 8 then era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(year) / 10) * 10)) end |
|||
-- проверка на пустые строки в параметрах или их отсутствие |
|||
if era then |
|||
if not options[ prefix .. '-module' ] or not options[ prefix .. '-function' ] then |
|||
if year < 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era) |
|||
throwError( 'unknown-' .. prefix .. '-module' ); |
|||
elseif year > 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) end |
|||
end |
|||
return era |
|||
-- динамическая загруза модуля с обработчиком указанным в параметре |
|||
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; |
|||
-- precision is year |
|||
end |
|||
if precision == 9 then |
|||
return year |
|||
end |
|||
-- Выбирает свойства по property id, дополнительно фильтруя их по рангу |
|||
-- precision is less than years |
|||
local function selectClaims( context, options, propertySelector ) |
|||
if precision > 9 then |
|||
if ( not context ) then error( 'context not specified' ); end; |
|||
--[[ the following code replaces the UTC suffix with the given negated timezone to convert the global time to the given local time |
|||
if ( not options ) then error( 'options not specified' ); end; |
|||
timezone = tonumber(timezone) |
|||
if ( not options.entity ) then error( 'options.entity is missing' ); end; |
|||
if timezone and timezone ~= 0 then |
|||
if ( not propertySelector ) then error( 'propertySelector not specified' ); end; |
|||
timezone = -timezone |
|||
timezone = string.format("%.2d%.2d", timezone / 60, timezone % 60) |
|||
result = WDS.filter( options.entity.claims, propertySelector ); |
|||
if timezone[1] ~= '-' then timezone = "+" .. timezone end |
|||
date = mw.text.trim(date, "Z") .. " " .. timezone |
|||
if ( not result or #result == 0 ) then |
|||
end |
|||
return nil; |
|||
]]-- |
|||
end |
|||
if options.limit and options.limit ~= '' and options.limit ~= '-' then |
|||
local formatstr = i18n.datetime[precision] |
|||
local limit = tonumber( options.limit, 10 ); |
|||
if year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "") |
|||
while #result > limit do |
|||
table.remove( result ); |
|||
-- Mediawiki formatDate doesn't support negative years |
|||
date = mw.ustring.sub(date, 2) |
|||
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.bc, "$1", i18n.datetime[9])) |
|||
elseif year > 0 and i18n.datetime.ad ~= "$1" then |
|||
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.ad, "$1", i18n.datetime[9])) |
|||
end |
end |
||
return mw.language.new(wiki.langcode):formatDate(formatstr, date) |
|||
end |
end |
||
return result; |
|||
end |
end |
||
--[[ |
|||
local function printDatavalueEntity(data, parameter) |
|||
Функция для получения значения свойства элемента в заданный момент времени. |
|||
-- data fields: entity-type [string], numeric-id [int, Wikidata id] |
|||
local id |
|||
Принимает: контекст, элемент, временные границы, таблица ID свойства |
|||
if data["entity-type"] == "item" then id = "Q" .. data["numeric-id"] |
|||
Возвращает: таблицу соответствующих значений свойства |
|||
elseif data["entity-type"] == "property" then id = "P" .. data["numeric-id"] |
|||
]] |
|||
else return printError("unknown-entity-type") |
|||
local function getPropertyInBoundaries( context, entity, boundaries, propertyIds ) |
|||
local results = {}; |
|||
if not propertyIds or #propertyIds == 0 then |
|||
return results; |
|||
end |
end |
||
if |
if entity.claims then |
||
for _, propertyId in ipairs( propertyIds ) do |
|||
if parameter == "link" then |
|||
local filteredClaims = WDS.filter( entity.claims, propertyId .. '[rank:preferred, rank:normal]' ); |
|||
local linkTarget = mw.wikibase.sitelink(id) |
|||
if filteredClaims then |
|||
local linkName = mw.wikibase.label(id) |
|||
for _, claim in pairs( filteredClaims ) do |
|||
if linkTarget then |
|||
if not boundaries or not propertyIds or #propertyIds == 0 then |
|||
-- if there is a local Wikipedia article link to it using the label or the article title |
|||
table.insert( results, claim.mainsnak ); |
|||
return "[[" .. linkTarget .. "|" .. (linkName or linkTarget) .. "]]" |
|||
else |
else |
||
local startBoundaries = getTimeBoundariesFromQualifiers( context, claim, 'P580' ); |
|||
-- if there is no local Wikipedia article output the label or link to the Wikidata object to let the user input a proper label |
|||
local endBoundaries = getTimeBoundariesFromQualifiers( context, claim, 'P582' ); |
|||
if linkName then return linkName else return "[[:d:" .. id .. "|" .. id .. "]]" end |
|||
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 |
||
else |
|||
return data[parameter] |
|||
end |
end |
||
else |
|||
return mw.wikibase.label(id) or id |
|||
end |
end |
||
end |
|||
return results; |
|||
local function printDatavalueTime(data, parameter) |
|||
-- data fields: time [ISO 8601 time], timezone [int in minutes], before [int], after [int], precision [int], calendarmodel [wikidata URI] |
|||
-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second |
|||
-- calendarmodel: e.g. http://www.wikidata.org/entity/Q1985727 for the proleptic Gregorian calendar or http://www.wikidata.org/wiki/Q11184 for the Julian calendar] |
|||
if parameter then |
|||
if parameter == "calendarmodel" then data.calendarmodel = mw.ustring.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI |
|||
elseif parameter == "time" then data.time = normalizeDate(data.time) end |
|||
return data[parameter] |
|||
else |
|||
return formatDate(data.time, data.precision, data.timezone) |
|||
end |
|||
end |
end |
||
--[[ |
|||
local function printDatavalueMonolingualText(data, parameter) |
|||
Функция для получения метки элемента в заданный момент времени. |
|||
-- data fields: language [string], text [string] |
|||
if parameter then |
|||
Принимает: контекст, элемент, временные границы |
|||
return data[parameter] |
|||
Возвращает: текстовую метку элемента, язык метки |
|||
else |
|||
]] |
|||
local result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"]) |
|||
function getLabelWithLang( context, options, entity, boundaries, propertyIds ) |
|||
return result |
|||
if not entity then |
|||
return nil; |
|||
end |
end |
||
end |
|||
local lang = mw.language.getContentLanguage(); |
|||
local function findClaims(entity, property) |
|||
local langCode = lang:getCode(); |
|||
if not property or not entity or not entity.claims then return end |
|||
-- name from label |
|||
if mw.ustring.match(property, "^P%d+$") then |
|||
local label = nil; |
|||
-- if the property is given by an id (P..) access the claim list by this id |
|||
if ( options.text and options.text ~= '' ) then |
|||
return entity.claims[property] |
|||
label = options.text; |
|||
else |
|||
else |
|||
-- otherwise, iterate over all properties, fetch their labels and compare this to the given property name |
|||
label, langCode = entity:getLabelWithLang(); |
|||
for k, v in pairs(entity.claims) do |
|||
if mw.wikibase.label(k) == property then return v end |
|||
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 |
|||
end |
end |
||
return label, langCode; |
|||
end |
end |
||
--[[ |
|||
local function getSnakValue(snak, parameter) |
|||
Функция для оформления утверждений (statement) |
|||
if snak.snaktype == "value" then |
|||
Подробнее о утверждениях см. d:Wikidata:Glossary/ru |
|||
-- call the respective snak parser |
|||
if snak.datavalue.type == "string" then return snak.datavalue.value |
|||
elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter) |
|||
elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter) |
|||
elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter) |
|||
elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter) |
|||
elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter) |
|||
end |
|||
end |
|||
return mw.wikibase.renderSnak(snak) |
|||
end |
|||
Принимает: таблицу параметров |
|||
local function getQualifierSnak(claim, qualifierId) |
|||
Возвращает: строку оформленного текста, предназначенного для отображения в статье |
|||
-- a "snak" is Wikidata terminology for a typed key/value pair |
|||
]] |
|||
-- a claim consists of a main snak holding the main information of this claim, |
|||
local function formatProperty( options ) |
|||
-- as well as a list of attribute snaks and a list of references snaks |
|||
-- Получение сущности по идентификатору |
|||
if qualifierId then |
|||
local entity = getEntityFromId( options.entityId ) |
|||
-- search the attribute snak with the given qualifier as key |
|||
if not entity then |
|||
return -- throwError( 'entity-not-found' ) |
|||
local qualifier = claim.qualifiers[qualifierId] |
|||
end |
|||
if qualifier then return qualifier[1] end |
|||
-- проверка на присутсвие у сущности заявлений (claim) |
|||
end |
|||
-- подробнее о заявлениях см. d:Викиданные:Глоссарий |
|||
return nil, printError("qualifier-not-found") |
|||
if (entity.claims == nil) then |
|||
else |
|||
return '' --TODO error? |
|||
end |
|||
return claim.mainsnak |
|||
-- improve options |
|||
options.frame = g_frame; |
|||
options.entity = entity; |
|||
options.extends = function( self, newOptions ) |
|||
return copyTo( newOptions, copyTo( self, {} ) ) |
|||
end |
end |
||
end |
|||
if ( options.i18n ) then |
|||
local function getValueOfClaim(claim, qualifierId, parameter) |
|||
options.i18n = copyTo( options.i18n, copyTo( i18n, {} ) ); |
|||
local error |
|||
local snak |
|||
snak, error = getQualifierSnak(claim, qualifierId) |
|||
if snak then |
|||
return getSnakValue(snak, parameter) |
|||
else |
else |
||
options.i18n = i18n; |
|||
return nil, error |
|||
end |
end |
||
end |
|||
-- create context |
|||
local function getReferences(frame, claim) |
|||
local |
local context = { |
||
entity = options.entity, |
|||
-- traverse through all references |
|||
formatSnak = formatSnak, |
|||
for ref in pairs(claim.references or {}) do |
|||
formatPropertyDefault = formatPropertyDefault, |
|||
local refparts |
|||
formatStatementDefault = formatStatementDefault } |
|||
-- traverse through all parts of the current reference |
|||
context.formatProperty = function( options ) |
|||
for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) do |
|||
local func = getUserFunction( options, 'property', context.formatPropertyDefault ); |
|||
if refparts then refparts = refparts .. ", " else refparts = "" end |
|||
return func( context, options ) |
|||
-- output the label of the property of the reference part, e.g. "imported from" for P143 |
|||
end; |
|||
refparts = refparts .. tostring(mw.wikibase.label(snakkey)) .. ": " |
|||
context.formatStatement = function( options, statement ) return formatStatement( context, options, statement ) end; |
|||
-- output all values of this reference part, e.g. "German Wikipedia" and "English Wikipedia" if the referenced claim was imported from both sites |
|||
context.formatSnak = function( options, snak, circumstances ) return formatSnak( context, options, snak, circumstances ) end; |
|||
for snakidx = 1, #snakval do |
|||
context.formatRefs = function( options, statement ) return formatRefs( context, options, statement ) end; |
|||
if snakidx > 1 then refparts = refparts .. ", " end |
|||
refparts = refparts .. getSnakValue(snakval[snakidx]) |
|||
context.parseTimeFromSnak = function( snak ) |
|||
if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then |
|||
return tonumber(os.time( splitISO8601( tostring( snak.datavalue.value.time ) ) ) ) * 1000; |
|||
end |
end |
||
return nil; |
|||
end |
end |
||
context.parseTimeBoundariesFromSnak = function( snak ) |
|||
if refparts then result = result .. frame:extensionTag("ref", refparts) end |
|||
if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time and snak.datavalue.value.precision ) then |
|||
end |
|||
return parseTimeBoundaries( snak.datavalue.value.time, snak.datavalue.value.precision ); |
|||
return result |
|||
end |
|||
return nil; |
|||
end |
|||
context.getSourcingCircumstances = function( statement ) return getSourcingCircumstances( statement ) end; |
|||
context.selectClaims = function( options, propertyId ) return selectClaims( context, options, propertyId ) end; |
|||
return context.formatProperty( options ); |
|||
end |
end |
||
function formatPropertyDefault( context, options ) |
|||
if ( not context ) then error( 'context not specified' ); end; |
|||
if ( not options ) then error( 'options not specified' ); end; |
|||
if ( not options.entity ) then error( 'options.entity missing' ); end; |
|||
local claims; |
|||
------------------------------------------------------------------------------ |
|||
if options.property then -- TODO: Почему тут может не быть property? |
|||
-- module global functions |
|||
claims = context.selectClaims( options, options.property ); |
|||
end |
|||
if claims == nil then |
|||
return '' --TODO error? |
|||
end |
|||
-- Обход всех заявлений утверждения и с накоплением оформленых предпочтительных |
|||
if debug then |
|||
-- заявлений в таблице |
|||
function p.inspectI18n(frame) |
|||
local formattedClaims = {} |
|||
for _, key in pairs(frame.args) do |
|||
for i, claim in ipairs(claims) do |
|||
key = mw.text.trim(key) |
|||
local formattedStatement = context.formatStatement( options, claim ) |
|||
val = val[key] |
|||
-- здесь может вернуться либо оформленный текст заявления |
|||
-- либо строка ошибки nil похоже никогда не возвращается |
|||
if (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 val |
|||
end |
end |
||
end |
|||
return out |
|||
function p.descriptionIn(frame) |
|||
local langcode = frame.args[1] |
|||
local id = frame.args[2] -- "id" must be nil, as access to other Wikidata objects is disabled in Mediawiki configuration |
|||
-- return description of a Wikidata entity in the given language or the default language of this Wikipedia site |
|||
return mw.wikibase.getEntityObject(id).descriptions[langcode or wiki.langcode].value |
|||
end |
end |
||
--[[ |
|||
function p.labelIn(frame) |
|||
Функция для оформления одного утверждения (statement) |
|||
local langcode = frame.args[1] |
|||
local id = frame.args[2] -- "id" must be nil, as access to other Wikidata objects is disabled in Mediawiki configuration |
|||
Принимает: объект-таблицу утверждение и таблицу параметров |
|||
-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site |
|||
Возвращает: строку оформленного текста с заявлением (claim) |
|||
return mw.wikibase.getEntityObject(id).labels[langcode or wiki.langcode].value |
|||
]] |
|||
function formatStatement( context, options, statement ) |
|||
if ( not statement ) then |
|||
error( 'statement is not specified or nil' ); |
|||
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 |
||
function getSourcingCircumstances( statement ) |
|||
-- This is used to get a value, or a comma separated list of them if multiple values exist |
|||
if (not statement) then error('statement is not specified') end; |
|||
p.getValue = function(frame) |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
local input_parm = mw.text.trim(frame.args[2] or "") |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
local entity = mw.wikibase.getEntityObject() |
|||
local claims |
|||
if entity and entity.claims then |
|||
claims = entity.claims[propertyID] |
|||
end |
|||
if claims then |
|||
-- if wiki-linked value output as link if possible |
|||
if (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == "wikibase-entityid") then |
|||
local out = {} |
|||
for k, v in pairs(claims) do |
|||
local sitelink = mw.wikibase.sitelink("Q" .. v.mainsnak.datavalue.value["numeric-id"]) |
|||
local label = mw.wikibase.label("Q" .. v.mainsnak.datavalue.value["numeric-id"]) |
|||
if label == nil then label = "Q" .. v.mainsnak.datavalue.value["numeric-id"] end |
|||
local circumstances = {}; |
|||
if sitelink then |
|||
if ( statement.qualifiers |
|||
out[#out + 1] = "[[" .. sitelink .. "|" .. label .. "]]" |
|||
and statement.qualifiers.P1480 ) then |
|||
else |
|||
for i, qualifier in pairs( statement.qualifiers.P1480 ) do |
|||
out[#out + 1] = "[[:d:Q" .. v.mainsnak.datavalue.value["numeric-id"] .. "|" .. label .. "]]<abbr title='" .. i18n["errors"]["local-article-not-found"] .. "'>[*]</abbr>" |
|||
if ( qualifier |
|||
end |
|||
and qualifier.datavalue |
|||
and qualifier.datavalue.type == 'wikibase-entityid' |
|||
and qualifier.datavalue.value |
|||
and qualifier.datavalue.value["entity-type"] == 'item' ) then |
|||
local circumstance = 'Q' .. qualifier.datavalue.value["numeric-id"]; |
|||
if ( 'Q5727902' == circumstance ) then |
|||
circumstances.circa = true; |
|||
end |
|||
if ( 'Q18122778' == circumstance ) then |
|||
circumstances.presumably = true; |
|||
end |
end |
||
return table.concat(out, ", ") |
|||
else |
|||
-- just return best values |
|||
return entity:formatPropertyValues(propertyID).value |
|||
end |
end |
||
else |
|||
return "" |
|||
end |
end |
||
else |
|||
return input_parm |
|||
end |
end |
||
return circumstances; |
|||
end |
end |
||
--[[ |
|||
-- Same as above, but uses the short name property for label if available. |
|||
Функция для оформления одного утверждения (statement) |
|||
p.getValueShortName = function(frame) |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
local input_parm = mw.text.trim(frame.args[2] or "") |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
local entity = mw.wikibase.getEntityObject() |
|||
local claims |
|||
if entity and entity.claims then |
|||
claims = entity.claims[propertyID] |
|||
end |
|||
if claims then |
|||
-- if wiki-linked value output as link if possible |
|||
if (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == "wikibase-entityid") then |
|||
local out = {} |
|||
for k, v in pairs(claims) do |
|||
local sitelink = mw.wikibase.sitelink("Q" .. v.mainsnak.datavalue.value["numeric-id"]) |
|||
local label |
|||
local claimEntity = mw.wikibase.getEntity("Q" .. v.mainsnak.datavalue.value["numeric-id"]) |
|||
if claimEntity ~= nil then |
|||
if claimEntity.claims.P1813 then |
|||
for k2, v2 in pairs(claimEntity.claims.P1813) do |
|||
if v2.mainsnak.datavalue.value.language == "en" then |
|||
label = v2.mainsnak.datavalue.value.text |
|||
end |
|||
end |
|||
end |
|||
end |
|||
if label == nil or label == "" then label = mw.wikibase.label("Q" .. v.mainsnak.datavalue.value["numeric-id"]) end |
|||
if label == nil then label = "Q" .. v.mainsnak.datavalue.value["numeric-id"] end |
|||
Принимает: объект-таблицу утверждение, таблицу параметров, |
|||
if sitelink then |
|||
объект-функцию оформления внутренних структур утверждения (snak) и |
|||
out[#out + 1] = "[[" .. sitelink .. "|" .. label .. "]]" |
|||
объект-функцию оформления ссылки на источники (reference) |
|||
else |
|||
Возвращает: строку оформленного текста с заявлением (claim) |
|||
out[#out + 1] = "[[:d:Q" .. v.mainsnak.datavalue.value["numeric-id"] .. "|" .. label .. "]]<abbr title='" .. i18n["errors"]["local-article-not-found"] .. "'>[*]</abbr>" |
|||
]] |
|||
end |
|||
function formatStatementDefault( context, options, statement ) |
|||
end |
|||
if (not context) then error('context is not specified') end; |
|||
return table.concat(out, ", ") |
|||
if (not options) then error('options is not specified') end; |
|||
else |
|||
if (not statement) then error('statement is not specified') end; |
|||
-- just return best vakues |
|||
return entity:formatPropertyValues(propertyID).value |
|||
local circumstances = context.getSourcingCircumstances( statement ); |
|||
end |
|||
else |
|||
if statement.qualifiers then |
|||
return "" |
|||
options.qualifiers = statement.qualifiers; |
|||
end |
|||
else |
|||
return input_parm |
|||
end |
end |
||
if ( options.references ) then |
|||
return context.formatSnak( options, statement.mainsnak, circumstances ) .. context.formatRefs( options, statement ); |
|||
else |
|||
return context.formatSnak( options, statement.mainsnak, circumstances ); |
|||
end |
|||
end |
end |
||
--[[ |
|||
-- This is used to get a value, or a comma separated list of them if multiple values exist |
|||
Функция для оформления части утверждения (snak) |
|||
-- from an arbitrary entry by using its QID. |
|||
Подробнее о snak см. d:Викиданные:Глоссарий |
|||
-- Use : {{#invoke:Wikidata|getValueFromID|<ID>|<Property>|FETCH_WIKIDATA}} |
|||
-- E.g.: {{#invoke:Wikidata|getValueFromID|Q151973|P26|FETCH_WIKIDATA}} - to fetch value of 'spouse' (P26) from 'Richard Burton' (Q151973) |
|||
-- Please use sparingly - this is an *expensive call*. |
|||
p.getValueFromID = function(frame) |
|||
local itemID = mw.text.trim(frame.args[1] or "") |
|||
local propertyID = mw.text.trim(frame.args[2] or "") |
|||
local input_parm = mw.text.trim(frame.args[3] or "") |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
local entity = mw.wikibase.getEntity(itemID) |
|||
local claims |
|||
if entity and entity.claims then |
|||
claims = entity.claims[propertyID] |
|||
end |
|||
if claims then |
|||
-- if wiki-linked value output as link if possible |
|||
if (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == "wikibase-entityid") then |
|||
local out = {} |
|||
for k, v in pairs(claims) do |
|||
local sitelink = mw.wikibase.sitelink("Q" .. v.mainsnak.datavalue.value["numeric-id"]) |
|||
local label = mw.wikibase.label("Q" .. v.mainsnak.datavalue.value["numeric-id"]) |
|||
if label == nil then label = "Q" .. v.mainsnak.datavalue.value["numeric-id"] end |
|||
Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций |
|||
if sitelink then |
|||
Возвращает: строку оформленного викитекста |
|||
out[#out + 1] = "[[" .. sitelink .. "|" .. label .. "]]" |
|||
]] |
|||
else |
|||
function formatSnak( context, options, snak, circumstances ) |
|||
out[#out + 1] = "[[:d:Q" .. v.mainsnak.datavalue.value["numeric-id"] .. "|" .. label .. "]]<abbr title='" .. i18n["errors"]["local-article-not-found"] .. "'>[*]</abbr>" |
|||
circumstances = circumstances or {}; |
|||
end |
|||
local hash = ''; |
|||
end |
|||
local mainSnakClass = ''; |
|||
return table.concat(out, ", ") |
|||
if ( snak.hash ) then |
|||
else |
|||
hash = ' data-wikidata-hash="' .. snak.hash .. '"'; |
|||
return entity:formatPropertyValues(propertyID).value |
|||
end |
|||
else |
|||
return "" |
|||
end |
|||
else |
else |
||
mainSnakClass = ' wikidata-main-snak'; |
|||
return input_parm |
|||
end |
end |
||
end |
|||
local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>' |
|||
p.getQualifierValue = function(frame) |
|||
local after = '</span>' |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
local qualifierID = mw.text.trim(frame.args[2] or "") |
|||
if snak.snaktype == 'somevalue' then |
|||
local input_parm = mw.text.trim(frame.args[3] or "") |
|||
if ( options['somevalue'] and options['somevalue'] ~= '' ) then |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
return before .. options['somevalue'] .. after; |
|||
local entity = mw.wikibase.getEntityObject() |
|||
end |
|||
if entity.claims[propertyID] ~= nil then |
|||
return before .. options.i18n['somevalue'] .. after; |
|||
local out = {} |
|||
elseif snak.snaktype == 'novalue' then |
|||
for k, v in pairs(entity.claims[propertyID]) do |
|||
if ( options['novalue'] and options['novalue'] ~= '' ) then |
|||
for k2, v2 in pairs(v.qualifiers[qualifierID]) do |
|||
return before .. options['novalue'] .. after; |
|||
if v2.snaktype == 'value' then |
|||
end |
|||
if (mw.wikibase.sitelink("Q" .. v2.datavalue.value["numeric-id"])) then |
|||
return before .. options.i18n['novalue'] .. after; |
|||
out[#out + 1] = "[[" .. mw.wikibase.sitelink("Q" .. v2.datavalue.value["numeric-id"]) .. "]]" |
|||
elseif snak.snaktype == 'value' then |
|||
else |
|||
if ( circumstances.presumably ) then |
|||
out[#out + 1] = "[[:d:Q" .. v2.datavalue.value["numeric-id"] .. "|" .. mw.wikibase.label("Q" .. v2.datavalue.value["numeric-id"]) .. "]]<abbr title='" .. i18n["errors"]["local-article-not-found"] .. "'>[*]</abbr>" |
|||
before = before .. options.i18n.presumably; |
|||
end |
|||
end |
|||
end |
|||
end |
|||
return table.concat(out, ", ") |
|||
else |
|||
return "" |
|||
end |
end |
||
if ( circumstances.circa ) then |
|||
else |
|||
before = before .. options.i18n.circa; |
|||
return input_parm |
|||
end |
|||
return before .. formatDatavalue( context, options, snak.datavalue, snak.datatype ) .. after; |
|||
else |
|||
throwError( 'unknown-snak-type' ); |
|||
end |
|||
end |
end |
||
--[[ |
|||
-- This is used to get a value like 'male' (for property p21) which won't be linked and numbers without the thousand separators |
|||
Функция для оформления объектов-значений с географическими координатами |
|||
p.getRawValue = function(frame) |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
local input_parm = mw.text.trim(frame.args[2] or "") |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
local entity = mw.wikibase.getEntityObject() |
|||
local claims |
|||
if entity and entity.claims then claims = entity.claims[propertyID] end |
|||
if claims then |
|||
local result = entity:formatPropertyValues(propertyID, mw.wikibase.entity.claimRanks).value |
|||
Принимает: объект-значение и таблицу параметров, |
|||
-- if number type: remove thousand separators, bounds and units |
|||
Возвращает: строку оформленного текста |
|||
if (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == "quantity") then |
|||
]] |
|||
result = mw.ustring.gsub(result, "(%d),(%d)", "%1%2") |
|||
function formatGlobeCoordinate( value, options ) |
|||
result = mw.ustring.gsub(result, "(%d)±.*", "%1") |
|||
-- проверка на требование в параметрах вызова на возврат сырого значения |
|||
end |
|||
if options['subvalue'] == 'latitude' then -- широты |
|||
return result |
|||
return value['latitude'] |
|||
else |
|||
elseif options['subvalue'] == 'longitude' then -- долготы |
|||
return "" |
|||
return value['longitude'] |
|||
end |
|||
elseif options['nocoord'] and options['nocoord'] ~= '' then |
|||
else |
|||
-- если передан параметр nocoord, то не выводить координаты |
|||
return input_parm |
|||
-- обычно это делается при использовании нескольких карточек на странице |
|||
end |
|||
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 |
end |
||
--[[ |
|||
-- This is used to get the unit name for the numeric value returned by getRawValue |
|||
Функция для оформления объектов-значений с файлами с Викисклада |
|||
p.getUnits = function(frame) |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
Принимает: объект-значение и таблицу параметров, |
|||
local input_parm = mw.text.trim(frame.args[2] or "") |
|||
Возвращает: строку оформленного текста |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
]] |
|||
local entity = mw.wikibase.getEntityObject() |
|||
function formatCommonsMedia( value, options ) |
|||
local claims |
|||
local image = '[[File:' .. value |
|||
if entity and entity.claims then claims = entity.claims[propertyID] end |
|||
if options['border'] and options['border'] ~= '' then |
|||
if claims then |
|||
image = image .. '|border' |
|||
local result = entity:formatPropertyValues(propertyID, mw.wikibase.entity.claimRanks).value |
|||
end |
|||
if (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == "quantity") then |
|||
result = mw.ustring.sub(result, mw.ustring.find(result, " ")+1, -1) |
|||
local size = options['size'] |
|||
end |
|||
if size and size ~= '' then |
|||
return result |
|||
if not string.match( size, 'px$' ) |
|||
else |
|||
and not string.match( size, 'пкс$' ) -- TODO: использовать перевод для языка вики |
|||
return "" |
|||
then |
|||
size = size .. 'px' |
|||
end |
|||
else |
else |
||
size = fileDefaultSize; |
|||
return input_parm |
|||
end |
|||
image = image .. '|' .. size |
|||
end |
|||
if options['alt'] and options['alt'] ~= '' then |
|||
-- This is used to get the unit's QID to use with the numeric value returned by getRawValue |
|||
image = image .. '|' .. options['alt'] |
|||
p.getUnitID = function(frame) |
|||
end |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
image = image .. ']]' |
|||
local input_parm = mw.text.trim(frame.args[2] or "") |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
if options['description'] and options['description'] ~= '' then |
|||
local entity = mw.wikibase.getEntityObject() |
|||
image = image .. '<br>' .. options['description'] |
|||
local claims |
|||
if entity and entity.claims then claims = entity.claims[propertyID] end |
|||
if claims then |
|||
local result |
|||
if (claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == "quantity") then |
|||
-- get the url for the unit entry on Wikidata: |
|||
result = claims[1].mainsnak.datavalue.value.unit |
|||
-- and just reurn the last bit from "Q" to the end (which is the QID): |
|||
result = mw.ustring.sub(result, mw.ustring.find(result, "Q"), -1) |
|||
end |
|||
return result |
|||
else |
|||
return "" |
|||
end |
|||
else |
|||
return input_parm |
|||
end |
end |
||
return image |
|||
end |
end |
||
--[[ |
|||
p.getRawQualifierValue = function(frame) |
|||
Функция для оформления внешних идентификаторов |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
local qualifierID = mw.text.trim(frame.args[2] or "") |
|||
Принимает: объект-значение и таблицу параметров, |
|||
local input_parm = mw.text.trim(frame.args[3] or "") |
|||
Возвращает: строку оформленного текста |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
]] |
|||
local entity = mw.wikibase.getEntityObject() |
|||
local function formatExternalId( value, options ) |
|||
if entity.claims[propertyID] ~= nil then |
|||
local formatter = options.formatter |
|||
for k, v in pairs(entity.claims[propertyID]) do |
|||
if not formatter or formatter == '' then |
|||
for k2, v2 in pairs(v.qualifiers[qualifierID]) do |
|||
local entity = mw.wikibase.getEntity( options.property:upper() ) |
|||
if v2.snaktype == 'value' then |
|||
if entity then |
|||
local statements = entity:getBestStatements( 'P1630' ) |
|||
out[#out + 1] = mw.wikibase.label("Q" .. v2.datavalue.value["numeric-id"]) |
|||
for _, statement in pairs( statements ) do |
|||
else |
|||
if statement.mainsnak.snaktype == 'value' then |
|||
out[#out + 1] = v2.datavalue.value |
|||
formatter = statement.mainsnak.datavalue.value |
|||
end |
|||
break |
|||
end |
end |
||
end |
end |
||
local ret = table.concat(out, ", ") |
|||
return string.upper(string.sub(ret, 1, 1)) .. string.sub(ret, 2) |
|||
else |
|||
return "" |
|||
end |
end |
||
else |
|||
return input_parm |
|||
end |
end |
||
end |
|||
if formatter and formatter ~= '' then |
|||
-- This is used to get a date value for date_of_birth (P569), etc. which won't be linked |
|||
local link = mw.ustring.gsub( formatter, '$1', value ) |
|||
-- Dates and times are stored in ISO 8601 format (sort of). |
|||
-- At present the local formatDate(date, precision, timezone) function doesn't handle timezone |
|||
local title = options.title |
|||
-- So I'll just supply "Z" in the call to formatDate below: |
|||
if not title or title == '' then |
|||
p.getDateValue = function(frame) |
|||
title = '$1' |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
local input_parm = mw.text.trim(frame.args[2] or "") |
|||
local date_format = mw.text.trim(frame.args[3] or i18n["datetime"]["default-format"]) |
|||
local date_addon = mw.text.trim(frame.args[4] or i18n["datetime"]["default-addon"]) |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
local entity = mw.wikibase.getEntityObject() |
|||
if entity.claims[propertyID] ~= nil then |
|||
local out = {} |
|||
for k, v in pairs(entity.claims[propertyID]) do |
|||
if v.mainsnak.datavalue.type == 'time' then |
|||
local timestamp = v.mainsnak.datavalue.value.time |
|||
local dateprecision = v.mainsnak.datavalue.value.precision |
|||
-- A year can be stored like this: "+1872-00-00T00:00:00Z", |
|||
-- which is processed here as if it were the day before "+1872-01-01T00:00:00Z", |
|||
-- and that's the last day of 1871, so the year is wrong. |
|||
-- So fix the month 0, day 0 timestamp to become 1 January instead: |
|||
timestamp = timestamp:gsub("%-00%-00T", "-01-01T") |
|||
out[#out + 1] = parseDateFull(timestamp, dateprecision, date_format, date_addon) |
|||
end |
|||
end |
|||
return table.concat(out, ", ") |
|||
else |
|||
return "" |
|||
end |
end |
||
title = mw.ustring.gsub( title, '$1', value ) |
|||
else |
|||
return input_parm |
|||
return '[' .. link .. ' ' .. title .. ']' |
|||
end |
end |
||
return value |
|||
end |
end |
||
--[[ |
|||
p.getQualifierDateValue = function(frame) |
|||
Функция для оформления числовых значений |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
local qualifierID = mw.text.trim(frame.args[2] or "") |
|||
Принимает: объект-значение и таблицу параметров, |
|||
local input_parm = mw.text.trim(frame.args[3] or "") |
|||
Возвращает: строку оформленного текста |
|||
local date_format = mw.text.trim(frame.args[4] or i18n["datetime"]["default-format"]) |
|||
]] |
|||
local date_addon = mw.text.trim(frame.args[5] or i18n["datetime"]["default-addon"]) |
|||
local function formatQuantity( value, options ) |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
-- диапазон значений |
|||
local entity = mw.wikibase.getEntityObject() |
|||
local amount = string.gsub( value['amount'], '^%+', '' ); |
|||
if entity.claims[propertyID] ~= nil then |
|||
local lang = mw.language.getContentLanguage(); |
|||
local out = {} |
|||
local langCode = lang:getCode(); |
|||
for k, v in pairs(entity.claims[propertyID]) do |
|||
for k2, v2 in pairs(v.qualifiers[qualifierID]) do |
|||
local function formatNum( number ) |
|||
if v2.snaktype == 'value' then |
|||
-- округление до 13 знаков после запятой, на 14-м возникает ошибка в точности |
|||
local timestamp = v2.datavalue.value.time |
|||
local mult = 10^13 |
|||
out[#out + 1] = parseDateValue(timestamp, date_format, date_addon) |
|||
number = math.floor( number * mult + 0.5 ) / mult |
|||
end |
|||
end |
|||
return lang:formatNum( number ) |
|||
end |
|||
return table.concat(out, ", ") |
|||
else |
|||
return "" |
|||
end |
|||
else |
|||
return input_parm |
|||
end |
end |
||
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 |
|||
-- This is used to fetch all of the images with a particular property, e.g. image (P18), Gene Atlas Image (P692), etc. |
|||
if options.unit ~= '-' then |
|||
-- Parameters are | propertyID | value / FETCH_WIKIDATA / nil | separator (default=space) | size (default=frameless) |
|||
out = out .. ' ' .. options.unit |
|||
-- It will return a standard wiki-markup [[File:Filename | size]] for each image with a selectable size and separator (which may be html) |
|||
end |
|||
-- e.g. {{#invoke:Wikidata|getImages|P18|FETCH_WIKIDATA}} |
|||
elseif value.unit and string.match( value.unit, 'http://www.wikidata.org/entity/' ) then |
|||
-- e.g. {{#invoke:Wikidata|getImages|P18|FETCH_WIKIDATA|<br>|250px}} |
|||
local unitEntityId = string.gsub( value.unit, 'http://www.wikidata.org/entity/', '' ); |
|||
-- If a property is chosen that is not of type "commonsMedia", it will return empty text. |
|||
local unitEntity = mw.wikibase.getEntity( unitEntityId ); |
|||
p.getImages = function(frame) |
|||
if unitEntity then |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
local writingSystemElementId = 'Q8209'; |
|||
local input_parm = mw.text.trim(frame.args[2] or "") |
|||
local langElementId = 'Q7737'; |
|||
local sep = mw.text.trim(frame.args[3] or " ") |
|||
local label = getLabelWithLang( context, options, unitEntity, nil, { |
|||
local imgsize = mw.text.trim(frame.args[4] or "frameless") |
|||
'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']', |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
'P558[!P282][!P407]' |
|||
local entity = mw.wikibase.getEntityObject() |
|||
} ); |
|||
if entity and entity.claims then |
|||
out = out .. ' ' .. label; |
|||
claims = entity.claims[propertyID] |
|||
end |
end |
||
end |
|||
if claims then |
|||
if (claims[1] and claims[1].mainsnak.datatype == "commonsMedia") then |
|||
local out = {} |
|||
for k, v in pairs(claims) do |
|||
local filename = v.mainsnak.datavalue.value |
|||
out[#out + 1] = "[[File:" .. filename .. "|" .. imgsize .. "]]" |
|||
end |
|||
return table.concat(out, sep) |
|||
else |
|||
return "" |
|||
end |
|||
else |
|||
return "" |
|||
end |
|||
else |
|||
return input_parm |
|||
end |
|||
end |
|||
return out; |
|||
-- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323) |
|||
-- which are then linked to http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/01.1.00.005%20Entity%20TA98%20EN.htm |
|||
-- uses the newer mw.wikibase calls instead of directly using the snaks |
|||
-- formatPropertyValues returns a table with the P1323 values concatenated with ", " so we have to split them out into a table in order to construct the return string |
|||
p.getTAValue = function(frame) |
|||
local ent = mw.wikibase.getEntityObject() |
|||
local props = ent:formatPropertyValues('P1323') |
|||
local out = {} |
|||
local t = {} |
|||
for k, v in pairs(props) do |
|||
if k == 'value' then |
|||
t = mw.text.split( v, ", ") |
|||
for k2, v2 in pairs(t) do |
|||
out[#out + 1] = "[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/" .. string.sub(v2, 2) .. "%20Entity%20TA98%20EN.htm " .. v2 .. "]" |
|||
end |
|||
end |
|||
end |
|||
local ret = table.concat(out, "<br> ") |
|||
if #ret == 0 then |
|||
ret = "Invalid TA" |
|||
end |
|||
return ret |
|||
end |
end |
||
--[[ |
--[[ |
||
Get property datatype by ID. |
|||
This is used to return an image legend from Wikidata |
|||
image is property P18 |
|||
@param string Property ID, e.g. 'P123'. |
|||
image legend is property P2096 |
|||
@return string Property datatype, e.g. 'commonsMedia', 'time' or 'url'. |
|||
Call as {{#invoke:Wikidata |getImageLegend | <PARAMETER> | lang=<ISO-639code> |id=<QID>}} |
|||
Returns PARAMETER, unless it is equal to "FETCH_WIKIDATA", from Item QID (expensive call) |
|||
If QID is omitted or blank, the current article is used (not an expensive call) |
|||
If lang is omitted, it uses the local wiki language, otherwise it uses the provided ISO-639 language code |
|||
ISO-639: https://docs.oracle.com/cd/E13214_01/wli/docs92/xref/xqisocodes.html#wp1252447 |
|||
Ranks are: 'preferred' > 'normal' |
|||
This returns the label from the first image with 'preferred' rank |
|||
Or the label from the first image with 'normal' rank if preferred returns nothing |
|||
Ranks: https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua |
|||
]] |
]] |
||
local function getPropertyDatatype( propertyId ) |
|||
if not propertyId or not string.match( propertyId, '^P%d+$' ) then |
|||
p.getImageLegend = function(frame) |
|||
return nil; |
|||
-- look for named parameter id; if it's blank make it nil |
|||
local id = frame.args.id |
|||
if id and (#id == 0) then |
|||
id = nil |
|||
end |
end |
||
local propertyEntity = mw.wikibase.getEntity( propertyId ); |
|||
-- look for named parameter lang |
|||
if not propertyEntity then |
|||
-- it should contain a two-character ISO-639 language code |
|||
return nil; |
|||
-- if it's blank fetch the language of the local wiki |
|||
local lang = frame.args.lang |
|||
if (not lang) or (#lang < 2) then |
|||
lang = mw.language.getContentLanguage().code |
|||
end |
end |
||
return propertyEntity.datatype; |
|||
-- first unnamed parameter is the local parameter, if supplied |
|||
end |
|||
local input_parm = mw.text.trim(frame.args[1] or "") |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
local function getDefaultValueFunction( datavalue, datatype ) |
|||
local ent = mw.wikibase.getEntityObject(id) |
|||
-- вызов обработчиков по умолчанию для известных типов значений |
|||
local imgs |
|||
if datavalue.type == 'wikibase-entityid' then |
|||
if ent and ent.claims then |
|||
-- Entity ID |
|||
imgs = ent.claims.P18 |
|||
return function( context, options, value ) return formatEntityId( context, options, getEntityIdFromValue( value ) ) end; |
|||
end |
|||
elseif datavalue.type == 'string' then |
|||
local imglbl |
|||
-- String |
|||
if imgs then |
|||
if datatype and datatype == 'commonsMedia' then |
|||
-- look for an image with 'preferred' rank |
|||
-- Media |
|||
for k1, v1 in pairs(imgs) do |
|||
return function( context, options, value ) |
|||
if v1.rank == "preferred" and v1.qualifiers and v1.qualifiers.P2096 then |
|||
if ( not options.description or options.description == '' ) |
|||
local imglbls = v1.qualifiers.P2096 |
|||
and options.qualifiers and options.qualifiers.P2096 then |
|||
for k2, v2 in pairs(imglbls) do |
|||
for i, qualifier in pairs( options.qualifiers.P2096 ) do |
|||
if v2.datavalue.value.language == lang then |
|||
if ( qualifier |
|||
and qualifier.datavalue |
|||
and qualifier.datavalue.type == 'monolingualtext' |
|||
and qualifier.datavalue.value |
|||
and qualifier.datavalue.value.language == contentLanguageCode ) then |
|||
options.description = qualifier.datavalue.value.text |
|||
break |
break |
||
end |
end |
||
end |
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 == 'url' then |
|||
-- URL |
|||
return function( context, options, value ) |
|||
local moduleUrl = require( 'Module:URL' ) |
|||
if not options.length or options.length == '' then |
|||
options.length = 25 |
|||
end |
end |
||
return moduleUrl.formatUrlSingle( context, options, value ); |
|||
end |
|||
end |
|||
-- if we don't find one, look for an image with 'normal' rank |
|||
end |
|||
if (not imglbl) then |
|||
return function( context, options, value ) return value end; |
|||
for k1, v1 in pairs(imgs) do |
|||
elseif datavalue.type == 'monolingualtext' then |
|||
if v1.rank == "normal" and v1.qualifiers and v1.qualifiers.P2096 then |
|||
-- моноязычный текст (строка с указанием языка) |
|||
local imglbls = v1.qualifiers.P2096 |
|||
return function( context, options, value ) |
|||
for k2, v2 in pairs(imglbls) do |
|||
if ( options.monolingualLangTemplate == 'lang' ) then |
|||
return options.frame:expandTemplate{ title = 'lang-' .. value.language, args = { value.text } }; |
|||
imglbl = v2.datavalue.value.text |
|||
elseif ( options.monolingualLangTemplate == 'ref' ) then |
|||
break |
|||
return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>' .. options.frame:expandTemplate{ title = 'ref-' .. value.language }; |
|||
end |
|||
else |
|||
end |
|||
return '<span class="lang" lang="' .. value.language .. '">' .. value.text .. '</span>'; |
|||
end |
|||
end |
|||
end; |
|||
elseif datavalue.type == 'globecoordinate' then |
|||
end |
|||
-- географические координаты |
|||
return imglbl |
|||
return function( context, options, value ) return formatGlobeCoordinate( value, options ) end; |
|||
else |
|||
elseif datavalue.type == 'quantity' then |
|||
return input_parm |
|||
return function( context, options, value ) return formatQuantity( value, options ) end; |
|||
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 |
||
--[[ |
|||
-- This is used to get the QIDs of all of the values of a property, as a comma separated list if multiple values exist |
|||
Функция для оформления значений (value) |
|||
-- Usage: {{#invoke:Wikidata |getPropertyIDs |<PropertyID> |FETCH_WIKIDATA}} |
|||
Подробнее о значениях см. d:Wikidata:Glossary/ru |
|||
-- Usage: {{#invoke:Wikidata |getPropertyIDs |<PropertyID> |<InputParameter> |qid=<QID>}} |
|||
Принимает: объект-значение и таблицу параметров, |
|||
p.getPropertyIDs = function(frame) |
|||
Возвращает: строку оформленного текста |
|||
local propertyID = mw.text.trim(frame.args[1] or "") |
|||
]] |
|||
local input_parm = mw.text.trim(frame.args[2] or "") |
|||
function formatDatavalue( context, options, datavalue, datatype ) |
|||
-- can take a named parameter |qid which is the Wikidata ID for the article. This will not normally be used. |
|||
if ( not context ) then error( 'context not specified' ); end; |
|||
local qid = frame.args.qid |
|||
if |
if ( not options ) then error( 'options not specified' ); end; |
||
if ( not datavalue ) then error( 'datavalue not specified' ); end; |
|||
if input_parm == "FETCH_WIKIDATA" then |
|||
if ( not datavalue.value ) then error( 'datavalue.value is missng' ); end; |
|||
local entity = mw.wikibase.getEntityObject(qid) |
|||
local propclaims |
|||
-- проверка на указание специализированных обработчиков в параметрах, |
|||
if entity and entity.claims then |
|||
-- переданных при вызове |
|||
propclaims = entity.claims[propertyID] |
|||
context.formatValueDefault = getDefaultValueFunction( datavalue, datatype ); |
|||
local functionToCall = getUserFunction( options, 'value', context.formatValueDefault ); |
|||
return functionToCall( context, options, datavalue.value ); |
|||
end |
|||
--[[ |
|||
Функция для оформления идентификатора сущности |
|||
Принимает: строку индентификатора (типа Q42) и таблицу параметров, |
|||
Возвращает: строку оформленного текста |
|||
]] |
|||
function formatEntityId( context, options, entityId ) |
|||
-- получение локализованного названия |
|||
local entity = mw.wikibase.getEntity( entityId ) |
|||
local label, labelLanguageCode = getLabelWithLang( context, options, entity ) |
|||
-- получение ссылки по идентификатору |
|||
local link = mw.wikibase.sitelink( entityId ) |
|||
if link then |
|||
if label then |
|||
if ( contentLanguageCode ~= labelLanguageCode ) then |
|||
return '[[' .. link .. '|' .. label .. ']]' .. categoryLinksToEntitiesWithMissingLocalLanguageLabel; |
|||
else |
|||
return '[[' .. link .. '|' .. label .. ']]'; |
|||
end |
|||
else |
|||
return '[[' .. link .. ']]'; |
|||
end |
|||
end |
|||
if label then |
|||
-- красная ссылка |
|||
-- TODO: разобраться, почему не всегда есть options.frame |
|||
if not mw.title.new( label ).exists and options.frame then |
|||
return '[[' .. label .. ']]<sup>[[:d:' .. entityId .. '|[d]]]</sup>'; |
|||
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 |
||
if propclaims then |
|||
-- одноимённая статья уже существует - выводится текст и ссылка на ВД |
|||
-- if wiki-linked value collect the QID in a table |
|||
return '<span class="iw" data-title="' .. label .. '">' .. label |
|||
if (propclaims[1] and propclaims[1].mainsnak.snaktype == "value" and propclaims[1].mainsnak.datavalue.type == "wikibase-entityid") then |
|||
.. sup |
|||
local out = {} |
|||
.. '</span>' |
|||
for k, v in pairs(propclaims) do |
|||
end |
|||
out[#out + 1] = "Q" .. v.mainsnak.datavalue.value["numeric-id"] |
|||
-- сообщение об отсутвии локализованного названия |
|||
end |
|||
-- not good, but better than nothing |
|||
return table.concat(out, ", ") |
|||
return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. categoryLinksToEntitiesWithMissingLabel; |
|||
else |
|||
-- not a wikibase-entityid, so return empty |
|||
return "" |
|||
end |
|||
else |
|||
-- no claim, so return empty |
|||
return "" |
|||
end |
|||
else |
|||
return input_parm |
|||
end |
|||
end |
end |
||
--[[ |
|||
-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata |
|||
Функция для оформления утверждений (statement) |
|||
function p.pageId(frame) |
|||
Подробнее о утверждениях см. d:Wikidata:Glossary/ru |
|||
local entity = mw.wikibase.getEntityObject() |
|||
if not entity then return nil else return entity.id end |
|||
Принимает: таблицу параметров |
|||
Возвращает: строку оформленного текста, предназначенного для отображения в статье |
|||
]] |
|||
-- устаревшее имя, не использовать |
|||
function p.formatStatements( frame ) |
|||
return p.formatProperty( frame ); |
|||
end |
end |
||
--[[ |
|||
function p.claim(frame) |
|||
Получение параметров, которые обычно используются для вывода свойства. |
|||
local property = frame.args[1] or "" |
|||
]] |
|||
local id = frame.args["id"] -- "id" must be nil, as access to other Wikidata objects is disabled in Mediawiki configuration |
|||
function getPropertyParams( propertyId, datatype, params ) |
|||
local qualifierId = frame.args["qualifier"] |
|||
local config = require( 'Module:Wikidata/config' ); |
|||
local parameter = frame.args["parameter"] |
|||
if not config then |
|||
local list = frame.args["list"] |
|||
return {}; |
|||
local references = frame.args["references"] |
|||
end |
|||
local showerrors = frame.args["showerrors"] |
|||
local default = frame.args["default"] |
|||
if default then showerrors = nil end |
|||
-- Различные уровни настройки параметров, по убыванию приоритета |
|||
-- get wikidata entity |
|||
local propertyParams = {}; |
|||
local entity = mw.wikibase.getEntityObject(id) |
|||
if not entity then |
|||
-- 1. Параметры, указанные явно при вызове |
|||
if showerrors then return printError("entity-not-found") else return default end |
|||
if params then |
|||
local tplParams = mw.clone( params ); |
|||
for key, value in pairs( tplParams ) do |
|||
if value ~= '' then |
|||
propertyParams[key] = value; |
|||
end |
|||
end |
|||
end |
end |
||
-- fetch the first claim of satisfying the given property |
|||
-- 2. Настройки конкретного параметра |
|||
local claims = findClaims(entity, property) |
|||
if config['properties'] and config['properties'][propertyId] then |
|||
if not claims or not claims[1] then |
|||
local selfParams = mw.clone( config['properties'][propertyId] ); |
|||
if showerrors then return printError("property-not-found") else return default end |
|||
for key, value in pairs( selfParams ) do |
|||
if propertyParams[key] == nil then |
|||
propertyParams[key] = value; |
|||
end |
|||
end |
|||
end |
end |
||
-- 3. Указанный пресет настроек |
|||
-- get initial sort indices |
|||
if propertyParams['preset'] and config['presets'] |
|||
local sortindices = {} |
|||
and config['presets'][propertyParams['preset']] then |
|||
for idx in pairs(claims) do |
|||
local presetParams = mw.clone( config['presets'][propertyParams['preset']] ); |
|||
sortindices[#sortindices + 1] = idx |
|||
for key, value in pairs( presetParams ) do |
|||
end |
|||
if propertyParams[key] == nil then |
|||
-- sort by claim rank |
|||
propertyParams[key] = value; |
|||
local comparator = function(a, b) |
|||
end |
|||
local rankmap = { deprecated = 2, normal = 1, preferred = 0 } |
|||
end |
|||
local ranka = rankmap[claims[a].rank or "normal"] .. string.format("%08d", a) |
|||
local rankb = rankmap[claims[b].rank or "normal"] .. string.format("%08d", b) |
|||
return ranka < rankb |
|||
end |
end |
||
table.sort(sortindices, comparator) |
|||
-- 4. Настройки для типа данных |
|||
local result |
|||
if datatype and config['datatypes'] and config['datatypes'][datatype] then |
|||
local error |
|||
local datatypeParams = mw.clone( config['datatypes'][datatype] ); |
|||
if list then |
|||
for key, value in pairs( datatypeParams ) do |
|||
local value |
|||
if propertyParams[key] == nil then |
|||
-- iterate over all elements and return their value (if existing) |
|||
propertyParams[key] = value; |
|||
result = {} |
|||
end |
|||
for idx in pairs(claims) do |
|||
local claim = claims[sortindices[idx]] |
|||
value, error = getValueOfClaim(claim, qualifierId, parameter) |
|||
if not value and showerrors then value = error end |
|||
if value and references then value = value .. getReferences(frame, claim) end |
|||
result[#result + 1] = value |
|||
end |
end |
||
result = table.concat(result, list) |
|||
else |
|||
-- return first element |
|||
local claim = claims[sortindices[1]] |
|||
result, error = getValueOfClaim(claim, qualifierId, parameter) |
|||
if result and references then result = result .. getReferences(frame, claim) end |
|||
end |
end |
||
-- 5. Общие настройки для всех свойств |
|||
if result then return result else |
|||
if config['global'] then |
|||
if showerrors then return error else return default end |
|||
local globalParams = mw.clone( config['global'] ); |
|||
for key, value in pairs( globalParams ) do |
|||
if propertyParams[key] == nil then |
|||
propertyParams[key] = value; |
|||
end |
|||
end |
|||
end |
end |
||
return propertyParams; |
|||
end |
end |
||
function p.formatProperty( frame ) |
|||
-- look into entity object |
|||
local plain = toBoolean( frame.args.plain, false ); |
|||
function p.ViewSomething(frame) |
|||
local args = frame.args |
|||
local id = f.args.id |
|||
-- проверка на отсутствие обязательного параметра property |
|||
if id and (#id == 0) then |
|||
if not args.property then |
|||
id = nil |
|||
throwError( 'property-param-not-provided' ) |
|||
end |
|||
end |
|||
local data = mw.wikibase.getEntityObject(id) |
|||
local propertyId = mw.language.getContentLanguage():ucfirst( string.gsub( args.property, '%[.*$', '' ) ) |
|||
if not data then |
|||
local datatype = getPropertyDatatype( propertyId ); |
|||
return nil |
|||
args = getPropertyParams( propertyId, datatype, args ); |
|||
-- проброс всех параметров из шаблона {wikidata} |
|||
local p_frame = frame:getParent(); |
|||
if p_frame and p_frame:getTitle() == mw.site.namespaces[10].name .. ':Wikidata' then |
|||
copyTo( p_frame.args, args ); |
|||
end |
end |
||
args.nocat = toBoolean( args.nocat, false ); |
|||
local i = 1 |
|||
args.references = toBoolean( args.references, true ); |
|||
while true do |
|||
local index = f.args[i] |
|||
-- если значение передано в параметрах вызова то выводим только его |
|||
if not index then |
|||
if args.value and args.value ~= '' then |
|||
-- специальное значение для скрытия Викиданных |
|||
return mw.text.jsonEncode(data, mw.text.JSON_PRESERVE_KEYS + mw.text.JSON_PRETTY) |
|||
if args.value == '-' then |
|||
else |
|||
return '' |
|||
end |
|||
local value = args.value |
|||
-- опция, запрещающая оформление значения, поэтому никак не трогаем |
|||
if 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' and not string.find( value, '[%[%]%{%}]' ) 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 == 'url' then |
|||
local moduleUrl = require( 'Module:URL' ); |
|||
value = moduleUrl.formatUrlSingle( nil, args, value ); |
|||
end |
end |
||
-- оборачиваем в тег для JS-функций |
|||
data = data[index] or data[tonumber(index)] |
|||
if string.match( propertyId, '^P%d+$' ) then |
|||
if not data then |
|||
value = mw.text.trim( value ) |
|||
return |
|||
-- временная штрафная категория для исправления табличных вставок |
|||
if ( propertyId ~= 'P166' |
|||
and string.match( value, '<t[dr][ >]' ) |
|||
and not string.match( value, '<table >]' ) |
|||
and not string.match( value, '^%{%|' ) ) then |
|||
value = value .. '[[Категория:Википедия:Статьи с табличной вставкой в карточке]]' |
|||
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 |
|||
value = value .. categoryLocalValuePresent; |
|||
end |
end |
||
return value |
|||
i = i + 1 |
|||
end |
|||
end |
|||
if ( plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain |
|||
-- getting sitelink of a given wiki |
|||
return frame:callParserFunction( '#property', propertyId ); |
|||
function p.getSiteLink(frame) |
|||
end |
|||
local f = frame.args[1] |
|||
local entity = mw.wikibase.getEntity() |
|||
g_frame = frame |
|||
if not entity then |
|||
-- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений) |
|||
return |
|||
return formatProperty( args ) |
|||
end |
|||
local link = entity:getSitelink( f ) |
|||
if not link then |
|||
return |
|||
end |
|||
return link |
|||
end |
end |
||
--[[ |
|||
function p.Dump(frame) |
|||
Функция оформления ссылок на источники (reference) |
|||
local f = (frame.args[1] or frame.args.id) and frame or frame:getParent() |
|||
Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru |
|||
local data = mw.wikibase.getEntityObject(f.args.id) |
|||
if not data then |
|||
Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context |
|||
return i18n.warnDump |
|||
Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства). |
|||
Принимает: объект-таблицу утверждение |
|||
Возвращает: строку оформленных ссылок для отображения в статье |
|||
]] |
|||
function formatRefs( context, options, statement ) |
|||
if ( not context ) then error( 'context not specified' ); end; |
|||
if ( not options ) then error( 'options not specified' ); end; |
|||
if ( not options.entity ) then error( 'options.entity missing' ); end; |
|||
if ( not statement ) then error( 'statement not specified' ); end; |
|||
if ( not outputReferences ) then |
|||
return ''; |
|||
end |
end |
||
local |
local result = ''; |
||
if ( statement.references ) then |
|||
while true do |
|||
local index = f.args[i] |
|||
if not index then |
|||
return "<pre>"..mw.dumpObject(data).."</pre>".. i18n.warnDump |
|||
end |
|||
local allReferences = statement.references; |
|||
data = data[index] or data[tonumber(index)] |
|||
local hasPreferred = false; |
|||
if not data then |
|||
for _, reference in pairs( statement.references ) do |
|||
return i18n.warnDump |
|||
if ( reference.snaks |
|||
and reference.snaks.P248 |
|||
and reference.snaks.P248[1] |
|||
and reference.snaks.P248[1].datavalue |
|||
and reference.snaks.P248[1].datavalue.value["numeric-id"] ) then |
|||
local entityId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"]; |
|||
if ( preferredSources[entityId] ) then |
|||
hasPreferred = true; |
|||
end |
|||
end |
|||
end |
end |
||
for _, reference in pairs( statement.references ) do |
|||
i = i + 1 |
|||
local display = true; |
|||
if ( hasPreferred ) then |
|||
if ( reference.snaks |
|||
and reference.snaks.P248 |
|||
and reference.snaks.P248[1] |
|||
and reference.snaks.P248[1].datavalue |
|||
and reference.snaks.P248[1].datavalue.value["numeric-id"] ) then |
|||
local entityId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"]; |
|||
if ( deprecatedSources[entityId] ) then |
|||
display = false; |
|||
end |
|||
end |
|||
end |
|||
if ( display ) then |
|||
result = result .. moduleSources.renderReference( g_frame, options.entity, reference ); |
|||
end |
|||
end |
|||
end |
end |
||
return result |
|||
end |
end |
||
14 апр 2017, 00:44 юрамасы
Игътибар! Бу иң еш кулланылган модульләрнең берсе. Аның һәр үзгәртелүе «Викимедиа Фондының» серверларына эш өсти. Зинһар, нәрсә эшләгәнегезне аңлагыз һәм махсус өйрәнү урыннарында татып карагыз.
|
{{Wikidata}} эчендә кулланыла (параметрлар тасвирламасын шунда ук карагыз). Модуль:Wikidata/config ярдәмендә көйләнелә
Башка телле бүлектә тулырак мәкалә бар: Модуль:Wikidata/doc (рус.) Сез тәрҗемә ярдәме белән бу мәкаләне язып бетереп проектка ярдәм итә аласыз.
|
Бу — мәкалә төпчеге. Сез мәкаләне үзгәртеп һәм мәгълүмат өстәп, Википедия проектына ярдәм итә аласыз. Мөмкинлек булса, бу искәрмәне төгәлрәккә алыштырырга кирәк.
|
local i18n = {
["errors"] = {
["property-param-not-provided"] = "Не дан параметр свойства",
["entity-not-found"] = "Сущность не найдена.",
["unknown-claim-type"] = "Неизвестный тип заявления.",
["unknown-snak-type"] = "Неизвестный тип снэка.",
["unknown-datavalue-type"] = "Неизвестный тип значения данных.",
["unknown-entity-type"] = "Неизвестный тип сущности.",
["unknown-property-module"] = "Вы должны установить и property-module, и property-function.",
["unknown-claim-module"] = "Вы должны установить и claim-module, и claim-function.",
["unknown-value-module"] = "Вы должны установить и value-module, и value-function.",
["property-module-not-found"] = "Модуль для отображения свойства не найден",
["property-function-not-found"] = "Функция для отображения свойства не найдена",
["claim-module-not-found"] = "Модуль для отображения утверждения не найден.",
["claim-function-not-found"] = "Функция для отображения утверждения не найдена.",
["value-module-not-found"] = "Модуль для отображения значения не найден.",
["value-function-not-found"] = "Функция для отображения значения не найдена."
},
["somevalue"] = "''неизвестно''",
["novalue"] = "",
["circa"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="якынча">як. </span>',
["presumably"] = '<span style="border-bottom: 1px dotted; cursor: help;" title="предположительно">предп. </span>',
}
-- settings, may differ from project to project
local categoryLinksToEntitiesWithMissingLabel = '[[Категория:Википедия:Статьи со ссылками на элементы Викиданных без подписи]]';
local categoryLinksToEntitiesWithMissingLocalLanguageLabel = '[[Категория:Википедия:Статьи со ссылками на элементы Викиданных без русской подписи]]';
local categoryLocalValuePresent = '[[Категория:Википедия:Статьи с переопределением значения из Викиданных]]';
local fileDefaultSize = '267x400px';
local outputReferences = true;
-- sources that shall be omitted if any preffered sources exists
local deprecatedSources = {
Q36578 = true, -- Gemeinsame Normdatei
Q63056 = true, -- Find a Grave
Q15222191 = true, -- BNF
};
local preferredSources = {
Q5375741 = true, -- Encyclopædia Britannica Online
Q17378135 = true, -- Great Soviet Encyclopedia (1969—1978)
};
-- Ссылки на используемые модули, которые потребуются в 99% случаев загрузки страниц (чтобы иметь на виду при переименовании)
local moduleSources = require( 'Module:Sources' )
local WDS = require( 'Module:WikidataSelectors' );
-- Константы
local contentLanguageCode = mw.getContentLanguage():getCode();
local p = {}
local formatDatavalue, formatEntityId, formatRefs, formatSnak, formatStatement,
formatStatementDefault, formatProperty, getSourcingCircumstances,
getPropertyDatatype, getPropertyParams, throwError, toBoolean;
local function copyTo( obj, target )
for k, v in pairs( obj ) do
target[k] = v
end
return target;
end
local function splitISO8601(str)
if 'table' == type(str) then
if str.args and str.args[1] then
str = '' .. str.args[1]
else
return 'unknown argument type: ' .. type( str ) .. ': ' .. table.tostring( str )
end
end
local Y, M, D = (function(str)
local pattern = "(%-?%d+)%-(%d+)%-(%d+)T"
local Y, M, D = mw.ustring.match( str, pattern )
return tonumber(Y), tonumber(M), tonumber(D)
end) (str);
local h, m, s = (function(str)
local pattern = "T(%d+):(%d+):(%d+)%Z";
local H, M, S = mw.ustring.match( str, pattern);
return tonumber(H), tonumber(M), tonumber(S);
end) (str);
local oh,om = ( function(str)
if str:sub(-1)=="Z" then return 0,0 end; -- ends with Z, Zulu time
-- matches ±hh:mm, ±hhmm or ±hh; else returns nils
local pattern = "([-+])(%d%d):?(%d?%d?)$";
local sign, oh, om = mw.ustring.match( str, pattern);
sign, oh, om = sign or "+", oh or "00", om or "00";
return tonumber(sign .. oh), tonumber(sign .. om);
end )(str)
return {year=Y, month=M, day=D, hour=(h+oh), min=(m+om), sec=s};
end
local function parseTimeBoundaries( time, precision )
local s = splitISO8601( time );
if (not s) then return nil; end
if ( precision >= 0 and precision <= 8 ) then
local powers = { 1000000000 , 100000000, 10000000, 1000000, 100000, 10000, 1000, 100, 10 }
local power = powers[ precision + 1 ];
local left = s.year - ( s.year % power );
return { tonumber(os.time( {year=left, month=1, day=1, hour=0, min=0, sec=0} )) * 1000,
tonumber(os.time( {year=left + power - 1, month=12, day=31, hour=29, min=59, sec=58} )) * 1000 + 1999 };
end
if ( precision == 9 ) then
return { tonumber(os.time( {year=s.year, month=1, day=1, hour=0, min=0, sec=0} )) * 1000,
tonumber(os.time( {year=s.year, month=12, day=31, hour=23, min=59, sec=58} )) * 1000 + 1999 };
end
if ( precision == 10 ) then
local lastDays = {31, 28.25, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
local lastDay = lastDays[s.month];
return { tonumber(os.time( {year=s.year, month=s.month, day=1, hour=0, min=0, sec=0} )) * 1000,
tonumber(os.time( {year=s.year, month=s.month, day=lastDay, hour=23, min=59, sec=58} )) * 1000 + 1999 };
end
if ( precision == 11 ) then
return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=0, min=0, sec=0} )) * 1000,
tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=23, min=59, sec=58} )) * 1000 + 1999 };
end
if ( precision == 12 ) then
return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=0, sec=0} )) * 1000,
tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=59, sec=58} )) * 1000 + 19991999 };
end
if ( precision == 13 ) then
return { tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} )) * 1000,
tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=58} )) * 1000 + 1999 };
end
if ( precision == 14 ) then
local t = tonumber(os.time( {year=s.year, month=s.month, day=s.day, hour=s.hour, min=s.min, sec=0} ) );
return { t * 1000, t * 1000 + 999 };
end
error('Unsupported precision: ' .. precision );
end
--[[
Преобразует строку в булевое значение
Принимает: строковое значение (может отсутствовать)
Возвращает: булевое значение true или false, если получается распознать значение, или 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
--[[
Функция для получения сущности (еntity) для текущей страницы
Подробнее о сущностях см. d:Wikidata:Glossary/ru
Принимает: строковый индентификатор (типа P18, Q42)
Возвращает: объект таблицу, элементы которой индексируются с нуля
]]
local function getEntityFromId( id )
if id then
return mw.wikibase.getEntityObject( id )
end
return mw.wikibase.getEntityObject();
end
--[[
Внутрення функция для формирования сообщения об ошибке
Принимает: ключ элемента в таблице i18n (например entity-not-found)
Возвращает: строку сообщения
]]
local function throwError( key )
error( i18n.errors[key] );
end
--[[
Функция для получения идентификатора сущностей
Принимает: объект таблицу сущности
Возвращает: строковый индентификатор (типа P18, Q42)
]]
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
-- проверка на наличие специилизированной функции в опциях
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
-- Выбирает свойства по property id, дополнительно фильтруя их по рангу
local function selectClaims( context, options, propertySelector )
if ( not context ) then error( 'context 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 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 = getTimeBoundariesFromQualifiers( context, claim, 'P580' );
local endBoundaries = getTimeBoundariesFromQualifiers( 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
--[[
Функция для получения метки элемента в заданный момент времени.
Принимает: контекст, элемент, временные границы
Возвращает: текстовую метку элемента, язык метки
]]
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
--[[
Функция для оформления утверждений (statement)
Подробнее о утверждениях см. d:Wikidata:Glossary/ru
Принимает: таблицу параметров
Возвращает: строку оформленного текста, предназначенного для отображения в статье
]]
local function formatProperty( options )
-- Получение сущности по идентификатору
local entity = getEntityFromId( options.entityId )
if not entity then
return -- throwError( 'entity-not-found' )
end
-- проверка на присутсвие у сущности заявлений (claim)
-- подробнее о заявлениях см. d:Викиданные:Глоссарий
if (entity.claims == nil) then
return '' --TODO error?
end
-- improve options
options.frame = g_frame;
options.entity = entity;
options.extends = function( self, newOptions )
return copyTo( newOptions, copyTo( self, {} ) )
end
if ( options.i18n ) then
options.i18n = copyTo( options.i18n, copyTo( i18n, {} ) );
else
options.i18n = i18n;
end
-- create context
local context = {
entity = options.entity,
formatSnak = formatSnak,
formatPropertyDefault = formatPropertyDefault,
formatStatementDefault = formatStatementDefault }
context.formatProperty = function( options )
local func = getUserFunction( options, 'property', context.formatPropertyDefault );
return func( context, options )
end;
context.formatStatement = function( options, statement ) return formatStatement( context, options, statement ) 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.parseTimeFromSnak = function( snak )
if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time ) then
return tonumber(os.time( splitISO8601( tostring( snak.datavalue.value.time ) ) ) ) * 1000;
end
return nil;
end
context.parseTimeBoundariesFromSnak = function( snak )
if ( snak and snak.datavalue and snak.datavalue.value and snak.datavalue.value.time and snak.datavalue.value.precision ) then
return parseTimeBoundaries( snak.datavalue.value.time, snak.datavalue.value.precision );
end
return nil;
end
context.getSourcingCircumstances = function( statement ) return getSourcingCircumstances( statement ) end;
context.selectClaims = function( options, propertyId ) return selectClaims( context, options, propertyId ) end;
return context.formatProperty( options );
end
function formatPropertyDefault( context, options )
if ( not context ) then error( 'context not specified' ); end;
if ( not options ) then error( 'options not specified' ); 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) 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
--[[
Функция для оформления одного утверждения (statement)
Принимает: объект-таблицу утверждение и таблицу параметров
Возвращает: строку оформленного текста с заявлением (claim)
]]
function formatStatement( context, options, statement )
if ( not statement ) then
error( 'statement is not specified or nil' );
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
function getSourcingCircumstances( statement )
if (not statement) then error('statement is not specified') end;
local circumstances = {};
if ( statement.qualifiers
and statement.qualifiers.P1480 ) then
for i, qualifier in pairs( statement.qualifiers.P1480 ) do
if ( qualifier
and qualifier.datavalue
and qualifier.datavalue.type == 'wikibase-entityid'
and qualifier.datavalue.value
and qualifier.datavalue.value["entity-type"] == 'item' ) then
local circumstance = 'Q' .. qualifier.datavalue.value["numeric-id"];
if ( 'Q5727902' == circumstance ) then
circumstances.circa = true;
end
if ( 'Q18122778' == circumstance ) then
circumstances.presumably = true;
end
end
end
end
return circumstances;
end
--[[
Функция для оформления одного утверждения (statement)
Принимает: объект-таблицу утверждение, таблицу параметров,
объект-функцию оформления внутренних структур утверждения (snak) и
объект-функцию оформления ссылки на источники (reference)
Возвращает: строку оформленного текста с заявлением (claim)
]]
function formatStatementDefault( context, options, statement )
if (not context) then error('context is not specified') end;
if (not options) then error('options is not specified') end;
if (not statement) then error('statement is not specified') end;
local circumstances = context.getSourcingCircumstances( statement );
if statement.qualifiers then
options.qualifiers = statement.qualifiers;
end
if ( options.references ) then
return context.formatSnak( options, statement.mainsnak, circumstances ) .. context.formatRefs( options, statement );
else
return context.formatSnak( options, statement.mainsnak, circumstances );
end
end
--[[
Функция для оформления части утверждения (snak)
Подробнее о snak см. d:Викиданные:Глоссарий
Принимает: таблицу snak объекта (main snak или же snak от квалификатора) и таблицу опций
Возвращает: строку оформленного викитекста
]]
function formatSnak( context, options, snak, circumstances )
circumstances = circumstances or {};
local hash = '';
local mainSnakClass = '';
if ( snak.hash ) then
hash = ' data-wikidata-hash="' .. snak.hash .. '"';
else
mainSnakClass = ' wikidata-main-snak';
end
local before = '<span class="wikidata-snak ' .. mainSnakClass .. '"' .. hash .. '>'
local after = '</span>'
if snak.snaktype == 'somevalue' then
if ( options['somevalue'] and options['somevalue'] ~= '' ) then
return before .. options['somevalue'] .. after;
end
return before .. options.i18n['somevalue'] .. after;
elseif snak.snaktype == 'novalue' then
if ( options['novalue'] and options['novalue'] ~= '' ) then
return before .. options['novalue'] .. after;
end
return before .. options.i18n['novalue'] .. after;
elseif snak.snaktype == 'value' then
if ( circumstances.presumably ) then
before = before .. options.i18n.presumably;
end
if ( circumstances.circa ) then
before = before .. options.i18n.circa;
end
return before .. formatDatavalue( context, options, snak.datavalue, snak.datatype ) .. after;
else
throwError( 'unknown-snak-type' );
end
end
--[[
Функция для оформления объектов-значений с географическими координатами
Принимает: объект-значение и таблицу параметров,
Возвращает: строку оформленного текста
]]
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 = '[[File:' .. value
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 options['description'] and options['description'] ~= '' then
image = image .. '<br>' .. options['description']
end
return image
end
--[[
Функция для оформления внешних идентификаторов
Принимает: объект-значение и таблицу параметров,
Возвращает: строку оформленного текста
]]
local function formatExternalId( value, options )
local formatter = options.formatter
if not formatter or formatter == '' then
local entity = mw.wikibase.getEntity( options.property:upper() )
if entity then
local statements = entity:getBestStatements( 'P1630' )
for _, statement in pairs( statements ) do
if statement.mainsnak.snaktype == 'value' then
formatter = statement.mainsnak.datavalue.value
break
end
end
end
end
if formatter and formatter ~= '' then
local link = mw.ustring.gsub( formatter, '$1', value )
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 )
-- округление до 13 знаков после запятой, на 14-м возникает ошибка в точности
local mult = 10^13
number = math.floor( number * mult + 0.5 ) / mult
return 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 unitEntity = mw.wikibase.getEntity( unitEntityId );
if unitEntity then
local writingSystemElementId = 'Q8209';
local langElementId = 'Q7737';
local label = getLabelWithLang( context, options, unitEntity, nil, {
'P558[P282:' .. writingSystemElementId .. ', P407:' .. langElementId .. ']',
'P558[!P282][!P407]'
} );
out = out .. ' ' .. label;
end
end
return out;
end
--[[
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 propertyEntity = mw.wikibase.getEntity( propertyId );
if not propertyEntity then
return nil;
end
return propertyEntity.datatype;
end
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.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.description = qualifier.datavalue.value.text
break
end
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 == 'url' then
-- URL
return function( context, options, value )
local moduleUrl = require( 'Module:URL' )
if not options.length or options.length == '' then
options.length = 25
end
return moduleUrl.formatUrlSingle( context, options, value );
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
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
--[[
Функция для оформления значений (value)
Подробнее о значениях см. d:Wikidata:Glossary/ru
Принимает: объект-значение и таблицу параметров,
Возвращает: строку оформленного текста
]]
function formatDatavalue( context, options, datavalue, datatype )
if ( not context ) then error( 'context not specified' ); end;
if ( not options ) then error( 'options not specified' ); end;
if ( not datavalue ) then error( 'datavalue not specified' ); 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
--[[
Функция для оформления идентификатора сущности
Принимает: строку индентификатора (типа Q42) и таблицу параметров,
Возвращает: строку оформленного текста
]]
function formatEntityId( context, options, entityId )
-- получение локализованного названия
local entity = mw.wikibase.getEntity( entityId )
local label, labelLanguageCode = getLabelWithLang( context, options, entity )
-- получение ссылки по идентификатору
local link = mw.wikibase.sitelink( entityId )
if link then
if label then
if ( contentLanguageCode ~= labelLanguageCode ) then
return '[[' .. link .. '|' .. label .. ']]' .. categoryLinksToEntitiesWithMissingLocalLanguageLabel;
else
return '[[' .. link .. '|' .. label .. ']]';
end
else
return '[[' .. link .. ']]';
end
end
if label then
-- красная ссылка
-- TODO: разобраться, почему не всегда есть options.frame
if not mw.title.new( label ).exists and options.frame then
return '[[' .. label .. ']]<sup>[[:d:' .. entityId .. '|[d]]]</sup>';
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
-- одноимённая статья уже существует - выводится текст и ссылка на ВД
return '<span class="iw" data-title="' .. label .. '">' .. label
.. sup
.. '</span>'
end
-- сообщение об отсутвии локализованного названия
-- not good, but better than nothing
return '[[:d:' .. entityId .. '|' .. entityId .. ']]<span style="border-bottom: 1px dotted; cursor: help; white-space: nowrap" title="В Викиданных нет русской подписи к элементу. Вы можете помочь, указав русский вариант подписи.">?</span>' .. categoryLinksToEntitiesWithMissingLabel;
end
--[[
Функция для оформления утверждений (statement)
Подробнее о утверждениях см. d:Wikidata:Glossary/ru
Принимает: таблицу параметров
Возвращает: строку оформленного текста, предназначенного для отображения в статье
]]
-- устаревшее имя, не использовать
function p.formatStatements( frame )
return p.formatProperty( frame );
end
--[[
Получение параметров, которые обычно используются для вывода свойства.
]]
function getPropertyParams( propertyId, datatype, params )
local config = require( 'Module:Wikidata/config' );
if not config then
return {};
end
-- Различные уровни настройки параметров, по убыванию приоритета
local propertyParams = {};
-- 1. Параметры, указанные явно при вызове
if params then
local tplParams = mw.clone( params );
for key, value in pairs( tplParams ) do
if value ~= '' then
propertyParams[key] = value;
end
end
end
-- 2. Настройки конкретного параметра
if config['properties'] and config['properties'][propertyId] then
local selfParams = mw.clone( config['properties'][propertyId] );
for key, value in pairs( selfParams ) 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
local presetParams = mw.clone( config['presets'][propertyParams['preset']] );
for key, value in pairs( presetParams ) do
if propertyParams[key] == nil then
propertyParams[key] = value;
end
end
end
-- 4. Настройки для типа данных
if datatype and config['datatypes'] and config['datatypes'][datatype] then
local datatypeParams = mw.clone( config['datatypes'][datatype] );
for key, value in pairs( datatypeParams ) do
if propertyParams[key] == nil then
propertyParams[key] = value;
end
end
end
-- 5. Общие настройки для всех свойств
if config['global'] then
local globalParams = mw.clone( config['global'] );
for key, value in pairs( globalParams ) do
if propertyParams[key] == nil then
propertyParams[key] = value;
end
end
end
return propertyParams;
end
function p.formatProperty( frame )
local plain = toBoolean( frame.args.plain, false );
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, '%[.*$', '' ) )
local datatype = getPropertyDatatype( propertyId );
args = getPropertyParams( propertyId, datatype, args );
-- проброс всех параметров из шаблона {wikidata}
local p_frame = frame:getParent();
if p_frame and p_frame:getTitle() == mw.site.namespaces[10].name .. ':Wikidata' then
copyTo( p_frame.args, args );
end
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 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' and not string.find( value, '[%[%]%{%}]' ) 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 == 'url' then
local moduleUrl = require( 'Module:URL' );
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 .. '[[Категория:Википедия:Статьи с табличной вставкой в карточке]]'
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
value = value .. categoryLocalValuePresent;
end
return value
end
if ( plain ) then -- вызова стандартного обработчика без оформления, если передана опция plain
return frame:callParserFunction( '#property', propertyId );
end
g_frame = frame
-- после проверки всех аргументов -- вызов функции оформления для свойства (набора утверждений)
return formatProperty( args )
end
--[[
Функция оформления ссылок на источники (reference)
Подробнее о ссылках на источники см. d:Wikidata:Glossary/ru
Экспортируется в качестве зарезервированной точки для вызова из функций-расширения вида claim-module/claim-function через context
Вызов из других модулей напрямую осуществляться не должен (используйте frame:expandTemplate вместе с одним из специлизированных шаблонов вывода значения свойства).
Принимает: объект-таблицу утверждение
Возвращает: строку оформленных ссылок для отображения в статье
]]
function formatRefs( context, options, statement )
if ( not context ) then error( 'context not specified' ); end;
if ( not options ) then error( 'options not specified' ); end;
if ( not options.entity ) then error( 'options.entity missing' ); end;
if ( not statement ) then error( 'statement not specified' ); end;
if ( not outputReferences ) then
return '';
end
local result = '';
if ( statement.references ) then
local allReferences = statement.references;
local hasPreferred = false;
for _, reference in pairs( statement.references ) do
if ( reference.snaks
and reference.snaks.P248
and reference.snaks.P248[1]
and reference.snaks.P248[1].datavalue
and reference.snaks.P248[1].datavalue.value["numeric-id"] ) then
local entityId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"];
if ( preferredSources[entityId] ) then
hasPreferred = true;
end
end
end
for _, reference in pairs( statement.references ) do
local display = true;
if ( hasPreferred ) then
if ( reference.snaks
and reference.snaks.P248
and reference.snaks.P248[1]
and reference.snaks.P248[1].datavalue
and reference.snaks.P248[1].datavalue.value["numeric-id"] ) then
local entityId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"];
if ( deprecatedSources[entityId] ) then
display = false;
end
end
end
if ( display ) then
result = result .. moduleSources.renderReference( g_frame, options.entity, reference );
end
end
end
return result
end
return p