Модуль:External links: различия между версиями

Материал из in.wiki
Перейти к навигации Перейти к поиску
м (1 версия импортирована: Импорт из Википедии)
w>Putnik
(более контрастные иконки в тёмной теме)
Строка 1: Строка 1:
 +
local data = require( 'Module:External links/data' )
 
-- Localizable part
 
-- Localizable part
-- Please, note, that labels to various sites and cataloges are taken from Wikidata (i.e. Wikidata label)
+
-- Please note that labels for websites and catalogs are taken from Wikidata (i.e. Wikidata label)
  
local linksPrefix = ''
+
-- Feel free to correct labels and categories here
local project = 'Википедия'
+
local categoryTemplateEmpty = 'Википедия:Шаблон «Внешние ссылки» пуст'
local categoryTemplateEmpty = project .. ':Шаблон «Внешние ссылки» пуст'
+
local categoryCustomColor = 'Википедия:Шаблон «Внешние ссылки» с нестандартным цветом'
local categoryWithWikimediaCommons = project .. ':Статьи со ссылками на Викисклад'
 
 
local templateLink = 'Внешние ссылки'
 
local templateLink = 'Внешние ссылки'
 +
local templateTitle = 'Ссылки на внешние ресурсы'
 +
local wikidataLink = 'Перейти к элементу Викиданных'
 +
local refLabels = {
 +
Q1406 = 'Windows',
 +
Q10677 = 'PS1',
 +
Q10680 = 'PS2',
 +
Q10683 = 'PS3',
 +
Q16338 = 'ПК',
 +
Q135321 = 'FDS',
 +
Q170323 = 'DS',
 +
Q170325 = 'PSP',
 +
Q172742 = 'NES',
 +
Q182172 = 'GameCube',
 +
Q183259 = 'SNES',
 +
Q184839 = 'N64',
 +
Q188642 = 'GBA',
 +
Q188808 = 'PS Vita',
 +
Q203597 = '3DS',
 +
Q5014725 = 'PS4',
 +
Q19610114 = 'Switch',
 +
Q63184502 = 'PS5',
 +
}
  
local group1Label = '[[' .. linksPrefix .. 'Социальная сеть|В социальных сетях]]'
+
-- The language codes that should always be displayed even if they have normal rank and a claim with another language and preferred rank exists
local group2Label = 'Тексты произведений'
+
local preferredLanguage = 'Q7737' -- russian
local group3Label = 'Фото, видео и аудио'
 
local group4Label = 'Тематические сайты'
 
local group5Label = 'Словари и энциклопедии'
 
local group6Label = 'Генеалогия и некрополистика'
 
local group7Label = '[[' .. linksPrefix .. 'Нормативный контроль|Нормативный контроль]]'
 
 
 
-- The language codes that should be always displayed even if they have normal rank and claim with another language and prefferered rank exists
 
local preferredLanguage = 'Q7737'; -- russian
 
  
local templateColorName = 'цвет';
+
local templateColorName = 'цвет'
 
-- Some projects have "named" colors, defined by templates
 
-- Some projects have "named" colors, defined by templates
function colorByTitle( frame, colorTitle )
+
local function colorByTitle( frame, colorTitle )
local templateName = 'Цвет/' .. colorTitle;
+
local templateName = 'Цвет/' .. colorTitle
local templateTitle = mw.title.makeTitle( 'Template', templateName );
+
local templateTitle = mw.title.makeTitle( 'Template', templateName )
if ( templateTitle == nil or not templateTitle.exists ) then
+
if not templateTitle or not templateTitle.exists then
return false;
+
return false
 
end
 
end
return frame:expandTemplate{ title = templateName };
+
return frame:expandTemplate{ title = templateName }
 
end
 
end
  
