Модул:Sources — разлика између измена

Iz Vojne Enciklopedije
Пређи на навигацију Пређи на претрагу
(Ажуриран модул. Ускоро ће почети да се користи...)
 
м (1 измена увезена)
Ред 1: Ред 1:
local p = {}
local p = {};
 
local langs = require('Module:Languages')
local utils = require('Module:Sources/utils')
 
local i18nDefaultLanguage = 'sr'
 
local i18nEtAlDefault = ' et al.'
local i18nEtAl = {
bs     = ' i dr.',
hr     = ' i dr.',
de     = ' et al.',
en     = ' et al.',
ru     = ' и др.',
sr     = ' и др.',
}


local i18nDefaultLanguage = 'ru';
local i18nEditors = {
local i18nEditors = {
bs     = 'ured.',
fr = '',
hr     = 'ured.',
de = 'Hrsg.: ',
de     = 'Hrsg.: ',
es = '',
en     = 'ed.',
en = '',
ru     = 'под ред. ',
it = '',
sr     = 'уред.',
ru = 'под ред. ',
}
}
 
local i18nEtAlDefault = ' et al.';
local i18nTranslators = {
local i18nEtAl = {
bs     = 'prev.',
ru = ' и др.',
hr     = 'prev.',
de     = 'Ü.: ',
en     = 'tr. ',
ru     = 'пер. ',
sr     = 'прев. ',
}
}
local i18nVolume = {
local i18nVolume = {
bs     = 'tom.',
fr = 'Vol.',
hr     = 'tom.',
es = 'Vol.',
de     = 'Jg.: ',
en = 'Vol.',
en     = 'vol. ',
it = 'Vol.',
ru     = 'Т. ',
ru = 'Т.',
sr     = 'том. ',
}
}
local i18nIssue = {
local i18nIssue = {
bs     = 'ed.',
en = 'Iss.',
hr     = 'ed.',
ru = 'вып.',
de     = 'H.: ',
en     = 'Iss. ',
ru     = 'вып. ',
sr     = 'изд. ',
}
}
local i18nPages = {
local i18nPages = {
bs     = 'str.',
fr = 'P.',
hr     = 'str.',
de = 'S.',
de     = 'S.: ',
es = 'P.',
en     = 'P. ',
en = 'P.',
ru     = 'С. ',
it = 'P.',
sr     = 'стр. ',
ru = 'С.',
}
}


local i18nNumberOfPages = {
local i18nNumberOfPages = {
bs     = 'br. str.',
en = 'p.',
hr     = 'br. str.',
ru = 'с.',
de     = 'u.S.: ',
en     = 'p. ',
ru     = 'с. ',
sr     = 'бр. стр. ',
}
}


local i18nTirage = {
local NORMATIVE_DOCUMENTS = {
bs     = 'tiraž: %d',
Q20754888 = 'Закон Российской Федерации',
hr     = 'tiraž: %d',
Q20754884 = 'Закон РСФСР',
de     = 'K.: %d',
Q20873831 = 'Распоряжение Президента Российской Федерации',
en     = 'ed. size: %d',
Q20873834 = 'Указ исполняющего обязанности Президента Российской Федерации',
ru     = '%d экз.',
Q2061228 = 'Указ Президента Российской Федерации',
sr  = 'тираж: %d',
}
}


local monthg = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', "сентября", "октября", "ноября", "декабря"};


local monthg = {'јануара', 'фебруара', 'марта', 'априла', 'маја', 'јуна', 'јула', 'августа', "септембра", "октобра", "новембра", "децембра"}
local PREFIX_CITEREF = "CITEREF_";


local PREFIX_CITEREF = "CITEREF_";
local options_arxiv = { separator = '; ', conjunction = '; ', format = function( id ) return '[http://arxiv.org/abs/' .. id .. ' arXiv:' .. id .. ']' end, nolinks = true, preferids = false };
local options_doi = { separator = '; ', conjunction = '; ', format = function( doi ) return '[http://dx.doi.org/' .. doi .. ' doi:' .. doi .. ']' end, nolinks = true, preferids = false };
 
local options_commas = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false };
local options_commas_short = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false, short = true };
local options_commas_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = true, preferids = false };
local options_commas_it = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false };
local options_commas_it_short = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false, short = true };
local options_commas_it_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = true , preferids = false };
local options_citetypes = { separator = ' ', conjunction = ' ', format = function( src ) return 'citetype_' .. src end, nolinks = true , preferids = true };
 
function assertNotNull( argName, arg )
if ( (not arg) or (arg == nil) ) then
error( argName .. ' is not specified' )
end
end
 
function coalesce( arg1, arg2, arg3, arg4 )
if ( not isEmpty( arg1 ) ) then return arg1 end
if ( not isEmpty( arg2 ) ) then return arg2 end
if ( not isEmpty( arg3 ) ) then return arg3 end
if ( not isEmpty( arg4 ) ) then return arg4 end
return nil;
end
 
function isEmpty( str )
return ( not str ) or ( str == nil ) or ( #str == 0 );
end
 
function isInstanceOf( entity, typeEntityId )
if ( not entity or not entity.claims or not entity.claims.P31 ) then
return false;
end
 
for _, claim in pairs( entity.claims.P31 ) do
if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value["numeric-id"] ) then
local actualTypeId = 'Q' .. claim.mainsnak.datavalue.value["numeric-id"];
if ( actualTypeId == typeEntityId ) then
return true;
end
end
end
 
return false;
end
 
function getEntity( context, entityId )
assertNotNull( 'context', context );
assertNotNull( 'entityId', entityId );
 
local cached = context.cache[ entityId ];
if ( cached ) then return cached; end;
 
local result = mw.wikibase.getEntity( entityId );
if ( result ) then
context.cache[ entityId ] = result;
end
 
return result;
end
 
function toStringSnak( propertyId, strValue )
assertNotNull('propertyId', strValue)
assertNotNull('strValue', strValue)
 
local snak = { snaktype = "value", property = propertyId, datatype = 'string'};
snak["datavalue"] = { value = strValue, type = 'string' };
return snak;
end
 
function toUrlSnak( propertyId, strValue )
assertNotNull('propertyId', strValue)
assertNotNull('strValue', strValue)
 
local snak = { snaktype = "value", property = propertyId, datatype = 'string'};
snak["datavalue"] = { value = strValue, type = 'url' };
return snak;
end
 
function toWikibaseEntityIdSnak( propertyId, entityId )
assertNotNull('propertyId', entityId)
assertNotNull('entityId', entityId)
if ( mw.ustring.sub( entityId, 1, 1 ) ~= 'Q' ) then error( 'Incorrect entity ID: «' .. entityId .. '»' ); end;
 
local value = {};
value["entity-type"] = 'item';
value["numeric-id"] = mw.ustring.sub( entityId , 2);
 
local snak = { snaktype = "value", property = propertyId, datatype = 'wikibase-item'};
snak["datavalue"] = { value = value, type = 'wikibase-entityid' };
return snak;
end
 
function renderSource( context, src )
mw.logObject( src );
context.lang = getLangCode( getSingle( src.lang ) ) or i18nDefaultLanguage;
 
preprocessPlaces( src, context.lang );
 
src.title = src.title or getSingle( src.url ) or '\'\'(unspecified title)\'\''
 
if ( src.entityId and not src.url ) then
local entity = getEntity( context, src.entityId );
if ( entity.sitelinks and entity.sitelinks[ context.lang .. 'wikisource'] ) then
src.url = ':' .. context.lang .. ':s:' .. entity.sitelinks[ context.lang .. 'wikisource' ].title;
end
end
 
if ( not src.year and src.dateOfPublication ) then
local date = getSingle( src.dateOfPublication );
src.year = mw.ustring.sub( date, 2, 5 );
end
 
if ( not src.year and src.dateOfCreation ) then
local date = getSingle( src.dateOfCreation );
src.year = mw.ustring.sub( date, 2, 5 );
end
 
local result;
if ( src.author ) then
result = getPeopleAsAuthorWikitext( context, src.author, options_commas );
end
if ( not isEmpty( result )) then
result = '\'\'' .. result .. '\'\' ';
else
result = '';
end
if ( src.part ) then
if ( src.url ) then
result = result .. wrapInUrl( src.url, toString( context, src.part, options_commas_nolinks ) );
else
result = result .. toString( context, src.part, options_commas );
end
result = result .. ' // ' .. toString( context, src.title, options_commas );
else
-- title only
if ( src.url ) then
result = result .. wrapInUrl( src.url, toString( context, src.title, options_commas_nolinks ) );
else
result = result .. toString( context, src.title, options_commas );
end
end
 
if ( src.subtitle ) then
result = result .. ": " .. toString( context, src.subtitle, options_commas );
end
 
if ( src.originaltitle ) then
result = result .. ' = ' .. toString( context, src.originaltitle, options_commas );
end
 
if ( src.publication ) then
if ( type( src.publication.title or '') ~= 'string' ) then error('type of src.publication.title is not string but ' .. type( src.publication.title ) ) end;


-- Враћа форматирани пар {презиме(на), име(на)}
result = result .. ' // ' .. toString( context, src.publication, options_commas_it_short );
local function tokenizeName( fullName )
if ( src.publication.subtitle ) then
local start = '^%s*' -- подудара почетак текстуалног низа + произвољан број размака
result = result .. ': ' .. toString( context, src.publication.subtitle, options_commas_it_short );
local finish = '%s*$' -- подудара крај текстуалног низа + произвољан број размака
end
local comma = '\,%s+' -- подудара тачку + један или више размака
local space = '%s+' -- подудара један или више размака
local name = '(%a[%a\-\']*)\.?' -- подудара појединачно име, које почиње словом, може садржати апострофе и наводнике, и може се завршавати тачком
local surname = '(%a[%a\-\']*)' -- Исто као за име, али се не може завршавати тачком
local f, i = mw.ustring.match(fullName, start .. surname .. comma .. name .. finish)
if f then
mw.log('tokenizeName: «' .. fullName .. '»: садржи «Fa, Im» подударање')
return {f, mw.ustring.sub( i, 1, 1 ) .. '.'}
end
end
 
local f, i, o = mw.ustring.match(fullName, start .. surname .. comma .. name .. space .. name .. finish)
if ( src.editor ) then
if f then
local prefix = i18nEditors[ context.lang ] or i18nEditors[ i18nDefaultLanguage ];
mw.log( 'tokenizeName: «' .. fullName .. '»: садржи «Fa, Im Ot» подударање')
result = result .. ' / ' .. prefix .. toString( context, src.editor, options_commas );
return {f, mw.ustring.sub( i, 1, 1 ) .. '. '
.. mw.ustring.sub( o, 1, 1 ) .. '.'}
end
end


local f1, f2, i = mw.ustring.match(fullName, start .. surname .. space .. surname .. comma .. name .. finish)
if ( src.place or src.publisher or src.year ) then
if f1 then
result = result .. ' — ';
mw.log('tokenizeName: «' .. fullName .. '»: садржи «Fa Fa, Im» подударање')
if ( src.place ) then
return {f1 .. ' ' .. f2, mw.ustring.sub( i, 1, 1 ) .. '.'}
result = result .. toString( context, src.place, options_commas_short );
if ( src.publisher or src.year ) then
result = result .. ': ';
end
end
if ( src.publisher ) then
result = result .. toString( context, src.publisher, options_commas_short );
if ( src.year ) then
result = result .. ', ';
end
end
if ( src.year ) then
result = result .. toString( context, src.year, options_commas );
end
result = result .. '.';
end
end
local i, o, f = mw.ustring.match(fullName, start .. name .. space .. name .. space .. 'оглы' .. space .. surname .. finish)
if ( src.volume or src.issue ) then
if f then
result = result .. ' — ';
mw.log('tokenizeName: «' .. fullName .. '»: садржи «Im Ot оглы Fa» подударање')
if ( src.volume ) then
return {f, mw.ustring.sub(i, 1, 1) .. '. ' .. mw.ustring.sub(o, 1, 1) .. '.'}
local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ];
result = result .. letter .. ' ' .. toString( context, src.volume, options_commas );
if ( src.issue ) then
local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
result = result .. ', ' .. letter .. ' ' .. toString( context, src.issue, options_commas ) .. '.';
else
result = result .. '.';
end
else
local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
result = result .. letter .. ' ' .. toString( context, src.issue, options_commas ) .. '.';
end
end
 
if ( src.pages ) then
local letter = i18nPages[ context.lang ] or i18nPages[ i18nDefaultLanguage ];
result = result .. ' — ' .. letter .. ' ' .. toString( context, src.pages, options_commas ) .. '.';
end
end


local i1, i2, f = mw.ustring.match(fullName, start .. name .. space .. name .. space .. 'de' .. space .. surname .. finish)
if ( src.numberOfPages ) then
if f then
local letter = i18nNumberOfPages[ context.lang ] or i18nNumberOfPages[ i18nDefaultLanguage ];
mw.log('tokenizeName: «' .. fullName .. '»: садржи «Im Im de Fa» подударање')
result = result .. ' ' .. toString( context, src.numberOfPages, options_commas ) .. ' ' .. letter;
return {f, mw.ustring.sub( i1, 1, 1 ) .. '. ' .. mw.ustring.sub( i2, 1, 1 ) .. '.'}
end
end
 
-- Try matching k names + surname
if ( src.bookSeries ) then
for k = 1, 4 do
result = result .. ' — (' .. toString( context, src.bookSeries, options_commas )
pattern = start .. string.rep(name .. space, k) .. surname .. finish
 
matched = {mw.ustring.match(fullName, pattern)}
if ( src.bookSeriesVolume or src.bookSeriesIssue ) then
if #matched ~= 0 then
result = result .. '; ';
mw.log('tokenizeName: «' .. fullName .. '»: садржи «Im (x' .. k .. ') Fa» подударање')
if ( src.bookSeriesVolume ) then
for i = 1, k do
local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ];
matched[i] = mw.ustring.sub(matched[i], 1, 1)
result = result .. letter .. ' ' .. toString( context, src.bookSeriesVolume, options_commas );
if ( src.bookSeriesIssue ) then
local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
result = result .. ', ' .. letter .. ' ' .. toString( context, src.bookSeriesIssue, options_commas );
else
result = result;
end
else
local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
result = result .. letter .. ' ' .. toString( context, src.bookSeriesIssue, options_commas );
end
end
return {matched[k + 1], table.concat(matched, '. ', 1, k) .. '.'}
end
 
result = result .. ')';
end
 
if ( src.isbn ) then
result = result .. ' — ISBN ' .. toString( context, src.isbn, options_commas );
end
 
if ( src.issn ) then
result = result .. ' — ISSN ' .. toString( context, src.issn, options_commas );
end
 
if ( src.doi ) then
result = result .. ' — ' .. toString( context, src.doi, options_doi );
end
 
if ( src.arxiv ) then
result = result .. ' — ' .. toString( context, src.arxiv, options_arxiv );
end
 
if ( src.entityId ) then
if ( src.type and src.entityId ) then
-- wrap into span to target from JS
result = '<span class="wikidata_cite ' .. toString( context, src.type, options_citetypes ) .. '" data-entity-id="' .. getSingle( src.entityId ) .. '">' .. result .. '</span>'
else
result = '<span class="wikidata_cite citetype_unknown" data-entity-id="' .. getSingle( src.entityId ) .. '">' .. result .. '</span>'
end
end
end
end
 
mw.log('Не подудара се ни са једним шаблоном: «' .. fullName .. '»')
if ( src.accessdate ) then
return {fullName}
local date = getSingle( src.accessdate );
local pattern = "(%-?%d+)%-(%d+)%-(%d+)T";
local y, m, d = mw.ustring.match( date , pattern );
y,m,d = tonumber(y),tonumber(m),tonumber(d);
result = result .. " <small>Проверено " .. tostring(d) .. " " .. monthg[m]  .. " " .. tostring(y) .. ".</small>";
end
 
    -- append invisible links to all elements used by source for tracking purposes
    local result = result .. '<div style="display:none">';
for key, entity in pairs( context.cache ) do
result = result .. '[[d:Track:' .. key .. ']]';
end
    result = result ..'</div>'
 
return {text = result, code = src.code};
end
end


local function personNameToAuthorName( fullName )
function wrapInUrl( urls, text )
if not fullName then return fullName end
local url = getSingle( urls );
local tokenized = tokenizeName(fullName)
if ( string.sub( url, 1, 1 ) == ':' ) then
if #tokenized == 1 then
return '[[' .. url .. '|' .. text .. ']]';
return tokenized[1]
else
else
return tokenized[1] .. '&nbsp;' .. tokenized[2]
return '[' .. url .. ' ' .. text .. ']';
end
end
end
end


