Модуль:Sources

local p = {};

local i18nDefaultLanguage = 'Q7737'; local i18nEditors = { Q150	= '',			-- French Q1321	= '',			-- Spanish Q1860	= '',			-- English Q7737	= 'под ред. ',	-- Russian Q188	= 'Hrsg.: ',		-- German } local i18nVolume = { Q150	= 'Vol.',	-- French Q1321	= 'Vol.',	-- Spanish Q1860	= 'Vol.',	-- English Q7737	= 'Т.',		-- Russian } local i18nPage = { Q150 = 'P.',	-- French Q188 = 'S.',	-- German Q1321 = 'P.',	-- Spanish Q1860 = 'P.',	-- English Q7737 = 'С.',	-- Russian }

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

local PREFIX_CITEREF = "CITEREF_";

function p.deepcopy(orig) local orig_type = type(orig) local copy if orig_type == 'table' then copy = {} for orig_key, orig_value in pairs(orig) do           copy[orig_key] = p.deepcopy( orig_value ); end else -- number, string, boolean, etc copy = orig end return copy end

local options_commas = { separator = ', ', conjunction = ', ', format = function( src ) return src end, nolinks = false, preferids = false }; 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_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 renderSource( src ) mw.logObject( src );

if ( src.code and not src.url ) then src.url = mw.wikibase.sitelink( src.code ) or ( 'd:' .. src.code ) src.url = ':' .. src.url; end src.lang = getSingle( src.lang ) or i18nDefaultLanguage; src.title = src.title or getSingle( src.url ) or '\'\'(unspecified title)\'\''

if ( not src.year and src.date ) then local date = getSingle( src.date ); src.year = mw.ustring.sub( date, 9, 12 ); end

local result = ''; if ( src.author ) then result = result .. toString( src.author, options_commas_it ); end if ( string.len( result ) ~= 0 ) then result = result .. ' ';	end if ( src.part ) then if ( src.url ) then local url = getSingle( src.url ); if ( string.sub( url, 1, 1 ) == ':' ) then result = result ..  .. toString( src.part, options_commas_nolinks ) .. ; else result = result .. '[' .. url .. ' ' .. toString( src.part, options_commas_nolinks ) .. ']';			end end result = result .. ' // ' .. toString( src.title, options_commas ); else -- title only if ( src.url ) then local url = getSingle( src.url ); if ( string.sub( url, 1, 1 ) == ':' ) then result = result ..  .. toString( src.title, options_commas_nolinks ) .. ; else result = result .. '[' .. url .. ' ' .. toString( src.title, options_commas_nolinks ) .. ']';			end end end

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

if ( src.publication ) then result = result .. ' // ' .. toString( src.publication, options_commas_it ); end

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

