Module:Biblio/Références

Utilisation modifier

Les fonctions de ce module sont prévues pour être utilisées depuis un autre module. Elles ne fonctionnent pas si elles sont directement appelées par #invoke.

Fonctions exportables :

  • isbn(args) – retourne un lien vers ISBN suivi de la liste des ISBN contenu dans les paramètres isbn, isbn erroné, isbn2, isbn3... avec lien vers spécial:Ouvrages de référence ; affiche aussi un message d'erreur si ce n'est pas un ISBN ou EAN 13 valide (sauf paramètre isbn erroné) ;
  • issn(args) – retourne un lien vers ISSN suivi de la liste des paramètres issn, issn2... avec un lien vers WorldCat ;
  • enLigne(args) – retourne un lien externe (paramètre url, lien ou doi) sous le libelé « lire en ligne » (personnalisable avec texte), éventuellement précédé de langue et suivi par une date (date ou consulté le) ;
  • wikisource – retourne un lien interne vers la page wikisource correspondant, avec le texte « lire sur wikisource » ;
  • indicationDeFormat(format) – retourne format entre crochet, avec si possible une abréviation.

Les fonctions suivantes retournent un lien interne pour une base de données suivi du code en paramètre avec un lien externe vers la page correspondante dans cette base.

Autres fonctions :

  • databaseExterne() – facilite le lien vers les bases de données externe

Modules externes et autres éléments dont ce module a besoin pour fonctionner :

  • Module:Outils – utilise les fonctions validTextArg, trim et notEmpty




-- Les fonctions de ce module sont destinées à être utilisées par un autre module.
-- Leur paramètre d'entrée est une table simple (args), voire une chaine (oclc, bnf...)

local References = { }


local Outils = require( 'Module:Outils' )
local validTextArg = Outils.validTextArg
local TableBuilder = require( 'Module:TableBuilder' )
-- local Date = require( 'Module:Date' ) -- chargé uniquement si nécessaire


--[[
ISBN-10 and ISSN validator code calculates checksum across all isbn/issn digits including the check digit. ISBN-13 is checked in checkisbn().
If the number is valid the result will be 0. Before calling this function, issbn/issn must be checked for length and stripped of dashes,
spaces and other non-isxn characters.
]]
function References.is_valid_isxn( isxn_str, len )
	local temp = 0
	isxn_str = isxn_str:gsub( 'x', 'X' )
	isxn_str = { isxn_str:byte(1, len) }	-- make a table of bytes
	len = len+1								-- adjust to be a loop counter
	for i, v in ipairs( isxn_str ) do		-- loop through all of the bytes and calculate the checksum
		if v == string.byte( 'X' ) then		-- if checkdigit is X
			temp = temp + 10 * ( len - i )	-- it represents 10 decimal
		else
			temp = temp + tonumber( string.char( v ) ) * ( len - i )
		end
	end
	return temp % 11 == 0					-- returns true if calculation result is zero
end

function References.isValidIsmn10( ismn )
	local temp = 9
	if ismn:match( 'M%d%d%d%d%d%d%d%d%d' ) then
		for i = 2, 10 do
			temp = temp + ( 1 + 2 * ( i % 2 ) ) * ismn:sub( i, i )
		end	
	end
	return temp % 10 == 0
end
	
-- Teste si une chaine ISBN est valide
function References.checkisbn( isbn_str )
	if type( isbn_str ) == 'string' then
		isbn_str = isbn_str:gsub( '[-%s]', '' )	-- supprime les traits d’union et espaces
		
		if isbn_str:len() == 10 then
			if isbn_str:match( '^%d+[xX]?$' ) then
				return  References.is_valid_isxn( isbn_str, 10 )
			end
		elseif isbn_str:match( '^97[89]' ) then
			return References.checkean13( isbn_str )
		end
	end
	return false
end