local function personNameToResponsibleName( fullName )
function renderShortReference( src )
if not fullName then return fullName end
context = {
local tokenized = tokenizeName(fullName)
cache = {},
if #tokenized == 1 then
lang = getSingle( src.lang ) or i18nDefaultLanguage;
return tokenized[1]
};
src.title = src.title or '\'\'(unspecified title)\'\''
 
local result = '[[#' .. PREFIX_CITEREF .. src.code .. '|';
if ( src.author ) then
result = result .. toString( context, src.author, options_authors_nolinks );
else
else
return tokenized[2] .. '&nbsp;' .. tokenized[1]
result = result .. toString( context, src.title, options_commas_it_nolinks );
end
result = result .. ']]'
 
if ( src.year ) then
result = result .. ', ' .. toString( context, src.year, options_commas );
end
 
if ( src.volume ) then
local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ];
result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.volume, options_commas ) .. '.';
end
end
if ( src.issue ) then
local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.issue, options_commas ) .. '.';
end
if ( src.pages ) then
local letter = i18nPages[ context.lang ] or i18nPages[ i18nDefaultLanguage ];
result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.pages, options_commas )  .. '.';
end
end
end


function getSingle( value )
if ( not value ) then
return;
end
if ( type( value ) == 'string' ) then
return value;
elseif ( type( value ) == 'table' ) then
if ( value.id ) then
return value.id;
end
for i, tableValue in pairs( value ) do
return getSingle( tableValue );
end
end
return '(unknown)';
end


local options_commas = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false };
function toString( context, value, options )
local options_commas_short = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false, short = true };
if ( type( value ) == 'string' ) then
local options_commas_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = true, preferids = false };
return options.format( value );
local options_commas_it = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false };
elseif ( type( value ) == 'table' ) then
local options_commas_it_short = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false, short = true };
if ( value.id ) then
local options_commas_it_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = true , preferids = false };
-- this is link
local options_citetypes = { separator = ' ', conjunction = ' ', format = function( src ) return 'citetype_' .. src end, nolinks = true , preferids = true };
if ( type( value.label or '' ) ~= 'string' ) then mw.logObject( value ); error('label of table value is not string but ' .. type( value.label ) ) end


local options_commas_authors = { separator = ', ', conjunction = ', ', format = personNameToAuthorName, nolinks = false, preferids = false };
if ( options.preferids ) then
local options_commas_responsible = { separator = ', ', conjunction = ', ', format = personNameToResponsibleName, nolinks = false, preferids = false };
return options.format( value.id );
else
if ( options.nolinks ) then
return options.format( value.label or mw.wikibase.label( value.id ) or '\'\'(untranslated title)\'\'' );
else
return options.format( renderLink( context, value.id, value.label, options ) );
end
end
end


local options_arxiv = { separator = '; ', conjunction = '; ', format = function( id ) return '[http://arxiv.org/abs/' .. id .. ' arXiv:' .. id .. ']' end, nolinks = true, preferids = false };
local resultList = {};
local options_doi = { separator = '; ', conjunction = '; ', format = function( doi ) return '[http://dx.doi.org/' .. doi .. ' doi:' .. doi .. ']' end, nolinks = true, preferids = false };
for i, tableValue in pairs( value ) do
local options_issn = { separator = '; ', conjunction = '; ', format = function( issn ) return '[https://www.worldcat.org/issn/' .. issn .. ' ' .. issn .. ']' end, nolinks = true, preferids = false };
table.insert( resultList, toString( context, tableValue, options ) );
local options_pmid = { separator = '; ', conjunction = '; ', format = function( pmid ) return '[https://www.ncbi.nlm.nih.gov/pubmed/?term=' .. pmid .. ' PMID:' .. pmid .. ']' end, nolinks = true, preferids = false };
end


local function getPersonNameAsLabel( context, entityId, providedLabel, options )
return mw.text.listToText( resultList, options.separator, options.conjunction);
-- would custom label provided we don't need to check entity at all
else
if ( not utils.isEmpty( providedLabel ) ) then
return options.format( '(unknown type)' );
mw.log( 'Custom label provided for ' .. entityId );
return options.format( providedLabel );
end
end


local entity = utils.getEntity( context, entityId );
return '';
if ( not entity ) then return '\'\'(ентитет ' .. entityId .. ' недостаје)\'\'' end;
end
 
function renderLink( context, entityId, customTitle, options )
if ( not entityId ) then error("entityId is not specified") end
if ( type( entityId ) ~= 'string' ) then error('entityId is not string, but ' .. type( entityId ) ) end
if ( type( customTitle or '' ) ~= 'string' ) then error('customTitle is not string, but ' .. type( customTitle ) ) end


local personName = nil;
local title = customTitle;
-- support only labels so far
 
if ( entity.labels[ context.lang ] ) then
if ( isEmpty( title ) ) then
personName = entity.labels[ context.lang ].value;
local entity = getEntity( context, entityId );
mw.log('Добављено име особе од ' .. entityId .. ' од назива: «' .. personName .. '»' )
 
-- ISO 4
if ( isEmpty( title ) ) then
if ( entity.claims and entity.claims.P1160 ) then
for _, claim in pairs( entity.claims.P1160 ) do
if ( claim
and claim.mainsnak
and claim.mainsnak.datavalue
and claim.mainsnak.datavalue.value
and claim.mainsnak.datavalue.value.language == context.lang ) then
title = claim.mainsnak.datavalue.value.text;
mw.log('Got title of ' .. entityId .. ' from ISO 4 claim: «' .. title .. '»' )
break;
end
end
end
end
 
-- official name P1448
-- short name P1813
if ( isEmpty( title ) and options.short ) then
if ( entity.claims and entity.claims.P1813 ) then
for _, claim in pairs( entity.claims.P1813 ) do
if ( claim
and claim.mainsnak
and claim.mainsnak.datavalue
and claim.mainsnak.datavalue.value
and claim.mainsnak.datavalue.value.language == context.lang ) then
title = claim.mainsnak.datavalue.value.text;
mw.log('Got title of ' .. entityId .. ' from short name claim: «' .. title .. '»' )
break;
end
end
end
end
-- person name P1559
-- labels
if ( isEmpty( title ) and entity.labels[ context.lang ] ) then
title = entity.labels[ context.lang ].value;
mw.log('Got title of ' .. entityId .. ' from label: «' .. title .. '»' )
end
end
end


if ( not utils.isInstanceOf( entity, 'Q5' ) ) then
local actualText = title or '\'\'(untranslated)\'\'';
mw.log( 'Ентитет ' .. entityId .. ' није особа' );
local link = getElementLink( context, entityId, entity);
return personName;
return wrapInUrl( link, actualText );
end
end
 
function getElementLink( context, entityId, entity )
-- fast sitelink lookup, not an expensive operation
local link = mw.wikibase.sitelink( entityId )
if ( link ) then return ':' .. link end


if ( utils.isEmpty( personName ) ) then
if ( not entity and entityId ) then
return '\'\'(није преведено на ' .. context.lang .. ')\'\'';
entity = getEntity( context, entityId )
else
return options.format( personName );
end
end
end


local function getPersonNameAsWikitext( context, entityId, customLabel, options )
if ( entity ) then
local personName = getPersonNameAsLabel( context, entityId, customLabel, options);
-- link to entity in source context language
if ( personName == nil ) then
local projectToCheck = context.lang .. 'wiki';
return nil;
if ( entity.sitelinks and entity.sitelinks[ projectToCheck ] ) then
return ':' .. context.lang .. ':' .. entity.sitelinks[ projectToCheck ].title;
end
end
end


local link = utils.getElementLink( context, entityId, nil );
if ( entityId ) then return ':d:' .. entityId end;
return utils.wrapInUrl( link, personName );
-- if ( entityId ) then return 'https://tools.wmflabs.org/reasonator/?q=' .. entityId .. '&lang=ru' end;
return nil;
end
end


local function getPeopleAsWikitext( context, value, options )
function getPeopleAsAuthorWikitext( context, value, options )
if type( value ) == 'string' then
if ( type( value ) == 'string' ) then
return options.format( value )
return personNameToAuthorName( value );
elseif type( value ) == 'table' then
elseif ( type( value ) == 'table' ) then
if value.id then
if ( value.id ) then
-- ово је веза
-- this is link
if options.preferids then
if ( options.preferids ) then
return value.id
return value.id;
else
else
if options.nolinks then
if ( options.nolinks ) then
return getPersonNameAsLabel( context, value.id, value.label, options )
return getPersonNameAsAuthorLabel( context, value.id, value.label, options );
else
else
return getPersonNameAsWikitext( context, value.id, value.label, options )
return getPersonNameAsAuthorWikitext( context, value.id, value.label, options );
end
end
end
end
end
end
 