if ( src.place or src.publisher or src.year ) then result = result .. ' — ';		if ( src.place ) then result = result .. toString( src.place, options_commas ); if ( src.publisher or src.year ) then result = result .. ': ';			end end if ( src.publisher ) then result = result .. toString( src.publisher, options_commas ); if ( src.year ) then result = result .. ', ';			end end if ( src.year ) then result = result .. toString( src.year, options_commas ); end result = result .. '.';	end if ( src.volume ) then local letter = i18nVolume[ src.lang ] or i18nVolume[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. ' ' .. toString(src.volume, options_commas ) .. '.';	end if ( src.issue ) then result = result .. ' — № ' .. toString(src.issue, options_commas ) .. '.';	end if ( src.page ) then local letter = i18nPage[ src.lang ] or i18nPage[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. ' ' .. toString(src.page, options_commas ) .. '.';	end if ( src.isbn13 ) then result = result .. ' — ISBN ' .. toString( src.isbn13, options_commas ); elseif ( src.isbn10 ) then result = result .. ' — ISBN ' .. toString( src.isbn10, options_commas ); end

if ( src.issn ) then result = result .. ' — ISSN ' .. toString( src.issn, options_commas ); end if ( src.doi ) then result = result .. ' — [http://dx.doi.org/' .. mw.uri.encode( src.doi ) .. ' DOI ' .. src.doi .. ']';	end

if ( src.entityId ) then if ( src.type and src.entityId ) then -- wrap into span to target from JS result = '' .. result .. ' '		else result = '' .. result .. ' '		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 .. " Проверено " .. tostring(d) .. " " .. monthg[m] .. " " .. tostring(y) .. ". ";	end

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

function renderShortReference( src ) src.lang = getSingle( src.lang ) or i18nDefaultLanguage; src.title = src.title or '\'\'(unspecified title)\'\''

local result = ;	if ( src.author ) then		result = result .. toString( src.author, options_commas_it_nolinks );	else		result = result .. toString( src.title, options_commas_it_nolinks );	end	result = result .. 

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

if ( src.volume ) then local letter = i18nVolume[ src.lang ] or i18nVolume[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. ' ' .. toString(src.volume, options_commas ) .. '.';	end if ( src.issue ) then result = result .. ' — № ' .. toString(src.issue, options_commas ) .. '.';	end if ( src.page ) then local letter = i18nPage[ src.lang ] or i18nPage[ i18nDefaultLanguage ]; result = result .. ' — ' .. letter .. ' ' .. toString(src.page, 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( value, options ) if ( type( value ) == 'string' ) then return options.format( value ); elseif ( type( value ) == 'table' ) then if ( value.id ) then -- this is link if ( options.preferids ) then return 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( value.id, value.label ) ); end end end

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

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

return ''; end

function renderLink( entityId, text ) if ( not entityId ) then error("entityId is not specified"); end local actualText = text or mw.wikibase.label( entityId ) or '\'\'(untranslated)\'\''; local link = mw.wikibase.sitelink( entityId ) or ( ':d:' .. entityId ) return  .. actualText .. ; 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 appendMainSnaks( currentEntity, 'P227', data, 'title', { format = function( gnd ) return 'Record #' .. gnd; end } ); appendMainSnaks( currentEntity, 'P227', data, 'url', { format = function( gnd ) return 'http://d-nb.info/gnd/' .. gnd .. '/'; end } ); end -- BNF -- specified by P268 if ( sourceId == 'Q15222191' ) then appendMainSnaks( currentEntity, 'P268', data, 'title', { format = function( id ) return 'Record #' .. id; end } ); appendMainSnaks( currentEntity, '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 appendMainSnaks( currentEntity, '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

-- Gran Enciclopèdia Catalana -- specified by P1296 if ( sourceId == 'Q2664168' ) then appendMainSnaks( currentEntity, '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 appendMainSnaks( currentEntity, 'P1417', data, 'url', { format = function( id ) return 'http://global.britannica.com/EBchecked/topic/' .. id .. '/'; end } ); expandSpecialsQualifiers( currentEntity, 'P1417', data ); end

-- Electronic Jewish Encyclopedia (Elektronnaja Evrejskaja Entsiklopedia) -- specified by P1438 if ( sourceId == 'Q1967250' ) then appendMainSnaks( currentEntity, '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 appendMainSnaks( currentEntity, 'P1447', data, 'url', { format = function( id ) return 'http://www.sports-reference.com/olympics/athletes/' .. id .. '.html'; end } ); expandSpecialsQualifiers( currentEntity, 'P1447', data ); end

-- do we have appropriate record in P1343 ? local claims = findClaimsByValue( currentEntity, 'P1343', sourceId ); if ( claims and #claims ~= 0 ) then appendQualifiers( claims, 'P958', data, 'part', {} ); appendQualifiers( claims, 'P854', data, 'url', {} ); appendQualifiers( claims, 'P357', data, 'title', {} ); appendQualifiers( claims, 'P478', data, 'volume', {} ); end end end

function expandSpecialsQualifiers( entity, propertyId, result ) if ( entity.claims and entity.claims[propertyId] ) then local claims = entity.claims[propertyId]; appendQualifiers( claims, 'P958', result, 'part', {} ); appendQualifiers( claims, 'P854', result, 'url', {} ); appendQualifiers( claims, 'P357', result, 'title', {} ); appendQualifiers( claims, 'P478', result, 'volume', {} ); 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 appendMainSnaks( entity, propertyId, result, property, options ) if ( entity.claims and entity.claims[propertyId] ) then for i, claim in pairs( entity.claims[propertyId] ) do			if ( claim.mainsnak and claim.mainsnak.datavalue ) then appendImpl( claim.mainsnak.datavalue, result, property, options ); end end end end

function appendSnaks( allSnaks, snakPropertyId, result, property, options ) if ( allSnaks and allSnaks[ snakPropertyId ] ) then for k, snak in pairs( allSnaks[ snakPropertyId ] ) do			if ( snak and snak.datavalue ) then appendImpl( snak.datavalue, result, property, options ); end end end end

function appendQualifiers( claims, qualifierPropertyId, result, property, options ) 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, result, property, options ); end end end end end

function appendImpl( datavalue, result, property, options ) if ( datavalue.type == 'string' ) then local value = datavalue.value; if ( options.format ) then value = options.format( value ); end if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], value); elseif ( datavalue.type == 'wikibase-entityid' ) then local value = datavalue.value; if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], { id = 'Q' .. value["numeric-id"] }); elseif datavalue.type == 'time' then local value = datavalue.value; if ( options.format ) then value = options.format( value ); end if ( not result[property] ) then result[property] = {}; elseif ( type( result[property] ) == 'string' or ( type( result[property] ) == 'table' and type( result[property].id ) == 'string' ) ) then result[property] = { result[property] }; end table.insert( result[property], tostring( value.time )); end end

function expandPublication( 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 ( publication and publication.id ) then populateSourceData( data, publication.id ); end end

function loadSafe( entityId ) if ( entityId == nil ) then error('entityId to load is not specified'); end local status, result = pcall( function return mw.loadData( 'Module:Source/' .. entityId ) end ); if ( status == true ) then return true, result; end return false, nil; end

function populateSourceData( data, sourceId ) local loaded, sourceData = loadSafe( sourceId ); if ( loaded and sourceData ) then populateSourceDataImpl( data, sourceData ); end end

function populateSourceDataImpl( data, sourceData ) for key, value in pairs( sourceData ) do		if ( not data[key] and key ~= 'title' ) then data[key] = value; end end

-- if we already have title, than it would be the current one, otherwise move it to publication if ( sourceData.title ) then if ( not data.title ) then data.title = sourceData.title; else if ( not data.publication ) then data.publication = sourceData.title; end end end end

function updateWithRef( reference, src ) -- specified if ( reference.snaks.O662 ) 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

appendSnaks( reference.snaks, 'P50', src, 'author', {} ); appendSnaks( reference.snaks, 'P364', src, 'lang', {} ); appendSnaks( reference.snaks, 'P958', src, 'part', {} ); -- part appendSnaks( reference.snaks, 'P357', src, 'title', {} ); -- title appendSnaks( reference.snaks, 'P854', src, 'url', {} ); appendSnaks( reference.snaks, 'P1433', src, 'publication', {} ); appendSnaks( reference.snaks, 'P123', src, 'publisher', {} ); appendSnaks( reference.snaks, 'P304', src, 'page', {} ); appendSnaks( reference.snaks, 'P478', src, 'volume', {} ); appendSnaks( reference.snaks, 'P577', src, 'date', {} ); appendSnaks( reference.snaks, 'P813', src, 'accessdate', {} ); return src; end

function p.renderSource( frame ) local arg = frame.args[1]; return p.renderSourceImpl( arg ); end

function p.renderSourceImpl( entityId ) local value = {}; value["numeric-id"] = string.sub( entityId, 2); local snak = { datavalue = { value =value } }; local properties = {}; properties[1] = snak; return renderReferenceImpl( {}, { snaks = { P248 = properties } } ).text; end

function p.renderReference( frame, currentEntity, reference ) -- template call if ( frame and not currentEntity and not reference ) then local value = {}; value["numeric-id"] = string.sub( frame.args[1], 2); local snak = { datavalue = { value =value } }; local properties = {}; properties[1] = snak; currentEntity = mw.wikibase.getEntityObject; reference = { snaks = { P248 = properties } }; 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} ) .. 'К:Википедия:Статьи с источниками из Викиданных';

if ( not rendered.found ) then result = result .. 'К:Википедия:Статьи с неоформленными источниками из Викиданных'; end

return result; end

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

local data = {}; local entityId, found, sourceData; if ( reference.snaks.P248 ) then entityId = "Q" .. reference.snaks.P248[1].datavalue.value["numeric-id"]; found, sourceData = loadSafe( entityId ); data.code = entityId; data.entityId = entityId; else found = true; end

updateWithRef( reference, data ); expandSpecials( currentEntity, reference, data ); if ( entityId ) then if ( found and sourceData ) then populateSourceDataImpl( data, sourceData ); else if ( data.title ) then data.publication = data.publication or { id = entityId, label = mw.wikibase.label( entityId ) }; else data.title = { id = entityId, label = mw.wikibase.label( entityId ) }; end end end expandPublication( data );

local rendered; if ( p.short ) then local toStore = p.deepcopy( data ); if (not p.list ) then p.list = {}; end p.list[toStore.code] = toStore; rendered = renderShortReference( data ); else rendered = renderSource( data ); end

if ( mw.ustring.len( rendered.text ) == 0 ) then return nil; end

rendered.found = found; return rendered; end

return p;