-- Teste si une chaine EAN 13 est valide
function References.checkean13( ean_str )
	if type( ean_str ) == 'string' then
		ean_str = ean_str:gsub( '[-%s]', '' )	-- supprime les traits d’union et espaces
		if ean_str:len() == 13 and ean_str:match( '^%d+$' ) then
			local temp = 0
			ean_str = { ean_str:byte( 1, 13 ) }
			for i = 1, #ean_str do
				temp = temp + ( 3 - 2 * ( i % 2 ) ) * tonumber( string.char( ean_str[i] ) )
			end
			return temp % 10 == 0
		end
	end
	return false
end

function References.checkissn( issn_str )
	if type( issn_str ) == 'string' then
		issn_str = issn_str:gsub( '[%s]', '' )
		if issn_str:match( '^%d%d%d%d%-%d%d%d[%dxX]$' ) then
			issn_str = issn_str:gsub( '-', '' )		-- supprime les traits d’union et espaces
			return References.is_valid_isxn( issn_str, 8 )
		end
	end
	return false
end

-- Teste si une chaine ISMN est valide
function References.checkismn( ismn_str )
	if type( ismn_str ) == 'string' then
		ismn_str = ismn_str:gsub( '[-%s]', '' )	-- supprime les traits d’union et espaces
		
		if ismn_str:len() == 10 then
			return  References.isValidIsmn10( ismn_str, 10 )
		elseif ismn_str:match( '^9790' ) then
			return References.checkean13( ismn_str )
		end
	end
	return false
end

local function isbn13to9( isbn_str )
	if type( isbn_str ) == 'string' then
		local isbn = isbn_str:gsub( '[-%s]', '' )
		if isbn:len() == 13 and isbn:sub( 1, 3 ) == '978' then
			isbn = isbn:sub( 4, 12 )
			return isbn
		elseif isbn:len() == 10 then
			return isbn:sub( 1, -2 )
		end
	end
	return isbn_str
end

local function isbn13to10( isbn_str )
	local isbn = isbn13to9( isbn_str )
	if isbn ~= isbn_str and isbn_str:len() ~= 10 then
		for i = 0, 9 do
			if References.checkisbn( isbn .. i ) then
				return isbn .. i
			end
		end
		return isbn .. 'X'
	end
	return isbn_str
end

function References.isbn13to10( frame )
	local args = Outils.extractArgs( frame )
	return isbn13to10( args[1] )
end

function References.same_isbn( isbn1, isbn2 )
	if type( isbn1 ) ~= 'string' or type( isbn2 ) ~= 'string' then
		return false
	end
	-- remove dash and spaces
	isbn1 = isbn1:gsub( '[-%s]', '' )
	isbn2 = isbn2:gsub( '[-%s]', '' )
    -- check if both isbn are valid
	if not ( References.checkisbn(isbn1) and References.checkisbn(isbn2) ) then
		return false
	end
	-- compare isbn
	return isbn13to9( isbn1 ) == isbn13to9( isbn2 )
end

local function doublonIsxn( isxn, liste2, xnCode)
	if type( References[ 'same_' .. xnCode ] ) == 'function' then
		for k, v in ipairs( liste2 ) do
			if References[ 'same_' .. xnCode ]( isxn, v ) then
				return true
			end
		end
	end
end

local function formatIsxn( args, validArg, xnCode, invalideCateg, checkFunction, formatLien, page )
	local validArg = validArg or function ( ... ) return validTextArg( args, ... ) end
	
	local liste = { }
	local liste2 = { }
	local i = 1
	local avertissementInvalid = '<sup style="color:red">[à vérifier : ' .. invalideCateg .. ']</sup>'
	
	local isxnErr = validArg( xnCode .. ' erroné' )
	local XN = xnCode:upper()
	local isxn = validArg( xnCode, XN, xnCode .. '1', XN .. '1' )
	if isxnErr or isxn then
		local isxnErrSanitised = isxnErr and isxnErr:match( '%d[%d -]+[%dXx]' )
		if isxnErrSanitised then
			local lien = formatLien:format( isxnErrSanitised, isxnErr ) .. ' (édité erroné)'
			table.insert( liste, lien )
		end
		
		-- boucle sur les isxn2, 3...		
		while isxn do
			-- vérifivation de la validité de l'ISXN
			local isxnValid = checkFunction( isxn )
			
			-- préparation du texte à afficher
			if isxnValid then
				local lien = formatLien:format( isxn, isxn )
				table.insert( liste, lien )
				if type( args.categ ) == 'table' and doublonIsxn( isxn, liste2, xnCode) then
					args.categ[ xnCode .. 'Dupliqué' ] = isxn
				end
				table.insert( liste2, isxn )
			else
				table.insert( liste, isxn .. avertissementInvalid )
				if type( args.categ ) == 'table' then
					args.categ[ xnCode .. 'Invalid' ] = true
				end
			end
			
			i = i + 1
			isxn = validArg( xnCode .. i, XN .. i )
		end
		
		if args['sansLabel'] then 
			page = ''
		else
			page = page .. '&nbsp;'
		end
		return page .. mw.text.listToText( liste )
	end