local maxAuthors = 10 -- потребна су ограничења, пошто поједине публикације садрже прекомеран број аутора (нпр. 115 аутора за Q68951544)
local resultList = {};
local resultList = {}
for i, tableValue in pairs( value ) do
for i, tableValue in pairs( value ) do
local nextWikitext = getPeopleAsWikitext( context, tableValue, options )
local nextWikitext = getPeopleAsAuthorWikitext( context, tableValue, options );
if not utils.isEmpty( nextWikitext ) then
if ( not isEmpty( nextWikitext ) ) then
table.insert( resultList, nextWikitext )
table.insert( resultList, nextWikitext );
if #resultList == maxAuthors + 1 then
if ( #resultList == 4 ) then
-- задржи још једно за означавање да их је превише
-- even 4 is too much, but we preserve 4th to mark that "it's more than 3"
break
break;
end
end
end
end
end
end


local resultWikitext = ''
local resultWikitext = '';
for i, wikitext in pairs( resultList ) do
for i, wikitext in pairs( resultList ) do
if i == maxAuthors + 1 then
if ( i == 4 ) then
resultWikitext = resultWikitext .. ( i18nEtAl[ context.lang ] or i18nEtAlDefault )
resultWikitext = resultWikitext .. ( i18nEtAl[ context.lang ] or i18nEtAlDefault );
break;
break;
end
end
if i ~= 1 then
if ( i ~= 1 ) then
resultWikitext = resultWikitext .. ', '
resultWikitext = resultWikitext .. ', ';
end
end
resultWikitext = resultWikitext .. wikitext
resultWikitext = resultWikitext .. wikitext;
end
end


return resultWikitext
return resultWikitext;
end
end


return options.format( '(непознат тип)' )
return options.format( '(unknown type)' );
end
end


local function generateAuthorLinks(context, src)
function getPersonNameAsAuthorWikitext( context, entityId, customLabel, options )
local result = ''
local personNameAsAuthor = getPersonNameAsAuthorLabel( context, entityId, customLabel, options);
if src.author then
if ( personNameAsAuthor == nil ) then
result = getPeopleAsWikitext( context, src.author, options_commas_authors )
return nil;
result = '<i class="wef_low_priority_links">' .. result .. '</i> '
end
end
return result
 
local link = getElementLink( context, entityId, nil );
return wrapInUrl( link, personNameAsAuthor );
end
end


local function appendProperty(result, context, src, conjunctor, property, url)
function getPersonNameAsAuthorLabel( context, entityId, providedLabel, options )
if src[property] then
-- would custom label provided we don't need to check entity at all
if url and src[url] then
if ( not isEmpty( providedLabel ) ) then
result = result .. conjunctor .. utils.wrapInUrl( src[url], utils.toString( context, src[property], options_commas_nolinks ) )
mw.log( 'Custom label provided for ' .. entityId );
else
return personNameToAuthorName( providedLabel );
result = result .. conjunctor .. utils.toString( context, src[property], options_commas )
end
end
 
local entity = getEntity( context, entityId );
if ( not entity ) then return '\'\'(entity ' .. entityId .. ' is missing)\'\'' end;
if ( not isInstanceOf( entity, 'Q5' ) ) then
mw.log( 'Entity ' .. entityId .. ' is not a person' );
return nil;
end
 
local personName = nil;
-- support only labels so far
if ( entity.labels[ context.lang ] ) then
personName = entity.labels[ context.lang ].value;
mw.log('Got person name of ' .. entityId .. ' from label: «' .. personName .. '»' )
end
 
if ( isEmpty( personName ) ) then
return '\'\'(not translated to ' .. context.lang .. ')\'\'';
else
return personNameToAuthorName( personName );
end
end
return result
end
end


local function appendTitle(result, context, src)
function personNameToAuthorName( fullName )
conjunctor = ''
if ( not fullName ) then return fullName; end
if src.part then
 
result = appendProperty(result, context, src, '', 'part', 'parturl')
local f, i, o = mw.ustring.match( fullName, '^%s*(%a[%a\-]*)\,%s(%a[%a\-]*)%s(%a[%a\-]*)%s*$' );
conjunctor = ' // '
if ( f ) then
end
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Fa, I. O.» match' );
result = appendProperty(result, context, src, conjunctor, 'title', 'url')
return f .. '&nbsp;'
return result
.. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;'
.. mw.ustring.sub( o, 1, 1 ) .. '.';
end
 
local f1, f2, i = mw.ustring.match( fullName, '^%s*(%a[%a\-]*)%s(%a[%a\-]*)\,%s(%a[%a\-]*)%s*$' );
if ( f1 ) then
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Fa Fa, I» match' );
return f1 .. '&nbsp;' .. f2 .. '&nbsp;'
.. mw.ustring.sub( i, 1, 1 ) .. '.';
end
 
local i, o, f = mw.ustring.match( fullName, '^%s*(%a)\.%s(%a)\.%s(%a[%a\-]+)%s*$');
if ( f ) then
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «I. O. Fa» match' );
return f .. '&nbsp;' .. i .. '.&nbsp;' .. o .. '.';
end
 
local i1, i2, i3, f = mw.ustring.match( fullName, '^%s*(%a)\.%s(%a)\.%s(%a)\.%s(%a[%a\-]+)%s*$');
if ( f ) then
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «I. O. ?. Fa» match' );
return f .. '&nbsp;' .. i1 .. '.&nbsp;' .. i2 .. '.&nbsp;' .. i3 .. '.';
end
 
    -- Joel J. P. C. Rodrigues
local i1, i2, i3, i4, f = mw.ustring.match( fullName, '^%s*(%a)[%a\-]+%s(%a)\.%s(%a)\.%s(%a)\.%s(%a[%a\-]+)%s*$');
if ( f ) then
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «I. O. ?. Fa» match' );
return f .. '&nbsp;' .. i1 .. '.&nbsp;' .. i2 .. '.&nbsp;' .. i3 .. '.&nbsp;' .. i4 .. '.';
end
 
local i, o, f = mw.ustring.match( fullName, '^%s*(%a[%a\-]*)%s(%a)\.%s(%a[%a\-]*)%s*$');
if ( f ) then
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Im O. Fa» match' );
return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. o .. '.';
end
 
local i, o, f = mw.ustring.match( fullName, '^%s*(%a[%a\-]*)%s(%a[%a\-]*)%s(%a[%a\-]*)%s*$');
if ( f ) then
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Im Ot Fa» match' );
return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. mw.ustring.sub( o, 1, 1 ) .. '.';
end
 
local i, o, f = mw.ustring.match( fullName, '^%s*(%a[%a\-]+)%s(%a[%a\-]+)%s+оглы%s+(%a[%a\-]+)%s*$');
if ( f ) then
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Im Ot оглы Fa» match' );
return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. mw.ustring.sub( o, 1, 1 ) .. '.';
end
 
local i, f = mw.ustring.match( fullName, '^%s*(%a[%a\-]+)%s(%a[%a\-]+)%s*$');
if ( f ) then
mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Im Fa» match' );
return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.';
end
 
mw.log( 'Unmatched any pattern: «' .. fullName .. '»' );
return fullName;
end
end


local function appendLanguage(result, context, src)
-- Expand special types of references when additional data could be found in OTHER entity properties
if context.lang ~= i18nDefaultLanguage then
function expandSpecials( currentEntity, reference, data )
result = result .. langs.list_ref(p.currentFrame:newChild{ args = {context.lang} })
if ( reference.snaks.P248
and reference.snaks.P248[1]
and reference.snaks.P248[1].datavalue
and reference.snaks.P248[1].datavalue.value["numeric-id"]) then
local sourceId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"];
 
-- Gemeinsame Normdatei -- specified by P227
if ( sourceId == 'Q36578' ) then
appendSnaks( currentEntity.claims, 'P227', data, 'title', { format = function( gnd ) return 'Record #' .. gnd; end } );
appendSnaks( currentEntity.claims, 'P227', data, 'url', { format = function( gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } );
data.publication = { id = 'Q36578', label = 'Gemeinsame Normdatei' }
data.year = '2012—2015'
end
 
-- BNF -- specified by P268
if ( sourceId == 'Q15222191' ) then
appendSnaks( currentEntity.claims, 'P268', data, 'title', { format = function( id ) return 'Record #' .. id; end } );
appendSnaks( currentEntity.claims, 'P268', data, 'url', { format = function( id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end } );
expandSpecialsQualifiers( currentEntity, 'P268', data );
end
 
-- Find a Grave -- specified by P535
if ( sourceId == 'Q63056' ) then
appendSnaks( currentEntity.claims, 'P535', data, 'url', { format = function( id ) return 'http://www.findagrave.com/cgi-bin/fg.cgi?page=gr&GRid=' .. id; end } );
expandSpecialsQualifiers( currentEntity, 'P535', data );
end
 
--  Dizionario Biografico degli Italiani -- specified by P1986
if ( sourceId == 'Q1128537' ) then
if ( not data.lang ) then data.lang = { id = 'Q652' } end;
appendSnaks( currentEntity.claims, 'P1986', data, 'url', { format = function( id ) return 'http://www.treccani.it/enciclopedia/' .. id .. '_%28Dizionario_Biografico%29/' end } );
expandSpecialsQualifiers( currentEntity, 'P1986', data );
end
 
-- Union List of Artist Names -- specified by P245
if ( sourceId == 'Q2494649' ) then
appendSnaks( currentEntity.claims, 'P245', data, 'url', { format = function( id ) return 'http://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid=' .. id end } );
expandSpecialsQualifiers( currentEntity, 'P245', data );
end
 
-- Gran Enciclopèdia Catalana -- specified by P1296
if ( sourceId == 'Q2664168' ) then
appendSnaks( currentEntity.claims, 'P1296', data, 'url', { format = function( id ) return 'http://www.enciclopedia.cat/enciclop%C3%A8dies/gran-enciclop%C3%A8dia-catalana/EC-GEC-' .. id .. '.xml'; end } );
expandSpecialsQualifiers( currentEntity, 'P1296', data );
end
 
-- Encyclopædia Britannica online -- specified by P1417
if ( sourceId == 'Q5375741' ) then
appendSnaks( currentEntity.claims, 'P1417', data, 'url', { format = function( id ) return 'http://global.britannica.com/' .. id; end } );
expandSpecialsQualifiers( currentEntity, 'P1417', data );
end
 
-- Electronic Jewish Encyclopedia (Elektronnaja Evrejskaja Entsiklopedia) -- specified by P1438
if ( sourceId == 'Q1967250' ) then
appendSnaks( currentEntity.claims, 'P1438', data, 'url', { format = function( id ) return 'http://www.eleven.co.il/article/' .. id; end } );
expandSpecialsQualifiers( currentEntity, 'P1438', data );
end
 
-- sports-reference.com -- specified by P1447
if ( sourceId == 'Q18002875' ) then
appendSnaks( currentEntity.claims, 'P1447', data, 'url', { format = function( id ) return 'http://www.sports-reference.com/olympics/athletes/' .. id .. '.html'; end } );
expandSpecialsQualifiers( currentEntity, 'P1447', data );
end
end
end
return result
end
end


local function appendSubtitle(result, context, src)
function expandSpecialsQualifiers( entity, propertyId, result )
return appendProperty(result, context, src, ': ', 'subtitle')
if ( entity.claims ~= nil and entity.claims[propertyId] ~= nil ) then
local claims = entity.claims[propertyId];
appendQualifiers( claims, 'P958', result, 'part', {} );
appendQualifiers( claims, 'P953', result, 'url', {} );
appendQualifiers( claims, 'P1065', result, 'url', {} );
appendQualifiers( claims, 'P854', result, 'url', {} );
appendQualifiers( claims, 'P357', result, 'title', {} ); -- obsolete
appendQualifiers( claims, 'P1476', result, 'title', {} );
appendQualifiers( claims, 'P478', result, 'volume', {} );
appendQualifiers( claims, 'P433', result, 'issue', {} );
end
end
end


local function appendOriginalTitle(result, context, src)
function findClaimsByValue( entity, propertyId, value )
return appendProperty(result, context, src, ' = ', 'originaltitle')
local result = {};
if ( entity and entity.claims and entity.claims[propertyId] ) then
for i, claim in pairs( entity.claims[propertyId] ) do
if ( claim.mainsnak and claim.mainsnak.datavalue ) then
local datavalue = claim.mainsnak.datavalue;
if ( datavalue.type == "string" and datavalue.value == value
or datavalue.type == "wikibase-entityid" and datavalue.value["entity-type"] == "item" and tostring( datavalue.value["numeric-id"] ) == mw.ustring.sub( value, 2 ) ) then
table.insert( result, claim );
end
end
end
end
return result;
end
end


local function appendPublication(result, context, src)
function appendSnaks( allSnaks, snakPropertyId, result, property, options )
if src.publication then
-- do not populate twice
if type( src.publication.title or '') ~= 'string' then
if ( result[property] ) then return result end;
error('тип src.publication.title није текстуални низ већ ' .. type( src.publication.title ) )
if ( not allSnaks ) then return result; end;
local selectedSnakes = allSnaks[ snakPropertyId ];
if ( not selectedSnakes ) then return result; end;
 
local hasPreferred = false;
for k, snak in pairs( selectedSnakes ) do
if ( snak and snak.mainsnak and snak.mainsnak.datavalue and snak.rank == 'preferred' ) then
--it's a preferred claim
appendImpl( snak.mainsnak.datavalue, snak.qualifiers, result, property, options );
hasPreferred = true;
end
end
end
result = result .. ' // ' .. utils.toString( context, src.publication, options_commas_it_short )
if ( hasPreferred ) then return result; end;
if src.publication.subtitle then
 
result = result .. ': ' .. utils.toString( context, src.publication.subtitle, options_commas_it_short )
for k, snak in pairs( selectedSnakes ) do
if ( snak and snak.mainsnak and snak.mainsnak.datavalue and snak.rank ~= 'deprecated' ) then
--it's a claim
appendImpl( snak.mainsnak.datavalue, snak.qualifiers, result, property, options );
elseif ( snak and snak.datavalue ) then
-- it's a snak
appendImpl( snak.datavalue, nil, result, property, options );
end
end
end
end
return result
end
end


local function appendEditor(result, context, src)
function appendQualifiers( claims, qualifierPropertyId, result, resultProperty, options )
if src.editor or src.translator then
-- do not populate twice
result = result .. ' / '
if ( not claims ) then return result end;
if src.editor then
if ( result[resultProperty] ) then return result end;
local prefix = i18nEditors[ context.lang ] or i18nEditors[ i18nDefaultLanguage ]
 
result = result .. prefix .. getPeopleAsWikitext( context, src.editor, options_commas_responsible )
for i, claim in pairs( claims ) do
if src.translator then
if ( claim.qualifiers and claim.qualifiers[ qualifierPropertyId ] ) then
result = result .. ', '
for k, qualifier in pairs( claim.qualifiers[ qualifierPropertyId ] ) do
if ( qualifier and qualifier.datavalue ) then
appendImpl( qualifier.datavalue, nil, result, resultProperty, options );
end
end
end
end
end
if src.translator then
end
local prefix = i18nTranslators[ context.lang ] or i18nTranslators[ i18nDefaultLanguage ]
end
result = result .. prefix .. getPeopleAsWikitext( context, src.translator, options_commas_responsible )
 
function appendImpl( datavalue, qualifiers, result, property, options )
if ( datavalue.type == 'string' ) then
local value = datavalue.value;
if ( options.format ) then
value = options.format( value );
end
appendImpl_toTable( result, property );
table.insert( result[property], value);
elseif ( datavalue.type == 'monolingualtext' ) then
local value = datavalue.value.text;
if ( options.format ) then
value = options.format( value );
end
appendImpl_toTable( result, property );
table.insert( result[property], value);
elseif ( datavalue.type == 'quantity' ) then
local value = datavalue.value.amount;
if ( mw.ustring.sub( value , 1, 1 ) == '+' ) then
value = mw.ustring.sub( value , 2 );
end
if ( options.format ) then
value = options.format( value );
end
appendImpl_toTable( result, property );
table.insert( result[property], value);
elseif ( datavalue.type == 'wikibase-entityid' ) then
local value = datavalue.value;
appendImpl_toTable( result, property );
local toInsert = {
id = 'Q' .. value["numeric-id"],
label = getSingleStringQualifierValue(qualifiers, 'P1932') -- stated as
};
table.insert( result[property], toInsert );
elseif datavalue.type == 'time' then
local value = datavalue.value;
if ( options.format ) then
value = options.format( value );
end
end
appendImpl_toTable( result, property );
table.insert( result[property], tostring( value.time ));
    end
end
function appendImpl_toTable(result, resultProperty)
if ( not result[resultProperty] ) then
result[resultProperty] = {};
elseif ( type( result[resultProperty] ) == 'string' or ( type( result[resultProperty] ) == 'table' and type( result[resultProperty].id ) == 'string' ) ) then
result[resultProperty] = { result[resultProperty] };
end
end
return result
end
end


local function appendEdition(result, context, src)
function getSingleStringQualifierValue( allQualifiers, qualifierPropertyId )
return appendProperty(result, context, src, ' ', 'edition')
if ( not allQualifiers ) then return end
if ( not allQualifiers[qualifierPropertyId] ) then return end
 
for k, qualifier in pairs( allQualifiers[qualifierPropertyId] ) do
if ( qualifier and qualifier.datatype == 'string'
and qualifier.datavalue and qualifier.datavalue.type == 'string' and not isEmpty( qualifier.datavalue.value ) ) then
return qualifier.datavalue.value;
end
end
 
return;
end
end


local function appendPublicationData(result, context, src)
function expandDescribed ( context, sourceEntity, data )
if src.place or src.publisher or src.year then
local described = data.described;
result = result .. ' — '
 
if src.place then
-- use only first one
result = result .. utils.toString( context, src.place, options_commas_short )
if ( type( described ) == 'table' and described[1] and described[1].id ) then
if src.publisher or src.year then
data.described = described[1];
result = result .. ': '
described = data.described;
end
 
data.publication = data.described;
 
if ( described and described.id ) then
if ( sourceEntity ) then
-- do we have appropriate record in P1433 ?
local claims = findClaimsByValue( sourceEntity, 'P1343', described.id );
if ( claims and #claims ~= 0 ) then
appendQualifiers( claims, 'P958', data, 'part', {} );
appendQualifiers( claims, 'P50', data, 'author', {} );
appendQualifiers( claims, 'P953', data, 'url', {} );
appendQualifiers( claims, 'P1065', data, 'url', {} );
appendQualifiers( claims, 'P854', data, 'url', {} );
appendQualifiers( claims, 'P357', data, 'title', {} ); -- obsolete
appendQualifiers( claims, 'P1476', data, 'title', {} );
appendQualifiers( claims, 'P478', data, 'volume', {} );
end
end
end
end
if src.publisher then
local titleWerePresent = not (not data.title);
result = result .. utils.toString( context, src.publisher, options_commas_short )
local desEntity = getEntity( context, described.id );
if src.year then
if ( desEntity ) then
result = result .. ', '
populateSourceDataImpl( desEntity, data );
if ( titleWerePresent and isEmpty( data.publication.label ) ) then
appendSnaks( desEntity.claims, 'P1160', data, 'publication-title', {} ); -- obsolete
data.publication.label = getSingle( data['publication-title'] );
end
if ( titleWerePresent and isEmpty( data.publication.label ) ) then
appendSnaks( desEntity.claims, 'P357', data, 'publication-title', {} ); -- obsolete
appendSnaks( desEntity.claims, 'P1476', data, 'publication-title', {} );
appendSnaks( desEntity.claims, 'P1680', data, 'publication-subtitle', {} );
data.publication.label = getSingle( data['publication-title'] );
data.publication.subtitle = getSingle( data['publication-subtitle'] );
end
end
end
end
if src.year then
result = result .. utils.toString( context, src.year, options_commas )
end
result = result .. '.';
end
end
return result
end
end


local function appendVolumeAndIssue(result, context, src)
function expandPublication( context, sourceEntity, data )
if src.volume or src.issue then
local publication = data.publication;
result = result .. ' '
 
local letter_vol = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]
-- use only first one
local letter_iss = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ]
if ( type( publication ) == 'table' and publication[1] and publication[1].id ) then
if src.volume then
data.publication = publication[1];
result = appendProperty(result, context, src, letter_vol .. '&nbsp;', 'volume')
publication = data.publication;
result = appendProperty(result, context, src, ', ' .. letter_iss .. '&nbsp;', 'issue')
end
else
 
result = appendProperty(result, context, src, letter_iss .. '&nbsp;', 'issue')
if ( not publication ) then return end;
if ( not publication.id ) then return end;
 
if ( sourceEntity ) then
-- do we have appropriate record in P1433 ?
 
local claims = findClaimsByValue( sourceEntity, 'P1433', publication.id );
if ( claims and #claims ~= 0 ) then
appendQualifiers( claims, 'P958', data, 'part', {} );
appendQualifiers( claims, 'P953', data, 'url', {} );
appendQualifiers( claims, 'P1065', data, 'url', {} );
appendQualifiers( claims, 'P854', data, 'url', {} );
appendQualifiers( claims, 'P856', data, 'url', {} );
appendQualifiers( claims, 'P123', data, 'publisher', {} );
appendQualifiers( claims, 'P291', data, 'place', {} );
appendQualifiers( claims, 'P304', data, 'pages', {} );
appendQualifiers( claims, 'P1104', data, 'numberOfPages', {} );
appendQualifiers( claims, 'P478', data, 'volume', {} );
appendQualifiers( claims, 'P433', data, 'issue', {} );
appendQualifiers( claims, 'P571', data, 'dateOfCreation', {} );
appendQualifiers( claims, 'P577', data, 'dateOfPublication', {} );
appendQualifiers( claims, 'P212', data, 'isbn', {} ); -- ISBN-13
appendQualifiers( claims, 'P957', data, 'isbn', {} ); -- ISBN-10
end
end
result = result .. '.'
end
end
return result
end


local function appendPages(result, context, src)
local titleWerePresent = not (not data.title);
if src.pages then
local pubEntity = getEntity( context, publication.id );
local letter = i18nPages[ context.lang ] or i18nPages[ i18nDefaultLanguage ]
populateSourceDataImpl( pubEntity, data );
local strPages = utils.toString( context, src.pages, options_commas )
if ( titleWerePresent and isEmpty( data.publication.label ) ) then
strPages = mw.ustring.gsub( strPages, '[-—]', '' );
appendSnaks( pubEntity.claims, 'P1160', data, 'publication-title', {} ); -- obsolete
result = result .. ' ' .. letter .. '&nbsp;' .. strPages .. '.'
data.publication.label = getSingle( data['publication-title'] );
end
if ( titleWerePresent and isEmpty( data.publication.label ) ) then
appendSnaks( pubEntity.claims, 'P357', data, 'publication-title', {} ); -- obsolete
appendSnaks( pubEntity.claims, 'P1476', data, 'publication-title', {} );
appendSnaks( pubEntity.claims, 'P1680', data, 'publication-subtitle', {} );
data.publication.label = getSingle( data['publication-title'] );
data.publication.subtitle = getSingle( data['publication-subtitle'] );
end
end
return result
end


local function appendNumberOfPages(result, context, src)
if ( pubEntity.claims and pubEntity.claims.P361 ) then
if src.numberOfPages then
for c, claim in pairs( pubEntity.claims.P361 ) do
local letter = i18nNumberOfPages[ context.lang ] or i18nNumberOfPages[ i18nDefaultLanguage ]
if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value["numeric-id"] ) then
result = appendProperty(result, context, src, ' ', 'numberOfPages') .. '&nbsp;' .. letter
local possibleBookSeriesEntityId = 'Q' .. claim.mainsnak.datavalue.value["numeric-id"];
end
local possibleBookSeriesEntity = getEntity( context, possibleBookSeriesEntityId );
return result
if ( isInstanceOf( possibleBookSeriesEntity, 'Q277759' ) ) then
end
appendImpl_toTable( data, 'bookSeries' );
table.insert( data.bookSeries, { id = possibleBookSeriesEntityId } );


local function appendBookSeries(result, context, src)
appendQualifiers( { claim }, 'P478', data, 'bookSeriesVolume', {} );
if src.bookSeries then
appendQualifiers( { claim }, 'P433', data, 'bookSeriesIssue', {} );
result = appendProperty(result, context, src, ' — (', 'bookSeries')
end
if src.bookSeriesVolume or src.bookSeriesIssue then
result = result .. '; '
local letter_vol = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ]
local letter_iss = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ]
if ( src.bookSeriesVolume ) then
result = appendProperty(result, context, src, letter_vol .. '&nbsp;', 'bookSeriesVolume')
result = appendProperty(result, context, src, ', ' .. letter_iss .. '&nbsp;', 'bookSeriesIssue')
else
result = appendProperty(result, context, src, letter_iss .. '&nbsp;', 'bookSeriesIssue')
end
end
end
end
result = result .. ')'
end
end
return result
 