local dictionaries = {
+
-- Non-localizable part (not need to localize )
-- Словари и энциклопедии
+
local moduleNavbox = require( 'Module:Navbox' )
{ id = 'P1907', title = 'Австралийский биографический', linkF = function ( id ) return 'http://adb.anu.edu.au/biography/' .. id .. ''; end },
+
local moduleNavbar = require( 'Module:Navbar' )._ruwikiNavbar -- can be changed to a regular one
{ id = 'P4211', title = 'Башкирская', linkF = function ( id ) return 'http://башкирская-энциклопедия.рф/index.php/component/content/article/' .. id; end, },
+
local moduleLanguages -- accessed if necessary
{ id = 'Q19217220', title = 'Березина', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q4086271', title = 'Библейская', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q42068474', title = 'Био-библиографический русских писателей XX века', project = 'ruwikisource', projectCode = 's:' },
 
{ title = 'Биографический итальянцев', id='P1986', linkF = function( id ) return 'http://www.treccani.it/enciclopedia/' .. id .. '_(Dizionario_Biografico)' end },
 
{ id = 'P1296', title = 'Большая каталонская', linkF = function ( id ) return 'http://www.enciclopedia.cat/enciclopèdies/gran-enciclopèdia-catalana/EC-GEC-' .. id .. '.xml' end },
 
{ id = 'P4342', title = 'Большая норвежская', linkF = function ( id ) return 'https://snl.no/' .. id; end, },
 
{ id = 'P2924', title = 'Большая российская', linkF = function ( id ) return 'https://bigenc.ru/text/' .. id; end, },
 
{ id = 'Q20078554', title = 'Большая советская (1 изд.)', project = 'ruwikisource', projectCode = 's:' },
 
-- { id = 'Q17378135', title = 'Большая советская', },
 
{ id = 'Q4091878', title = 'Большая Южакова', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q602358', title = 'Брокгауза и Ефрона', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'P1648', title = 'Валлийский биографический', linkF = function ( id ) return 'http://yba.llgc.org.uk/en/' .. id .. '.html' end, },
 
{ id = 'Q4114391', title = 'Военная Сытина', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q4135594', title = 'Географическо-статистический Российской Империи', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q4173137', title = 'Еврейская Брокгауза и Ефрона', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'P5395', title = 'Канадская', linkF = function ( id ) return 'https://www.thecanadianencyclopedia.ca/en/article/' .. id .. '/'; end, },
 
{ id = 'Q4091875', title = 'Кирилла и Мефодия', },
 
{ id = 'P1438', title = 'Краткая еврейская', linkF = function ( id ) return 'http://www.eleven.co.il/article/' .. id; end, },
 
{ id = 'Q4239850', title = 'Краткая литературная' },
 
{ id = 'P6385', title = 'Кругосвет', linkF = function ( id ) return 'https://www.krugosvet.ru/enc/' .. id; end, },
 
--исключение для нетипичных адресов типа node/39767
 
{ id = 'Q2627728', title = 'Кругосвет' },
 
{ id = 'P6058',     title = 'Ларусса',                          linkF = function  (id)  return 'https://www.larousse.fr/encyclopedie/' .. id end, },
 
{ id = 'Q17290934', title = 'Лентапедия', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q4263804', title = 'Литературная', },
 
{ id = 'P6504', title = 'Литовская спортивная', linkF = function ( id ) return 'http://www.lse.lt/index.php?' .. id; end, },
 
{ id = 'Q19180675', title = 'Малый Брокгауза и Ефрона', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q27680201', title = 'Музыкальный Римана', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q19190511', title = 'Новый', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q2498180', title = 'Православная', },
 
{ id = 'Q19211082', title = 'Православная богословская', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q30059240', title = 'Реальный словарь классических древностей', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q1960551', title = 'Русский биографический', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q20078551', title = 'Техническая (1 изд.)', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q1970746', title = 'В. Даля', project = 'ruwikisource', projectCode = 's:' },
 
{ id = 'Q4532135', title = 'Энциклопедический лексикон', project = 'ruwikisource', projectCode = 's:' },
 
 
 
{ id = 'Q590208', title = 'Allgemeine Deutsche Biographie', project = 'dewikisource', projectCode = 's:de:' },
 
{ id = 'Q19077875', title = 'American Cycl. (1879)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'Q19037977', title = 'American Med. Biogr. (1920)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'Q12912667', title = 'Appletons\' (1887—1901)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'P2580', title = 'BBLD', linkF = function ( id ) return 'https://bbld.de/' .. id; end, },
 
{ id = 'Q20096917', title = 'Britannica (9-th)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'Q867541', title = 'Britannica (11-th)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'Q15987490', title = 'Britannica (12-th)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'P1417', title = 'Britannica (онлайн)', linkF = function ( id ) return 'https://www.britannica.com/'.. id; end},
 
{ id = 'P5019', title = 'Brockhaus', linkF = function ( id ) return 'https://brockhaus.de/ecs/enzy/article/'.. id; end},
 
{ id = 'Q302556', title = 'Catholic (1907—13)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'P3241', title = 'Catholic (1997—…)', linkF = function ( id ) return 'http://www.newadvent.org/cathen/'.. id .. '.htm'; end},
 
    { id = 'Q16011749',    title = 'Dictionary of Music and Musicians',    project = 'enwikisource',      projectCode = 'wikisource:'},
 
{ id = 'Q15987216', title = 'Dictionary of National Biography', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'Q16014700', title = 'Dictionary of National Biography (1st suppl.)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'Q16014697', title = 'Dictionary of National Biography (2nd suppl.)', project = 'enwikisource', projectCode = 'wikisource:' },
 
{ id = 'P1614', title = 'History of Parliament', linkF = function ( id ) return 'http://www.historyofparliamentonline.org/volume/' .. id; end, },
 
{ id = 'Q20961706', title = 'Infernal (6e éd.)', project = 'frwikisource', projectCode = 's:fr:' },
 
    { id = 'P1285', title = 'Munzinger', linkF = function( id ) return 'http://www.munzinger.de/search/go/document.jsp?id=' .. id; end },
 
{ id = 'Q20089963', title = 'New International', project = 'enwikisource', projectCode = 'wikisource:' },
 
    { id = 'P1263', title = 'Notable Names Database', linkF = function( id ) return 'http://nndb.com/people/' .. id; end },
 
{ id = 'P3365', title = 'Treccani', linkF = function( id ) return 'http://www.treccani.it/enciclopedia/'.. id; end},
 
    { id = 'P6561', title = 'АиФ Досье', linkF = function( id ) return 'http://www.aif.ru/dossier/' .. id; end },
 
    { id = 'P6384', title = 'Виперсон', linkF = function( id ) return 'http://viperson.ru/people/' .. id; end },
 
    { id = 'P6596', title = 'Всегда со мною…', linkF = function( id ) return 'http://a-tremasov.ru/' .. id; end },
 
    { id = 'P6741', title = 'Киносозвездие', linkF = function( id ) return 'http://www.kinosozvezdie.ru/actors/' .. id .. '/' .. id .. '.html'; end },
 
    { id = 'P6980', title = 'Левый берег', linkF = function( id ) return 'https://lb.ua/file/' .. id; end },
 
    { id = 'P6210', title = 'Ліга.Досье', linkF = function( id ) return 'https://file.liga.net/' .. id; end },
 
{ id = 'P1415', title = 'Оксфордский биографический', linkF = function( id ) return 'https://doi.org/10.1093/ref:odnb/' .. id; end, },
 
{ id = 'P6739', title = 'Россия-Культура', linkF = function( id ) return 'https://tvkultura.ru/person/show/person_id/' .. id .. '/'; end, },
 
{ id = 'P4613', title = 'Современной Украины', linkF = function ( id ) return 'http://esu.com.ua/search_articles.php?id=' .. id; end, },
 
    { id = 'P6081', title = 'Справка РИА', linkF = function( id ) return 'https://ria.ru/spravka/00000000/' .. id .. '.html'; end },
 
    { id = 'P6219', title = 'Справка ТАСС', linkF = function( id ) return 'https://tass.ru/info/' .. id; end },
 
{ id = 'P3217', title = 'Шведский биографический', linkF = function ( id ) return 'https://sok.riksarkivet.se/sbl/Presentation.aspx?id=' .. id; end, },
 
{ id = 'P902', title = 'Швейцарский исторический', linkF = function ( id ) return 'http://www.hls-dhs-dss.ch/textes/f/F' .. id .. '.php'; end, },
 
{ id = 'P886', title = 'Швейцарский исторический (online)', linkF = function ( id ) return 'http://www.e-lir.ch/e-LIR___Lexicon.' .. id .. '.450.0.html'; end, },
 
{ id = 'Q63985075', title = 'Энциклопедия ТАСС', },
 
  
 +
local propertyQualifiers = {
 +
P553 = {
 +
P554 = 'url',
 +
},
 +
P1343 = {
 +
P805 = 'iw',
 +
P248 = 'iw', -- deprecated, fallback for P805
 +
P953 = 'url',
 +
P854 = 'url', -- deprecated, fallback for P953
 +
},
 
}
 
}
  
-- Feel free to correct labels and categories, or add/remove sources here
+
local p = {}
  
-- Non-localizable part (not need to localize )
+
-- Helper functions
local moduleNavbox = require('Module:Navbox')
+
local function replace( str, pattern, repl )
 +
    pattern = mw.ustring.gsub( pattern, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1" ) -- escape pattern
 +
    repl = mw.ustring.gsub( repl, "[%%]", "%%%%" ) -- escape replacement
 +
    repl = mw.ustring.gsub( repl, " ", "%%%%20" ) -- escape replacement
 +
    return mw.ustring.gsub( str, pattern, repl )
 +
end
  
local titleBasedLinks = { ['Q602358'] = true, ['Q17290934'] = true, ['Q1960551'] = true }
 
  
local p = {}
+
-- Render functions
 +
local function renderList( elements )
 +
if #elements == 0 then
 +
return ''
 +
end
  
function link( url )
+
return '*' .. table.concat( elements , '\n*' ) .. '\n'
return url
 
 
end
 
end
  
function bavLink( id ) return 'http://viaf.org/processed/BAV%7C' .. id; end
+
local function renderLabel( params )
function bibsysLink( id ) return 'http://ask.bibsys.no/ask/action/result?cmd=&kilde=biblio&cql=bs.autid+%3D+' .. id .. '&feltselect=bs.autid'; end
+
if type( params ) == 'string' then
function bncLink( id ) return 'http://cantic.bnc.cat/registres/CUCId/' .. id; end
+
return params
function bneLink( id ) return 'http://catalogo.bne.es/uhtbin/authoritybrowse.cgi?action=display&authority_id=' .. id; end
+
end
function bnfLink( id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end
 
function boxofficemojoLink( id ) return 'http://www.boxofficemojo.com/movies/?id=' .. id .. '.htm'; end
 
function bpnLink( id ) return 'http://www.biografischportaal.nl/persoon/' .. id; end
 
function calisLink( id ) return 'http://opac.calis.edu.cn/aopac/ajsp/detail.jsp?actionfrom=1&actl=CAL++' .. id; end
 
function cbdbLink( id ) return 'http://db1.ihp.sinica.edu.tw/cbdbc/cbdbkmeng?~~AAA' .. id; end
 
function ciniiLink( id ) return 'http://ci.nii.ac.jp/author/' .. id; end
 
function conorLink( id ) return 'http://www.cobiss.si/scripts/cobiss?command=DISPLAY&base=CONOR&rid=' .. id; end
 
function gtaaLink( id ) return 'http://data.beeldengeluid.nl/gtaa/' .. id; end
 
function chitalnyaRuLink( id ) return 'http://www.chitalnya.ru/users/' .. id .. '/'; end
 
function commonsWikimediaLink( id ) return ':commons:Category:' .. id; end
 
function egaxaLink( id ) return 'http://viaf.org/processed/EGAXA%7Cvtls' .. id; end
 
  
 +
local qid = params[ 1 ]
 +
local default = params[ 2 ]
  
function fanLibRuLink( id )
+
if #params >= 3 then
local firstChar = mw.ustring.sub( id, 1, 1 );
+
local label = params[ 3 ]
return 'http://fan.lib.ru/' .. firstChar .. '/' .. id .. '/';
+
local link = mw.wikibase.sitelink( qid )
 +
if link then
 +
return '[[' .. link .. '|' .. label .. ']]'
 +
end
 +
local title = mw.wikibase.label( qid ) or default
 +
if title ~= label then
 +
return '<span title="' .. title .. '" style="border-bottom: 1px dotted; cursor: help;">' .. label .. '</span>'
 +
end
 +
end
 +
 
 +
return mw.wikibase.label( qid ) or default
 
end
 
end
  
function flickrLink( id ) return 'https://www.flickr.com/' .. id; end
+
local function renderLink( resourceData, label, formatter, idAsLabel )
function findagraveLink( id ) return 'http://www.findagrave.com/cgi-bin/fg.cgi?page=gr&GRid=' .. id; end
+
if resourceData.itemId == nil then
function dnbLink( id ) return 'http://d-nb.info/' .. id; end
+
return '<span class="error">' .. label .. ': Не удаётся определить элемент</span>[[Категория:Статьи с ошибкой в шаблоне Внешние ссылки]]'
function gndLink( id ) return 'http://d-nb.info/gnd/' .. id; end
+
end
function ibdbPersonLink( id ) return 'http://www.ibdb.com/person.php?id=' .. id; end
 
function ibdbProductionLink( id ) return 'http://www.ibdb.com/production.php?id=' .. id; end
 
function ibdbShowLink( id ) return 'http://www.ibdb.com/show.php?id=' .. id; end
 
function ibdbVenueLink( id ) return 'http://www.ibdb.com/venue.php?id=' .. id; end
 
function isfdbAuthorLink( id ) return 'http://www.isfdb.org/cgi-bin/ea.cgi?' .. id; end
 
function isfdbPublicationLink( id ) return 'http://www.isfdb.org/cgi-bin/pl.cgi?' .. id; end
 
function isfdbSeriesLink( id ) return 'http://www.isfdb.org/cgi-bin/pe.cgi?' .. id; end
 
function isfdbPublisherLink( id ) return 'http://www.isfdb.org/cgi-bin/publisher.cgi?' .. id; end
 
function imslpLink( id ) return 'http://imslp.org/wiki/' .. string.gsub( id, ' ', '_' ); end
 
  
function imdbLink( id )
+
local link
if string.match( id, '^ch' ) then
+
if not formatter then
return 'http://www.imdb.com/character/' .. id;
+
link = resourceData.itemId
 +
idAsLabel = false
 +
elseif type( formatter ) == 'string' then
 +
link = replace( formatter, '$1', resourceData.itemId )
 +
elseif type ( formatter ) == 'function' then
 +
link = formatter( resourceData.itemId, resourceData.qualifiers )
 
end
 
end
if string.match( id, '^co' ) then
+
return 'http://www.imdb.com/company/' .. id;
+
-- "Label: ID" without link
 +
if not link or link == '' then
 +
return renderLabel( label ) .. ':&nbsp;' .. resourceData.itemId
 
end
 
end
if string.match( id, '^nm' ) then
+
 
return 'http://www.imdb.com/name/' .. id;
+
local resourceLabel = resourceData.itemId
 +
if not idAsLabel then
 +
resourceLabel = renderLabel( label )
 
end
 
end
if string.match( id, '^tt' ) then
+
 
return 'http://www.imdb.com/title/' .. id;
+
local linkFirstChar = mw.ustring.sub( link, 1, 1 )
 +
if linkFirstChar == ':' then
 +
return '[[' .. link .. '|' .. resourceLabel .. ']]'
 
end
 
end
  
return false
+
return '[' .. link .. ' ' .. resourceLabel .. ']'
 
end
 
end
  
function isniLink( id )
+
local function renderLangRef( languages )
id = id:gsub( '[ %-]', '' ):upper();
+
local result = ''
return 'http://isni-url.oclc.nl/isni/' .. id;
+
if languages and #languages > 0 then
 +
if moduleLanguages ~= false then -- not false, but maybe nil
 +
if mw.title.makeTitle( 'Module', 'Languages' ).exists
 +
and mw.title.makeTitle( 'Module', 'Languages/data' ).exists
 +
and mw.title.makeTitle( 'Module', 'Wikidata/Language-codes' ).exists then
 +
moduleLanguages = require( 'Module:Languages' )
 +
else
 +
moduleLanguages = false
 +
end
 +
end
 +
 +
if moduleLanguages then
 +
for langIndex, language in pairs( languages ) do
 +
result = result .. '&nbsp;' .. moduleLanguages.getWikidataRefHtml( language )
 +
end
 +
end
 +
end
 +
 
 +
return result
 
end
 
end
  
function lccnLink( id )
+
local function renderRefs( refs )
return 'http://id.loc.gov/authorities/' .. id;
+
local result = ''
end
+
if refs and #refs > 0 then
function locLink( id )
+
for _, ref in ipairs( refs ) do
return 'https://lccn.loc.gov/' .. id;
+
result = result .. '&nbsp;<span class="ref-info">(' .. ref .. ')</span>'
end
+
end
function lastfmLink( id )
+
end
return 'http://www.lastfm.ru/music/' .. id;
+
 
 +
return result
 
end
 
end
  
function iccuLink( id )
+
local function renderLinkWithRef( resourceData, label, formatter, idAsLabel )
id = id:gsub( '\\\\', '%5C' ):upper();
+
local link = renderLink( resourceData, label, formatter, idAsLabel )
return 'http://opac.sbn.it/opacsbn/opac/iccu/scheda_authority.jsp?bid=' .. id;
+
if link ~= '' then
 +
link = link .. renderLangRef( resourceData.languages )
 +
link = link .. renderRefs( resourceData.refs )
 +
end
 +
return link
 
end
 
end
  
function lnbLink( id ) return 'http://viaf.org/processed/LNB%7CLNC10-' .. id; end
 
function rgaliLink( id ) return 'http://rgali.ru/obj/' .. id .. '?lc=ru'; end
 
function rslLink0( id ) return 'http://aleph.rsl.ru/F?func=direct-set&l_base=xall&doc_number=' .. id; end
 
function merimeeLink( id ) return false end
 
function microsoftLink( id ) return 'https://academic.microsoft.com/#/detail/' .. id; end
 
function mixcloudLink( id ) return 'https://mixcloud.com/' .. id .. '/'; end
 
function naukaUkrLink( id ) return 'http://irbis-nbuv.gov.ua/ASUA/' .. id; end
 
function nclLink( id ) return 'http://aleweb.ncl.edu.tw/F/?func=accref&acc_sequence=' .. id; end
 
function ndlLink( id ) return 'http://id.ndl.go.jp/auth/ndlna/' .. id; end
 
function nlcLink( id ) return false end
 
function nliLink( id ) return 'http://a20.libnet.ac.il/F?func=find-b&REQUEST=' .. id .. '&find_code=SYS&local_base=NNL10'; end
 
function nkcLink( id ) return 'http://aut.nkp.cz/' .. id; end
 
function nlaLink( id ) return 'http://nla.gov.au/anbd.aut-an' .. id; end
 
function nlrLink( id ) return 'http://alephnew.bibnat.ro:8991/F?func=find-b&request=' .. id .. '000354872&find_code=SYS&adjacent=Y&local_base=NLR10' end
 
function nszlLink( id ) return 'http://viaf.org/processed/NSZL%7C' .. id; end
 
function nskLink( id ) return 'http://viaf.org/processed/NSK%7C' .. id; end
 
function ntaLink( id ) return 'http://data.bibliotheken.nl/id/thes/p' .. id; end
 
function nukatLink( id ) return 'http://viaf.org/processed/NUKAT%7C' .. id; end
 
  
 
+
-- Data fetching functions
function orcidLink( id )
+
local function getValueFromSnak( snak )
id = id:gsub( '[ %-]', '' ):upper();
+
if not snak.datavalue then
return 'http://orcid.org/' .. id; end
+
return  
 
+
end
 
+
if snak.datavalue.type == 'wikibase-entityid' then
function declaratorLink( id ) return 'http://declarator.org/person/' .. id .. '/'; end
+
return snak.datavalue.value.id
function gutenbergLink( id ) return 'http://www.gutenberg.org/ebooks/' .. id; end
+
end
function promodjLink( id ) return 'http://promodj.com/' .. id; end
+
if snak.datavalue.type == 'monolingualtext' then
function prozaRuLink( id ) return 'http://proza.ru/avtor/' .. id; end
+
return snak.datavalue.value.text
function ptbnpLink( id ) return 'http://viaf.org/processed/PTBNP%7C' .. id; end
 
function rkdArtistsLink( id ) return 'https://rkd.nl/nl/explore/artists/' .. id; end
 
function rkdImagesLink( id ) return 'http://explore.rkd.nl/en/images/' .. id; end
 
function rodovidLink( id ) return 'http://ru.rodovid.org/wk/Person:' .. id; end
 
function rottentomatoesLink( id ) return 'http://www.rottentomatoes.com/' .. id; end
 
function rslLink( id ) return 'http://aleph.rsl.ru/F?func=find-b&find_code=SYS&adjacent=Y&local_base=RSL11&request=' .. id; end
 
 
 
function rutubeLink( id )
 
if string.match( id, '^%d+$' ) then
 
return 'http://rutube.ru/video/persion/' .. id .. '/';
 
 
end
 
end
return 'http://' .. id .. '.rutube.ru/';
+
return snak.datavalue.value
end
 
 
 
function samlibRuLink( id )
 
local firstChar = mw.ustring.sub( id, 1, 1 );
 
return 'http://samlib.ru/' .. firstChar .. '/' .. id .. '/';
 
 
end
 
end
  
function selibrLink( id ) return 'http://libris.kb.se/auth/' .. id; end
+
local function getQualifierSingleValue( statement, qualifierName )
function stihiRuLink( id ) return 'http://stihi.ru/avtor/' .. id; end
+
if not statement or not statement.qualifiers or not statement.qualifiers[ qualifierName ] then
function sudocLink( id ) return 'http://www.idref.fr/' .. id; end
+
return nil
function ulanLink( id ) return 'http://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid=' .. id; end
 
function vimeoLink( id ) return 'https://vimeo.com/' .. id; end
 
function viafLink( id ) return 'http://viaf.org/viaf/' .. id; end
 
function tiktokLink( id ) return 'https://www.tiktok.com/@' .. id; end
 
function youtubeLink( id ) return 'https://youtube.com/' .. id; end
 
function youtubeLinkLong( id )  return 'https://youtube.com/channel/' .. id; end
 
function worldcatLink( id ) return 'https://www.worldcat.org/identities/containsVIAFID/' .. id; end
 
 
 
function renderLabel( params )
 
if type( params ) == 'string' then
 
return params;
 
 
end
 
end
  
local id = params[ 1 ];
+
for qualifierIndex, qualifier in pairs( statement.qualifiers[ qualifierName ] ) do
local default = params[ 2 ];
+
if qualifier.datavalue and qualifier.datavalue.type and qualifier.datavalue.value then
 
+
return getValueFromSnak( qualifier )
if #params >= 3 then
 
local label = params[ 3 ];
 
local link = mw.wikibase and mw.wikibase.sitelink( id );
 
if ( link ~= nil ) then
 
return '[[' .. link .. '|' .. label .. ']]';
 
 
end
 
end
local title = mw.wikibase and mw.wikibase.label( id ) or default;
 
return '<span title="' .. title .. '" style="border-bottom: 1px dotted; cursor: help;">' .. label .. '</span>'
 
 
end
 
end
  
return mw.wikibase and mw.wikibase.label( id ) or default;
+
return nil
 
end
 
end
  
local socialNetworkProperties = {
+
local function getQualifierValues( statement )
-- Социальные сети
+
if not statement or not statement.qualifiers then
{ { 'Q116933', 'ВКонтакте' },  'P3185', function( id ) return 'https://vk.com/' .. id; end },
+
return {}
{ { 'Q4101720', 'В кругу друзей' }, 'Q4101720', function( id ) return 'http://' .. id .. '.vkrugudruzei.ru/'; end },
+
end
{ { 'Q219523', 'Живой журнал' }, 'P3258', function( id ) return 'http://' .. id .. '.livejournal.com/'; end },
 
{ { 'Q219523', 'Живой журнал' }, 'Q219523', function( id ) return 'http://' .. id .. '.livejournal.com/'; end },
 
{ { 'Q4299813', 'Мой круг' }, 'Q4299813', function( id ) return 'http://' .. id .. '.moikrug.ru/'; end },
 
{ { 'Q4299858', 'Мой мир' }, 'Q4299858', function( id ) return 'http://my.mail.ru/' .. id; end },
 
{ { 'Q1123836', 'Одноклассники' }, 'P5163', function( id ) return 'https://ok.ru/' .. id; end },
 
{ { 'Q17195318', 'Спрашивай.Ру' }, 'Q17195318', function( id ) return 'http://sprashivai.ru/' .. id; end },
 
{ { 'Q309959', 'ASKfm' },  'Q309959', function( id ) return 'https://ask.fm/' .. id; end },
 
{ { 'Q171186', 'Blogger' },  'Q171186', function( id ) return 'http://' .. id .. '.blogspot.com' end },
 
{ { 'Q4037665', 'Dudu' },    'Q4037665', function( id ) return 'http://dudu.com/' .. id; end },
 
{ { 'Q355', 'Facebook' },   'P2013', function( id ) return 'https://www.facebook.com/' .. id; end },
 
{ { 'Q355', 'Facebook' },   'P4003', function( id ) return 'https://www.facebook.com/pages/' .. id; end },
 
-- { { 'Q356', 'Google+' }, 'P2847', function( id ) return 'https://plus.google.com/' .. id .. '/posts'; end },
 
-- { { 'Q356', 'Google+' }, 'Q356', function( id ) return 'https://plus.google.com/' .. id .. '/posts'; end },
 
{ { 'Q213660', 'LinkedIn' }, 'P2035', function( id ) return id; end },
 
{ { 'Q213660', 'LinkedIn' }, 'P4264', function( id ) return 'https://www.linkedin.com/company/' .. id .. '/'; end },
 
{ { 'Q4043051', 'LiveInternet' }, 'Q4043051', function( id ) return 'http://www.liveinternet.ru/users/' .. id; end },
 
{ { 'Q40629', 'MySpace' }, 'Q40629', function( id ) return 'https://myspace.com/' .. id; end },
 
{ { 'Q40629', 'MySpace' }, 'P3265', function( id ) return 'https://myspace.com/' .. id; end },
 
{ { 'Q17144398', 'QRoom' }, 'Q17144398', function( id ) return 'http://qroom.ru/' .. id; end },
 
{ { 'Q92526', 'Sina Weibo' }, 'P3579', function( id ) return 'https://weibo.com/' .. id; end },
 
{ { 'Q1002972', 'Spring.me' }, 'Q1002972', function( id ) return 'http://www.spring.me/' .. id; end },
 
{ { 'Q15616276', 'Telegram' }, 'P3789', function( id ) return 'https://t.me/' .. id; end },
 
{ { 'Q15616276', 'Telegram' }, 'Q15616276', function( id ) return 'https://t.me/' .. id; end },
 
{ { 'Q384060', 'Tumblr' }, 'Q384060', function( id ) return 'http://' .. id .. '.tumblr.com/'; end },
 
{ { 'Q918', 'Twitter' }, 'P2002', function( id ) return 'https://twitter.com/' .. id; end },
 
}
 
  
local textsProperties = {
+
local result = {}
-- Тексты произведений
 
{ { 'Q17254543', 'Изба-читальня' }, 'Q17254543', chitalnyaRuLink },
 
{ { 'Q17195344', 'Журнал «Самиздат»' }, 'Q17195344', samlibRuLink },
 
{ { 'Q22673', 'Проект «Гутенберг»' }, 'P2034', gutenbergLink },
 
{ { 'Q4380129', 'Проза.ру' }, 'Q4380129', prozaRuLink },
 
{ { 'Q4442644', 'Стихи.ру' }, 'Q4442644', stihiRuLink },
 
{ { 'Q17300505', 'Lib.Ru/Фантастика' }, 'Q17300505', fanLibRuLink },
 
}
 
  
local labelAllocine = { 'Q31165', 'AlloCine' };
+
for qualifierName, qualifiers in pairs( statement.qualifiers ) do
local labelAnimeNewsNetwork = { 'Q220509', 'Anime News Network' };
+
for _, qualifier in pairs( qualifiers ) do
local labelDeezer = { 'Q602243', 'Deezer' };
+
if qualifier.datavalue and qualifier.datavalue.type and qualifier.datavalue.value then
local labelDiscogs = { 'Q504063', 'Discogs' };
+
if not result[ qualifierName ] then
local labelIBDb = { 'Q31964', 'Internet Broadway Database' };
+
result[ qualifierName ] = {}
local labelISFDb = { 'Q2629164', 'Internet Speculative Fiction Database' };
 
local labelMusicBrainz = { 'Q14005', 'MusicBrainz' };
 
local labelEncyclopaediaMetallum = { 'Q938726', 'Encyclopaedia Metallum' };
 
local labelYandexMusic = { 'Q4537983', 'Яндекс.Музыка' };
 
local labelYouTube = { 'Q866', 'YouTube' };
 
local labelTikTok = { 'Q48938223', 'TikTok' };
 
 
 
local contentHostingProperties = {
 
-- Фото, видео и аудио
 
-- { { 'Q565', 'Wikimedia Commons' }, 'P373', commonsWikimediaLink, categoryWithWikimediaCommons, false },
 
 
 
{ { 'Q545966', 'Bandcamp' }, 'P3283', function( id ) return 'https://' .. id .. '.bandcamp.com'; end },
 
{ labelDeezer, 'P2722', function( id ) return 'http://www.deezer.com/artist/' .. id; end, false },
 
{ labelDeezer, 'P2723', function( id ) return 'http://www.deezer.com/album/' .. id; end, false },
 
{ labelDeezer, 'P2724', function( id ) return 'http://www.deezer.com/track/' .. id; end, false },
 
{ { 'Q103204', 'Flickr' }, 'Q103204', flickrLink, false },
 
{ { 'Q3238917', 'Google Play Music' }, 'P4198', function( id ) return 'https://play.google.com/store/music/artist?id=' .. id; end, false },
 
{ { 'Q209330', 'Instagram' }, 'P2003', function( id ) return 'https://instagram.com/' .. id; end, false },
 
{ { 'Q9589', 'iTunes' }, 'P2850', function( id ) return 'https://itunes.apple.com/ru/artist/' .. id; end, false },
 
{ { 'Q183718', 'Last.fm' }, 'P3192', lastfmLink, false },
 
{ { 'Q6883832', 'Mixcloud' }, 'Q6883832', mixcloudLink, false },
 
{ { 'Q2572292', 'Musopen' }, 'P2338', function( id ) return 'https://musopen.org/composer/' .. id .. '/'; end, false },
 
{ { 'Q17117201', 'PROMODJ' }, 'Q17117201', promodjLink, false },
 
{ { 'Q372827', 'Rutube' }, 'Q372827', rutubeLink, false },
 
{ { 'Q568769', 'SoundCloud' }, 'P3040', function( id ) return 'http://soundcloud.com/' .. id .. '/'; end, false },
 
{ { 'Q568769', 'SoundCloud' }, 'Q568769', function( id ) return 'http://soundcloud.com/' .. id .. '/'; end, false },
 
{ { 'Q689141', 'Spotify' }, 'P1902', function( id ) return 'https://open.spotify.com/artist/' .. id; end, false },
 
{ labelTikTok, 'P7085', tiktokLink, false },
 
{ { 'Q156376', 'Vimeo' }, 'Q156376', vimeoLink, false },
 
{ { 'Q156376', 'Vimeo' }, 'P4015', vimeoLink, false },
 
{ labelYandexMusic, 'P1553', function( id ) return 'https://music.yandex.ru/artist/' .. id; end, false },
 
{ labelYandexMusic, 'P2819', function( id ) return 'https://music.yandex.ru/album/' .. id; end, false },
 
{ labelYouTube, 'P2397', youtubeLinkLong, false },
 
{ labelYouTube, 'Q866', youtubeLink, false },
 
}
 
 
 
local themeProfilesProperties = {
 
-- Тематические сайты: наука и техника
 
{ { 'Q364', 'GitHub ' }, 'P2037', function( id ) return 'https://github.com/' .. id; end },
 
{ { 'Q124688', 'Open Hub ' }, 'P1972', function( id ) return 'https://www.openhub.net/p/' .. id; end },
 
{ { 'Q494817', 'Google Scholar' }, 'P1960', function( id ) return 'https://scholar.google.com/citations?user=' .. id; end },
 
{ { 'Q829984', 'Mathematics Genealogy Project' }, 'P549', function( id ) return 'https://genealogy.math.ndsu.nodak.edu/id.php?id=' .. id; end },
 
{ { 'Q51044', 'ORCID' }, 'P496', orcidLink },
 
{ { 'Q7315186', 'ResearcherID' }, 'P1053', function( id ) return 'http://www.researcherid.com/rid/' .. id; end },
 
{ { 'Q371467', 'Scopus' }, 'P1153', function( id ) return 'http://www.scopus.com/authid/detail.url?authorId=' .. id; end },
 
{ { 'Q1061861', 'Structurae' }, 'P2418', function( id ) return 'http://structurae.de/personen/' .. id; end },
 
{ { 'Q18241050', 'zbMATH' }, 'P1556', function( id ) return 'https://www.zbmath.org/authors/?q=ai:' .. id; end },
 
{ { 'Q4330205', 'Math-Net.ru' }, 'P4252', function( id ) return 'http://www.mathnet.ru/rus/person' .. id; end },
 
{ { 'Q547473', 'MacTutor' }, 'P1563', function( id ) return 'http://www-history.mcs.st-andrews.ac.uk/Biographies/' .. id .. '.html'; end },
 
{ { 'Q4026990', 'JPL Small-Body Database' },'P716', function( id ) return 'https://ssd.jpl.nasa.gov/sbdb.cgi?sstr=' .. id; end },
 
 
 
-- Тематические сайты: политика
 
{ 'INSEE', 'P374', function( id ) return 'https://www.insee.fr/fr/statistiques/2011101?geo=COM-' .. id .. ''; end },
 
{ 'Верховная рада', 'P3391', function( id ) return 'http://itd.rada.gov.ua/mps/info/page/' .. id; end },
 
{ 'Герои страны', 'P2943', function( id ) return 'http://www.warheroes.ru/hero/hero.asp?Hero_id=' .. id; end },
 
{ { 'Q20035614', 'Декларатор' }, 'P1883', declaratorLink },
 
{ 'Европарламент', 'P1186', function( id ) return 'http://www.europarl.europa.eu/meps/en/' .. id; end },
 
{ 'Риксдаг', 'P1214', function( id ) return 'http://data.riksdagen.se/personlista/?iid=' .. id .. '&utformat=html'; end },
 
{ 'Русская армия в Великой войне', 'P6737', function( id ) return 'http://www.grwar.ru/persons/persons.html?id=' .. id; end },
 
{ 'ЧЕСНО', 'P7145', function( id ) return 'https://www.chesno.org/politician/' .. id .. '/'; end },
 
 
 
-- Тематические сайты: спорт
 
{ '100 бомбардиров', 'P6632', function( id ) return 'http://100bombardirov.ru/ru/player/' .. id .. '.htm'; end },
 
{ '365chess', 'P3314', function( id ) return 'http://www.365chess.com/players/' .. id; end },
 
{ '90minut', 'P3605', function( id ) return 'http://www.90minut.pl/kariera.php?id=' .. id; end },
 
{ 'ACB (игрок)', 'P3525', function( id ) return 'http://www.acb.com/jugador.php?id=' .. id; end },
 
{ 'ACB (тренер)', 'P6297', function( id ) return 'http://www.acb.com/entrenador.php?id=' .. id; end },
 
{ 'AIC', 'P6319', function( id ) return 'https://aic.football.it/scheda/' .. id; end },
 
{ 'AS.com', 'P3427', function( id ) return 'http://resultados.as.com/resultados/ficha/deportista/' .. id; end },
 
{ 'ATP', 'P536', function( id ) return 'https://www.atptour.com/en/players/-/' .. id .. '/overview'; end },
 
{ 'Baseball-Reference.com', 'P1825', function( id ) return 'https://www.baseball-reference.com/players/' .. id .. '.html' end },
 
{ 'Baseball-Reference.com (кроме MLB)', 'P1826', function( id ) return 'https://www.baseball-reference.com/register/player.fcgi?id=' .. id; end },
 
{ 'Basketball-Reference.com (игрок)', 'P2685', function( id ) return 'https://www.basketball-reference.com/players/' .. id .. '.html' end },
 
{ 'Basketball-Reference.com (тренер)', 'P4718', function( id ) return 'https://www.basketball-reference.com/coaches/' .. id .. '.html' end },
 
{ 'Basketball-Reference.com (игрок)', 'P4561', function( id ) return 'https://www.basketball-reference.com/wnba/players/' .. id .. '.html' end },
 
{ 'Basketball-Reference.com (тренер)', 'P4720', function( id ) return 'https://www.basketball-reference.com/wnba/coaches/' .. id .. '.html' end },
 
{ 'Basketball-Reference.com (Джи-Лига)', 'P4744', function( id ) return 'https://www.basketball-reference.com/gleague/players/' .. id .. '.html' end },
 
{ 'Basketball-Reference.com (Европа)', 'P4790', function( id ) return 'https://www.basketball-reference.com/euro/players/' .. id .. '.html' end },
 
{ 'BDFA', 'P6188', function( id ) return 'https://bdfa.com.ar/jugadores-' .. id .. '.html'; end },
 
{ 'BDFutbol', 'P3655', function( id ) return 'http://www.bdfutbol.com/en/j/j' .. id .. '.html'; end },
 
{ 'Beachsoccer.ru', 'P6253', function( id ) return 'http://www.beachsoccer.ru/players/player' .. id .. '/'; end },
 
{ 'Beach Volleyball Database', 'P2800', function( id ) return 'http://www.bvbinfo.com/player.asp?ID=' .. id; end },
 
{ 'biathlon.com.ua', 'P6386', function( id ) return 'https://www.biathlon.com.ua/profile.php?id=' .. id .. '&lang=rus'; end },
 
{ 'BoxRec', 'P1967', function( id ) return 'http://boxrec.com/boxer/' .. id; end },
 
{ 'BSRussia', 'P6252', function( id ) return 'http://bsrussia.com/profiles/persons/' .. id; end },
 
{ 'BWF', 'P2729', function( id ) return 'https://bwf.tournamentsoftware.com/player-profile/' .. id; end },
 
{ 'Chess Games', 'P1665', function( id ) return 'http://www.chessgames.com/perl/chessplayer?pid=' .. id; end },
 
{ 'Curlingzone.com', 'P3556', function( id ) return 'http://www.curlingzone.com/player.php?playerid=' .. id; end },
 
{ 'Cycling Archives', 'P1409', function( id ) return 'http://www.cyclingarchives.com/coureurfiche.php?coureurid=' .. id; end },
 
{ 'DraftExpress', 'P3533', function( id ) return 'http://www.draftexpress.com/profile/' .. id .. '/'; end },
 
{ 'Driver Database', 'P3684', function( id ) return 'https://www.driverdb.com/drivers/' .. id .. '/'; end },
 
{ 'EFC', 'P4475', function( id ) return 'https://www.eurofencing.info/competitions/fencers/case:fencer/licence:' .. id; end },
 
{ 'Elite Prospects (игрок)', 'P2481', function( id ) return 'http://www.eliteprospects.com/player.php?player=' .. id; end },
 
{ 'Elite Prospects (тренер)', 'P4319', function( id ) return 'http://www.eliteprospects.com/staff.php?staff=' .. id; end },
 
{ 'ENARD', 'P3958', function( id ) return 'http://infoenard.org.ar/' .. id; end },
 
{ 'England Football Online (игрок)','P6331', function( id ) return 'http://www.englandfootballonline.com/TeamPlyrsBios/Players' .. id .. '.html'; end },
 
{ 'England Football Online (тренер)','P6330', function( id ) return 'http://www.englandfootballonline.com/TeamMgr/Mgr_' .. id .. '.html'; end },
 
{ 'ESBL', 'P4042', function( id ) return 'http://www.esbl.ee/biograafia/' .. id; end },
 
{ 'EspritBleu', 'P4050', function( id ) return 'http://espritbleu.franceolympique.com/espritbleu/athletes/' .. id .. '.php'; end },
 
{ 'ESPN.com', 'P3571', function( id ) return 'http://www.espn.com/mlb/player/stats/_/id/' .. id; end },
 
{ 'ESPNFC.com', 'P3681', function( id ) return 'http://www.espnfc.com/player/' .. id; end },
 
{ 'eu-football.info', 'P3726', function( id ) return 'http://eu-football.info/_player.php?id=' .. id; end },
 
{ 'Eurobasket.com (игрок)', 'P3527', function( id ) return 'http://www.eurobasket.com/player.asp?PlayerID=' .. id; end },
 
{ 'Eurobasket.com (тренер)', 'P6865', function( id ) return 'http://www.eurobasket.com/coach.asp?CoachID=' .. id; end },
 
{ 'Eurohockey.com', 'P2601', function( id ) return 'http://www.eurohockey.com/player/' .. id .. '-.html'; end },
 
{ 'EuroLeague (игрок)', 'P3536', function( id ) return 'https://www.euroleague.net/competition/players/showplayer?pcode=' .. id; end },
 
{ 'EuroLeague (тренер)', 'P6866', function( id ) return 'https://www.euroleague.net/competition/coaches/showcoach?pcode=' .. id; end },
 
{ 'European Athletics', 'P3766', function( id ) return 'http://www.european-athletics.org/athletes/group=' .. id .. '/index.html'; end },
 
{ 'eWRC-results.com', 'P3927', function( id ) return 'https://www.ewrc-results.com/profile/' .. id; end },
 
{ 'FaroeSoccer (игрок)', 'P6627', function( id ) return 'http://www.faroesoccer.com/player.php?playerID=' .. id; end },
 
{ 'FaroeSoccer (тренер)', 'P6628', function( id ) return ' http://www.faroesoccer.com/coach.php?coachID=' .. id; end },
 
{ 'FEI', 'P3111', function( id ) return 'https://data.fei.org/Person/Performance.aspx?personfeiid=' .. id; end },
 
    { 'FIBT',                         'P2991', function( id ) return 'http://www.ibsf.org/en/athletes/athlete/' .. id; end },
 
    { 'FIL',                         'P2990', function( id ) return 'https://www.fil-luge.org/en/athletes/' .. id; end },
 
    { 'FIS',                         'P2773', function( id ) return 'https://data.fis-ski.com/dynamic/athlete-biography.html?sector=CC&competitorid=' .. id; end },
 
{ 'FIS', 'P2772', function( id ) return 'https://data.fis-ski.com/dynamic/athlete-biography.html?sector=AL&competitorid=' .. id; end },
 
{ 'FIS', 'P2774', function( id ) return 'https://data.fis-ski.com/dynamic/athlete-biography.html?sector=FS&competitorid=' .. id; end },
 
{ 'FIS', 'P2775', function( id ) return 'https://data.fis-ski.com/dynamic/athlete-biography.html?sector=JP&competitorid=' .. id; end },
 
{ 'FIS', 'P2776', function( id ) return 'https://data.fis-ski.com/dynamic/athlete-biography.html?sector=NK&competitorid=' .. id; end },
 
{ 'FIS', 'P2777', function( id ) return 'https://data.fis-ski.com/dynamic/athlete-biography.html?sector=SB&competitorid=' .. id; end },
 
{ 'FIS', 'P6669', function( id ) return 'https://data.fis-ski.com/dynamic/athlete-biography.html?sector=TM&competitorid=' .. id; end },
 
{ 'FISA', 'P2091', function( id ) return 'http://www.worldrowing.com/athletes/athlete/' .. id .. '/'; end },
 
-- { 'fogis.se', 'P5038', function( id ) return 'http://fogis.se/information/?scr=player&fplid=' .. id; end },
 
{ 'FootballDatabase.eu', 'P3537', function( id ) return 'https://www.footballdatabase.eu/en/player/details/' .. id; end },
 
{ 'FootballFacts.ru', 'P3660', function( id ) return 'http://footballfacts.ru/players/' .. id; end },
 
{ 'footofeminin.fr', 'P4262', function( id ) return 'http://www.statsfootofeminin.fr/joueur.php?joueur=' .. id; end },
 
{ 'ForaDeJogo (игрок)', 'P3046', function( id ) return 'https://www.foradejogo.net/player.php?player=' .. id; end },
 
{ 'ForaDeJogo (тренер)', 'P3661', function( id ) return 'https://www.foradejogo.net/manager.php?manager=' .. id; end },
 
{ 'FSkate.ru', 'P6624', function( id ) return 'http://www.fskate.ru/skaters/' .. id .. '.html'; end },
 
{ 'Fussballdaten.de', 'P3538', function( id ) return 'http://www.fussballdaten.de/person/' .. id .. '/'; end },
 
{ 'FIE', 'P2423', function( id ) return 'https://fie.org/athletes/' .. id; end },
 
{ 'FIG', 'P2696', function( id ) return 'http://www.gymnastics.sport/site/athletes/bio_detail.php?id=' .. id .. '&type=licence'; end },
 
{ 'FINA', 'P3408', function( id ) return 'https://www.fina.org/athletes/' .. id; end },
 
{ 'Goratings', 'P2805', function( id ) return 'http://www.goratings.org/players/' .. id .. '.html'; end },
 
{ 'Handball123', 'P7002', function( id ) return 'https://www.handball123.com/player/' .. id .. '.html' end },
 
{ 'Hockey-Reference.com', 'P3598', function( id ) return 'http://www.hockey-reference.com/players/' .. id .. '.html' end },
 
{ 'hockeyDB', 'P2602', function( id ) return 'http://www.hockeydb.com/ihdb/stats/pdisplay.php?pid=' .. id; end },
 
{ 'IAT', 'P2778', function( id ) return 'https://www.iat.uni-leipzig.de/datenbanken/dbtriathlon/daten.php?spid=' .. id; end },
 
{ 'IAT', 'P2779', function( id ) return 'https://www.iat.uni-leipzig.de/datenbanken/dbgwh/daten.php?spid=' .. id; end },
 
{ 'IAAF', 'P1146', function( id ) return 'https://www.iaaf.org/athletes/-/' .. id; end },
 
{ 'IAAF Diamond League', 'P3923', function( id ) return 'https://www.diamondleague.com/athletes/' .. id .. '.html'; end },
 
    { 'IBU',                         'P2459', function( id ) return 'https://biathlonresults.com/?IBUId=' .. id; end },
 
    { 'ICF',                         'P3689', function( id ) return 'https://www.canoeicf.com/athlete/' .. id; end },
 
{ 'iDNES.cz', 'P3663', function( id ) return 'https://fotbal.idnes.cz/databanka.aspx?t=hrac&id=' .. id; end },
 
{ 'IFJ', 'P4559', function( id ) return 'https://www.ijf.org/judoka/' .. id; end },
 
{ 'IFSC', 'P3690', function( id ) return 'http://www.ifsc-climbing.org/index.php?option=com_ifsc&view=athlete&id=' .. id; end },
 
{ 'ISSF', 'P2730', function( id ) return 'https://www.issf-sports.org/athletes/athlete.ashx?personissfid=' .. id; end },
 
{ 'ISU', 'P2694', function( id ) return 'http://www.isuresults.com/bios/isufs' .. id.. '.htm'; end },
 
{ 'ITF', 'P599', function( id ) return 'https://www.itftennis.com/procircuit/players/player/profile.aspx?playerid=' .. id; end },
 
{ 'ITTF', 'P1364', function( id ) return 'http://results.ittf.link/index.php?option=com_fabrik&view=details&formid=99&rowid=' .. id; end },
 
{ 'ITU', 'P3604', function( id ) return 'http://www.triathlon.org/athletes/profile/' .. id; end },
 
{ 'IWF', 'P3667', function( id ) return 'http://www.iwf.net/results/athletes/?athlete=&id=' .. id; end },
 
{ 'IWRP', 'P4504', function( id ) return 'http://iwrp.net/?view=contestant&id_zawodnik=' .. id; end },
 
{ 'J.League (игрок)', 'P3565', function( id ) return 'https://data.j-league.or.jp/SFIX04/?player_id=' .. id; end },
 
{ 'J.League (тренер)', 'P4048', function( id ) return 'https://data.j-league.or.jp/SFIX07/?staff_id=' .. id; end },
 
{ 'JudoInside', 'P2767', function( id ) return 'https://www.judoinside.com/judoka/' .. id .. '/'; end },
 
{ 'Juwra.com', 'P3949', function( id ) return 'http://www.juwra.com/' .. id .. '.html'; end },
 
{ 'K League', 'P3053', function( id ) return 'http://www.kleague.com/en/content/playersearch?playercode=' .. id; end },
 
{ 'Kicker.de', 'P6615', function( id ) return 'http://www.kicker.de/' .. id .. '/vereinsspieler_profil.html'; end },
 
{ 'L’Équipe', 'P3665', function( id ) return 'https://www.lequipe.fr/Football/FootballFicheJoueur' .. id .. '.html'; end },
 
{ 'LevskiSofia.info (игрок)', 'P6411', function( id ) return 'https://levskisofia.info/player/' .. id .. '/'; end },
 
{ 'LevskiSofia.info (тренер)', 'P6410', function( id ) return 'https://levskisofia.info/coach/' .. id .. '/'; end },
 
{ 'LFB', 'P4382', function( id ) return 'http://www.basketlfb.com/' .. id; end },
 
{ 'LFH', 'P4289', function( id ) return 'http://www.handlfh.org/joueuse/' .. id .. '/'; end },
 
{ 'LFP', 'P3683', function( id ) return 'https://www.ligue1.com/joueur/' .. id; end },
 
{ 'LNH', 'P4192', function( id ) return 'https://www.lnh.fr/joueurs/' .. id; end },
 
{ 'MLB', 'P3541', function( id ) return 'https://www.mlb.com/player/' .. id; end },
 
{ 'MLS', 'P2398', function( id ) return 'https://www.mlssoccer.com/players/' .. id; end },
 
{ 'MotoGP.com', 'P3928', function( id ) return 'http://www.motogp.com/en/riders/' .. id; end },
 
{ 'National Football Teams.com', 'P2574', function( id ) return 'http://www.national-football-teams.com/player/' .. id .. '.html'; end },
 
{ 'National Wrestling Hall of Fame', 'P6688', function( id ) return 'https://nwhof.org/stillwater/champions-database/#type=cdb&wrestler=' .. id; end },
 
{ 'OnsOranje', 'P5158', function( id ) return 'https://www.onsoranje.nl/teams/' .. id; end },
 
{ 'Pro-Football-Reference.com (игрок)', 'P3561', function( id ) return 'https://www.pro-football-reference.com/players/' .. id .. '.html' end },
 
{ 'Pro-Football-Reference.com (тренер)', 'P6836', function( id ) return 'https://www.pro-football-reference.com/coaches/' .. id .. '.html' end },
 
{ 'racing-reference', 'P3048', function( id ) return 'http://www.racing-reference.info/driver/' .. id; end },
 
{ 'Rallye-info.com', 'P3930', function( id ) return 'http://www.rallye-info.com/driverprofile.asp?driver=' .. id; end },
 
    { 'RealGM', 'P3957', function( id ) return 'http://basketball.realgm.com/player/wd/Summary/' .. id .. ''; end },
 
{ 'Red Bull', 'P3883', function( id ) return 'https://www.redbull.com/int-en/athlete/' .. id; end },
 
{ 'Reprezentacija.rs', 'P3908', function( id ) return 'http://www.reprezentacija.rs/' .. id .. '/#content'; end },
 
{ 'Rollerstory.net', 'P7146', function( id ) return 'https://www.rollerstory.net/skater.php?id=' .. id; end },
 
{ 'Romanian Soccer', 'P6399', function( id ) return 'http://www.romaniansoccer.ro/players/' .. id .. '/'; end },
 
{ 'Rugby League Project', 'P6881', function( id ) return 'https://www.rugbyleagueproject.org/' .. id .. '.html'; end },
 
{ 'Sambafoot', 'P3668', function( id ) return 'http://www.sambafoot.com/en/players/' .. id .. '.html' end },
 
-- { 'Scoresway', 'P6063', function( id ) return 'http://www.scoresway.com/?sport=basketball&page=player&id=' .. id; end },
 
-- { 'Scoresway', 'P6062', function( id ) return 'http://www.scoresway.com/?sport=baseball&page=player&id=' .. id; end },
 
-- { 'Scoresway', 'P6066', function( id ) return 'http://www.scoresway.com/?sport=volleyball&page=player&id=' .. id; end },
 
-- { 'Scoresway', 'P4451', function( id ) return 'http://www.scoresway.com/?sport=handball&page=person&id=' .. id; end },
 
-- { 'Scoresway', 'P6065', function( id ) return 'http://www.scoresway.com/?sport=rugby&page=player&id=' .. id; end },
 
-- { 'Scoresway', 'P3043', function( id ) return 'http://www.scoresway.com/?sport=soccer&page=person&id=' .. id; end },
 
-- { 'Scoresway', 'P6064', function( id ) return 'http://www.scoresway.com/?sport=hockey&page=player&id=' .. id; end },
 
{ 'Sherdog', 'P2818', function( id ) return 'https://www.sherdog.com/fighter/' .. id; end },
 
{ 'ShorttrackOnLine.info', 'P3693', function( id ) return 'http://www.shorttrackonline.info/skaterbio.php?id=' .. id; end },
 
{ 'ski-db.com', 'P3619', function( id ) return 'https://www.ski-db.com/db/profiles/' .. id .. '.php'; end },
 
{ 'Soccer Base (игрок)', 'P2193', function( id ) return 'https://www.soccerbase.com/players/player.sd?player_id=' .. id; end },
 
{ 'Soccer Base (тренер)', 'P2195', function( id ) return 'https://www.soccerbase.com/managers/manager.sd?manager_id=' .. id; end },
 
{ 'soccerdonna.de', 'P4381', function( id ) return 'https://www.soccerdonna.de/wiki/pedia/profil/spieler_' .. id .. '.html'; end },
 
{ 'Soccerway', 'P2369', function( id ) return 'http://int.soccerway.com/players/' .. id .. '/' end },
 
{ 'Soccerway', 'P6131', function( id ) return 'https://int.soccerway.com/teams/wd/wd/' .. id .. '/' end },
 
{ 'Speedskatingbase.eu', 'P2350', function( id ) return 'http://www.speedskatingbase.eu/?section=skaters&subsection=skater&skaterid=' .. id; end },
 
{ 'SpeedSkatingNews.info', 'P3694', function( id ) return 'http://www.speedskatingnews.info/en/data/skater/' .. id .. '/'; end },
 
{ 'SpeedskatingResults.com', 'P4314', function( id ) return 'http://speedskatingresults.com/index.php?p=17&s=' .. id; end },
 
{ 'SpeedSkatingStats', 'P3695', function( id ) return 'http://www.speedskatingstats.com/index.php?file=skater&code=' .. id; end },
 
{ 'Sportbox.ru', 'P4421', function( id ) return 'http://news.sportbox.ru/Vidy_sporta/' .. id; end },
 
{ 'sports.md', 'P6401', function( id ) return 'http://moldova.sports.md/' .. id .. '/'; end },
 
-- { 'Sports.ru', 'P4408', function( id ) return 'https://www.sports.ru/tags/' .. id; end },
 
{ 'Sports-Reference.com', 'P1447', function( id ) return 'http://www.sports-reference.com/olympics/athletes/' .. id .. '.html' end },
 
{ 'Sports-Reference.com (колледж)', 'P3696', function( id ) return 'https://www.sports-reference.com/cbb/players/' .. id .. '.html' end },
 
{ 'Sports-Reference.com (тренер)', 'P4751', function( id ) return 'https://www.sports-reference.com/cbb/coaches/' .. id .. '.html' end },
 
{ 'StadiumDB', 'P5288', function( id ) return 'http://stadiumdb.com/stadiums/' .. id; end },
 
{ 'svenskfotboll.se', 'P1238', function( id ) return 'https://www.svenskfotboll.se/spelarfakta/' .. id .. '/'; end },
 
{ 'Swimrankings.net', 'P2640', function( id ) return 'https://www.swimrankings.net/index.php?page=athleteDetail&athleteId=' .. id; end },
 
{ 'Taekwondo Data', 'P2987', function( id ) return 'http://www.taekwondodata.com/' .. id .. '.html'; end },
 
{ 'teams.by', 'P6321', function( id ) return 'http://www.teams.by/player/info/' .. id; end },
 
{ 'TheFinalBall.com (игрок)', 'P3047', function( id ) return 'https://www.thefinalball.com/player.php?id=' .. id end },
 
{ 'TheFinalBall.com (тренер)', 'P6316', function( id ) return 'http://www.thefinalball.com/coach.php?id=' .. id end },
 
{ 'TheFinalBall.com (судья)', 'P6315', function( id ) return 'http://www.thefinalball.com/arbitro.php?id=' .. id end },
 
{ 'TheSports.org', 'P4391', function( id ) return 'http://www.the-sports.org/t-spf' .. id .. '.html'; end },
 
{ 'THW Kiel', 'P4263', function( id ) return 'http://archiv.thw-handball.de/thw/sp_' .. id .. '.htm'; end },
 
{ 'Tilastopaja', 'P3882', function( id ) return 'https://www.tilastopaja.eu/db/at.php?Sex=2&ID=' .. id; end },
 
{ 'Tilastopaja', 'P3884', function( id ) return 'https://www.tilastopaja.eu/db/at.php?Sex=1&ID=' .. id; end },
 
{ 'Track and Field Statistics', 'P3924', function( id ) return 'http://trackfield.brinkster.net/Profile.asp?ID=' .. id .. '&Gender=W'; end },
 
{ 'Track and Field Statistics', 'P3925', function( id ) return 'http://trackfield.brinkster.net/Profile.asp?ID=' .. id .. '&Gender=M'; end },
 
{ 'Transfermarkt (судья)', 'P3699', function( id ) return 'https://www.transfermarkt.com/transfermarkt/profil/schiedsrichter/' .. id; end },
 
{ 'Transfermarkt (игрок)', 'P2446', function( id ) return 'https://www.transfermarkt.com/transfermarkt/profil/spieler/' .. id; end },
 
{ 'Transfermarkt (тренер)', 'P2447', function( id ) return 'https://www.transfermarkt.com/transfermarkt/profil/trainer/' .. id; end },
 
{ 'TuttoCalciatori.Net (игрок)', 'P7036', function( id ) return 'https://www.tuttocalciatori.net/' .. id; end },
 
{ 'TuttoCalciatori.Net (тренер)', 'P7035', function( id ) return 'https://www.tuttocalciatori.net/allenatori/' .. id .. '-'; end },
 
{ 'UIPM', 'P2726', function( id ) return 'http://www.uipmworld.org/athlete/' .. id; end },
 
{ 'Vesti.kz', 'P6215', function( id ) return 'http://vesti.kz/player/' .. id .. '/' end },
 
{ 'Voetbalstats', 'P5742', function( id ) return 'https://voetbalstats.nl/spelernedxi.php?persid=' .. id; end },
 
{ 'World Curling Tour', 'P3556', function( id ) return 'http://www.worldcurl.com/player.php?playerid=' .. id; end },
 
{ 'worldfootball.net (игрок)', 'P2020', function( id ) return 'http://www.worldfootball.net/player_summary/' .. id .. '/' end },
 
{ 'worldfootball.net (судья)', 'P6314', function( id ) return 'https://www.worldfootball.net/referee_summary/' .. id .. '/' end },
 
{ 'WorldSBK.com', 'P4076', function( id ) return 'http://www.worldsbk.com/en/rider/' .. id; end },
 
{ 'X Games', 'P4893', function( id ) return 'http://www.xgames.com/athletes/' .. id; end },
 
{ 'Адриатическая лига', 'P6850', function( id ) return 'https://www.aba-liga.com/player.php?id=' .. id; end },
 
{ 'АМФР', 'P6320', function( id ) return 'http://amfr.ru/persons/' .. id .. '/'; end },
 
{ 'Британская олимпийская ассоциация', 'P4490', function( id ) return 'https://www.teamgb.com/athletes/' .. id; end },
 
{ 'Бундеслига', 'P5057', function( id ) return 'https://www.liquimoly-hbl.de/en/import/players/' .. id .. '/'; end },
 
{ 'Всемирная федерация кёрлинга', 'P3557', function( id ) return 'http://results.worldcurling.org/Person/Details/' .. id; end },
 
{ 'Всемирная федерация стрельбы из лука', 'P3010', function( id ) return 'https://worldarchery.org/athlete/' .. id; end },
 
{ 'Всемирные игры', 'P4588', function( id ) return 'https://www.theworldgames.org/results#athlete=:' .. id; end },
 
{ 'ВФЛА', 'P5077', function( id ) return 'http://sport.rusathletics.com/AthleteProfile/' .. id; end },
 
{ 'Датский футбольный союз', 'P6109', function( id ) return 'https://www.dbu.dk/landshold/landsholdsdatabasen/PlayerInfo/' .. id; end },
 
{ 'ЕГФ', 'P3573', function( id ) return 'http://www.eurohandball.com/player/' .. id; end },
 
{ 'Единая лига ВТБ', 'P6732', function( id ) return 'http://www.vtb-league.com/ru/player/' .. id .. '/'; end },
 
{ 'Зал хоккейной славы', 'P3567', function( id ) return 'https://www.hhof.com/LegendsOfHockey/jsp/SearchPlayer.jsp?player=' .. id; end },
 
{ 'Игры Содружества', 'P4548', function( id ) return 'https://thecgf.com/results/athletes/' .. id; end },
 
{ 'Игры Содружества 2006', 'P5716', function( id ) return 'http://m2006.thecgf.com/Participants/Participants?ID=' .. id; end },
 
{ 'Игры Содружества 2014', 'P6953', function( id ) return 'http://g2014results.thecgf.com/athlete/-/' .. id .. '/'; end },
 
{ 'Израильская футбольная ассоциация', 'P3748', function( id ) return 'http://www.football.org.il/players/player/?player_id=' .. id; end },
 
{ 'Итальянская федерация лёгкой атлетики', 'P3446', function( id ) return 'http://www.fidal.it/atleta_one.php?t=' .. id; end },
 
{ 'Крымский футбольный союз', 'P6318', function( id ) return 'https://www.cfu2015.com/players/' .. id .. '/'; end },
 
{ 'КХЛ', 'P3652', function( id ) return 'https://www.khl.ru/players/' .. id .. '/' end },
 
{ 'Международная федерация парусного спорта', 'P2804', function( id ) return 'https://site-isaf.soticcloud.net/biog?memberid=' .. id; end },
 
{ 'МОК', 'P3171', function( id ) return 'https://www.olympic.org/' .. id; end },
 
{ 'МПК', 'P4397', function( id ) return 'https://www.paralympic.org/asp/redirect/ipc.asp?page=athletebio&personid=' .. id; end },
 
{ 'Немецкий футбольный союз', 'P4023', function( id ) return 'https://datencenter.dfb.de/en/profile/' .. id; end },
 
{ 'Норвежская футбольная ассоциация', 'P3936', function( id ) return 'https://www.fotball.no/fotballdata/person/profil/?fiksId=' .. id; end },
 
{ 'Норвежская гандбольная федерация', 'P3700', function( id ) return 'https://handballold.nif.no/Statistikk_Landskamper.asp?SpillerId=' .. id; end },
 
{ 'НХЛ', 'P3522', function( id ) return 'http://www.nhl.com/ice/player.htm?id=' .. id; end },
 
{ 'Объединённый мир борьбы', 'P2727', function( id ) return 'https://www.iat.uni-leipzig.de/datenbanken/dbwrestling/daten.php?spid=' .. id; end },
 
{ 'Одесский футбол', 'P6415', function( id ) return 'http://football.odessa.ua/person/?' .. id; end },
 
{ 'Олимпийская спортивная конфедерация Германии', 'P4053', function( id ) return 'https://www.teamdeutschland.de/de/athleten/detail/a_action/show/a_athletes/' .. id .. '.html'; end },
 
{ 'Олимпийский комитет Австралии', 'P3682', function( id ) return 'http://corporate.olympics.com.au/athlete/' .. id; end },
 
{ 'Олимпийский комитет Бразилии', 'P4060', function( id ) return 'https://www.cob.org.br/pt/atletas/' .. id; end },
 
{ 'Олимпийский комитет Канады', 'P4054', function( id ) return 'http://olympic.ca/team-canada/' .. id .. '/'; end },
 
{ 'Олимпийский комитет Латвии', 'P2593', function( id ) return 'http://olimpiade.lv/en/olympic-athletes/' .. id; end },
 
{ 'Олимпийский комитет Новой Зеландии', 'P4056', function( id ) return 'http://www.olympic.org.nz/athletes/' .. id .. '/'; end },
 
{ 'Олимпийский комитет США', 'P4063', function( id ) return 'https://www.teamusa.org/Athletes/' .. id; end },
 
{ 'Олимпийский комитет Швеции', 'P2323', function( id ) return 'http://sok.se/idrottare/idrottare/' .. id .. '.html'; end },
 
{ 'Польская ассоциация легкой атлетики', 'P5075', function( id ) return 'http://www.domtel-sport.pl/statystykaLA/personal.php?page=profile&nr_zaw=' .. id; end },
 
{ 'Прессбол', 'P6311', function( id ) return 'https://www.pressball.by/footballstat/' .. id .. '/'; end },
 
{ 'Российский биатлон', 'P5771', function( id ) return 'http://base.biathlonrus.com/athlete/card/id/' .. id; end },
 
{ 'Российский биатлон', 'P5772', function( id ) return 'http://base.biathlonrus.com/coach/card/id/' .. id; end },
 
{ 'Российский хоккей', 'P6154', function( id ) return 'http://r-hockey.ru/player.asp?TXT=' .. id; end },
 
{ 'РПЛ (игрок)', 'P4417', function( id ) return 'https://premierliga.ru/players/' .. id; end },
 
{ 'РПЛ (сотрудник)', 'P6312', function( id ) return 'https://premierliga.ru/staff/staff_' .. id .. '.html'; end },
 
{ 'РПЛ (судья)', 'P6313', function( id ) return 'https://premierliga.ru/rfpl/arbitr/arbitr_' .. id .. '.html'; end },
 
{ 'РФБ', 'P5969', function( id ) return 'https://russiabasket.ru/players/' .. id; end },
 
{ 'РФГС', 'P6825', function( id ) return 'http://www.fgssr.ru/Sport/UI/Page/PersonList/PersonPage.aspx?personid=' .. id; end },
 
{ 'Сборная России по футболу', 'P6317', function( id ) return 'http://www.rusteam.permian.ru/players/' .. id .. '.html'; end },
 
{ 'СБР', 'P6387', function( id ) return 'http://biathlonrus.com/team/main/' .. id .. '/'; end },
 
{ 'Спорт-страна.ру', 'P6416', function( id ) return 'http://sport-strana.ru/' .. id .. '/'; end },
 
-- { 'Спорт на Урале', 'P6388', function( id ) return 'http://sportufo.ru/index.php?option=com_content&view=article&id=' .. id; end },
 
{ 'Спортивная Россия', 'P6421', function( id ) return 'https://infosport.ru/person/' .. id; end },
 
{ 'ТФФ',                         'P2448', function( id ) return 'http://www.tff.org/Default.aspx?pageId=526&kisiID=' .. id; end },
 
{ 'УЕФА', 'P2276', function( id ) return 'http://ru.uefa.com/teamsandplayers/players/player=' .. id .. '/profile/index.html'; end },
 
{ 'ХФС', 'P3577', function( id ) return 'http://hns-cff.hr/en/players/' .. id .. '/-/'; end },
 
{ 'ФБР', 'P6975', function( id ) return 'http://rusboxing.ru/prof-box/boxers/' .. id .. '/'; end },
 
{ 'ФБР', 'P6994', function( id ) return 'http://rusboxing.ru/calendar/boxers/' .. id .. '/'; end },
 
{ 'ФИБА', 'P3542', function( id ) return 'http://archive.fiba.com/pages/eng/fa/p/rpp//q//pid/' .. id .. '/_//players.html' end },
 
{ 'ФИДЕ', 'P1440', function( id ) return 'https://ratings.fide.com/card.phtml?event=' .. id; end },
 
{ 'ФИФА', 'P1469', function( id ) return 'https://static.fifa.com/fifa-tournaments/players-coaches/people=' .. id .. '/index.html'; end },
 
{ 'ФЛГР', 'P6598', function( id ) return 'https://flgr-results.ru/?ActivePage=Profile&RusCode=' .. id; end },
 
{ 'ФПБР', 'P6940', function( id ) return 'http://pro-box.ru/boksyery/' .. id .. '/'; end },
 
{ 'Французская федерация лыжного спорта', 'P6612', function( id ) return 'http://www.ffs.fr/biathlon/competitions/fiche-individuelle?licence=' .. id; end },
 
{ 'Футбольная ассоциация Исландии', 'P6495', function( id ) return 'https://www.ksi.is/mot/leikmadur/$PlayerDetails/Games/?leikmadur=' .. id; end },
 
{ 'Футбольная ассоциация Чехии', 'P3050', function( id ) return 'http://nv.fotbal.cz/reprezentace/reprezentace-a/statistiky/viewstat3.asp?name=' .. id; end },
 
{ 'Футбольные арбитры России', 'P6701', function( id ) return 'http://referee.ru/arbitrator/' .. id .. '.html'; end },
 
{ 'ФФУ',                         'P3662', function( id ) return 'http://www.ffu.org.ua/ukr/tournaments/prof/' .. id .. '/' end },
 
{ 'ФФФ',                         'P4886', function( id ) return 'https://www.fff.fr/equipes-de-france/toutes-les-joueuses/fiche-joueuse/' .. id .. '-wd' end },
 
{ 'ФФФ',                         'P4883', function( id ) return 'https://www.fff.fr/equipes-de-france/tous-les-joueurs/fiche-joueur/' .. id .. '-wd' end },
 
{ 'ФХМР',                         'P3622', function( id ) return 'http://www.rusbandy.ru/gamer/' .. id .. '/'; end },
 
{ 'ЦСКА',                         'P6613', function( id ) return 'http://cska.ru/person/' .. id; end },
 
{ 'ШФА',                         'P3049', function( id ) return 'https://www.scottishfa.co.uk/players/?pid=' .. id; end },
 
{ 'Эстонский футбольный союз', 'P3659', function( id ) return 'http://jalgpall.ee/voistlused/player/' .. id; end },
 
 
 
-- Тематические сайты: аудио и видео
 
{ 'AFDb', 'P3351', function( id ) return 'http://www.adultfilmdatabase.com/actor.cfm?actorid=' .. id .. ''; end },
 
{ { 'Q477809', 'AllMovie' }, 'P1562', function(id) return 'http://www.allmovie.com/movie/' .. id; end },
 
{ { 'Q477809', 'AllMovie' }, 'P2019', function(id) return 'http://www.allmovie.com/artist/' .. id; end },
 
 
 
{ { 'Q31181', 'AllMusic' }, 'P1728', function(id) return 'http://www.allmusic.com/artist/' .. id; end },
 
{ { 'Q31181', 'AllMusic' }, 'P1729', function(id) return 'http://www.allmusic.com/album/' .. id; end },
 
{ { 'Q31181', 'AllMusic' }, 'P1730', function(id) return 'http://www.allmusic.com/song/' .. id; end },
 
{ { 'Q31181', 'AllMusic' }, 'P1994', function(id) return 'http://www.allmusic.com/composition/' .. id; end },
 
 
 
{ labelAllocine, 'P1265', function(id) return 'http://www.allocine.fr/film/fichefilm_gen_cfilm=' .. id .. '.html'; end },
 
{ labelAllocine, 'P1266', function(id) return 'http://www.allocine.fr/personne/fichepersonne_gen_cpersonne=' .. id .. '.html'; end },
 
{ labelAllocine, 'P1267', function(id) return 'http://www.allocine.fr/series/ficheserie_gen_cserie=' .. id .. '.html'; end },
 
 
 
    { 'AniDB', 'P5646', function( id ) return 'https://anidb.net/perl-bin/animedb.pl?show=anime&aid=' .. id .. ''; end },
 
    { 'AniDB', 'P5648', function( id ) return 'https://anidb.net/perl-bin/animedb.pl?show=character&charid=' .. id .. ''; end },
 
    { 'AniDB', 'P5649', function( id ) return 'https://anidb.net/perl-bin/animedb.pl?show=creator&creatorid=' .. id .. ''; end },
 
 
 
{ labelAnimeNewsNetwork, 'P1982', function(id) return 'http://www.animenewsnetwork.com/encyclopedia/people.php?id=' .. id; end },
 
{ labelAnimeNewsNetwork, 'P1983', function(id) return 'http://www.animenewsnetwork.com/encyclopedia/company.php?id=' .. id; end },
 
{ labelAnimeNewsNetwork, 'P1984', function(id) return 'http://www.animenewsnetwork.com/encyclopedia/manga.php?id=' .. id; end },
 
{ labelAnimeNewsNetwork, 'P1985', function(id) return 'http://www.animenewsnetwork.com/encyclopedia/anime.php?id=' .. id; end },
 
 
 
{ { 'Q223142', 'Box Office Mojo' }, 'P1237', boxofficemojoLink },
 
 
 
{ { 'Q3561957', 'ČSFD' }, 'P2529', function(id) return 'http://www.csfd.cz/film/' .. id; end },
 
{ { 'Q3561957', 'ČSFD' }, 'P2605', function(id) return 'http://www.csfd.cz/tvurce/' .. id; end },
 
 
 
{ labelDiscogs, 'P1953', function(id) return 'http://www.discogs.com/artist/' .. id; end },
 
{ labelDiscogs, 'P1954', function(id) return 'http://www.discogs.com/master/' .. id; end },
 
{ labelDiscogs, 'P1955', function(id) return 'http://www.discogs.com/label/' .. id; end },
 
{ labelDiscogs, 'P2206', function(id) return 'http://www.discogs.com/release/' .. id; end },
 
 
 
{ { 'Q2638147', 'FilmAffinity' }, 'P480', function(id) return 'http://www.filmaffinity.com/en/film' .. id .. '.html' end },
 
{ 'Filmportal', 'P2639', function( id ) return 'https://www.filmportal.de/' .. id .. ''; end },
 
{ labelEncyclopaediaMetallum, 'P1952', function(id) return 'https://www.metal-archives.com/bands//' .. id; end },
 
{ labelEncyclopaediaMetallum, 'P1989', function(id) return 'https://www.metal-archives.com/artists//' .. id; end },
 
{ labelEncyclopaediaMetallum, 'P2721', function(id) return 'http://www.metal-archives.com/release.php?id=' .. id; end },
 
{ 'IAFD', 'P3869', function( id ) return 'http://www.iafd.com/person.rme/perfid=' .. id .. '/gender=f'; end },
 
{ labelIBDb, 'P1217', ibdbVenueLink },
 
{ labelIBDb, 'P1218', ibdbProductionLink },
 
{ labelIBDb, 'P1219', ibdbShowLink },
 
{ labelIBDb, 'P1220', ibdbPersonLink },
 
{ { 'Q37312', 'Internet Movie Database' }, 'P345', imdbLink },
 
{ { 'Q523660', 'International Music Score Library Project' }, 'P839', imslpLink },
 
{ { 'Q2389071', 'КиноПоиск' }, 'P2604', function( id ) return 'https://www.kinopoisk.ru/name/' .. id .. '/'; end },
 
{ { 'Q150248', 'Metacritic' }, 'P1712', function( id ) return 'http://www.metacritic.com/' .. id; end },
 
{ { 'Q6824428', 'MetroLyrics' }, 'P2624', function( id ) return 'http://www.metrolyrics.com/' .. id .. '.html'; end },
 
{ { 'Q2158761', 'MovieMeter' }, 'P1969', function( id ) return 'https://www.moviemeter.nl/director/' .. id; end },
 
{ labelMusicBrainz, 'P434', function( id ) return 'https://musicbrainz.org/artist/' .. id; end },
 
{ labelMusicBrainz, 'P435', function( id ) return 'https://musicbrainz.org/work/' .. id; end },
 
{ labelMusicBrainz, 'P436', function( id ) return 'https://musicbrainz.org/release-group/' .. id; end },
 
{ 'Penthouse', 'P6290', function( id ) return 'https://penthousegold.com/models/' .. id .. '.html'; end },
 
{ { 'Q22673', 'Project Gutenberg' }, 'P1938', function(id) return 'http://www.gutenberg.org/ebooks/author/' .. id; end },
 
{ 'Rate Your Music', 'P5404', function( id ) return 'https://rateyourmusic.com/artist/' .. id .. ''; end },
 
{ { 'Q105584', 'Rotten Tomatoes' }, 'P1258', rottentomatoesLink },
 
{ { 'Q4066284', 'Аниматор.ру' }, 'P1934', function(id) return 'http://www.animator.ru/db/?p=show_film&fid=' .. id; end },
 
{ { 'Q4066284', 'Аниматор.ру' }, 'P5770', function(id) return 'http://www.animator.ru/db/?p=show_person&pid=' .. id; end },
 
{ { 'Q7713473', 'AFI' }, 'P3593', function(id) return 'https://catalog.afi.com/Catalog/moviedetails/' .. id; end },
 
 
 
-- Тематические сайты: литература
 
{ labelISFDb, 'P1233', isfdbAuthorLink },
 
{ labelISFDb, 'P1234', isfdbPublicationLink },
 
{ labelISFDb, 'P1235', isfdbSeriesLink },
 
{ labelISFDb, 'P1239', isfdbPublisherLink },
 
 
 
{ { 'Q17299517', 'RKDartists' }, 'P650', rkdArtistsLink },
 
{ { 'Q17299580', 'RKDimages' }, 'P350', rkdImagesLink },
 
 
 
{ { 'Q24694883', 'ECARTICO' }, 'P2915', function(id) return 'http://www.vondel.humanities.uva.nl/ecartico/persons/' .. id; end },
 
{ 'OCLC', 'P243', function( id ) return 'https://www.worldcat.org/oclc/' .. id .. ''; end },
 
 
 
-- Тематические сайты: видеоигры
 
{ 'Behind The Voice Actors','P4965', function( id ) return 'http://www.behindthevoiceactors.com/video-games/' .. id; end },
 
{ 'CPC-Power', 'P4847', function( id ) return 'http://www.cpc-power.com/index.php?page=detail&num=' .. id; end },
 
    { 'IGN', 'P5385', function( id ) return 'http://ign.com/games/' .. id .. ''; end },
 
{ 'GameRankings', 'P4769', function( id ) return 'http://www.gamerankings.com/-/' .. id .. '-/index.html'; end },
 
    { 'GameSpot', 'P5494', function( id ) return 'https://www.gamespot.com/' .. id .. ''; end },
 
{ 'Gaming-History', 'P4806', function( id ) return 'https://www.arcade-history.com/?page=detail&id=' .. id; end },
 
{ 'Giant Bomb', 'P5247', function( id ) return 'https://www.giantbomb.com/wd/' .. id .. '/'; end },
 
{ 'Guardiana', 'P4710', function( id ) return 'https://www.guardiana.net/?game_id=' .. id; end },
 
{ 'Hall of Light', 'P4671', function( id ) return 'http://hol.abime.net/' ..id; end },
 
{ 'KLOV', 'P2858', function( id ) return 'http://www.arcade-museum.com/game_detail.php?game_id=' .. id; end },
 
{ 'Lemon 64', 'P4816', function( id ) return 'http://www.lemon64.com/?game_id=' .. id; end },
 
{ 'MobyGames', 'P1933', function( id ) return 'https://www.mobygames.com/game/' .. id; end },
 
    { 'MobyGames',   'P4773', function( id ) return 'http://www.mobygames.com/company/' .. id .. ''; end },
 
{ 'MobyGames', 'P5360', function( id ) return 'https://www.mobygames.com/game-group/' .. id; end },
 
{ 'Ready64', 'P4916', function( id ) return 'http://ready64.org/giochi/scheda_gioco/id/' .. id .. '/'; end },
 
{ 'Redump', 'P5290', function( id ) return 'http://redump.org/disc/' .. id .. '/'; end },
 
{ 'VGMdb', 'P5659', function( id ) return 'https://vgmdb.net/product/' .. id .. ''; end },
 
{ 'World of Spectrum', 'P4705', function( id ) return 'https://www.worldofspectrum.org/infoseekid.cgi?id=' .. id; end },
 
 
 
-- Тематические сайты: другие
 
{ 'Flags of the World', 'P3089', function( id ) return 'http://flagspot.net/flags/' .. id .. '.html'; end },
 
{ { 'Q41226', 'Open Directory Project' }, 'P998', function ( id ) return 'http://curlie.org/' .. id; end },
 
{ 'The Peerage', 'P4638', function( id ) return 'https://tools.wmflabs.org/wikidata-externalid-url/?p=4638&url_prefix=http://www.thepeerage.com/&id=' .. id .. ''; end },
 
}
 
 
 
local geniGraves = {
 
{ { 'Q63056', 'Find a Grave' }, 'P535', findagraveLink },
 
{ 'JewAge', 'P4116', function( id ) return 'http://www.jewage.org/wiki/ru/Profile:' .. id; end },
 
{ 'WikiTree', 'P2949', function( id ) return 'https://www.wikitree.com/wiki/' .. id; end },
 
{ 'WeRelate', 'P4159', function( id ) return 'http://www.werelate.org/wiki/Person:' .. id; end },
 
{ { 'Q649227', 'Родовод' }, 'P1185', rodovidLink },
 
}
 
 
 
local authorityControl = {
 
{ { 'Q213678', 'Bibliotheca Apostolica Vaticana', 'BAV' }, 'P1017', bavLink, },
 
{ { 'Q4584301', '', 'BIBSYS' }, 'P1015', bibsysLink, },
 
{ { 'Q1200925', 'Biblioteca de Catalunya', 'BNC' }, 'P1273', bncLink, },
 
{ { 'Q750403', 'Biblioteca Nacional de España', 'BNE' }, 'P950', bneLink, },
 
{ { 'Q193563', 'Bibliothèque nationale de France', 'BNF' }, 'P268', bnfLink, },
 
{ { 'Q1868372', 'Biografisch Portaal', 'BPN' }, 'P651', bpnLink, },
 
{ { 'Q9384291', '中国高等教育文献保障系统', 'CALIS' }, 'P270', calisLink, },
 
{ { 'Q17299677', 'China Biographical Database Project', 'CBDB' }, 'P497', cbdbLink, },
 
{ { 'Q10726338', 'Citation Information by NII', 'CiNii' }, 'P271', ciniiLink, },
 
{ { 'Q16744133', 'CONOR', 'CONOR' }, 'P1280', conorLink, },
 
{ { 'Q27302', 'Deutsche Nationalbibliothek', 'DNB' }, 'P1292', dnbLink, },
 
{ { 'Q501851', 'مكتبة الإسكندرية الجديدة', 'EGAXA' }, 'P1309', egaxaLink, },
 
{ { 'Q36578', 'Gemeinsame Normdatei', 'GND' }, 'P227', gndLink, },
 
{ { 'Q19366588', 'Gemeinsamer Thesaurus für audiovisuelle Archive', 'GTAA' }, 'P1741', gtaaLink, },
 
{ { 'Q3803707', 'Istituto Centrale per il Catalogo Unico', 'ICCU' }, 'P396', iccuLink, },
 
{ { 'Q423048', 'International Standard Name Identifier', 'ISNI' }, 'P213', isniLink, },
 
{ { 'Q620946', 'Library of Congress Control Number', 'LCCN' }, 'P244', lccnLink, },
 
{ { 'Q13219454', 'Library of Congress Control Number', 'LOC' }, 'P1144', locLink, },
 
{ { 'Q1133733', 'Latvijas Nacionālā bibliotēka', 'LNB' }, 'P1368', lnbLink, },
 
{ { 'Q809830', 'Base Mérimée', 'Mérimée' }, 'P380', merimeeLink, },
 
{ { 'Q28136779', 'Microsoft Academic', 'Microsoft' }, 'P6366', microsoftLink, },
 
 
 
{ { 'Q618340', '國家圖書館 (中華民國)', 'NCL' }, 'P1048', nclLink, },
 
{ { 'Q477675', '国立国会図書館', 'NDL' }, 'P349', ndlLink, },
 
{ { 'Q732353', '中国国家图书馆', 'NLC' }, 'P1213', nlcLink, },
 
-- NLI numbers seems unstable
 
{ { 'Q1967876', 'Národní knihovna České republiky', 'NKC' }, 'P691', nkcLink, },
 
{ { 'Q623578', 'National Library of Australia', 'NLA' }, 'P409', nlaLink, },
 
{ { 'Q1467610', 'Αναγνωριστικό Καθιερωμένων από τον Κατάλογο Καθιερωμένων Όρων της Εθνικής Βιβλιοθήκης της Ελλάδος', 'NLG' }, 'P3348', function(id) return 'http://nlg.okfn.gr/resource/authority/record' .. id; end, },
 
{ { 'Q622012', 'Biblioteca Națională a României', 'NLR' }, 'P1003', nlrLink, },
 
{ { 'Q856423', 'Polska Biblioteka Narodowa', 'NLP' }, 'P1695', function(id) return 'http://mak.bn.org.pl/cgi-bin/KHW/makwww.exe?BM=01&IM=04&NU=01&WI=' .. id; end, },
 
{ { 'Q631375', 'Nacionalna i sveučilišna knjižnica u Zagrebu', 'NSK' }, 'P1375', nskLink, },
 
{ { 'Q1526131', 'Koninklijke Bibliotheek', 'NTA' }, 'P1006', ntaLink, },
 
{ { 'Q1063819', 'Országos Széchényi Könyvtár', 'NSZL' }, 'P951', nszlLink, },
 
{ { 'Q11789729', 'Narodowy Uniwersalny Katalog Centralny', 'NUKAT' }, 'P1207', nukatLink, },
 
{ { 'Q245966', 'Biblioteca Nacional de Portugal', 'PTBNP' }, 'P1005', ptbnpLink, },
 
{ { 'Q1048694', 'Российская государственная библиотека', 'РГБ' }, 'P947', rslLink, },
 
{ { 'Q953058', 'Kungliga biblioteket', 'LIBRIS' }, 'P906', selibrLink, },
 
{ { 'Q2597810', 'Système universitaire de documentation', 'SUDOC' }, 'P269', sudocLink, },
 
{ { 'Q54919', 'Virtual International Authority File', 'VIAF' }, 'P214', viafLink, },
 
{ { 'Q2494649', 'Union List of Artist Names', 'ULAN' }, 'P245', ulanLink, },
 
{ { 'Q65212904', 'Наука Украины', 'ИРБИС-НБУВ' }, 'P7148', naukaUkrLink, },
 
{ { 'Q2860429', 'Российский государственный архив литературы и искусства', 'РГАЛИ' }, 'P6752', rgaliLink, },
 
{ { 'Q1048694', 'Российская государственная библиотека', 'РГБ' }, 'P1973', rslLink0, },
 
}
 
 
 
local authorityControlExt = {
 
{ { 'Q846596', 'WorldCat Identities', 'WorldCat VIAF' },'P214',    worldcatLink, },
 
}
 
 
 
function getQualifierSingleValue( statement, qualifierName )
 
if (statement ~= nil
 
and statement.qualifiers ~= nil
 
and statement.qualifiers[qualifierName] ~= nil) then
 
 
 
for qualifierIndex, qualifier in pairs( statement.qualifiers[qualifierName] ) do
 
if (qualifier.datavalue ~= nil
 
and qualifier.datavalue.type ~= nil
 
and qualifier.datavalue.value ~= nil) then
 
 
 
if ( qualifier.datavalue.type == "monolingualtext" ) then
 
return qualifier.datavalue.value.text;
 
 
end
 
end
if ( qualifier.datavalue.type == "string" ) then
+
table.insert( result[ qualifierName ], getValueFromSnak( qualifier ) )
return qualifier.datavalue.value;
 
end
 
if ( qualifier.datavalue.type == "wikibase-entityid" ) then
 
return qualifier.datavalue.value.id;
 
end
 
mw.log( 'Unknown qualifier type: ' .. qualifier.datavalue.type )
 
return qualifier.datavalue.value;
 
 
 
 
end
 
end
 
end
 
end
 +
end
  
end
+
return result
return nil;
 
 
end
 
end
  
function getQualifierValues( statement, qualifierName )
+
local function contains( tableStructure, value )
local result = {}
+
if not tableStructure or not value then
if (statement ~= nil
+
return true
and statement.qualifiers ~= nil
+
end
and statement.qualifiers[qualifierName] ~= nil) then
+
for index, line in pairs( tableStructure ) do
local qualifiers = statement.qualifiers[qualifierName];
+
if line == value then
for _, qualifier in pairs( qualifiers ) do
+
return true
if (qualifier.datavalue ~= nil
 
and qualifier.datavalue.type ~= nil
 
and qualifier.datavalue.value ~= nil) then
 
 
 
if ( qualifier.datavalue.type == "string" ) then
 
result[ #result + 1 ] = qualifier.datavalue.value;
 
elseif ( qualifier.datavalue.type == "wikibase-entityid" ) then
 
result[ #result + 1 ] = qualifier.datavalue.value.id;
 
else
 
mw.log( 'Unknown qualifier type: ' .. qualifier.datavalue.type );
 
result[ #result + 1 ] = qualifier.datavalue.value;
 
end
 
end
 
 
end
 
end
 
end
 
end
return result;
+
return false
 
end
 
end
  
function collectLinks( configuration, elementId )
+
local function filterByRank( resourceDatas )
 +
-- itemId, languages. rank = rank
  
local moduleLanguages
+
local hasPreffered = false
if ( mw.title.makeTitle( 'Module', 'Languages' ).exists
+
for index, resourceData in pairs( resourceDatas ) do
and mw.title.makeTitle( 'Module', 'Languages/data' ).exists
+
if resourceData.rank == 'preferred' then
and mw.title.makeTitle( 'Module', 'Wikidata/Language-codes' ).exists) then
+
hasPreffered = true
moduleLanguages = require('Module:Languages');
+
end
else
 
moduleLanguages = false;
 
 
end
 
end
  
--Create rows
+
if not hasPreffered then
local elements = {}
+
return resourceDatas
local data = {}
 
 
 
 
 
local item = mw.wikibase and mw.wikibase.getEntity( elementId )
 
if item == nil or item.claims == nil then
 
return elements
 
 
end
 
end
  
if ( item.claims['P553'] ~= nil ) then
+
local result = {}
local claim = item.claims['P553']
+
for index, resourceData in pairs( resourceDatas ) do
for _, statement in pairs( claim ) do
+
if resourceData.rank == 'preferred' or contains( resourceData.languages, preferredLanguage ) then
if (statement ~= nil) then
+
table.insert( result, resourceData )
-- profile ID
 
local rank = statement.rank or 'normal';
 
if ( rank ~= 'deprecated' ) then
 
local itemId = getQualifierSingleValue( statement, 'P554' );
 
if (itemId ~= nil) then
 
-- language
 
local languages = getQualifierValues( statement, 'P407' );
 
local resourceId = statement.mainsnak.datavalue.value.id;
 
if (data[resourceId] == nil) then
 
data[resourceId] = {};
 
end
 
table.insert( data[resourceId], { itemId = itemId, languages = languages, rank = rank} );
 
end
 
end
 
end
 
 
end
 
end
 
end
 
end
  
for _, params in pairs( configuration ) do
+
return result
local resourceId = params[2]
+
end
  
local claim = item.claims[ resourceId ]
+
local function getLinkData( statement, qualifier, project )
if ( claim ) then
+
local rank = statement.rank or 'normal'
for _, statement in pairs( claim ) do
+
if rank == 'deprecated' or not statement.mainsnak.datavalue then
local rank = statement.rank or 'normal';
+
return nil
if ( rank ~= 'deprecated' and statement.mainsnak.datavalue) then
 
local itemId = statement.mainsnak.datavalue.value;
 
local languages = getQualifierValues( statement, 'P407' );
 
if (data[resourceId] == nil) then
 
data[resourceId] = {};
 
end
 
table.insert( data[resourceId], { itemId = itemId, languages = languages, rank = rank} );
 
end
 
end
 
end
 
 
end
 
end
  
for resourceId, resourceDatas in pairs( data ) do
+
local itemId
data[resourceId] = filterByRank( resourceDatas );
+
if qualifier then
 +
itemId = getQualifierSingleValue( statement, qualifier )
 +
else
 +
itemId = statement.mainsnak.datavalue.value
 
end
 
end
  
local hasNonOptionalLinks = false
+
if itemId and project then
 +
itemId = mw.wikibase.getSitelink( itemId, project )
 +
end
  
for _, params in pairs( configuration ) do
+
if not itemId then
local resourceId = params[2]
+
return nil
local optional = params[5] or false;
+
end
  
local resourceDatas = data[resourceId];
+
local qualifiers = getQualifierValues( statement )
if resourceDatas ~= nil then
+
local languages = qualifiers[ 'P407' ]
if ( not optional ) then
+
if not languages then
hasNonOptionalLinks = true
+
languages = {}
end
+
end
  
local resourceLabel = renderLabel( params[1] );
+
local refs = {}
local firstChar = mw.ustring.sub( resourceLabel, 1, 1 );
+
if qualifiers[ 'P400' ] then
local separateDesign = firstChar == '[' or firstChar == '<';
+
for _, refQid in ipairs( qualifiers[ 'P400' ] ) do
 
+
local refLabel = refLabels
local html = '';
+
if refLabels[ refQid ] then
if ( separateDesign ) then
+
refLabel = refLabels[ refQid ]
html = html .. resourceLabel .. ':&nbsp;';
+
else
 +
refLabel = mw.wikibase.label( refQid ) or refQid
 
end
 
end
 
+
if refLabel ~= refQid then
local preitemId
+
if #qualifiers[ 'P400' ] > 3 then
for index, resourceData in pairs(resourceDatas) do
+
refLabel = refLabel .. ' +&nbsp;' .. ( #qualifiers[ 'P400' ] - 1 ) .. '&nbsp;платформ'
local itemId = resourceData.itemId;
+
table.insert( refs, refLabel )
if index == 2 then
+
break
--даёт возможность поставить id из одного свойства в разные ссылки
 
if itemId == preitemId then
 
break
 
end
 
 
end
 
end
  
local languages = resourceData.languages;
+
table.insert( refs, refLabel )
local link = params[3] ( itemId );
 
local linkFirstChar;
 
local interwiki;
 
if ( link ) then
 
linkFirstChar = mw.ustring.sub( link, 1, 1 );
 
interwiki = linkFirstChar == ':'
 
end
 
if ( separateDesign ) then
 
if ( index ~= 1 ) then
 
html = html .. ',&nbsp;'
 
end
 
if ( link ) then
 
if ( interwiki ) then
 
html = html .. '[[' .. link .. '|' .. itemId .. ']]';
 
else
 
html = html .. '[' .. link .. ' ' .. itemId .. ']';
 
end
 
else
 
html = html .. itemId;
 
end
 
else
 
if ( index ~= 1 ) then
 
html = html .. ' · '
 
end
 
if ( link ) then
 
if ( interwiki ) then
 
html = html .. '[[' .. link .. '|' .. resourceLabel .. ']]';
 
else
 
html = html .. '[' .. link .. ' ' .. resourceLabel .. ']';
 
end
 
else
 
-- it should not happen
 
html = html .. resourceLabel .. ':&nbsp;' .. itemId;
 
end
 
 
 
if ( moduleLanguages ) then
 
if ( languages ~= nil and #languages > 0 ) then
 
for langIndex, language in pairs(languages) do
 
html = html .. '&nbsp;' .. moduleLanguages.getRefHtml( language )
 
end
 
end
 
end
 
end
 
preitemId = resourceData.itemId;
 
 
end
 
end
if ( #params >= 4 and params[4] ) then
 
html = html .. '[[Category:' .. params[4] .. ']]'
 
end
 
table.insert( elements, html )
 
 
end
 
end
 
end
 
end
  
if ( not hasNonOptionalLinks ) then
+
return {
return {}
+
itemId = itemId,
end
+
qualifiers = qualifiers,
 
+
languages = languages,
return elements
+
refs = refs,
 +
rank = rank,
 +
}
 
end
 
end
  
function collectDictionaryLinks( elementId )
+
local function collectLinks( configuration, entityId, separateLabel )
--Create rows
+
-- Create rows
 
local elements = {}
 
local elements = {}
 +
local data = {}
  
local item = mw.wikibase and mw.wikibase.getEntity( elementId );
+
for _, params in pairs( configuration ) do
if ( item == nil or item.claims == nil) then
+
local resourceId = params
return elements
+
local propertyId = resourceId[ 2 ]
end
+
local qid
 
+
if string.match( propertyId, '^P%d+:Q%d+$' ) then
local sourceToElementLinks = {};
+
local parts = mw.text.split( propertyId, ':', true )
 
+
propertyId = parts[ 1 ]
local claim = item.claims['P1343']
+
qid = parts[ 2 ]
if ( claim ) then
+
end
for _, statement in pairs( claim ) do
 
if (statement ~= nil) then
 
local rank = statement.rank or 'normal';
 
if ( rank ~= 'deprecated' ) then
 
local resourceId = statement.mainsnak.datavalue.value.id;
 
local languages = getQualifierValues( statement, 'P407' );
 
  
-- Wikisource link ?
+
local claims = mw.wikibase.getBestStatements( entityId, propertyId ) or {}
local entityId = getQualifierSingleValue( statement, 'P805' ) or getQualifierSingleValue( statement, 'P248' );
+
for _, statement in pairs( claims ) do
if ( entityId ) then
+
local linkData
if (sourceToElementLinks[resourceId] == nil) then
+
if not qid then
sourceToElementLinks[resourceId] = {};
+
linkData = getLinkData( statement )
end
+
elseif getValueFromSnak( statement.mainsnak ) == qid then
table.insert( sourceToElementLinks[resourceId], { entityId = entityId, languages = languages, rank = rank } );
+
for qualifierId, qualifierType in pairs( propertyQualifiers[ propertyId ] ) do
 +
local project = nil
 +
if qualifierType == 'iw' then
 +
project = params.project
 
end
 
end
 
+
linkData = getLinkData( statement, qualifierId, project )
-- URL to encyclopedia
+
if linkData then
local url = getQualifierSingleValue( statement, 'P953' );
+
break
if (url == nil) then
 
-- no longer recommend, but widely used
 
url = getQualifierSingleValue( statement, 'P854' );
 
end
 
if ( url ~= nil ) then
 
if (sourceToElementLinks[resourceId] == nil) then
 
sourceToElementLinks[resourceId] = {};
 
end
 
table.insert( sourceToElementLinks[resourceId], { url = url, languages = languages, rank = rank } );
 
 
end
 
end
 
end
 
end
 
end
 
end
end
 
end
 
  
for _, description in pairs( dictionaries ) do
+
if linkData then
if ( description.linkF ) then
+
if not data[ resourceId ] then
local claim = item.claims[ description.id ];
+
data[ resourceId ] = {}
if ( claim ) then
 
for _, statement in pairs( claim ) do
 
local rank = statement.rank or 'normal';
 
if ( rank ~= 'deprecated' and statement.mainsnak.datavalue) then
 
local value = statement.mainsnak.datavalue.value;
 
local url = description.linkF( value );
 
local languages = getQualifierValues( statement, 'P407' );
 
if ( sourceToElementLinks[description.id] == nil) then
 
sourceToElementLinks[description.id] = {};
 
end
 
table.insert( sourceToElementLinks[description.id], { url = url, languages = languages, rank = rank} );
 
end
 
 
end
 
end
 +
table.insert( data[ resourceId ], linkData )
 
end
 
end
 
end
 
end
 
end
 
end
  
local html = '';
+
for resourceId, resourceDatas in pairs( data ) do
for _, description in pairs( dictionaries ) do
+
data[ resourceId ] = filterByRank( resourceDatas )
local links = sourceToElementLinks[ description.id ];
+
end
if ( links ) then
 
for _, link in pairs( links ) do
 
if ( link.url ) then
 
table.insert( elements, '[' .. link.url .. ' ' .. description.title .. ']' );
 
end
 
  
if ( link.entityId ) then
+
for _, params in pairs( configuration ) do
local sitelink = mw.wikibase and mw.wikibase.getSitelink( link.entityId, description.project );
+
local resourceId = params
if ( sitelink ) then
+
local resourceDatas = data[ resourceId ]
table.insert( elements, '[[' ..  description.projectCode .. sitelink .. '|' .. description.title .. ']]' );
+
if resourceDatas ~= nil then
end
+
local links = {}
 +
for _, resourceData in pairs( resourceDatas ) do
 +
if separateLabel then
 +
-- Label: ID1, ID2
 +
table.insert( links, renderLinkWithRef( resourceData, '', params[ 3 ], true ) )
 +
else
 +
-- Label · Label
 +
local label = params[ 1 ]
 +
table.insert( elements, renderLinkWithRef( resourceData, label, params[ 3 ] ) )
 
end
 
end
 +
end
 +
if separateLabel and #links then
 +
local result = renderLabel( params[ 1 ] ) .. ': ' .. table.concat( links, ', ' )
 +
table.insert( elements, result )
 
end
 
end
 
end
 
end
Строка 1087: Строка 394:
 
end
 
end
  
function contains( tableStructure, value )
 
if ( tableStructure == nil or value == nil) then
 
return true;
 
end
 
for index, line in pairs( tableStructure ) do
 
if ( line == value ) then
 
return true;
 
end
 
end
 
return false;
 
end
 
 
function filterByRank( resourceDatas )
 
-- itemId, languages. rank = rank
 
 
local hasPreffered = false;
 
for index, resourceData in pairs(resourceDatas) do
 
if ( resourceData.rank == 'preferred' ) then
 
hasPreffered = true;
 
end
 
end
 
 
if (not hasPreffered) then
 
return resourceDatas;
 
end
 
 
local result = {};
 
for index, resourceData in pairs(resourceDatas) do
 
if ( resourceData.rank == 'preferred' or contains(resourceData.languages, preferredLanguage) ) then
 
table.insert(result, resourceData);
 
end
 
end
 
 
return result;
 
end
 
  
 
function p.render( frame )
 
function p.render( frame )
local colorArg = '';
+
local colorArg = ''
local elementId = nil;
+
local byTemplate
if ( frame ~= nil ) then
+
local entityId = nil
 +
if frame ~= nil then
 
local parentArgs = frame:getParent().args
 
local parentArgs = frame:getParent().args
colorArg = parentArgs[templateColorName] or parentArgs['color'] or parentArgs[1] or '';
+
colorArg = parentArgs[ templateColorName ] or parentArgs[ 'color' ] or parentArgs[ 1 ] or ''
if parentArgs['d'] and parentArgs['d'] ~= '' then
+
if parentArgs[ 'from' ] and parentArgs[ 'from' ] ~= '' then
elementId = string.upper( parentArgs['d'] );
+
entityId = string.upper( parentArgs[ 'from' ] )
 +
elseif parentArgs[ 'd' ] and parentArgs[ 'd' ] ~= '' then
 +
entityId = string.upper( parentArgs[ 'd' ] )
 +
else
 +
entityId = mw.wikibase.getEntityIdForCurrentPage()
 
end
 
end
if ( colorArg ~= '' ) then
+
if colorArg ~= '' then
local firstChar = mw.ustring.sub( colorArg, 1, 1 );
+
local firstChar = mw.ustring.sub( colorArg, 1, 1 )
if ( firstChar ~= '#' ) then
+
if firstChar ~= '#' then
local byTemplate = colorByTitle( frame, colorArg );
+
byTemplate = colorByTitle( frame, colorArg )
if ( byTemplate ) then
+
if byTemplate then
colorArg = byTemplate;
+
colorArg = byTemplate
 
end
 
end
 
end
 
end
 
end
 
end
 +
end
 +
 +
if not entityId then
 +
return ''
 
end
 
end
  
 
local navboxData = {
 
local navboxData = {
 
name  = 'External links',
 
name  = 'External links',
navboxclass = 'navbox ruwikiArticleExternalLinksTable',
+
title = templateTitle,
 +
titlestyle = 'display:none',
 +
state = 'plain',
 
bodyclass = 'hlist',
 
bodyclass = 'hlist',
};
+
bodystyle = 'padding-top:1px',
 +
}
 
if colorArg and colorArg ~= '' then
 
if colorArg and colorArg ~= '' then
navboxData.groupstyle = 'background: ' .. colorArg .. ';';
+
navboxData.groupstyle = 'background: ' .. colorArg .. ';'
 
end
 
end
  
local rowIndex = 1;
+
local rowIndex = 1
  
local socialNetworksElements = collectLinks( socialNetworkProperties, elementId );
+
for _, groupData in pairs( data ) do
if ( #socialNetworksElements > 0 ) then
+
local isAuthorityControl = groupData.isAuthorityControl
navboxData['group' .. rowIndex] = group1Label;
+
local groupLabel = groupData.label
navboxData['list' .. rowIndex] = table.concat( socialNetworksElements , ' · ' );
+
local groupList = groupData.list
rowIndex = rowIndex + 1;
+
local groupElements = collectLinks( groupList, entityId, isAuthorityControl )
end
+
if #groupElements > 0 then
 +
navboxData[ 'group' .. rowIndex ] = groupLabel
 +
navboxData[ 'list' .. rowIndex ] = renderList( groupElements )
  
local textsElements = collectLinks( textsProperties, elementId );
+
if isAuthorityControl then
if ( #textsElements > 0 ) then
+
if #groupElements > 5 then
navboxData['group' .. rowIndex] = group2Label;
+
navboxData[ 'group' .. rowIndex ] = nil
navboxData['list' .. rowIndex] = table.concat( textsElements , ' · ' );
+
local templateStyles = frame:extensionTag{
rowIndex = rowIndex + 1;
+
name = 'templatestyles',
end
+
args = { src = 'Шаблон:Навигационная таблица/styles.css' }
 +
}
 +
local collapsibleNavbox = moduleNavbox._navbox( {
 +
title = groupLabel,
 +
list1 = navboxData['list' .. rowIndex],
 +
border = 'subgroup',
 +
navbar = 'plain',
 +
state = 'collapsed',
 +
titleclass = 'ts-navbox-plaintitle',
 +
bodyclass = 'authoritycontrol',
 +
titlestyle = navboxData.groupstyle,
 +
bodystyle = 'text-align: left;',
 +
} )
 +
navboxData[ 'list' .. rowIndex ] = templateStyles .. collapsibleNavbox
 +
end
 +
end
  
local contentHostingElements = collectLinks( contentHostingProperties, elementId );
+
rowIndex = rowIndex + 1
if ( #contentHostingElements > 0 ) then
+
end
navboxData['group' .. rowIndex] = group3Label;
 
navboxData['list' .. rowIndex] = table.concat( contentHostingElements , ' · ' );
 
rowIndex = rowIndex + 1;
 
 
end
 
end
  
local themeProfilesElements = collectLinks( themeProfilesProperties, elementId );
+
if rowIndex == 1 then
if ( #themeProfilesElements > 0 ) then
+
if mw.title.getCurrentTitle().namespace == 0 then
navboxData['group' .. rowIndex] = group4Label;
+
return '[[Category:' .. categoryTemplateEmpty .. ']]'
navboxData['list' .. rowIndex] = table.concat( themeProfilesElements , ' · ' );
+
end
rowIndex = rowIndex + 1;
+
return ''
 
end
 
end
  
local dictionaryElements = collectDictionaryLinks();
+
local buttons = moduleNavbar({ templatelink }) ..
if ( #dictionaryElements > 0 ) then
+
'&nbsp;[[File:OOjs UI icon edit-ltr-progressive.svg|14px|' .. wikidataLink .. '|link=d:'
navboxData['group' .. rowIndex] = group5Label;
+
.. (entityId or mw.wikibase.getEntityIdForCurrentPage()) .. '#identifiers]]'
navboxData['list' .. rowIndex] = table.concat( dictionaryElements , ' · ' );
+
if navboxData[ 'group1' ] then
rowIndex = rowIndex + 1;
+
navboxData[ 'group1' ] = '<div style="padding: 0 35px 0 0; width: 100%;">' ..
end
+
'<div class="skin-invert-image" style="float: left;">' .. buttons .. '</div>&nbsp;&nbsp;' ..
 +
navboxData[ 'group1' ] .. '</div>'
  
local geniElements = collectLinks( geniGraves, elementId );
 
if ( #geniElements > 0 ) then
 
navboxData['group' .. rowIndex] = group6Label;
 
navboxData['list' .. rowIndex] = table.concat( geniElements , ' · ' );
 
rowIndex = rowIndex + 1;
 
end
 
 
local authorityControlElements = collectLinks( authorityControl, elementId );
 
local authorityControlExtElements = collectLinks( authorityControlExt, elementId );
 
if ( #authorityControlElements > 0 ) then
 
navboxData['group' .. rowIndex] = group7Label;
 
if ( #authorityControlExtElements > 0 ) then
 
navboxData['list' .. rowIndex] = table.concat( authorityControlElements , ' · ' ) .. ' · ' .. table.concat( authorityControlExtElements , ' · ' );
 
else
 
navboxData['list' .. rowIndex] = table.concat( authorityControlElements , ' · ' );
 
end
 
rowIndex = rowIndex + 1;
 
end
 
 
if ( rowIndex == 1 ) then
 
if ( mw.title.getCurrentTitle().namespace == 0 ) then
 
return '[[Category:' .. categoryTemplateEmpty .. ']]';
 
end
 
 
else
 
else
navboxData['group1'] = '<div style="padding: 0px 18px 0px 0px; width: 100%;"><div style="float: left;">' ..
+
navboxData[ 'group1' ] = '<div style="padding: 0; width: 100%;">' .. buttons .. '</div>'
frame:expandTemplate{ title = 'tnavbar-view', args = { templateLink } } ..
 
'</div>&nbsp;&nbsp;' .. navboxData['group1'] .. '</div>';
 
 
end
 
end
  
local navbox = moduleNavbox._navbox( navboxData )
+
local out = moduleNavbox._navbox( navboxData )
return navbox
 
end
 
 
 
function p.renderDocumentation()
 
local cols = tostring (5)
 
local result = ''
 
result = result .. '|-\n';
 
result = result .. '! colspan=' .. cols .. ' | ' .. group1Label .. '\n';
 
result = result .. '|-\n';
 
result = result .. renderDocumentationCategory( socialNetworkProperties );
 
result = result .. '|-\n';
 
result = result .. '! colspan=' .. cols .. ' | ' .. group2Label .. '\n';
 
result = result .. '|-\n';
 
result = result .. renderDocumentationCategory( textsProperties );
 
result = result .. '|-\n';
 
result = result .. '! colspan=' .. cols .. ' | ' .. group3Label .. '\n';
 
result = result .. '|-\n';
 
result = result .. renderDocumentationCategory( contentHostingProperties );
 
result = result .. '|-\n';
 
result = result .. '! colspan=' .. cols .. ' | ' .. group4Label .. '\n';
 
result = result .. '|-\n';
 
result = result .. renderDocumentationCategory( themeProfilesProperties );
 
result = result .. '|-\n';
 
result = result .. '! colspan=' .. cols .. ' | ' .. group5Label .. '\n';
 
result = result .. '|-\n';
 
result = result .. renderDocumentationCategory( dictionaries );
 
result = result .. '|-\n';
 
result = result .. '! colspan=' .. cols .. ' | ' .. group6Label .. '\n';
 
result = result .. '|-\n';
 
result = result .. renderDocumentationCategory( geniGraves );
 
result = result .. '|-\n';
 
result = result .. '! colspan=' .. cols .. ' | ' .. group7Label .. '\n';
 
result = result .. '|-\n';
 
result = result .. renderDocumentationCategory( authorityControl );
 
return result;
 
end
 
 
 
function renderDocumentationCategory( links )
 
local result = '';
 
 
 
for _, params in pairs( links ) do
 
-- mw.log (mw.dumpObject (links))
 
local resourceLabel = renderLabel( params[ 1 ] or params.title );
 
local resourceId = params[ 2 ] or params.id;
 
local link = type (params.linkF) == 'function' and params.linkF '$id'
 
  or type (params [3]) == 'function' and params [3] '$id'
 
  or tostring (params [3])
 
local category = params[ 4 ];
 
local optional;
 
if ( params[ 5 ] or false ) then
 
optional = 'TRUE';
 
else
 
optional = 'FALSE';
 
end
 
 
 
result = result .. '| ' .. resourceLabel .. '\n';
+
if colorArg and colorArg ~= '' and not byTemplate then
if string.match( resourceId, '^P' ) then
+
out = out .. '[[Category:' .. categoryCustomColor .. ']]'
result = result .. '| [[:d:Property:' .. resourceId .. '|' .. resourceId .. ']]\n';
 
elseif string.match( resourceId, '^Q' ) then
 
result = result .. '| [[:d:' .. resourceId .. '' .. '|' .. resourceId .. ']]\n';
 
else
 
result = result .. '| &nbsp; \n';
 
end
 
 
 
if ( category ~= nil and category ~= false ) then
 
result = result .. '| [[:Category:' .. category .. '|' .. category .. ']]\n';
 
else
 
result = result .. '| &nbsp; \n';
 
end
 
result = result .. '| ' .. optional .. '\n';
 
 
result = result .. '| <code>' .. link .. '</code>' .. '\n'
 
 
result = result .. '|-\n';
 
 
end
 
end
 
+
return result;
+
return out
 
end
 
end
  
 
return p
 
return p

Версия от 22:51, 8 июля 2024

Для документации этого модуля может быть создана страница Модуль:External links/doc

local data = require( 'Module:External links/data' )
-- Localizable part
-- Please note that labels for websites and catalogs are taken from Wikidata (i.e. Wikidata label)

-- Feel free to correct labels and categories here
local categoryTemplateEmpty = 'Википедия:Шаблон «Внешние ссылки» пуст'
local categoryCustomColor = 'Википедия:Шаблон «Внешние ссылки» с нестандартным цветом'
local templateLink = 'Внешние ссылки'
local templateTitle = 'Ссылки на внешние ресурсы'
local wikidataLink = 'Перейти к элементу Викиданных'
local refLabels = {
	Q1406 = 'Windows',
	Q10677 = 'PS1',
	Q10680 = 'PS2',
	Q10683 = 'PS3',
	Q16338 = 'ПК',
	Q135321 = 'FDS',
	Q170323 = 'DS',
	Q170325 = 'PSP',
	Q172742 = 'NES',
	Q182172 = 'GameCube',
	Q183259 = 'SNES',
	Q184839 = 'N64',
	Q188642 = 'GBA',
	Q188808 = 'PS Vita',
	Q203597 = '3DS',
	Q5014725 = 'PS4',
	Q19610114 = 'Switch',
	Q63184502 = 'PS5',
}

-- The language codes that should always be displayed even if they have normal rank and a claim with another language and preferred rank exists
local preferredLanguage = 'Q7737' -- russian

local templateColorName = 'цвет'
-- Some projects have "named" colors, defined by templates
local function colorByTitle( frame, colorTitle )
	local templateName = 'Цвет/' .. colorTitle
	local templateTitle = mw.title.makeTitle( 'Template', templateName )
	if not templateTitle or not templateTitle.exists then
		return false
	end
	return frame:expandTemplate{ title = templateName }
end

-- Non-localizable part (not need to localize )
local moduleNavbox = require( 'Module:Navbox' )
local moduleNavbar = require( 'Module:Navbar' )._ruwikiNavbar -- can be changed to a regular one
local moduleLanguages -- accessed if necessary

local propertyQualifiers = {
	P553 = {
		P554 = 'url',
	},
	P1343 = {
		P805 = 'iw',
		P248 = 'iw', -- deprecated, fallback for P805
		P953 = 'url',
		P854 = 'url', -- deprecated, fallback for P953
	},
}

local p = {}

-- Helper functions
local function replace( str, pattern, repl )
    pattern = mw.ustring.gsub( pattern, "[%(%)%.%+%-%*%?%[%]%^%$%%]", "%%%1" ) -- escape pattern
    repl = mw.ustring.gsub( repl, "[%%]", "%%%%" ) -- escape replacement
    repl = mw.ustring.gsub( repl, " ", "%%%%20" ) -- escape replacement
    return mw.ustring.gsub( str, pattern, repl )
end


-- Render functions
local function renderList( elements )
	if #elements == 0 then
		return ''
	end

	return '*' .. table.concat( elements , '\n*' ) .. '\n'
end

local function renderLabel( params )
	if type( params ) == 'string' then
		return params
	end

	local qid = params[ 1 ]
	local default = params[ 2 ]

	if #params >= 3 then
		local label = params[ 3 ]
		local link = mw.wikibase.sitelink( qid )
		if link then
			return '[[' .. link .. '|' .. label .. ']]'
		end
		local title = mw.wikibase.label( qid ) or default
		if title ~= label then
			return '<span title="' .. title .. '" style="border-bottom: 1px dotted; cursor: help;">' .. label .. '</span>'
		end
	end

	return mw.wikibase.label( qid ) or default
end

local function renderLink( resourceData, label, formatter, idAsLabel )
	if resourceData.itemId == nil then
		return '<span class="error">' .. label .. ': Не удаётся определить элемент</span>[[Категория:Статьи с ошибкой в шаблоне Внешние ссылки]]'
	end

	local link
	if not formatter then
		link = resourceData.itemId
		idAsLabel = false
	elseif type( formatter ) == 'string' then
		link = replace( formatter, '$1', resourceData.itemId )
	elseif type ( formatter ) == 'function' then
		link = formatter( resourceData.itemId, resourceData.qualifiers )
	end
	
	-- "Label: ID" without link
	if not link or link == '' then
		return renderLabel( label ) .. ':&nbsp;' .. resourceData.itemId
	end

	local resourceLabel = resourceData.itemId
	if not idAsLabel then
		resourceLabel = renderLabel( label )
	end

	local linkFirstChar = mw.ustring.sub( link, 1, 1 )
	if linkFirstChar == ':' then
		return '[[' .. link .. '|' .. resourceLabel .. ']]'
	end

	return '[' .. link .. ' ' .. resourceLabel .. ']'
end

local function renderLangRef( languages )
	local result = ''
	if languages and #languages > 0 then
		if moduleLanguages ~= false then -- not false, but maybe nil
			if mw.title.makeTitle( 'Module', 'Languages' ).exists
					and mw.title.makeTitle( 'Module', 'Languages/data' ).exists
					and mw.title.makeTitle( 'Module', 'Wikidata/Language-codes' ).exists then
				moduleLanguages = require( 'Module:Languages' )
			else
				moduleLanguages = false
			end
		end
		
		if moduleLanguages then
			for langIndex, language in pairs( languages ) do
				result = result .. '&nbsp;' .. moduleLanguages.getWikidataRefHtml( language )
			end
		end
	end

	return result
end

local function renderRefs( refs )
	local result = ''
	if refs and #refs > 0 then
		for _, ref in ipairs( refs ) do
			result = result .. '&nbsp;<span class="ref-info">(' .. ref .. ')</span>'
		end
	end

	return result
end

local function renderLinkWithRef( resourceData, label, formatter, idAsLabel )
	local link = renderLink( resourceData, label, formatter, idAsLabel )
	if link ~= '' then
		link = link .. renderLangRef( resourceData.languages )
		link = link .. renderRefs( resourceData.refs )
	end
	return link
end


-- Data fetching functions
local function getValueFromSnak( snak )
	if not snak.datavalue then 
		return 
	end
	if snak.datavalue.type == 'wikibase-entityid' then
		return snak.datavalue.value.id
	end
	if snak.datavalue.type == 'monolingualtext' then
		return snak.datavalue.value.text
	end
	return snak.datavalue.value
end

local function getQualifierSingleValue( statement, qualifierName )
	if not statement or not statement.qualifiers or not statement.qualifiers[ qualifierName ] then
		return nil
	end

	for qualifierIndex, qualifier in pairs( statement.qualifiers[ qualifierName ] ) do
		if qualifier.datavalue and qualifier.datavalue.type and qualifier.datavalue.value then
			return getValueFromSnak( qualifier )
		end
	end

	return nil
end

local function getQualifierValues( statement )
	if not statement or not statement.qualifiers then
		return {}
	end

	local result = {}

	for qualifierName, qualifiers in pairs( statement.qualifiers ) do
		for _, qualifier in pairs( qualifiers ) do
			if qualifier.datavalue and qualifier.datavalue.type and qualifier.datavalue.value then
				if not result[ qualifierName ] then
					result[ qualifierName ] = {}
				end
				table.insert( result[ qualifierName ], getValueFromSnak( qualifier ) )
			end
		end
	end

	return result
end

local function contains( tableStructure, value )
	if not tableStructure or not value then
		return true
	end
	for index, line in pairs( tableStructure ) do
		if line == value then
			return true
		end
	end
	return false
end

local function filterByRank( resourceDatas )
	-- itemId, languages. rank = rank

	local hasPreffered = false
	for index, resourceData in pairs( resourceDatas ) do
		if resourceData.rank == 'preferred' then
			hasPreffered = true
		end
	end

	if not hasPreffered then
		return resourceDatas
	end

	local result = {}
	for index, resourceData in pairs( resourceDatas ) do
		if resourceData.rank == 'preferred' or contains( resourceData.languages, preferredLanguage ) then
			table.insert( result, resourceData )
		end
	end

	return result
end

local function getLinkData( statement, qualifier, project )
	local rank = statement.rank or 'normal'
	if rank == 'deprecated' or not statement.mainsnak.datavalue then
		return nil
	end

	local itemId
	if qualifier then
		itemId = getQualifierSingleValue( statement, qualifier )
	else
		itemId = statement.mainsnak.datavalue.value
	end

	if itemId and project then
		itemId = mw.wikibase.getSitelink( itemId, project )
	end

	if not itemId then
		return nil
	end

	local qualifiers = getQualifierValues( statement )
	local languages = qualifiers[ 'P407' ]
	if not languages then
		languages = {}
	end

	local refs = {}
	if qualifiers[ 'P400' ] then
		for _, refQid in ipairs( qualifiers[ 'P400' ] ) do
			local refLabel = refLabels
			if refLabels[ refQid ] then
				refLabel = refLabels[ refQid ]
			else
				refLabel = mw.wikibase.label( refQid ) or refQid
			end
			if refLabel ~= refQid then
				if #qualifiers[ 'P400' ] > 3 then
					refLabel = refLabel .. ' +&nbsp;' .. ( #qualifiers[ 'P400' ] - 1 ) .. '&nbsp;платформ'
					table.insert( refs, refLabel )
					break
				end

				table.insert( refs, refLabel )
			end
		end
	end

	return {
		itemId = itemId,
		qualifiers = qualifiers,
		languages = languages,
		refs = refs,
		rank = rank,
	}
end

local function collectLinks( configuration, entityId, separateLabel )
	-- Create rows
	local elements = {}
	local data = {}

	for _, params in pairs( configuration ) do
		local resourceId = params
		local propertyId = resourceId[ 2 ]
		local qid
		if string.match( propertyId, '^P%d+:Q%d+$' ) then
			local parts = mw.text.split( propertyId, ':', true )
			propertyId = parts[ 1 ]
			qid = parts[ 2 ]
		end

		local claims = mw.wikibase.getBestStatements( entityId, propertyId ) or {}
		for _, statement in pairs( claims ) do
			local linkData
			if not qid then
				linkData = getLinkData( statement )
			elseif getValueFromSnak( statement.mainsnak ) == qid then
				for qualifierId, qualifierType in pairs( propertyQualifiers[ propertyId ] ) do
					local project = nil
					if qualifierType == 'iw' then
						project = params.project
					end
					linkData = getLinkData( statement, qualifierId, project )
					if linkData then
						break
					end
				end
			end

			if linkData then
				if not data[ resourceId ] then
					data[ resourceId ] = {}
				end
				table.insert( data[ resourceId ], linkData )
			end
		end
	end

	for resourceId, resourceDatas in pairs( data ) do
		data[ resourceId ] = filterByRank( resourceDatas )
	end

	for _, params in pairs( configuration ) do
		local resourceId = params
		local resourceDatas = data[ resourceId ]
		if resourceDatas ~= nil then
			local links = {}
			for _, resourceData in pairs( resourceDatas ) do
				if separateLabel then
					-- Label: ID1, ID2
					table.insert( links, renderLinkWithRef( resourceData, '', params[ 3 ], true ) )
				else
					-- Label · Label
					local label = params[ 1 ]
					table.insert( elements, renderLinkWithRef( resourceData, label, params[ 3 ] ) )
				end
			end
			if separateLabel and #links then
				local result = renderLabel( params[ 1 ] ) .. ': ' .. table.concat( links, ', ' )
				table.insert( elements, result )
			end
		end
	end

	return elements
end


function p.render( frame )
	local colorArg = ''
	local byTemplate
	local entityId = nil
	if frame ~= nil then
		local parentArgs = frame:getParent().args
		colorArg = parentArgs[ templateColorName ] or parentArgs[ 'color' ] or parentArgs[ 1 ] or ''
		if parentArgs[ 'from' ] and parentArgs[ 'from' ] ~= '' then
			entityId = string.upper( parentArgs[ 'from' ] )
		elseif parentArgs[ 'd' ] and parentArgs[ 'd' ] ~= '' then
			entityId = string.upper( parentArgs[ 'd' ] )
		else
			entityId = mw.wikibase.getEntityIdForCurrentPage()
		end
		if colorArg ~= '' then
			local firstChar = mw.ustring.sub( colorArg, 1, 1 )
			if firstChar ~= '#' then
				byTemplate = colorByTitle( frame, colorArg )
				if byTemplate then
					colorArg = byTemplate
				end
			end
		end
	end
	
	if not entityId then
		return ''
	end

	local navboxData = {
		name  = 'External links',
		title = templateTitle,
		titlestyle = 'display:none',
		state = 'plain',
		bodyclass = 'hlist',
		bodystyle = 'padding-top:1px',
	}
	if colorArg and colorArg ~= '' then
		navboxData.groupstyle = 'background: ' .. colorArg .. ';'
	end

	local rowIndex = 1

	for _, groupData in pairs( data ) do
		local isAuthorityControl = groupData.isAuthorityControl
		local groupLabel = groupData.label
		local groupList = groupData.list
		local groupElements = collectLinks( groupList, entityId, isAuthorityControl )
		if #groupElements > 0 then
			navboxData[ 'group' .. rowIndex ] = groupLabel
			navboxData[ 'list' .. rowIndex ] = renderList( groupElements )

			if isAuthorityControl then
				if #groupElements > 5 then
					navboxData[ 'group' .. rowIndex ] = nil
					local templateStyles = frame:extensionTag{
						name = 'templatestyles',
						args = { src = 'Шаблон:Навигационная таблица/styles.css' }
					}
					local collapsibleNavbox = moduleNavbox._navbox( {
						title = groupLabel,
						list1 = navboxData['list' .. rowIndex],
						border = 'subgroup',
						navbar = 'plain',
						state = 'collapsed',
						titleclass = 'ts-navbox-plaintitle',
						bodyclass = 'authoritycontrol',
						titlestyle = navboxData.groupstyle,
						bodystyle = 'text-align: left;',
					} )
					navboxData[ 'list' .. rowIndex ] = templateStyles .. collapsibleNavbox
				end
			end

			rowIndex = rowIndex + 1
		end
	end

	if rowIndex == 1 then
		if mw.title.getCurrentTitle().namespace == 0 then
			return '[[Category:' .. categoryTemplateEmpty .. ']]'
		end
		return ''
	end

	local buttons = moduleNavbar({ templatelink }) ..
		'&nbsp;[[File:OOjs UI icon edit-ltr-progressive.svg|14px|' .. wikidataLink .. '|link=d:'
		.. (entityId or mw.wikibase.getEntityIdForCurrentPage()) .. '#identifiers]]'
	if navboxData[ 'group1' ] then
		navboxData[ 'group1' ] = '<div style="padding: 0 35px 0 0; width: 100%;">' ..
			'<div class="skin-invert-image" style="float: left;">' .. buttons .. '</div>&nbsp;&nbsp;' ..
			navboxData[ 'group1' ] .. '</div>'

	else
		navboxData[ 'group1' ] = '<div style="padding: 0; width: 100%;">' .. buttons .. '</div>'
	end

	local out = moduleNavbox._navbox( navboxData )
	
	if colorArg and colorArg ~= '' and not byTemplate then
		out = out .. '[[Category:' .. categoryCustomColor .. ']]'
	end
	
	return out
end

return p