end	

-- voir Modèle:ISBN
-- renvoie une liste de chaines formant le résultat du modèle une fois concaténées
function References.isbn( args, validArg )
	return formatIsxn(
		args,
		validArg,
		'isbn',
		'[[:Catégorie:Ouvrage avec ISBN invalide|ISBN invalide]]',
		References.checkisbn,
		'[[Spécial:Ouvrages de référence/%s|<span class="nowrap">%s</span>]]',
		'[[International Standard Book Number|ISBN]]'
	)
end

-- voir Modèle:EAN
-- renvoie une liste de chaines formant le résultat du modèle une fois concaténées
function References.ean( args, validArg )
	return formatIsxn(
		args,
		validArg,
		'ean',
		'[[:Catégorie:Page avec EAN invalide|EAN invalide]]',
		References.checkean13,
		'[[Spécial:Ouvrages de référence/%s|<span class="nowrap">%s</span>]]',
		'[[EAN 13|EAN]]'
	)
end

-- voir Modèle:ISSN
-- renvoie une liste de chaines formant le résultat du modèle une fois concaténées
function References.issn( args, validArg )
	return formatIsxn(
		args,
		validArg,
		'issn',
		'[[:Catégorie:Page avec ISSN invalide|ISSN invalide]]',
		References.checkissn,
		'<span class="plainlinks noarchive">[http://worldcat.org/issn/%s&lang=fr %s]</span>',
		'[[International Standard Serial Number|ISSN]]'
	)
end

function References.eissn( args, validArg )
	return formatIsxn(
		args,
		validArg,
		'e-issn',
		'[[:Catégorie:Page avec ISSN invalide|ISSN invalide]]',
		References.checkissn,
		'<span class="plainlinks noarchive">[http://worldcat.org/issn/%s&lang=fr %s]</span>',
		'[[International Standard Serial Number#ISSN électronique|e-ISSN]]'
	)
end

-- voir Modèle:ISMN
-- renvoie une liste de chaines formant le résultat du modèle une fois concaténées
function References.ismn( args, validArg )
	return formatIsxn(
		args,
		validArg,
		'ismn',
		'[[:Catégorie:Ouvrage avec ISBN invalide|ISMN invalide]]',
		References.checkismn,
		'<span class="nowrap">%s</span>',
		'[[International Standard Music Number|ISMN]]'
	)
end

-- fonctions liant des bases de données externes
local function databaseExterne( num, lienIinterne, lienExterne, complement, texteAffiche )
	if Outils.notEmpty( num ) then
		local adresse = 
				lienIinterne	
				.. '&nbsp;<span class="plainlinks noarchive nowrap">[http://'
				.. lienExterne 
				.. mw.uri.encode( num, 'PATH' )
				.. ( complement or ' ' ) 
				.. mw.text.nowiki( texteAffiche or num )
				.. ']</span>'
		
		return adresse
	end
end

function References.arkId( base )
	--  Nice Opaque Identifiern utilisé par les formats Ark pour générer une clé
	base = tostring( base )
	if base then
		local xdigits = '0123456789bcdfghjkmnpqrstvwxz'
		local sum = 0 
		local position
		for i = 1, base:len() do
			position = xdigits:find( base:sub( i, i ), 1, true ) or 1
			sum = sum + i * ( position - 1 )
		end
		local index = sum % 29 + 1
		return xdigits:sub( index, index )
	end