expandBookSeries( context, data );
end
end


local function appendTirage(result, context, src)
function expandBookSeries( context, data )
if src.tirage then
local bookSeries = data.bookSeries;
local tirageTemplate = i18nTirage[ context.lang ] or i18nTirage[ i18nDefaultLanguage ]
if ( not bookSeries ) then return end;
result = result .. ' — ' .. utils.toString( context, src.tirage, { separator = '; ', conjunction = '; ', format = function( data ) return mw.ustring.format(tirageTemplate, data) end } )
 
-- use only first one
if ( type( bookSeries ) == 'table' and bookSeries[1] and bookSeries[1].id ) then
data.bookSeries = bookSeries[1];
bookSeries = data.bookSeries;
end
end
return result
 
if ( not bookSeries ) then return end;
if ( not bookSeries.id ) then return end;
 
local bookSeriesEntity = getEntity( context, bookSeries.id );
appendSnaks( bookSeriesEntity.claims, 'P123', data, 'publisher', {} );
appendSnaks( bookSeriesEntity.claims, 'P291', data, 'place', {} );
appendSnaks( bookSeriesEntity.claims, 'P236', data, 'issn', {} );
end
end


local function appendIdentifiers(result, context, src)
function populateSourceDataImpl( entity, plainData )
if src.isbn  then result = result .. ' — ISBN ' .. utils.toString( context, src.isbn, options_commas ) end
populateDataFromClaims( entity.id, entity.claims, plainData );
if src.issn  then result = result .. ' — ISSN ' .. utils.toString( context, src.issn, options_issn ) end
if src.doi  then result = result .. ' — ' .. utils.toString( context, src.doi, options_doi ) end
if src.pmid  then result = result .. ' — ' .. utils.toString( context, src.pmid, options_pmid ) end
if src.arxiv then result = result .. ' — ' .. utils.toString( context, src.arxiv, options_arxiv ) end
return result
end


local function appendSourceId(result, context, src)
local normativeTitle = getNormativeTitle( entity )
if src.sourceId then
if ( normativeTitle ) then
local citetype = src.type and utils.toString(context, src.type, options_citetypes) or 'citetype_unknown'
local y, m, d = mw.ustring.match( getSingle( plainData.dateOfCreation ) , "(%-?%d+)%-(%d+)%-(%d+)T" );
result = '<span class="wikidata_cite ' .. citetype .. '" data-entity-id="' .. utils.getSingle(src.sourceId) .. '">' .. result .. '</span>'
y,m,d = tonumber(y),tonumber(m),tonumber(d);
local title = toString( { lang='ru' }, plainData.title, options_commas_nolinks );
plainData.title = { normativeTitle .. " от&nbsp;" .. tostring(d) .. "&nbsp;" .. monthg[m]  .. " " .. tostring(y) .. "&nbsp;г. №&nbsp;" .. getSingle( plainData.docNumber ) .. ' «' .. title.. '»' }
end
end
return result
end


local function appendAccessDate(result, context, src)
if ( not plainData.title ) then
if src.accessdate then
if ( entity.labels and entity.labels.ru and entity.labels.ru.value ) then
local date = utils.getSingle(src.accessdate)
plainData.title = { entity.labels.ru.value };
local pattern = "(%-?%d+)%-(%d+)%-(%d+)T";
end
local y, m, d = mw.ustring.match(date, pattern)
y, m, d = tonumber(y), tonumber(m), tonumber(d)
result = result .. " <small>Проверено " .. tostring(d) .. " " .. monthg[m]  .. " " .. tostring(y) .. ".</small>"
end
end
return result
 
return plainData;
end
end


local function populateUrl(context, src)
function populateDataFromClaims( entityId, claims, data )
if src.sourceId and not src.url then
appendSnaks( claims, 'P50', data, 'author', {} );
local entity = utils.getEntity(context, src.sourceId)
appendSnaks( claims, 'P407', data, 'lang', {} );
if entity.sitelinks and entity.sitelinks[context.lang .. 'wikisource'] then
appendSnaks( claims, 'P364', data, 'lang', {} );
src.url = ':' .. context.lang .. ':s:' .. entity.sitelinks[context.lang .. 'wikisource'].title
appendSnaks( claims, 'P958', data, 'part', {} );
if ( not data.title ) then
if ( not isEmpty( entityId ) ) then
local optionsAsLinks = { format = function( text ) return { id = entityId, label = text } end };
appendSnaks( claims, 'P357', data, 'title', optionsAsLinks ); -- obsolete
appendSnaks( claims, 'P1476', data, 'title', optionsAsLinks );
else
appendSnaks( claims, 'P357', data, 'title', {} ); -- obsolete
appendSnaks( claims, 'P1476', data, 'title', {} );
end
end
appendSnaks( claims, 'P1680', data, 'subtitle', {} );
end
end
appendSnaks( claims, 'P953', data, 'url', {} );
appendSnaks( claims, 'P1065', data, 'url', {} );
appendSnaks( claims, 'P854', data, 'url', {} );
appendSnaks( claims, 'P856', data, 'url', {} );
appendSnaks( claims, 'P1343', data, 'described', {} );
appendSnaks( claims, 'P1433', data, 'publication', {} );
appendSnaks( claims, 'P123', data, 'publisher', {} );
appendSnaks( claims, 'P291', data, 'place', {} );
appendSnaks( claims, 'P304', data, 'pages', {} );
appendSnaks( claims, 'P1104', data, 'numberOfPages', {} );
appendSnaks( claims, 'P478', data, 'volume', {} );
appendSnaks( claims, 'P433', data, 'issue', {} );
appendSnaks( claims, 'P571', data, 'dateOfCreation', {} );
appendSnaks( claims, 'P577', data, 'dateOfPublication', {} );
appendSnaks( claims, 'P212', data, 'isbn', {} ); -- ISBN-13
appendSnaks( claims, 'P957', data, 'isbn', {} ); -- ISBN-10
appendSnaks( claims, 'P236', data, 'issn', {} );
    -- web
-- appendSnaks( claims, 'P813', data, 'accessdate', {} );
    -- docs
appendSnaks( claims, 'P1545', data, 'docNumber', {} );
-- other
appendSnaks( claims, 'P31', data, 'type', {} );
appendSnaks( claims, 'P818', data, 'arxiv', {} );
appendSnaks( claims, 'P356', data, 'doi', {} );
-- JSTOR
appendSnaks( claims, 'P888', data, 'url', { format = function( id ) return 'http://www.jstor.org/stable/' .. id end } );
return src;
end
end


local function populateYear(src)
function updateWithRef( reference, src )
if not src.year and src.dateOfPublication then
-- specified
local date = utils.getSingle(src.dateOfPublication)
if ( reference.snaks.P662 ) then
src.year = mw.ustring.sub(date, 2, 5)
local cid = reference.snaks.P662[1].datavalue.value;
end
src.code = src.code .. '-cid:' .. cid;
if not src.year and src.dateOfCreation then
src.title = 'Compound Summary for: CID ' .. cid;
local date = utils.getSingle(src.dateOfCreation)
src.url = 'http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?cid=' .. cid;
src.year = mw.ustring.sub(date, 2, 5)
src.publication = { id = 'Q278487', label = 'PubChem' };
end
end
populateDataFromClaims( nil, reference.snaks, src);
return src;
end
function p.renderSource( frame )
p.currentFrame = frame;
local arg = frame.args[1];
local refAnchor = frame.args['ref'];
local refAnchorYear = frame.args['ref-year'];
local args = {};
args.refAnchor = frame.args['ref'];
args.refAnchorYear = frame.args['ref-year'];
args.part = frame.args['part'];
args.pages = frame.args['pages'];
return p.renderSourceImpl( mw.text.trim( arg ), args );
end
function copyArgsToSnaks( args, snaks )
if ( not isEmpty( args.part ) ) then snaks.P958 = { toStringSnak( 'P958', tostring( args.part ) ) } end
if ( not isEmpty( args.pages ) ) then snaks.P304 = { toStringSnak( 'P304', tostring( args.pages ) ) } end
if ( not isEmpty( args.issue ) ) then snaks.P433 = { toStringSnak( 'P433', tostring( args.issue ) ) } end
if ( not isEmpty( args.volume ) ) then snaks.P478 = { toStringSnak( 'P478', tostring( args.volume ) ) } end
if ( not isEmpty( args.url ) ) then snaks.P953 = { toUrlSnak( 'P953', tostring( args.url ) ) } end
end
end


local function populateTitle(src)
function p.renderSourceImpl( entityId, args )
src.title = src.title or utils.getSingle(src.url) or '\'\'(неназначен наслов)\'\''
args = args or {};
 
local snaks = {};
snaks.P248 = { toWikibaseEntityIdSnak( 'P248', entityId ) };
copyArgsToSnaks( args, snaks );
 
local rendered = renderReferenceImpl( mw.wikibase.getEntity(), { snaks = snaks }, args.refAnchor, args.refAnchorYear );
if ( rendered ) then return rendered.text end;
end
end


local function renderSource(context, src)
function p.renderReference( frame, currentEntity, reference )
options_commas_authors.format = personNameToAuthorName
p.currentFrame = frame;
options_commas_responsible.format = personNameToResponsibleName
 
-- template call
if ( frame and not currentEntity and not reference ) then
local args = frame.args;
if ( #frame.args == 0 ) then
args = frame:getParent().args;
end
 
local snaks = {};


context.lang = utils.getLangCode(utils.getSingle(src.lang)) or i18nDefaultLanguage
if ( args[1] ) then
snaks.P248 = { toWikibaseEntityIdSnak( "P248", args[1] ) };
end
copyArgsToSnaks( args, snaks );


utils.preprocessPlaces(src, context.lang)
currentEntity = mw.wikibase.getEntity();
reference = { snaks = snaks };
end


populateUrl(context, src)
local rendered = renderReferenceImpl( currentEntity, reference );
populateTitle(src)
populateYear(src)


local result = generateAuthorLinks(context, src)
if ( not rendered ) then
result = appendTitle(result, context, src)
return '';
result = appendLanguage(result, context, src)
end
result = appendSubtitle(result, context, src)
 
result = appendOriginalTitle(result, context, src)
local result;
result = appendPublication(result, context, src)
local code = rendered.code or mw.text.encode( rendered.text );
result = frame:extensionTag( 'ref', rendered.text, {name = code} ) .. '[[К:Википедия:Статьи с источниками из Викиданных]]';
result = result .. '<span class="wef_low_priority_links">'
result = appendEditor(result, context, src) -- Можда користи тренутог уредника уместо жељеног. Користити пажљиво
result = appendEdition(result, context, src)
result = appendPublicationData(result, context, src)
result = appendVolumeAndIssue(result, context, src)
result = appendPages(result, context, src)
result = appendNumberOfPages(result, context, src)
result = appendBookSeries(result, context, src)
result = appendTirage(result, context, src)
result = appendIdentifiers(result, context, src)
result = appendSourceId(result, context, src)
result = appendAccessDate(result, context, src)
result = result .. '</span>'


return result
return result;
end
end


local function renderReferenceImpl(currentEntity, reference, refAnchor, refAnchorYear)
function renderReferenceImpl( currentEntity, reference, refAnchor, refAnchorYear )
if not reference.snaks then
if ( not reference.snaks ) then
return nil
return nil;
end
end


-- контекст, содержит также кеш элементов
-- контекст, содержит также кеш элементов
local context = {
local context = {
cache = {}
cache = {},
}
}


-- подаци у основном формату у складу са библиографским описним модулима
-- данные в простом формате, согласованном с модулями формирования библиографического описания
local data = {}
local data = {};


    -- прибављање података из референце
local entityId, sourceEntity;
    utils.populateDataFromClaims(context, nil, reference.snaks, data)
if ( reference and reference.snaks and reference.snaks.P248 ) then
for _, snak in pairs ( reference.snaks.P248 ) do
if ( snak.datavalue ) then
entityId = 'Q' .. snak.datavalue.value["numeric-id"];
sourceEntity = getEntity( context, entityId );
data.code = entityId;
data.entityId = entityId;
break;
end
end
end


utils.expandSpecials(context, currentEntity, reference, data)
updateWithRef( reference, data );
-- update ref name with ref-specific properties
if ( data.code ) then
if ( data.part ) then data.code = data.code .. '-' .. getSingle( data.part ) end
if ( data.pages ) then data.code = data.code .. '-' .. getSingle( data.pages ) end
if ( data.volume ) then data.code = data.code .. '-' .. getSingle( data.volume ) end
if ( data.issue ) then data.code = data.code .. '-' .. getSingle( data.issue ) end
if ( data.url ) then data.code = data.code .. '-' .. getSingle( data.url ) end
end


local sourceEntity = nil
expandSpecials( currentEntity, reference, data );
if data.sourceId then
if ( sourceEntity ) then
sourceEntity = utils.getEntity(context, data.sourceId)
populateSourceDataImpl( sourceEntity, data );
if sourceEntity then
end
utils.populateSourceDataImpl(context, sourceEntity, data)
end
if ( not data.publication and data.described ) then
expandDescribed( context, sourceEntity, data );
else
if ( data.publication ) then expandPublication( context, sourceEntity, data ); end
end
end


if data.publication then
if ( next( data ) == nil ) then
utils.expandPublication(context, sourceEntity, data)
return nil;
end
end


utils.expandBookSeries(context, data)
local rendered;
if ( p.short ) then
rendered = renderShortReference( data );
if ( mw.ustring.len( rendered.text ) == 0 ) then
return nil;
end
 
else
rendered = renderSource( context, data );
if ( mw.ustring.len( rendered.text ) == 0 ) then
return nil;
end


if next(data) == nil then
if ( refAnchor ) then
return nil
local anchorValue = 'CITEREF' .. refAnchor .. ( coalesce( refAnchorYear, data.year ) or '' );
rendered.text = '<span class="citation" id="' .. mw.uri.anchorEncode( anchorValue ) .. '">' .. rendered.text .. '</span>';
end
end
end


local rendered = renderSource(context, data)
return rendered;
if mw.ustring.len(rendered) == 0 then
end
return nil
 
function getNormativeTitle( entity )
if ( not entity or not entity.claims or not entity.claims.P31 ) then
return;
end
end


if refAnchor then
for _, claim in pairs( entity.claims.P31 ) do
local anchorValue = 'CITEREF' .. refAnchor .. (utils.coalesce(refAnchorYear, data.year) or '')
if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value["numeric-id"] ) then
rendered = '<span class="citation" id="' .. mw.uri.anchorEncode(anchorValue) .. '">' .. rendered .. '</span>'
local classId = 'Q' .. claim.mainsnak.datavalue.value["numeric-id"];
local title = NORMATIVE_DOCUMENTS[ classId ];
if ( title ) then
return title;
end
end
end
end


return rendered
return;
end
end


local function artificialSnaks(args)
local LANG_CACHE = {
local snaks = {}
Q150 = 'fr',
if args[1] then
Q188 = 'de',
entityId = mw.text.trim(args[1])
Q1321 = 'es',
snaks.P248 = {utils.toWikibaseEntityIdSnak("P248", entityId)}
Q1860 = 'en',
snaks.P805 = {utils.toWikibaseEntityIdSnak("P805", entityId)}
Q652 = 'it',
Q7737 = 'ru',
}
 
function getLangCode( langEntityId )
if ( not langEntityId ) then
return;
end
 
-- small optimization
local cached = LANG_CACHE[ langEntityId ];
if ( cached ) then return cached; end
 
