fr>Od1n (code peut-être plus simple (et davantage future-proof), pour détecter les cas "année" et "mois année", autrement dit en excluant les cas "jour mois année" et "erreur" ; refs 186028058, 186059581 et 186440975) |
m (1 version importée) |
(Aucune différence)
|
Dernière version du 15 janvier 2024 à 01:08
La documentation pour ce module peut être créée à Module:Biblio/Références/doc
-- 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 Commun = require 'Module:Biblio/Commun' -- 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, strVal = 0 ean_str = { ean_str:byte( 1, 13 ) } for i = 1, #ean_str do strVal = tonumber( string.char( ean_str[i] ) ) temp = temp + ( 3 - 2 * ( i % 2 ) ) * strVal 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 invalide = '<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 ) table.insert( liste, lien .. ' (édité erroné)' ) end -- boucle sur les isxn2, 3... while isxn do -- vérification 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 .. invalide ) if type( args.categ ) == 'table' then -- pour les propriétés isbnInvalid, issnInvalid et eanInvalid 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 .. ' ' 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:Page 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">[https://portal.issn.org/resource/issn/%s %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">[https://portal.issn.org/resource/issn/%s %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:Page avec ISMN 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 numEncoded if num:find( '[^a-zA-Z0-9/_.~-]' ) then numEncoded = mw.uri.encode( num, 'PATH' ):gsub( '%%2F', '/' ) else numEncoded = num end local adresse = lienIinterne .. ' <span class="plainlinks noarchive nowrap">[' .. 'https://' .. lienExterne .. numEncoded .. ( 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/' ) 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]]', 'ui.adsabs.harvard.edu/abs/' ) end function References.bnf( bnf ) bnf = Outils.trim( bnf ) if bnf then local texte = '' 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 ) local arkId = References.arkId( 'cb' .. base ) if bnfId:len() == 8 then -- il manque la clé, on l'ajoute bnf = base .. arkId texte = base elseif bnfId:len() > 8 and bnfId:sub( 9, 9 ) == arkId 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 texte = bnf 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, '[[Bibliothèque nationale de France|BNF]]', 'catalogue.bnf.fr/ark:/12148/cb', '.public', texte ) return lien .. category end end function References.citeseerx( citeseerx ) return databaseExterne( citeseerx, '[[CiteSeerX|CiteSeer<sup>x</sup>]]', 'citeseerx.ist.psu.edu/viewdoc/summary?doi=' ) end function References.dnb( dnb ) return databaseExterne( dnb, '[[Bibliothèque nationale allemande|DNB]]', 'd-nb.info/' ) end function References.doi( doi, paywall ) local result = databaseExterne( doi, '[[Digital Object Identifier|DOI]]', 'dx.doi.org/' ) if result and paywall then result = result .. ( References.indicationIconeAcces( paywall ) or '' ) end return result end function References.jfm( jfm ) return databaseExterne( jfm, '[[zbMATH|JFM]]', 'zbmath.org/?format=complete&q=an:' ) end function References.jstor( jstor ) return databaseExterne( jstor, '[[JSTOR]]', 'jstor.org/stable/' ) end function References.hal( hal ) if Outils.trim( hal ) then if not hal:match( '^%w+%-%d' ) then hal = 'hal-' .. hal end return databaseExterne( hal, '[[HAL (archive ouverte)|HAL]]', 'hal.science/' ) end end function References.hdl( hdl, paywall ) local result = databaseExterne( hdl, '[[Handle System|hdl]]', 'hdl.handle.net/' ) if result and paywall then result = result .. ( References.indicationIconeAcces( paywall ) or '' ) end return result 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.libris( libris ) return databaseExterne( libris, '[[LIBRIS]]', 'libris.kb.se/bib/' ) end function References.mathreviews( mathreviews ) return databaseExterne( mathreviews, '[[Mathematical Reviews|MR]]', 'www.ams.org/mathscinet-getitem?mr=' ) end function References.oclc( oclc ) return databaseExterne( oclc, '[[Online Computer Library Center|OCLC]]', 'worldcat.org/fr/title/' ) 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.s2cid( s2cid ) return databaseExterne( s2cid, '[[Semantic Scholar|S2CID]]', 'api.semanticscholar.org/CorpusID:' ) 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 = ' ' end local url = validArg( 'lien', 'url' ) local urlSimple = true if url then if url:match( '^https?://' ) or url:match( '^ftp://' ) or url:match( '^//' ) then url = url:gsub( '%[', '%%5B' ):gsub( '%]', '%%5D' ):gsub( ' ', '%%20' ) else urlSimple = false end elseif validArg( 'doi' ) then url = 'https://dx.doi.org/' .. mw.uri.encode( args.doi ) else return end local lien if urlSimple then local texte = validArg( 'texte' ) or 'en ligne' lien = '[' .. url .. ' ' .. texte .. ']' else lien = url end local date = validArg( 'date', 'consulté le' ) if date then return lang .. esp .. lien .. ' (consultée le' .. date .. ')' else return lang .. esp .. lien end end function References.affichageLiensExternes( args, validArg, lireEnLigne, consulteLe ) local validArg = validArg or function ( ... ) return validTextArg( args, ... ) end local liensExternes = {} local function minsert( ... ) for i = 1, select( '#', ... ) do local sel = select( i, ... ) if sel then liensExternes[#liensExternes+1] = sel end end end -- isbn et issn minsert( References.isbn( args, validArg ), References.ean( args, validArg ), References.issn( args, validArg ), References.eissn( args, validArg ), References.ismn( args, validArg ) ) 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' ), validArg( 'accès doi' ) ), References.jstor( args.jstor ), References.bibcode( args.bibcode ), References.mathreviews( validArg( 'math reviews', 'mathreviews', 'mr' ) ), References.zbl( validArg( 'zbl', 'zbmath' ) ), References.arxiv( args.arxiv ), References.hal( args.hal ), References.hdl( args.hdl, validArg( 'accès hdl') ), References.s2cid( args.s2cid ), References.libris( args.libris ), References.citeseerx( args.citeseerx ), References.jfm( args.jfm ), References.asin( args.asin ), References.sudoc( args.sudoc ), References.wikisource( args.wikisource ) ) 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' ) local paywall = validArg( 'accès url' ) if url and lireEnLigne then local archiveUrl = validArg( 'archive-url', 'archiveurl' ) if archiveUrl then if not ( string.match( archiveUrl, '^http' ) or string.match( archiveUrl, '^//' ) ) then archiveUrl = 'http://' .. archiveUrl end local textArchive = 'archive' local dateArchive = validArg( 'archive-date', 'archivedate' ) if dateArchive then textArchive = 'archive du ' .. Commun.inscriptionDate{ date = dateArchive } end minsert( '<span class="noarchive">' .. References.enLigne{ url = url, texte = 'lire en ligne' } .. ' <small class="cachelinks">[[' .. archiveUrl .. ' ' .. textArchive .. ']]</small>' .. ( paywall and References.indicationIconeAcces( paywall ) or '' ) .. '</span>' .. ( References.indicationDeFormat( args['format électronique'] ) or '' ) ) else minsert( References.enLigne{ url = url, texte = 'lire en ligne' } .. ( paywall and References.indicationIconeAcces( paywall ) or '' ) .. ( References.indicationDeFormat( args['format électronique'] ) or '' ) ) end end -- consulté le if consulteLe then local consult = validArg( 'consulté le', 'accessdate', 'access-date', 'Consulté le', 'consulté', 'consultée le' ) if consult then local prefixe = 'consulté le ' if consult:sub( -1, -1 ) == '.' then consult = consult:sub( 1, -2 ) end consult = consult:lower() local Date = require( 'Module:Date' ) local dateWikifiee = Date.modeleDate{ consult, nolinks = true, afficherErreurs = true, categoriserErreurs = 'Page contenant un paramètre « consulté le » avec une date invalide' } if dateWikifiee:match( 'datetime="%d%d%d%d"' ) or dateWikifiee:match( 'datetime="%d%d%d%d%-%d%d"' ) then prefixe = 'consulté en ' end minsert( prefixe .. dateWikifiee ) end end if #liensExternes > 0 then return ' <small style="line-height:1em;">(' .. table.concat( liensExternes, ', ' ) .. ')</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-' .. tabFormat[1]:lower() .. '" ' .. '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, "^([%S]+) (.+)$") if ext and listeFormat[ string.lower( ext ) ] then return References.indicationDeFormat( ext ) .. ' ' .. texte else return ' [' .. format .. ']' -- '[' = '<nowiki>[</nowiki>', ']' = '<nowiki>]</nowiki>', end end end function References.indicationIconeAcces( paywall ) local val = string.lower( paywall ) -- nettoyage qui devrait être suffisant if val == 'payant' then return ' [[File:Lock-red-alt-2.svg|9px|link=|alt=Accès payant|Accès payant au document]]' elseif val == 'libre' then return ' [[File:Lock-green.svg|9px|link=|alt=Accès libre|Accès libre au document]]' elseif val == 'limité' then return ' [[File:Lock-gray-alt-2.svg|9px|link=|alt=Accès limité|Accès limité au document]]' elseif val == 'inscription' then return ' [[File:Lock-gray-alt-2.svg|9px|link=|alt=Inscription nécessaire|Inscription nécessaire pour accéder au document]]' else return '' end end return References