end

function References.arxiv( arxiv )
	if Outils.trim( arxiv ) then
		return databaseExterne( arxiv, '[[arXiv]]', 'arxiv.org/abs/' ):gsub( '%%2F', '/' )
	end
end

function References.asin( asin )
	return databaseExterne( asin, '[[Amazon Standard Identification Number|ASIN]]', 'www.amazon.fr/s/?url=search-alias&lang=fr&field-keywords=' )
end

function References.bibcode( bibcode )
	return databaseExterne( bibcode, '[[Bibcode]]', 'adsabs.harvard.edu/abs/' )
end

function References.bnf( bnf )
	bnf = Outils.trim( bnf )
	if bnf then
		local texte = bnf
		local category = ''
		local bnfId = bnf:upper():match( 'BNF(%d+%w)' ) or bnf:lower():match( 'cb(%d+%w)' ) or bnf:match( '^%d+%w' )
		
		if bnfId then
			-- bnf contient une suite de chiffres qui peut être un ark valide
			local base = bnfId:sub( 1, 8 )
			if bnfId:len() == 8 then 
				-- il manque la clé, on l'ajoute
				bnf = base .. References.arkId( 'cb' .. base )
				texte = base
			elseif bnfId:len() > 8 and bnfId:sub( 9, 9 ) == References.arkId( 'cb' .. base ) then
				-- ark valide
				bnf = bnfId:sub( 1, 9 )
				texte = base
			else
				-- ark qui semble non valide
				bnf = bnfId
				texte = bnfId
				category = '[[Catégorie:Recension temporaire pour le modèle Ouvrage|bnf]]'
			end
		else
			-- le paramètre ne semble pas un ark valide
			category = '[[Catégorie:Recension temporaire pour le modèle Ouvrage|bnf]]'
		end
		
		-- dans tous les cas on renvoie l'adresse, on catégorise juste pour vérifier ce qui ne va pas
		local lien = databaseExterne( bnf, 
			'notice [[Bibliothèque nationale de France|BnF]] n<sup>o</sup>', 
			'catalogue.bnf.fr/ark:/12148/cb', 
			'/PUBLIC FRBNF', 
			texte 
		)
		
		return lien .. category
	end
end

function References.dnb( dnb )
	return databaseExterne( dnb, '[[Bibliothèque nationale allemande|DNB]]', 'd-nb.info/' )
end

function References.doi( doi )
	return databaseExterne( doi, '[[Digital Object Identifier|DOI]]', 'dx.doi.org/' )
end

function References.jstor( jstor )
	return databaseExterne( jstor, '[[JSTOR]]', 'jstor.org/stable/' )
end

function References.lccn( lccn )
	return databaseExterne( lccn, '[[Numéro de contrôle de la Bibliothèque du Congrès|LCCN]]', 'lccn.loc.gov/' )
end

function References.mathreviews( mathreviews )
	return databaseExterne( mathreviews, '[[Mathematical Reviews|Math Reviews]]', 'www.ams.org/mathscinet-getitem?mr=' )
end

function References.oclc( oclc )
	return databaseExterne( oclc, '[[Online Computer Library Center|OCLC]]', 'worldcat.org/oclc/', '&lang=fr ' )
end

function References.pmcid( pmcid )
	return databaseExterne( pmcid, '[[PubMed Central|PMCID]]', 'www.ncbi.nlm.nih.gov/pmc/articles/' )
end

function References.pmid( pmid )
	return databaseExterne( pmid, '[[PubMed|PMID]]', 'www.ncbi.nlm.nih.gov/pubmed/' )
end

function References.sbn( sbn )
	local id = ( sbn or '' ):upper():gsub( '\\', '' ):gsub( '^ITICCU', '' )
	return databaseExterne( id, '[[Service bibliothécaire national]]', 'opac.sbn.it/bid/' )
end

function References.sudoc( sudoc )
	return databaseExterne( sudoc, '[[Système universitaire de documentation|SUDOC]]', 'www.sudoc.fr/' )
end