local langEntity = mw.wikibase.getEntity( langEntityId );
if ( not langEntity ) then
mw.log( '[getLangCode] Missing entity ' .. langEntityId );
else
if ( langEntity.claims and langEntity.claims.P424 ) then
for _, claim in pairs( langEntity.claims.P424 ) do
if ( claim
and claim.mainsnak
and claim.mainsnak.datavalue
and claim.mainsnak.datavalue.value ) then
return '' .. claim.mainsnak.datavalue.value;
end
end
end
end
end
utils.copyArgsToSnaks(args, snaks)
 
return mw.wikibase.getEntity(), {snaks = snaks}
return;
end
end


function p.renderReference(frame, currentEntity, reference)
function preprocessPlaces( data, lang )
p.currentFrame = frame
if ( not data.place ) then
return;
end;


-- позив шаблона
local newPlaces = {};
if frame and not currentEntity and not reference then
for index, place in pairs( data.place ) do
currentEntity, reference = artificialSnaks(frame.args)
if ( place.id ) then
local newPlaceStr = getPlaceName(lang, place.id)
if ( newPlaceStr ) then
newPlaces[index] = newPlaceStr;
else
newPlaces[index] = place;
end
else
newPlaces[index] = place;
end
end
end
data.place = newPlaces;
end


local rendered = renderReferenceImpl(currentEntity, reference)
function getPlaceName( lang, placeId )
if not rendered then
-- ГОСТ Р 7.0.12—2011
return ''
if ( lang == 'ru' ) then
if ( placeId == 'Q649' ) then return toTextWithTip('М.', 'Москва'); end
if ( placeId == 'Q656' ) then return toTextWithTip('СПб.', 'Санкт-Петербург'); end
if ( placeId == 'Q891' ) then return toTextWithTip('Н. Новгород', 'Нижний Новгород'); end
if ( placeId == 'Q908' ) then return toTextWithTip('Ростов н/Д.', 'Ростов-на-Дону'); end
end
end
-- За избор алгоритма распршивања вид. [[Модуль:Hash]]. Подвлака на почетку омогућава
return nil;
    -- да искључите грешку када је назив фусноте чисто бројевна вредност, која понекад садржи хешеве.
return frame:extensionTag('ref', rendered, {name = '_' .. mw.hash.hashValue('fnv164', rendered)}) .. '[[Category:Википедија:Чланци са изворима са Викидата]]'
end
end


function p.renderSource(frame)
function toTextWithTip( text, tip )
p.currentFrame = frame
return '<span title="' .. tip .. '" style="border-bottom: 1px dotted; cursor: help; white-space: nowrap">' .. text .. '</span>';
currentEntity, reference = artificialSnaks(frame.args)
return renderReferenceImpl(currentEntity, reference, frame.args['ref'], frame.args['ref-year'])
end
end


return p;
return p;

Верзија на датум 1. новембар 2020. у 13:58

Начин рада

Овај модул генерише текст коришћен у фуснотама изведеним са ставки Википодатака.

Функције

Спољашње

Спољашње функције прихватају објекте типа фрејма и предвиђени су за позивање из других модула или преко функције рашчлањивања {{#invoke:}}.

Директно позивање на функције модула у чланцима је крајње непожељно! У ту сврху користите одговарајуће шаблоне.

p.renderSource(frame)

Враћа текст вики-везе до одређеног извора ради уметања у референцу или библиографију. Погледајте шаблоне {{source}} и {{извор}} који користе ову функцију. Подржава следеће аргументе:

  • frame.args[1] — анонимни аргумент који одређује идентификатор објекта на Википодацима помоћу којег би веза требало да се генерише. На пример, Q20750516.
  • frame.args['ref'] — поставља ознаку ref, која се касније може користити у шаблонима попут {{sfn}}.
  • frame.args['ref-year'] — поставља ознаку ref-year, која се користи слично као на ознаци ref.
  • frame.args['part'] — додатни аргумент за разјашњење дела извора на који се позива (на пример, поглавље у књизи).
  • frame.args['parturl'] — веза за део који је описан претходним аргументом.
  • frame.args['pages'] — одређене странице у извору на који се референца успоставља.
  • frame.args['url'] — омогућава изричито одређење која веза ће требати да се постави на извор.
  • frame.args['volume'] — омогућава изричито назначавање тома извора на који се референца односи.
  • frame.args['issue'] — омогућава изричито назначавање издања извора на који се референца односи.

Већина аргумената происходи из utils.copyArgsToSnaks. Сам преносни оквир се налази у p.currentFrame за даљу употребу и на основу аргумената прослеђених функцији artificialSnaks формира вештачки делови, која повезује на извор поменут у frame.args[1], кроз својства Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value). и Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).. Подаци се затим преносе у renderReferenceImpl за даљу обраду.

p.renderReference(frame, currentEntity, reference)

Враћа форматиран вики текст референце датом извору. Подржава исте аргументе као и p.renderSource, а поред тога ref и ref-year. Погледајте шаблоне {{source-ref}} и {{ВП-фуснота}}, користећи ову функцију. Такође се користи у Модул:Wikidata да бисте приказали везе наведене поред упита са Википодатака. Ако недостају currentEntity и reference, ствара сопствени снек помоћу функције artificialSnaks, након чега се прослеђује у renderReferenceImpl. Ако је вики-текст за референцу успешно генерисан, обвијен је ознаком &lt;ref&gt; уз помоћ frame:extensionTag, при чему назив референце настаје хеширањем прослеђеног вики-текста у mw.hash.hashValue. Чланци са таквим фуснотама стављају се у Категорија:Википедија:Чланци са изворима из Викидата.

Унутрашње

tokenizeName(fullName)

Претвара пуно име у пар {презимена одвојена размацима, имена одвојена размацима} . Реализовано у облицима који се могу пронаћи на Википодацима:

  1. Презиме, Име
  2. Презиме, Име Име
  3. Презиме Презиме, Име
  4. Име Име огли Презиме
  5. Име Име де Презиме
  6. Име … Име Презиме (најмање једно и не више од четири појединачна имена)

Овде име, за разлику од презимена, може бити почетно. Ако није испуњен ниједан од горњих формата, враћа се пуно име непромењено.

personNameToAuthorName(fullName)

Претвара пуно име у облик Презиме И. СИ. користећи tokenizeName.

personNameToResponsibleName(fullName)

Претвара пуно име у облик И. СИ. Презиме користећи tokenizeName.

getPeopleAsWikitext(context, value, options)

Претвара списак имена value у викитекст према списку опција options. Опције морају садржати следећа поља:

  1. separator — разделитељ на списку;
  2. conjunction — разделитељ пре последњег елемента списка;
  3. format — функција која трансформише имена у неком нормализованом облику (на пример, personNameToAuthorName);
  4. nolinks — логичка вредност, мора бити тачно ако је повезивање непожељно;
  5. preferids — логичка вредност, мора да буде тачно ако желите да вратите ид са Википодатака, а не имена.

Ако на спису има више аутора од броја maxAuthors (тренутно 10), остали се замењују са и др. или његових аналога (ако је језик одређен у контексту, тада i18nEtAl[context.lang], иначе се користи i18nEtAlDefault).

appendProperty(result, context, src, conjunctor, property, url)

Надодаје src[property] на result, раздвајајући их линијом уписаном conjunctor. Ако је могуће, формира везу до src[url].

generateAuthorLinks(context, src)

Враћа списак аутора src.author, оформљен путем getPeopleAsWikitext и уоквирен у &lt;i class="wef_low_priority_links"&gt;&lt;/i&gt;.

appendTitle(result, context, src)

Надодаје на result текстуални низ src.part // src.title или само src.title ако src.part није прецизирано. Ако је могуће уоквиривање src.part (или src.title ако src.part није прецизирано) у src.url.

appendLanguage(result, context, src)

Ако се context.lang разликује од i18nDefaultLanguage (српски у нашем случају), тада се назнака тога приписује result кроз Модул:Languages у формату {{ref-lang}}.

appendSubtitle(result, context, src)

Надодаје на result текстуални низ : src.subtitle ако је src.subtitle дефинисано.

appendOriginalTitle(result, context, src)

Надодаје на result текстуални низ &nbsp;= src.originaltitle ако је src.originaltitle дефинисано.

appendPublication(result, context, src)

Надодаје на result текстуални низ &nbsp;// src.publication: src.publication.subtitle ако је дефинисано src.publication.subtitle, или &nbsp;// src.publication ако је дефинисано src.publication.

appendEditor(result, context, src)

Надодаје на result текстуални низ &nbsp;/ prefix src.editor ако је дефинисано src.editor, где је prefix дефинисано према context.lang (уобичајено, под ред.).

appendEdition(result, context, src)

Надодаје на result текстуални низ &nbsp;— src.edition ако је src.edition дефинисано.

appendPublicationData(result, context, src)

Надодаје на result текстуални низ у облику &nbsp;— src.place: src.publisher, src.year. ако је дефинисан бар један од наведених параметара. Неодређени део је изостављен, заједно са одговарајућом интерпункцијом. Дебело црево се користи само ако је наведено src.place и бар неки од src.publisher и src.year, зарез се ставља само ако је наведен и src.publisher, и src.year. Цртица и тачка користе се ако је наведен барем један од параметара.

appendVolumeAndIssue(result, context, src)

Надодаје на result текстуални низ у облику &nbsp;— letter_vol src.volume, letter_iss src.issue. ако је дефинисан бар један од наведених параметара. Зарез се користи ако су наведена оба параметра. letter_vol и letter_iss одређени на основу context.lang (на пример, Т. и вып. за руски текст, Vol. и Iss. за енглески).

appendPages(result, context, src)

Надодаје на result текстуални низ у облику &nbsp;— letter src.pages. ако је src.pages дефинисано, док је као сепаратор у src.pages, ако је распон страница, користи се симбол «—», а letter одређена на основу context.lang (на пример, P. за енглески и С. за руски).

appendNumberOfPages(result, context, src)

Надодаје на result текстуални низ у облику &nbsp;— src.numberOfPages letter ако је src.numberOfPages дефинисано. При чему је letter утврђено из context.lang (p. за енглески и с. за руски).

appendBookSeries(result, context, src)

Надодаје на result текстуални низ у облику &nbsp;— (src.bookSeries; letter_vol src.bookSeriesVolume, letter_iss src.bookSeriesIssue) ако је src.bookSeries дефинисано. Тачка са зарезом само ако је дефинисано src.bookSeriesVolume или src.bookSeriesIssue, зарез се користи ако су наведена оба параметра. letter_vol и letter_iss утврђено из context.lang, слично ономе како ради appendVolumeAndIssue.

appendBookSeries(result, context, src)

Надодаје на result информацију из src.tirage ако је дефинисано. Формат се одређује из context.lang, за енглески језик је то &nbsp;— ed. size: src.tirage, а за руски &nbsp;— src.tirage экз..

appendIdentifiers(result, context, src)

Надодаје на result идентификаторе ISBN, ISSN, DOI, PMID и arXiv ако су наведени. Идентификаторима се додељује цртица, тачнији формат је дефинисан у табелама options_commas, options_issn, options_doi, options_pmid и options_arxiv.

appendSourceId(result, context, src)

Надодаје на result у &lt;span class="wikidata_cite citetype" data-entity-id="src.sourceId"&gt;&lt;/span&gt;, где citetyle је src.type ако је ово поље дефинисано и citetype_unknown у супоротном случају.

appendAccessDate(result, context, src)

Надодаје на result текстуални низ у виду <small>Проверено dd month yyyy.</small>, где dd, month и yyyy потичу од src.accessdate ако је ово поље дефинисано.

populateUrl(context, src)

Ако је src.url недефинисан, али је src.sourceId познат, покушава доделити src.url везу.

populateYear(src)

Ако је src.year недефинисан, покушава да испуни са src.dateOfPublication и src.dateOfCreation.

populateTitle(src)

Ако је src.title недефинисан, покушава да придружи src.url, ако и ово није могуће, додељује ''(недефинисан наслов)''.

renderSource(context, src)

Унутрашња функција која генерише текст који ће се приказати у референци. Понаша се на следећи начин:

  1. Записује src.lang у context.lang (или i18nDefaultLanguage ако src.lang није записан).
  2. Позива populateUrl, populateTitle и populateYear.
  3. Формира променљиву result, с почетка дефинисану са generateAuthorLinks(context, src).
  4. Доследно примењује на result функцију appendTitle—appendAccessDate, док је блок appendEditor—appendAccessDate додатно уоквирен са &lt;span class="wef_low_priority_links"&gt;&lt;/span&gt;

artificialSnaks(args)

Прави вештачки снек, која повезује извор са идентификатором frame.args[1] путем својства Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value). и Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value)., а такође прослеђује и аргументе (том, издање итд.).

Услужни подмодули

Коришћена својства Википодатака

Својство Напомене
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value). користи се за означавање наслова чланка у енциклопедији
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value). ако је потребно преправити назив ознаке ставке
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).
Lua грешка in Модул:WD at line 450: attempt to index field 'wikibase' (a nil value).

Тестови [ уређивање ]

Неуспелих тестова: 6

test_Sources:

Текст Очекивано Стварно
НеН {{#invoke:Sources | renderSource | Q20750516}} Президент Российской Федерации Указ Президента Российской Федерации от 15 јануара 1992 г. № 23 «О Генеральном директоре Агентства федеральной безопасности Российской Федерации и Министре внутренних дел Российской Федерации» (рус.) // Собрание законодательства Российской Федерации — 1992. Lua грешка на линији 1113: attempt to index field 'wikibase' (a nil value).
НеН {{#invoke:Sources | renderSource | Q21683979}} Advances in Cryptology — EUROCRYPT 2004 (енг.): International Conference on the Theory and Applications of Cryptographic Techniques, Interlaken, Switzerland, May 2-6, 2004. Proceedings / ed.C. Cachin, J. L. CamenischSpringer, Berlin, Heidelberg, 2004. — 630 p. — ISBN 978-3-540-21935-4 — doi:10.1007/B97182 Lua грешка на линији 1113: attempt to index field 'wikibase' (a nil value).
НеН {{#invoke:Sources | renderSource | Q21683981}} Nguyen P. Q. Can We Trust Cryptographic Software? Cryptographic Flaws in GNU Privacy Guard v1.2.3 (енг.) // Advances in Cryptology — EUROCRYPT 2004: International Conference on the Theory and Applications of Cryptographic Techniques, Interlaken, Switzerland, May 2-6, 2004. Proceedings / ed.C. Cachin, J. L. CamenischSpringer, Berlin, Heidelberg, 2004. — P.  555—570. — 630 p. — ISBN 978-3-540-21935-4 — doi:10.1007/978-3-540-24676-3_33 Lua грешка на линији 1113: attempt to index field 'wikibase' (a nil value).
НеН {{#invoke:Sources | renderSource | Q21725400}} Eichenauer J., Lehn J. A non-linear congruential pseudo random number generator (енг.) // Statistische HefteSpringer Berlin Heidelberg, 1986. — vol.  27, Iss.  1. — P.  315—326. — ISSN 0932-5026doi:10.1007/BF02932576 Lua грешка на линији 1113: attempt to index field 'wikibase' (a nil value).
НеН {{#invoke:Sources | renderSource | Q21725116}} Menezes A. J., Oorschot P. v., Vanstone S. A. Handbook of Applied Cryptography (енг.)CRC Press, 1996. — 816 p. — (Discrete Mathematics and Its Applications) — ISBN 978-0-8493-8523-0 Lua грешка на линији 1113: attempt to index field 'wikibase' (a nil value).
НеН {{#invoke:Sources | renderSource | Q27450585}} Введение в криптографию (рус.) / под ред. В. В. ЯщенкоМ.: МЦНМО, 2000. — 271 с. — ISBN 978-5-900916-26-2 Lua грешка на линији 1113: attempt to index field 'wikibase' (a nil value).



local p = {};

local i18nDefaultLanguage = 'ru';
local i18nEditors = {
	fr	= '',
	de	= 'Hrsg.: ',
	es	= '',
	en	= '',
	it	= '',
	ru	= 'под ред. ',
}
local i18nEtAlDefault = ' et al.';
local i18nEtAl = {
	ru	= ' и др.',
}
local i18nVolume = {
	fr	= 'Vol.',
	es	= 'Vol.',
	en	= 'Vol.',
	it	= 'Vol.',
	ru	= 'Т.',
}
local i18nIssue = {
	en	= 'Iss.',
	ru	= 'вып.',
}
local i18nPages = {
	fr = 'P.',
	de = 'S.',
	es = 'P.',
	en = 'P.',
	it = 'P.',
	ru = 'С.',
}

local i18nNumberOfPages = {
	en = 'p.',
	ru = 'с.',
}

local NORMATIVE_DOCUMENTS = {
	Q20754888 = 'Закон Российской Федерации',
	Q20754884 = 'Закон РСФСР',
	Q20873831 = 'Распоряжение Президента Российской Федерации',
	Q20873834 = 'Указ исполняющего обязанности Президента Российской Федерации',
	Q2061228 = 'Указ Президента Российской Федерации',
}

local monthg = {'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', "сентября", "октября", "ноября", "декабря"};

local PREFIX_CITEREF = "CITEREF_";

local options_arxiv = { separator = '; ', conjunction = '; ', format = function( id ) return '[http://arxiv.org/abs/' .. id .. ' arXiv:' .. id .. ']' end, nolinks = true, preferids = false };
local options_doi = { separator = '; ', conjunction = '; ', format = function( doi ) return '[http://dx.doi.org/' .. doi .. ' doi:' .. doi .. ']' end, nolinks = true, preferids = false };

local options_commas = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false };
local options_commas_short = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false, short = true };
local options_commas_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = true, preferids = false };
local options_commas_it = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false };
local options_commas_it_short = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = false, preferids = false, short = true };
local options_commas_it_nolinks = { separator = ', ', conjunction = ', ', format = function( src ) return "''" .. src .. "''" end, nolinks = true , preferids = false };
local options_citetypes = { separator = ' ', conjunction = ' ', format = function( src ) return 'citetype_' .. src end, nolinks = true , preferids = true };

function assertNotNull( argName, arg )
	if ( (not arg) or (arg == nil) ) then
		error( argName .. ' is not specified' )
	end
end

function coalesce( arg1, arg2, arg3, arg4 )
	if ( not isEmpty( arg1 ) ) then return arg1 end
	if ( not isEmpty( arg2 ) ) then return arg2 end
	if ( not isEmpty( arg3 ) ) then return arg3 end
	if ( not isEmpty( arg4 ) ) then return arg4 end
	return nil;
end

function isEmpty( str )
	return ( not str ) or ( str == nil ) or ( #str == 0 );
end

function isInstanceOf( entity, typeEntityId )
	if ( not entity or not entity.claims or not entity.claims.P31 ) then
		return false;
	end

	for _, claim in pairs( entity.claims.P31 ) do
		if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value["numeric-id"] ) then
			local actualTypeId = 'Q' .. claim.mainsnak.datavalue.value["numeric-id"];
			if ( actualTypeId == typeEntityId ) then
				return true;
			end
		end
	end

	return false;
end

function getEntity( context, entityId )
	assertNotNull( 'context', context );
	assertNotNull( 'entityId', entityId );

	local cached = context.cache[ entityId ];
	if ( cached ) then return cached; end;

	local result = mw.wikibase.getEntity( entityId );
	if ( result ) then
		context.cache[ entityId ] = result;
	end

	return result;
end

function toStringSnak( propertyId, strValue )
	assertNotNull('propertyId', strValue)
	assertNotNull('strValue', strValue)

	local snak = { snaktype = "value", property = propertyId, datatype = 'string'};
	snak["datavalue"] = { value = strValue, type = 'string' };
	return snak;
end

function toUrlSnak( propertyId, strValue )
	assertNotNull('propertyId', strValue)
	assertNotNull('strValue', strValue)

	local snak = { snaktype = "value", property = propertyId, datatype = 'string'};
	snak["datavalue"] = { value = strValue, type = 'url' };
	return snak;
end

function toWikibaseEntityIdSnak( propertyId, entityId )
	assertNotNull('propertyId', entityId)
	assertNotNull('entityId', entityId)
	if ( mw.ustring.sub( entityId, 1, 1 ) ~= 'Q' ) then error( 'Incorrect entity ID: «' .. entityId .. '»' ); end;

	local value = {};
	value["entity-type"] = 'item';
	value["numeric-id"] = mw.ustring.sub( entityId , 2);

	local snak = { snaktype = "value", property = propertyId, datatype = 'wikibase-item'};
	snak["datavalue"] = { value = value, type = 'wikibase-entityid' };
	return snak;
end

function renderSource( context, src )
	mw.logObject( src );
	context.lang = getLangCode( getSingle( src.lang ) ) or i18nDefaultLanguage;

	preprocessPlaces( src, context.lang );

	src.title = src.title or getSingle( src.url ) or '\'\'(unspecified title)\'\''

	if ( src.entityId and not src.url ) then
		local entity = getEntity( context, src.entityId );
		if ( entity.sitelinks and entity.sitelinks[ context.lang .. 'wikisource'] ) then
			src.url = ':' .. context.lang .. ':s:' .. entity.sitelinks[ context.lang .. 'wikisource' ].title;
		end
	end

	if ( not src.year and src.dateOfPublication ) then
		local date = getSingle( src.dateOfPublication );
		src.year = mw.ustring.sub( date, 2, 5 );
	end

	if ( not src.year and src.dateOfCreation ) then
		local date = getSingle( src.dateOfCreation );
		src.year = mw.ustring.sub( date, 2, 5 );
	end

	local result;
	if ( src.author ) then
		result = getPeopleAsAuthorWikitext( context, src.author, options_commas );
	end
	if ( not isEmpty( result )) then
		result = '\'\'' .. result .. '\'\' ';
	else
		result = '';
	end
 
 	if ( src.part ) then
 		if ( src.url ) then
			result = result .. wrapInUrl( src.url, toString( context, src.part, options_commas_nolinks ) );
		else
			result = result .. toString( context, src.part, options_commas );
		end
		result = result .. ' // ' .. toString( context, src.title, options_commas );
	else
		-- title only
 		if ( src.url ) then
			result = result .. wrapInUrl( src.url, toString( context, src.title, options_commas_nolinks ) );
		else
			result = result .. toString( context, src.title, options_commas );
		end
 	end

	if ( src.subtitle ) then
		result = result .. ": " .. toString( context, src.subtitle, options_commas );
	end

	if ( src.originaltitle ) then
		result = result .. ' = ' .. toString( context, src.originaltitle, options_commas );
	end

	if ( src.publication ) then
		if ( type( src.publication.title or '') ~= 'string' ) then error('type of src.publication.title is not string but ' .. type( src.publication.title ) ) end;

		result = result .. ' // ' .. toString( context, src.publication, options_commas_it_short );
		if ( src.publication.subtitle ) then
			result = result .. ': ' .. toString( context, src.publication.subtitle, options_commas_it_short );
		end
	end

	if ( src.editor ) then
		local prefix = i18nEditors[ context.lang ] or i18nEditors[ i18nDefaultLanguage ];
		result = result .. ' / ' .. prefix .. toString( context, src.editor, options_commas );
	end

	if ( src.place or src.publisher or src.year ) then
		result = result .. ' — ';
		if ( src.place ) then
			result = result .. toString( context, src.place, options_commas_short );
			if ( src.publisher or src.year ) then
				result = result .. ': ';
			end
		end
		if ( src.publisher ) then
			result = result .. toString( context, src.publisher, options_commas_short );
			if ( src.year ) then
				result = result .. ', ';
			end
		end
		if ( src.year ) then
			result = result .. toString( context, src.year, options_commas );
		end
		result = result .. '.';
	end
 
 	if ( src.volume or src.issue ) then
 		result = result .. ' — ';
		if ( src.volume ) then
			local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ];
			result = result .. letter .. '&nbsp;' .. toString( context, src.volume, options_commas );
			if ( src.issue ) then
				local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
				result = result .. ', ' .. letter .. '&nbsp;' .. toString( context, src.issue, options_commas ) .. '.';
			else
				result = result .. '.';
			end
		else
			local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
			result = result .. letter .. '&nbsp;' .. toString( context, src.issue, options_commas ) .. '.';
		end
 	end

	if ( src.pages ) then
		local letter = i18nPages[ context.lang ] or i18nPages[ i18nDefaultLanguage ];
		result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.pages, options_commas )  .. '.';
	end

	if ( src.numberOfPages ) then
		local letter = i18nNumberOfPages[ context.lang ] or i18nNumberOfPages[ i18nDefaultLanguage ];
		result = result .. ' — ' .. toString( context, src.numberOfPages, options_commas ) .. '&nbsp;' .. letter;
	end

	if ( src.bookSeries ) then
		result = result .. ' — (' .. toString( context, src.bookSeries, options_commas )

	 	if ( src.bookSeriesVolume or src.bookSeriesIssue ) then
	 		result = result .. '; ';
			if ( src.bookSeriesVolume ) then
				local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ];
				result = result .. letter .. '&nbsp;' .. toString( context, src.bookSeriesVolume, options_commas );
				if ( src.bookSeriesIssue ) then
					local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
					result = result .. ', ' .. letter .. '&nbsp;' .. toString( context, src.bookSeriesIssue, options_commas );
				else
					result = result;
				end
			else
				local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
				result = result .. letter .. '&nbsp;' .. toString( context, src.bookSeriesIssue, options_commas );
			end
	 	end

		result = result .. ')';
	end

	if ( src.isbn ) then
		result = result .. ' — ISBN ' .. toString( context, src.isbn, options_commas );
	end

	if ( src.issn ) then
		result = result .. ' — ISSN ' .. toString( context, src.issn, options_commas );
	end

	if ( src.doi ) then
		result = result .. ' — ' .. toString( context, src.doi, options_doi );
	end

	if ( src.arxiv ) then
		result = result .. ' — ' .. toString( context, src.arxiv, options_arxiv );
	end

	if ( src.entityId ) then
		if ( src.type and src.entityId ) then
			-- wrap into span to target from JS
			result = '<span class="wikidata_cite ' .. toString( context, src.type, options_citetypes ) .. '" data-entity-id="' .. getSingle( src.entityId ) .. '">' .. result .. '</span>'
		else
			result = '<span class="wikidata_cite citetype_unknown" data-entity-id="' .. getSingle( src.entityId ) .. '">' .. result .. '</span>'
		end
	end

	if ( src.accessdate ) then
			local date = getSingle( src.accessdate );
			local pattern = "(%-?%d+)%-(%d+)%-(%d+)T";
			local y, m, d = mw.ustring.match( date , pattern );
			y,m,d = tonumber(y),tonumber(m),tonumber(d);
			result = result .. " <small>Проверено " .. tostring(d) .. " " .. monthg[m]  .. " " .. tostring(y) .. ".</small>";
	end

    -- append invisible links to all elements used by source for tracking purposes
    local result = result .. '<div style="display:none">';
	for key, entity in pairs( context.cache ) do
		result = result .. '[[d:Track:' .. key .. ']]';
	end
    result = result ..'</div>'

	return {text = result, code = src.code};
end

function wrapInUrl( urls, text )
	local url = getSingle( urls );
	if ( string.sub( url, 1, 1 ) == ':' ) then
		return '[[' .. url .. '|' .. text .. ']]';
	else
		return '[' .. url .. ' ' .. text .. ']';
	end
end

function renderShortReference( src )
	context = {
		cache = {},
		lang = getSingle( src.lang ) or i18nDefaultLanguage;
	};
	src.title = src.title or '\'\'(unspecified title)\'\''

	local result = '[[#' .. PREFIX_CITEREF .. src.code .. '|';
	if ( src.author ) then
		result = result .. toString( context, src.author, options_authors_nolinks );
	else
		result = result .. toString( context, src.title, options_commas_it_nolinks );
	end
	result = result .. ']]'

	if ( src.year ) then
		result = result .. ', ' .. toString( context, src.year, options_commas );
	end

	if ( src.volume ) then
		local letter = i18nVolume[ context.lang ] or i18nVolume[ i18nDefaultLanguage ];
		result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.volume, options_commas ) .. '.';
	end

	if ( src.issue ) then
		local letter = i18nIssue[ context.lang ] or i18nIssue[ i18nDefaultLanguage ];
		result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.issue, options_commas ) .. '.';
	end
 
	if ( src.pages ) then
		local letter = i18nPages[ context.lang ] or i18nPages[ i18nDefaultLanguage ];
		result = result .. ' — ' .. letter .. '&nbsp;' .. toString( context, src.pages, options_commas )  .. '.';
	end
 
end

function getSingle( value )
	if ( not value ) then
		return;
	end
	if ( type( value ) == 'string' ) then
		return value;
	elseif ( type( value ) == 'table' ) then
		if ( value.id ) then
			return value.id;
		end

		for i, tableValue in pairs( value ) do
			return getSingle( tableValue );
		end
	end

	return '(unknown)';
end

function toString( context, value, options )
	if ( type( value ) == 'string' ) then
		return options.format( value );
	elseif ( type( value ) == 'table' ) then
		if ( value.id ) then
			-- this is link
			if ( type( value.label or '' ) ~= 'string' ) then mw.logObject( value ); error('label of table value is not string but ' .. type( value.label ) ) end

			if ( options.preferids ) then
				return options.format( value.id );
			else
				if ( options.nolinks ) then
					return options.format( value.label or mw.wikibase.label( value.id ) or '\'\'(untranslated title)\'\'' );
				else
					return options.format( renderLink( context, value.id, value.label, options ) );
				end
			end
		end

		local resultList = {};
		for i, tableValue in pairs( value ) do
			table.insert( resultList, toString( context, tableValue, options ) );
		end

		return mw.text.listToText( resultList, options.separator, options.conjunction);
	else
		return options.format( '(unknown type)' );
	end

	return '';
end

function renderLink( context, entityId, customTitle, options )
	if ( not entityId ) then error("entityId is not specified") end
	if ( type( entityId ) ~= 'string' ) then error('entityId is not string, but ' .. type( entityId ) ) end
	if ( type( customTitle or '' ) ~= 'string' ) then error('customTitle is not string, but ' .. type( customTitle ) ) end

	local title = customTitle;

	if ( isEmpty( title ) ) then
		local entity = getEntity( context, entityId );

		-- ISO 4
		if ( isEmpty( title ) ) then
			if ( entity.claims and entity.claims.P1160 ) then
				for _, claim in pairs( entity.claims.P1160 ) do
					if ( claim
							and claim.mainsnak
							and claim.mainsnak.datavalue
							and claim.mainsnak.datavalue.value
							and claim.mainsnak.datavalue.value.language == context.lang ) then
						title = claim.mainsnak.datavalue.value.text;
						mw.log('Got title of ' .. entityId .. ' from ISO 4 claim: «' .. title .. '»' )
						break;
					end
				end
			end
		end

		-- official name P1448
		-- short name P1813
		if ( isEmpty( title ) and options.short ) then
			if ( entity.claims and entity.claims.P1813 ) then
				for _, claim in pairs( entity.claims.P1813 ) do
					if ( claim
							and claim.mainsnak
							and claim.mainsnak.datavalue
							and claim.mainsnak.datavalue.value
							and claim.mainsnak.datavalue.value.language == context.lang ) then
						title = claim.mainsnak.datavalue.value.text;
						mw.log('Got title of ' .. entityId .. ' from short name claim: «' .. title .. '»' )
						break;
					end
				end
			end
		end
		-- person name P1559
		-- labels
		if ( isEmpty( title ) and entity.labels[ context.lang ] ) then
			title = entity.labels[ context.lang ].value;
			mw.log('Got title of ' .. entityId .. ' from label: «' .. title .. '»' )
		end
	end

	local actualText = title or '\'\'(untranslated)\'\'';
	local link = getElementLink( context, entityId, entity);
	return wrapInUrl( link, actualText );
end

function getElementLink( context, entityId, entity )
	-- fast sitelink lookup, not an expensive operation
	local link = mw.wikibase.sitelink( entityId )
	if ( link ) then return ':' .. link end

	if ( not entity and entityId ) then
		entity = getEntity( context, entityId )
	end

	if ( entity ) then
		-- link to entity in source context language
		local projectToCheck = context.lang .. 'wiki';
		if ( entity.sitelinks and entity.sitelinks[ projectToCheck ] ) then
			return ':' .. context.lang .. ':' .. entity.sitelinks[ projectToCheck ].title;
		end
	end

	if ( entityId ) then return ':d:' .. entityId end;
	-- if ( entityId ) then return 'https://tools.wmflabs.org/reasonator/?q=' .. entityId .. '&lang=ru' end;
	return nil;
end

function getPeopleAsAuthorWikitext( context, value, options )
	if ( type( value ) == 'string' ) then
		return personNameToAuthorName( value );
	elseif ( type( value ) == 'table' ) then
		if ( value.id ) then
			-- this is link
			if ( options.preferids ) then
				return value.id;
			else
				if ( options.nolinks ) then
					return getPersonNameAsAuthorLabel( context, value.id, value.label, options );
				else
					return getPersonNameAsAuthorWikitext( context, value.id, value.label, options );
				end
			end
		end

		local resultList = {};
		for i, tableValue in pairs( value ) do
			local nextWikitext = getPeopleAsAuthorWikitext( context, tableValue, options );
			if ( not isEmpty( nextWikitext ) ) then
				table.insert( resultList, nextWikitext );
				if ( #resultList == 4 ) then
					-- even 4 is too much, but we preserve 4th to mark that "it's more than 3"
					break;
				end
			end
		end

		local resultWikitext = '';
		for i, wikitext in pairs( resultList ) do
			if ( i == 4 ) then
				resultWikitext = resultWikitext .. ( i18nEtAl[ context.lang ] or i18nEtAlDefault );
				break;
			end
			if ( i ~= 1 ) then
				resultWikitext = resultWikitext .. ', ';
			end
			resultWikitext = resultWikitext .. wikitext;
		end

		return resultWikitext;
	end

	return options.format( '(unknown type)' );
end

function getPersonNameAsAuthorWikitext( context, entityId, customLabel, options )
	local personNameAsAuthor = getPersonNameAsAuthorLabel( context, entityId, customLabel, options);
	if ( personNameAsAuthor == nil ) then
		return nil;
	end

	local link = getElementLink( context, entityId, nil );
	return wrapInUrl( link, personNameAsAuthor );
end

function getPersonNameAsAuthorLabel( context, entityId, providedLabel, options )
	-- would custom label provided we don't need to check entity at all
	if ( not isEmpty( providedLabel ) ) then
		mw.log( 'Custom label provided for ' .. entityId );
		return personNameToAuthorName( providedLabel );
	end

	local entity = getEntity( context, entityId );
	if ( not entity ) then return '\'\'(entity ' .. entityId .. ' is missing)\'\'' end;
	if ( not isInstanceOf( entity, 'Q5' ) ) then
		mw.log( 'Entity ' .. entityId .. ' is not a person' );
		return nil;
	end

	local personName = nil;
	-- support only labels so far
	if ( entity.labels[ context.lang ] ) then
		personName = entity.labels[ context.lang ].value;
		mw.log('Got person name of ' .. entityId .. ' from label: «' .. personName .. '»' )
	end

	if ( isEmpty( personName ) ) then
		return '\'\'(not translated to ' .. context.lang .. ')\'\'';
	else
		return personNameToAuthorName( personName );
	end
end

function personNameToAuthorName( fullName )
	if ( not fullName ) then return fullName; end

	local f, i, o = mw.ustring.match( fullName, '^%s*(%a[%a\-]*)\,%s(%a[%a\-]*)%s(%a[%a\-]*)%s*$' );
	if ( f ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Fa, I. O.» match' );
		return f .. '&nbsp;'
			.. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;'
			.. mw.ustring.sub( o, 1, 1 ) .. '.';
	end

	local f1, f2, i = mw.ustring.match( fullName, '^%s*(%a[%a\-]*)%s(%a[%a\-]*)\,%s(%a[%a\-]*)%s*$' );
	if ( f1 ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Fa Fa, I» match' );
		return f1 .. '&nbsp;' .. f2 .. '&nbsp;'
			.. mw.ustring.sub( i, 1, 1 ) .. '.';
	end

	local i, o, f = mw.ustring.match( fullName, '^%s*(%a)\.%s(%a)\.%s(%a[%a\-]+)%s*$');
	if ( f ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «I. O. Fa» match' );
		return f .. '&nbsp;' .. i .. '.&nbsp;' .. o .. '.';
	end

	local i1, i2, i3, f = mw.ustring.match( fullName, '^%s*(%a)\.%s(%a)\.%s(%a)\.%s(%a[%a\-]+)%s*$');
	if ( f ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «I. O. ?. Fa» match' );
		return f .. '&nbsp;' .. i1 .. '.&nbsp;' .. i2 .. '.&nbsp;' .. i3 .. '.';
	end

    -- Joel J. P. C. Rodrigues
	local i1, i2, i3, i4, f = mw.ustring.match( fullName, '^%s*(%a)[%a\-]+%s(%a)\.%s(%a)\.%s(%a)\.%s(%a[%a\-]+)%s*$');
	if ( f ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «I. O. ?. Fa» match' );
		return f .. '&nbsp;' .. i1 .. '.&nbsp;' .. i2 .. '.&nbsp;' .. i3 .. '.&nbsp;' .. i4 .. '.';
	end

	local i, o, f = mw.ustring.match( fullName, '^%s*(%a[%a\-]*)%s(%a)\.%s(%a[%a\-]*)%s*$');
	if ( f ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Im O. Fa» match' );
		return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. o .. '.';
	end

	local i, o, f = mw.ustring.match( fullName, '^%s*(%a[%a\-]*)%s(%a[%a\-]*)%s(%a[%a\-]*)%s*$');
	if ( f ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Im Ot Fa» match' );
		return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. mw.ustring.sub( o, 1, 1 ) .. '.';
	end

	local i, o, f = mw.ustring.match( fullName, '^%s*(%a[%a\-]+)%s(%a[%a\-]+)%s+оглы%s+(%a[%a\-]+)%s*$');
	if ( f ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Im Ot оглы Fa» match' );
		return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.&nbsp;' .. mw.ustring.sub( o, 1, 1 ) .. '.';
	end

	local i, f = mw.ustring.match( fullName, '^%s*(%a[%a\-]+)%s(%a[%a\-]+)%s*$');
	if ( f ) then
		mw.log( 'personNameToAuthorName: «' .. fullName .. '»: have «Im Fa» match' );
		return f .. '&nbsp;' .. mw.ustring.sub( i, 1, 1 ) .. '.';
	end

	mw.log( 'Unmatched any pattern: «' .. fullName .. '»' );
	return fullName;
end

-- Expand special types of references when additional data could be found in OTHER entity properties
function expandSpecials( currentEntity, reference, data )
	if ( reference.snaks.P248
			and reference.snaks.P248[1]
			and reference.snaks.P248[1].datavalue
			and reference.snaks.P248[1].datavalue.value["numeric-id"]) then
		local sourceId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"];

		-- Gemeinsame Normdatei -- specified by P227
		if ( sourceId == 'Q36578' ) then
			appendSnaks( currentEntity.claims, 'P227', data, 'title', { format = function( gnd ) return 'Record #' .. gnd; end } );
			appendSnaks( currentEntity.claims, 'P227', data, 'url', { format = function( gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } );
			data.publication = { id = 'Q36578', label = 'Gemeinsame Normdatei' }
			data.year = '2012—2015'
		end

		-- BNF -- specified by P268
		if ( sourceId == 'Q15222191' ) then
			appendSnaks( currentEntity.claims, 'P268', data, 'title', { format = function( id ) return 'Record #' .. id; end } );
			appendSnaks( currentEntity.claims, 'P268', data, 'url', { format = function( id ) return 'http://catalogue.bnf.fr/ark:/12148/cb' .. id; end } );
			expandSpecialsQualifiers( currentEntity, 'P268', data );
		end

		-- Find a Grave -- specified by P535
		if ( sourceId == 'Q63056' ) then
			appendSnaks( currentEntity.claims, 'P535', data, 'url', { format = function( id ) return 'http://www.findagrave.com/cgi-bin/fg.cgi?page=gr&GRid=' .. id; end } );
			expandSpecialsQualifiers( currentEntity, 'P535', data );
		end

		--  Dizionario Biografico degli Italiani -- specified by P1986
		if ( sourceId == 'Q1128537' ) then
			if ( not data.lang ) then data.lang = { id = 'Q652' } end;
			appendSnaks( currentEntity.claims, 'P1986', data, 'url', { format = function( id ) return 'http://www.treccani.it/enciclopedia/' .. id .. '_%28Dizionario_Biografico%29/' end } );
			expandSpecialsQualifiers( currentEntity, 'P1986', data );
		end

		-- Union List of Artist Names -- specified by P245
		if ( sourceId == 'Q2494649' ) then
			appendSnaks( currentEntity.claims, 'P245', data, 'url', { format = function( id ) return 'http://www.getty.edu/vow/ULANFullDisplay?find=&role=&nation=&subjectid=' .. id end } );
			expandSpecialsQualifiers( currentEntity, 'P245', data );
		end

		-- Gran Enciclopèdia Catalana -- specified by P1296
		if ( sourceId == 'Q2664168' ) then
			appendSnaks( currentEntity.claims, 'P1296', data, 'url', { format = function( id ) return 'http://www.enciclopedia.cat/enciclop%C3%A8dies/gran-enciclop%C3%A8dia-catalana/EC-GEC-' .. id .. '.xml'; end } );
			expandSpecialsQualifiers( currentEntity, 'P1296', data );
		end

		-- Encyclopædia Britannica online -- specified by P1417
		if ( sourceId == 'Q5375741' ) then
			appendSnaks( currentEntity.claims, 'P1417', data, 'url', { format = function( id ) return 'http://global.britannica.com/' .. id; end } );
			expandSpecialsQualifiers( currentEntity, 'P1417', data );
		end

		-- Electronic Jewish Encyclopedia (Elektronnaja Evrejskaja Entsiklopedia) -- specified by P1438
		if ( sourceId == 'Q1967250' ) then
			appendSnaks( currentEntity.claims, 'P1438', data, 'url', { format = function( id ) return 'http://www.eleven.co.il/article/' .. id; end } );
			expandSpecialsQualifiers( currentEntity, 'P1438', data );
		end

		-- sports-reference.com -- specified by P1447
		if ( sourceId == 'Q18002875' ) then
			appendSnaks( currentEntity.claims, 'P1447', data, 'url', { format = function( id ) return 'http://www.sports-reference.com/olympics/athletes/' .. id .. '.html'; end } );
			expandSpecialsQualifiers( currentEntity, 'P1447', data );
		end
	end
end

function expandSpecialsQualifiers( entity, propertyId, result )
	if ( entity.claims ~= nil and entity.claims[propertyId] ~= nil ) then
		local claims = entity.claims[propertyId];
		appendQualifiers( claims, 'P958', result, 'part', {} );
		appendQualifiers( claims, 'P953', result, 'url', {} );
		appendQualifiers( claims, 'P1065', result, 'url', {} );
		appendQualifiers( claims, 'P854', result, 'url', {} );
		appendQualifiers( claims, 'P357', result, 'title', {} ); -- obsolete
		appendQualifiers( claims, 'P1476', result, 'title', {} );
		appendQualifiers( claims, 'P478', result, 'volume', {} );
		appendQualifiers( claims, 'P433', result, 'issue', {} );
	end
end

function findClaimsByValue( entity, propertyId, value )
	local result = {};
	if ( entity and entity.claims and entity.claims[propertyId] ) then
		for i, claim in pairs( entity.claims[propertyId] ) do
			if ( claim.mainsnak and claim.mainsnak.datavalue ) then
				local datavalue = claim.mainsnak.datavalue;
				if ( datavalue.type == "string" and datavalue.value == value 
					or datavalue.type == "wikibase-entityid" and datavalue.value["entity-type"] == "item" and tostring( datavalue.value["numeric-id"] ) == mw.ustring.sub( value, 2 ) ) then
					table.insert( result, claim );
				end
			end
		end
	end
	return result;
end

function appendSnaks( allSnaks, snakPropertyId, result, property, options )
	-- do not populate twice
	if ( result[property] ) then return result end;
	if ( not allSnaks ) then return result; end;
	
	local selectedSnakes = allSnaks[ snakPropertyId ];
	if ( not selectedSnakes ) then return result; end;

	local hasPreferred = false;
	for k, snak in pairs( selectedSnakes ) do
		if ( snak and snak.mainsnak and snak.mainsnak.datavalue and snak.rank == 'preferred' ) then
			--it's a preferred claim
			appendImpl( snak.mainsnak.datavalue, snak.qualifiers, result, property, options );
			hasPreferred = true;
		end
	end
	if ( hasPreferred ) then return result; end;

	for k, snak in pairs( selectedSnakes ) do
		if ( snak and snak.mainsnak and snak.mainsnak.datavalue and snak.rank ~= 'deprecated' ) then
			--it's a claim
			appendImpl( snak.mainsnak.datavalue, snak.qualifiers, result, property, options );
		elseif ( snak and snak.datavalue ) then
			-- it's a snak
			appendImpl( snak.datavalue, nil, result, property, options );
		end
	end
end

function appendQualifiers( claims, qualifierPropertyId, result, resultProperty, options )
	-- do not populate twice
	if ( not claims ) then return result end;
	if ( result[resultProperty] ) then return result end;

	for i, claim in pairs( claims ) do
		if ( claim.qualifiers and claim.qualifiers[ qualifierPropertyId ] ) then
			for k, qualifier in pairs( claim.qualifiers[ qualifierPropertyId ] ) do
				if ( qualifier and qualifier.datavalue ) then
					appendImpl( qualifier.datavalue, nil, result, resultProperty, options );
				end
			end
		end
	end
end

function appendImpl( datavalue, qualifiers, result, property, options )
	if ( datavalue.type == 'string' ) then
		local value = datavalue.value;
		if ( options.format ) then
			value = options.format( value );
		end
		appendImpl_toTable( result, property );
		table.insert( result[property], value);
	elseif ( datavalue.type == 'monolingualtext' ) then
		local value = datavalue.value.text;
		if ( options.format ) then
			value = options.format( value );
		end
		appendImpl_toTable( result, property );
		table.insert( result[property], value);
	elseif ( datavalue.type == 'quantity' ) then
		local value = datavalue.value.amount;
		if ( mw.ustring.sub( value , 1, 1 ) == '+' ) then
			value = mw.ustring.sub( value , 2 );
		end
		if ( options.format ) then
			value = options.format( value );
		end
		appendImpl_toTable( result, property );
		table.insert( result[property], value);
	elseif ( datavalue.type == 'wikibase-entityid' ) then
		local value = datavalue.value;
		appendImpl_toTable( result, property );
		local toInsert = {
			id = 'Q' .. value["numeric-id"],
			label = getSingleStringQualifierValue(qualifiers, 'P1932') -- stated as
		};
		table.insert( result[property], toInsert );
	elseif datavalue.type == 'time' then
		local value = datavalue.value;
		if ( options.format ) then
			value = options.format( value );
		end
		appendImpl_toTable( result, property );
		table.insert( result[property], tostring( value.time ));
    end 
end

function appendImpl_toTable(result, resultProperty)
	if ( not result[resultProperty] ) then
		result[resultProperty] = {};
	elseif ( type( result[resultProperty] ) == 'string' or ( type( result[resultProperty] ) == 'table' and type( result[resultProperty].id ) == 'string' ) ) then
		result[resultProperty] = { result[resultProperty] };
	end
end

function getSingleStringQualifierValue( allQualifiers, qualifierPropertyId )
	if ( not allQualifiers ) then return end
	if ( not allQualifiers[qualifierPropertyId] ) then return end

	for k, qualifier in pairs( allQualifiers[qualifierPropertyId] ) do
		if ( qualifier and qualifier.datatype == 'string'
				and qualifier.datavalue and qualifier.datavalue.type == 'string' and not isEmpty( qualifier.datavalue.value ) ) then
			return qualifier.datavalue.value;
		end
	end

	return;
end

function expandDescribed ( context, sourceEntity, data )
	local described = data.described;

	-- use only first one
	if ( type( described ) == 'table' and described[1] and described[1].id ) then
		data.described = described[1];
		described = data.described;
	end

	data.publication = data.described;

	if ( described and described.id ) then
		if ( sourceEntity ) then
			-- do we have appropriate record in P1433 ?
			local claims = findClaimsByValue( sourceEntity, 'P1343', described.id );
			if ( claims and #claims ~= 0 ) then
				appendQualifiers( claims, 'P958', data, 'part', {} );
				appendQualifiers( claims, 'P50', data, 'author', {} );
				appendQualifiers( claims, 'P953', data, 'url', {} );
				appendQualifiers( claims, 'P1065', data, 'url', {} );
				appendQualifiers( claims, 'P854', data, 'url', {} );
				appendQualifiers( claims, 'P357', data, 'title', {} ); -- obsolete
				appendQualifiers( claims, 'P1476', data, 'title', {} );
				appendQualifiers( claims, 'P478', data, 'volume', {} );
			end
		end
		local titleWerePresent = not (not data.title);
		local desEntity = getEntity( context, described.id );
		if ( desEntity ) then
			populateSourceDataImpl( desEntity, data );
			if ( titleWerePresent and isEmpty( data.publication.label ) ) then
				appendSnaks( desEntity.claims, 'P1160', data, 'publication-title', {} ); -- obsolete
				data.publication.label = getSingle( data['publication-title'] );
			end
			if ( titleWerePresent and isEmpty( data.publication.label ) ) then
				appendSnaks( desEntity.claims, 'P357', data, 'publication-title', {} ); -- obsolete
				appendSnaks( desEntity.claims, 'P1476', data, 'publication-title', {} );
				appendSnaks( desEntity.claims, 'P1680', data, 'publication-subtitle', {} );
				data.publication.label = getSingle( data['publication-title'] );
				data.publication.subtitle = getSingle( data['publication-subtitle'] );
			end
		end
	end
end

function expandPublication( context, sourceEntity, data )
	local publication = data.publication;

	-- use only first one
	if ( type( publication ) == 'table' and publication[1] and publication[1].id ) then
		data.publication = publication[1];
		publication = data.publication;
	end

	if ( not publication ) then return end;
	if ( not publication.id ) then return end;

	if ( sourceEntity ) then
		-- do we have appropriate record in P1433 ?

		local claims = findClaimsByValue( sourceEntity, 'P1433', publication.id );
		if ( claims and #claims ~= 0 ) then
			appendQualifiers( claims, 'P958', data, 'part', {} );
			appendQualifiers( claims, 'P953', data, 'url', {} );
			appendQualifiers( claims, 'P1065', data, 'url', {} );
			appendQualifiers( claims, 'P854', data, 'url', {} );
			appendQualifiers( claims, 'P856', data, 'url', {} );
			appendQualifiers( claims, 'P123', data, 'publisher', {} );
			appendQualifiers( claims, 'P291', data, 'place', {} );
			appendQualifiers( claims, 'P304', data, 'pages', {} );
			appendQualifiers( claims, 'P1104', data, 'numberOfPages', {} );
			appendQualifiers( claims, 'P478', data, 'volume', {} );
			appendQualifiers( claims, 'P433', data, 'issue', {} );
			appendQualifiers( claims, 'P571', data, 'dateOfCreation', {} );
			appendQualifiers( claims, 'P577', data, 'dateOfPublication', {} );
			appendQualifiers( claims, 'P212', data, 'isbn', {} ); -- ISBN-13
			appendQualifiers( claims, 'P957', data, 'isbn', {} ); -- ISBN-10
		end
	end

	local titleWerePresent = not (not data.title);
	local pubEntity = getEntity( context, publication.id );
	populateSourceDataImpl( pubEntity, data );
	if ( titleWerePresent and isEmpty( data.publication.label ) ) then
		appendSnaks( pubEntity.claims, 'P1160', data, 'publication-title', {} ); -- obsolete
		data.publication.label = getSingle( data['publication-title'] );
	end
	if ( titleWerePresent and isEmpty( data.publication.label ) ) then
		appendSnaks( pubEntity.claims, 'P357', data, 'publication-title', {} ); -- obsolete
		appendSnaks( pubEntity.claims, 'P1476', data, 'publication-title', {} );
		appendSnaks( pubEntity.claims, 'P1680', data, 'publication-subtitle', {} );
		data.publication.label = getSingle( data['publication-title'] );
		data.publication.subtitle = getSingle( data['publication-subtitle'] );
	end

	if ( pubEntity.claims and pubEntity.claims.P361 ) then
		for c, claim in pairs( pubEntity.claims.P361 ) do
			if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value["numeric-id"] ) then
				local possibleBookSeriesEntityId = 'Q' .. claim.mainsnak.datavalue.value["numeric-id"];
				local possibleBookSeriesEntity = getEntity( context, possibleBookSeriesEntityId );
				if ( isInstanceOf( possibleBookSeriesEntity, 'Q277759' ) ) then
					appendImpl_toTable( data, 'bookSeries' );
					table.insert( data.bookSeries, { id = possibleBookSeriesEntityId } );

					appendQualifiers( { claim }, 'P478', data, 'bookSeriesVolume', {} );
					appendQualifiers( { claim }, 'P433', data, 'bookSeriesIssue', {} );
				end
			end
		end
	end

	expandBookSeries( context, data );
end

function expandBookSeries( context, data )
	local bookSeries = data.bookSeries;
	if ( not bookSeries ) then return end;

	-- use only first one
	if ( type( bookSeries ) == 'table' and bookSeries[1] and bookSeries[1].id ) then
		data.bookSeries = bookSeries[1];
		bookSeries = data.bookSeries;
	end

	if ( not bookSeries ) then return end;
	if ( not bookSeries.id ) then return end;

	local bookSeriesEntity = getEntity( context, bookSeries.id );
	appendSnaks( bookSeriesEntity.claims, 'P123', data, 'publisher', {} );
	appendSnaks( bookSeriesEntity.claims, 'P291', data, 'place', {} );
	appendSnaks( bookSeriesEntity.claims, 'P236', data, 'issn', {} );
end

function populateSourceDataImpl( entity, plainData )
	populateDataFromClaims( entity.id, entity.claims, plainData );

	local normativeTitle = getNormativeTitle( entity )
	if ( normativeTitle ) then
		local y, m, d = mw.ustring.match( getSingle( plainData.dateOfCreation ) , "(%-?%d+)%-(%d+)%-(%d+)T" );
		y,m,d = tonumber(y),tonumber(m),tonumber(d);
		local title = toString( { lang='ru' }, plainData.title, options_commas_nolinks );
		plainData.title = { normativeTitle .. " от&nbsp;" .. tostring(d) .. "&nbsp;" .. monthg[m]  .. " " .. tostring(y) .. "&nbsp;г. №&nbsp;" .. getSingle( plainData.docNumber ) .. ' «' .. title.. '»' }
	end

	if ( not plainData.title ) then
		if ( entity.labels and entity.labels.ru and entity.labels.ru.value ) then
			plainData.title = { entity.labels.ru.value };
		end
	end

	return plainData;
end

function populateDataFromClaims( entityId, claims, data )
	appendSnaks( claims, 'P50', data, 'author', {} );
	appendSnaks( claims, 'P407', data, 'lang', {} );
	appendSnaks( claims, 'P364', data, 'lang', {} );
	appendSnaks( claims, 'P958', data, 'part', {} );
	if ( not data.title ) then
		if ( not isEmpty( entityId ) ) then
			local optionsAsLinks = { format = function( text ) return { id = entityId, label = text } end };
			appendSnaks( claims, 'P357', data, 'title', optionsAsLinks ); -- obsolete
			appendSnaks( claims, 'P1476', data, 'title', optionsAsLinks );
		else
			appendSnaks( claims, 'P357', data, 'title', {} ); -- obsolete
			appendSnaks( claims, 'P1476', data, 'title', {} );
		end
		appendSnaks( claims, 'P1680', data, 'subtitle', {} );
	end
	appendSnaks( claims, 'P953', data, 'url', {} );
	appendSnaks( claims, 'P1065', data, 'url', {} );
	appendSnaks( claims, 'P854', data, 'url', {} );
	appendSnaks( claims, 'P856', data, 'url', {} );
	appendSnaks( claims, 'P1343', data, 'described', {} );
	appendSnaks( claims, 'P1433', data, 'publication', {} );
	appendSnaks( claims, 'P123', data, 'publisher', {} );
	appendSnaks( claims, 'P291', data, 'place', {} );
	appendSnaks( claims, 'P304', data, 'pages', {} );
	appendSnaks( claims, 'P1104', data, 'numberOfPages', {} );
	appendSnaks( claims, 'P478', data, 'volume', {} );
	appendSnaks( claims, 'P433', data, 'issue', {} );
	appendSnaks( claims, 'P571', data, 'dateOfCreation', {} );
	appendSnaks( claims, 'P577', data, 'dateOfPublication', {} );
	appendSnaks( claims, 'P212', data, 'isbn', {} ); -- ISBN-13
	appendSnaks( claims, 'P957', data, 'isbn', {} ); -- ISBN-10
	appendSnaks( claims, 'P236', data, 'issn', {} );
	
    -- web
	-- appendSnaks( claims, 'P813', data, 'accessdate', {} );

    -- docs
	appendSnaks( claims, 'P1545', data, 'docNumber', {} );

	-- other
	appendSnaks( claims, 'P31', data, 'type', {} );

	appendSnaks( claims, 'P818', data, 'arxiv', {} );
	appendSnaks( claims, 'P356', data, 'doi', {} );
	-- JSTOR
	appendSnaks( claims, 'P888', data, 'url', { format = function( id ) return 'http://www.jstor.org/stable/' .. id end } );

	return src;
end

function updateWithRef( reference, src )
	-- specified
	if ( reference.snaks.P662 ) then
		local cid = reference.snaks.P662[1].datavalue.value;
		src.code = src.code .. '-cid:' .. cid;
		src.title = 'Compound Summary for: CID ' .. cid;
		src.url = 'http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?cid=' .. cid;
		src.publication = { id = 'Q278487', label = 'PubChem' };
	end

	populateDataFromClaims( nil, reference.snaks, src);
	return src;
end

function p.renderSource( frame )
	p.currentFrame = frame;

	local arg = frame.args[1];
	local refAnchor = frame.args['ref'];
	local refAnchorYear = frame.args['ref-year'];
	local args = {};
	args.refAnchor = frame.args['ref'];
	args.refAnchorYear = frame.args['ref-year'];
	args.part = frame.args['part'];
	args.pages = frame.args['pages'];

	return p.renderSourceImpl( mw.text.trim( arg ), args );
end

function copyArgsToSnaks( args, snaks )
	if ( not isEmpty( args.part ) ) then snaks.P958 = { toStringSnak( 'P958', tostring( args.part ) ) } end
	if ( not isEmpty( args.pages ) ) then snaks.P304 = { toStringSnak( 'P304', tostring( args.pages ) ) } end
	if ( not isEmpty( args.issue ) ) then snaks.P433 = { toStringSnak( 'P433', tostring( args.issue ) ) } end
	if ( not isEmpty( args.volume ) ) then snaks.P478 = { toStringSnak( 'P478', tostring( args.volume ) ) } end
	if ( not isEmpty( args.url ) ) then snaks.P953 = { toUrlSnak( 'P953', tostring( args.url ) ) } end
end

function p.renderSourceImpl( entityId, args )
	args = args or {};

	local snaks = {};
	snaks.P248 = { toWikibaseEntityIdSnak( 'P248', entityId ) };
	copyArgsToSnaks( args, snaks );

	local rendered = renderReferenceImpl( mw.wikibase.getEntity(), { snaks = snaks }, args.refAnchor, args.refAnchorYear );
	if ( rendered ) then return rendered.text end;
end

function p.renderReference( frame, currentEntity, reference )
	p.currentFrame = frame;

	-- template call
	if ( frame and not currentEntity and not reference ) then
		local args = frame.args;
		if ( #frame.args == 0 ) then
			args = frame:getParent().args;
		end

		local snaks = {};

		if ( args[1] ) then
			snaks.P248 = { toWikibaseEntityIdSnak( "P248", args[1] ) };
		end
		copyArgsToSnaks( args, snaks );

		currentEntity = mw.wikibase.getEntity();
		reference = { snaks = snaks };
	end

	local rendered = renderReferenceImpl( currentEntity, reference );

	if ( not rendered ) then
		return '';
	end

	local result;
	local code = rendered.code or mw.text.encode( rendered.text );
	result = frame:extensionTag( 'ref', rendered.text, {name = code} ) .. '[[К:Википедия:Статьи с источниками из Викиданных]]';

	return result;
end

function renderReferenceImpl( currentEntity, reference, refAnchor, refAnchorYear )
	if ( not reference.snaks ) then
		return nil;
	end

	-- контекст, содержит также кеш элементов
	local context = {
		cache = {},
	}

	-- данные в простом формате, согласованном с модулями формирования библиографического описания
	local data = {};

	local entityId, sourceEntity;
	if ( reference and reference.snaks and reference.snaks.P248 ) then
		for _, snak in pairs ( reference.snaks.P248 ) do
			if ( snak.datavalue ) then
				entityId = 'Q' .. snak.datavalue.value["numeric-id"];
				sourceEntity = getEntity( context, entityId );
				data.code = entityId;
				data.entityId = entityId;
				break;
			end
		end
	end

	updateWithRef( reference, data );
	-- update ref name with ref-specific properties
	if ( data.code ) then
		if ( data.part ) then data.code = data.code .. '-' .. getSingle( data.part ) end
		if ( data.pages ) then data.code = data.code .. '-' .. getSingle( data.pages ) end
		if ( data.volume ) then data.code = data.code .. '-' .. getSingle( data.volume ) end
		if ( data.issue ) then data.code = data.code .. '-' .. getSingle( data.issue ) end
		if ( data.url ) then data.code = data.code .. '-' .. getSingle( data.url ) end
	end

	expandSpecials( currentEntity, reference, data );
	if ( sourceEntity ) then
		populateSourceDataImpl( sourceEntity, data );
	end
	
	if ( not data.publication and data.described ) then
		expandDescribed( context, sourceEntity, data );
	else
		if ( data.publication ) then expandPublication( context, sourceEntity, data ); end
	end

	if ( next( data ) == nil ) then
		return nil;
	end

	local rendered;
	if ( p.short ) then
		rendered = renderShortReference( data );
		if ( mw.ustring.len( rendered.text ) == 0 ) then
			return nil;
		end

	else
		rendered = renderSource( context, data );
		if ( mw.ustring.len( rendered.text ) == 0 ) then
			return nil;
		end

		if ( refAnchor ) then
			local anchorValue = 'CITEREF' .. refAnchor .. ( coalesce( refAnchorYear, data.year ) or '' );
			rendered.text = '<span class="citation" id="' .. mw.uri.anchorEncode( anchorValue ) .. '">' .. rendered.text .. '</span>';
		end
	end

	return rendered;
end

function getNormativeTitle( entity )
	if ( not entity or not entity.claims or not entity.claims.P31 ) then
		return;
	end

	for _, claim in pairs( entity.claims.P31 ) do
		if ( claim and claim.mainsnak and claim.mainsnak.datavalue and claim.mainsnak.datavalue.value and claim.mainsnak.datavalue.value["numeric-id"] ) then
			local classId = 'Q' .. claim.mainsnak.datavalue.value["numeric-id"];
			local title = NORMATIVE_DOCUMENTS[ classId ];
			if ( title ) then
				return title;
			end
		end
	end

	return;
end

local LANG_CACHE = 	{
	Q150	= 'fr',
	Q188	= 'de',
	Q1321	= 'es',
	Q1860	= 'en',
	Q652	= 'it',
	Q7737	= 'ru',
}

function getLangCode( langEntityId )
	if ( not langEntityId ) then
		return;
	end

	-- small optimization
	local cached = LANG_CACHE[ langEntityId ];
	if ( cached ) then return cached; end

	local langEntity = mw.wikibase.getEntity( langEntityId );
	if ( not langEntity ) then
		mw.log( '[getLangCode] Missing entity ' .. langEntityId );
	else
		if ( langEntity.claims and langEntity.claims.P424 ) then
			for _, claim in pairs( langEntity.claims.P424 ) do
				if ( claim
						and claim.mainsnak
						and claim.mainsnak.datavalue
						and claim.mainsnak.datavalue.value ) then
					return '' .. claim.mainsnak.datavalue.value;
				end
			end
		end
	end

	return;
end

function preprocessPlaces( data, lang )
	if ( not data.place ) then
		return;
	end;

	local newPlaces = {};
	for index, place in pairs( data.place ) do
		if ( place.id ) then
			local newPlaceStr = getPlaceName(lang, place.id)
			if ( newPlaceStr ) then
				newPlaces[index] = newPlaceStr;
			else
				newPlaces[index] = place;
			end
		else
			newPlaces[index] = place;
		end
	end
	data.place = newPlaces;
end

function getPlaceName( lang, placeId )
	-- ГОСТ Р 7.0.12—2011
	if ( lang == 'ru' ) then
		if ( placeId == 'Q649' ) then return toTextWithTip('М.', 'Москва'); end
		if ( placeId == 'Q656' ) then return toTextWithTip('СПб.', 'Санкт-Петербург'); end
		if ( placeId == 'Q891' ) then return toTextWithTip('Н. Новгород', 'Нижний Новгород'); end
		if ( placeId == 'Q908' ) then return toTextWithTip('Ростов н/Д.', 'Ростов-на-Дону'); end
	end
	return nil;
end

function toTextWithTip( text, tip )
	return '<span title="' .. tip .. '" style="border-bottom: 1px dotted; cursor: help; white-space: nowrap">' .. text .. '</span>';
end

return p;