function References.wikisource( wikisource )
	if Outils.notEmpty( wikisource ) then
		return '[[s:' .. wikisource .. '|lire sur Wikisource]]'
	end
end

function References.zbl( zbl )
	return databaseExterne( zbl, '[[Zentralblatt MATH|zbMATH]]', 'zbmath.org/?q=an:' )
end


-- enLigne est destiné à remplacer "lire en ligne", "écouter en ligne", "présentation en ligne"
function References.enLigne( args, validArg )
	local validArg = validArg or function ( ... ) return validTextArg( args, ... ) end
	local lang, esp = '', ''
	if args.langue then
		local Langue = require( 'Module:Langue' )
		lang = Langue.indicationMultilingue{ args.langue, args.langue2, args.langue3 }
		esp = '&nbsp'
	end
	
	local url = validArg( 'lien', 'url' )
	if url == nil then 
		if validArg( 'doi' ) then 
			url = 'http://dx.doi.org/' .. mw.uri.encode( args.doi ) 
		else
			return
		end
	end
	url = url:gsub( '%[', '%%5B' ):gsub( '%]', '%%5D' ):gsub( ' ', '%%20' )
	
	local texte = validArg( 'texte' ) or 'en ligne'
	local date = validArg( 'date', 'consulté le' )
	
	if date then 
		return lang .. esp .. '[' .. url .. ' ' .. texte .. ']&nbsp;(consultée le' .. date .. ')'
	else
		return lang .. esp .. '[' .. url .. ' ' .. texte .. ']'
	end
end

function References.affichageLiensExternes( args, validArg, lireEnLigne, consulteLe )
	local validArg = validArg or function ( ... ) return validTextArg( args, ... ) end
		
		local liensExternes = TableBuilder.new(  )
		
		-- isbn et issn
		liensExternes.minsert(
			References.isbn( args, validArg ),
			References.ean( args, validArg ),
			References.issn( args, validArg ),
			References.eissn( args, validArg ),
			References.ismn( args, validArg )
		)
		
		liensExternes.minsert( 
			References.oclc( args.oclc ),
			References.bnf ( args.bnf ),
			References.sbn ( args.sbn ),
			References.lccn( args.lccn ),
			References.dnb ( args.dnb ),
			References.pmid( validArg( 'pmid', 'PMID' ) ),
			References.pmcid ( validArg( 'pmcid', 'pmc'  ) ),
			References.doi( validArg( 'doi', 'DOI' ) ),
			References.jstor( args.jstor ),
			References.bibcode( args.bibcode ),
			References.mathreviews( args['math reviews'] ),
			References.zbl( validArg( 'zbl', 'zbmath' ) ),
			References.arxiv( args.arxiv ),
			References.asin( args.asin ),
			References.sudoc( args.sudoc ),
			References.wikisource( args.wikisource )
		)
		
		liensExternes.minsert( 
				References.enLigne{ url = args['résumé'], texte = 'résumé' },
				References.enLigne{ url = args['présentation en ligne'], texte = 'présentation en ligne' },
				References.enLigne{ url = args['écouter en ligne'], texte = 'écouter en ligne' }
			)
		
		local url = validArg( 'lire en ligne', 'url texte', 'url', 'texte' )
		if url and lireEnLigne then
			liensExternes.minsert( 
				References.enLigne{
					lien = url,
					texte = 'lire en ligne',
				} .. ( References.indicationDeFormat( args['format électronique'] ) or '' )
			)
		end
		
		-- consulté le
		if consulteLe then
			local consult = validArg( 'consulté le', 'accessdate', 'Consulté le', 'consulté', 'consultée le' )
			if consult then
				if string.sub( consult, -1,-1) == '.' then
					consult = string.sub( consult, 1, -2)
				end
				local consulteLe = 'consulté en '
				local Date = require( 'Module:Date' )
				local test, tdate = Date.separationJourMoisAnnee( consult )
				if test then
					if tdate.jour then
						consulteLe = 'consulté le '
                        if tdate.jour == 1 then
                        	tdate.jour = Outils.abr( '1<sup>er</sup>', 'premier' )
                        end
					end
					consult = TableBuilder.new()
						.minsert( tdate.jour, tdate.mois, tdate.annee )
						.concat( ' ' )
				end					
				liensExternes.minsert( consulteLe .. Outils.nobr( consult:lower() ) )
			end
		end
		
		if #liensExternes > 0 then
			return ' <small style="line-height:1em;">(' .. liensExternes.concat( ', ' ) ..  ')</small>'
		end
end

function References.indicationDeFormat( format )
	if not Outils.trim( format ) then 
		return
	end
	local listeFormat = {
		audio = { "audio", "Fichiers audio au format MP3, Ogg..." },
		bat   = { "bat",   "Script de traitement par lot (batch)" },
		djvu  = { "DjVu",  "Document au format DjVu" },
		doc   = { "doc",   "Document Microsoft Word" },
		epub  = { "EPUB",  "Document au format Epub" },
		flash = { "flash", "Animation vectorielle au format Macromedia Flash" },
		hlp   = { "hlp",   "Fichier HeLP (aide) datant de Microsoft Windows 3.1" },
		html  = { "html",  "Fichier au format Hypertext Markup Language (HTML)" },
		image = { "image", "Image au format JPEG, PNG, GIF..." },
		java  = { "java",  "Applet Java" },
		mov   = { "mov",   "Vidéo au format Apple QuickTime" },
		mp3   = { "MP3",   "Fichier audio au format MP3" },
		odt   = { "odt",   "Document au format OpenDocument" },
		ogg   = { "ogg",   "Fichier au format conteneur Ogg" },
		pdf   = { "PDF",   "Document au format Portable Document Format (PDF) d'Adobe" },
		php   = { "php",   "Script PHP" },
		pl    = { "pl",    "Script Practical Extraction and Report Language (Perl)" },
		ppt   = { "ppt",   "Présentation Microsoft PowerPoint" },
		ps    = { "ps",    "Fichier de description vectorielle au format PostScript" },
		radio = { "radio", "Radio au format MPEG, AVI..." },
		rar   = { "rar",   "Document compressé au format RAR" },
		rm    = { "rm",    "Vidéo au format RealMedia, RealAudio..." },
		rtf   = { "RTF",   "Document texte en Rich Text Format (RTF)" },
		svg   = { "SVG",   "Image vectorielle au format Scalable Vector Graphics (SVG)" },
		sxi   = { "sxi",   "Présentation OpenOffice.org Impress" },
		sxw   = { "sxw",   "Document OpenOffice.org Writer" },
		tex   = { "TeX",   "Document TeX" },
		txt   = { "txt",   "Fichier au format texte brut" },
		video = { "vidéo", "Vidéo au format MPEG, AVI..." },
		xls   = { "xls",   "Classeur Microsoft Excel" },
		xml   = { "XML",   "Document au format Extensible Markup Language (XML)" },
		zip   = { "zip",   "Archive au format Zip" },
	}
	listeFormat['vidéo'] = listeFormat.video
	listeFormat.vid = listeFormat.video
	listeFormat.htm = listeFormat.html
	listeFormat.excel = listeFormat.xls
	listeFormat.powerpoint = listeFormat.ppt
	listeFormat.word = listeFormat.doc
	listeFormat.aud = listeFormat.audio
	
	local tabFormat = listeFormat[ string.lower( format ) ]
	if tabFormat then
		return ( ' <abbr class="abbr indicateur-format format-' .. string.lower(tabFormat[1]) .. '" title="' .. tabFormat[2] 
			.. '">' .. mw.text.nowiki( '[' .. tabFormat[1] .. ']' ) .. '</abbr>' )
	else
		-- teste si le suffixe est suivi d'une précision (ex : pdf 6 Mo)
		local ext, texte = string.match( format, "^(...) (.*)$")
		if ext and listeFormat[ string.lower( ext ) ] then
			return References.indicationDeFormat( ext ) .. ' ' .. texte
		else
			return ' ' .. '&#91;' .. format .. '&#93;'  -- '&#91;' = '<nowiki>[</nowiki>',  '&#93;' = '<nowiki>]</nowiki>',
		end
	end
	
end


return References