356 lines
11 KiB
JavaScript
356 lines
11 KiB
JavaScript
/**
|
|
* Plausibilitätsprüfung von ISBN-10, ISBN-13, ISSN, Handle, DOI und URN
|
|
*/
|
|
|
|
/**
|
|
* Prüft eine ISBN-10 auf Plausibilität
|
|
* @param {string} isbn10 - Die zu prüfende ISBN-10
|
|
* @returns {boolean} - true wenn gültig, false wenn ungültig
|
|
*/
|
|
function validateISBN10(isbn10) {
|
|
if (!isbn10) return false;
|
|
|
|
// Normalisierung: Bindestriche und Leerzeichen entfernen
|
|
const clean = isbn10.replace(/[-\s]/g, '');
|
|
|
|
// Muss genau 10 Zeichen haben
|
|
if (clean.length !== 10) return false;
|
|
|
|
// Erste 9 Zeichen müssen Ziffern sein, letztes Zeichen Ziffer oder X
|
|
if (!/^\d{9}[\dX]$/.test(clean)) return false;
|
|
|
|
// Prüfziffer berechnen
|
|
let sum = 0;
|
|
for (let i = 0; i < 9; i++) {
|
|
sum += parseInt(clean[i]) * (10 - i);
|
|
}
|
|
|
|
const checkDigit = clean[9];
|
|
const calculatedCheck = (11 - (sum % 11)) % 11;
|
|
|
|
return (calculatedCheck === 10 && checkDigit === 'X') ||
|
|
(calculatedCheck < 10 && checkDigit === calculatedCheck.toString());
|
|
}
|
|
|
|
/**
|
|
* Prüft eine ISBN-13 auf Plausibilität
|
|
* @param {string} isbn13 - Die zu prüfende ISBN-13
|
|
* @returns {boolean} - true wenn gültig, false wenn ungültig
|
|
*/
|
|
function validateISBN13(isbn13) {
|
|
if (!isbn13) return false;
|
|
|
|
// Normalisierung: Bindestriche und Leerzeichen entfernen
|
|
const clean = isbn13.replace(/[-\s]/g, '');
|
|
|
|
// Muss genau 13 Ziffern haben
|
|
if (clean.length !== 13 || !/^\d{13}$/.test(clean)) return false;
|
|
|
|
// Muss mit 978 oder 979 beginnen
|
|
if (!clean.startsWith('978') && !clean.startsWith('979')) return false;
|
|
|
|
// Prüfziffer berechnen (modulo 10)
|
|
let sum = 0;
|
|
for (let i = 0; i < 12; i++) {
|
|
sum += parseInt(clean[i]) * (i % 2 === 0 ? 1 : 3);
|
|
}
|
|
|
|
const checkDigit = parseInt(clean[12]);
|
|
const calculatedCheck = (10 - (sum % 10)) % 10;
|
|
|
|
return checkDigit === calculatedCheck;
|
|
}
|
|
|
|
/**
|
|
* Prüft einen DOI auf Plausibilität
|
|
* @param {string} doi - Der zu prüfende DOI
|
|
* @returns {boolean} - true wenn gültig, false wenn ungültig
|
|
*/
|
|
function validateDOI(doi) {
|
|
if (!doi) return false;
|
|
|
|
// DOI kann mit oder ohne "doi:" Präfix sein
|
|
const clean = doi.replace(/^doi:\s*/i, '');
|
|
|
|
// Grundlegendes DOI-Format: 10.xxxx/yyyy
|
|
// Registrant Code muss mit 10. beginnen
|
|
const doiRegex = /^10\.\d{4,}\/[^\s]+$/;
|
|
|
|
if (!doiRegex.test(clean)) return false;
|
|
|
|
// Weitere Plausibilitätsprüfungen
|
|
const parts = clean.split('/');
|
|
if (parts.length < 2) return false;
|
|
|
|
const prefix = parts[0];
|
|
const suffix = parts.slice(1).join('/');
|
|
|
|
// Präfix muss 10.xxxx Format haben (mindestens 4 Ziffern nach 10.)
|
|
if (!/^10\.\d{4,}$/.test(prefix)) return false;
|
|
|
|
// Suffix darf nicht leer sein und keine Leerzeichen enthalten
|
|
if (!suffix.trim() || /\s/.test(suffix)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Prüft eine URN auf Plausibilität
|
|
* @param {string} urn - Die zu prüfende URN
|
|
* @returns {boolean} - true wenn gültig, false wenn ungültig
|
|
*/
|
|
function validateURN(urn) {
|
|
if (!urn) return false;
|
|
|
|
// URN muss mit "urn:" beginnen (case-insensitive)
|
|
if (!urn.toLowerCase().startsWith('urn:')) return false;
|
|
|
|
// Grundlegendes URN-Format: urn:namespace:identifier
|
|
const parts = urn.split(':');
|
|
if (parts.length < 3) return false;
|
|
|
|
const namespace = parts[1];
|
|
const identifier = parts.slice(2).join(':');
|
|
|
|
// Namespace-Validierung
|
|
if (!namespace || !/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(namespace)) {
|
|
return false;
|
|
}
|
|
|
|
// Namespace darf nicht länger als 32 Zeichen sein
|
|
if (namespace.length > 32) return false;
|
|
|
|
// Identifier darf nicht leer sein
|
|
if (!identifier) return false;
|
|
|
|
// Prüfung auf reservierte Zeichen (vereinfacht)
|
|
const reservedChars = /[<>"{}|\\^`\[\]]/;
|
|
if (reservedChars.test(identifier)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Prüft eine ISSN auf Plausibilität
|
|
* @param {string} issn - Die zu prüfende ISSN
|
|
* @returns {boolean} - true wenn gültig, false wenn ungültig
|
|
*/
|
|
function validateISSN(issn) {
|
|
if (!issn) return false;
|
|
|
|
// Normalisierung: Bindestriche und Leerzeichen entfernen
|
|
const clean = issn.replace(/[-\s]/g, '');
|
|
|
|
// Muss genau 8 Zeichen haben
|
|
if (clean.length !== 8) return false;
|
|
|
|
// Erste 7 Zeichen müssen Ziffern sein, letztes Zeichen Ziffer oder X
|
|
if (!/^\d{7}[\dX]$/.test(clean)) return false;
|
|
|
|
// Prüfziffer berechnen (modulo 11)
|
|
let sum = 0;
|
|
for (let i = 0; i < 7; i++) {
|
|
sum += parseInt(clean[i]) * (8 - i);
|
|
}
|
|
|
|
const checkDigit = clean[7];
|
|
const calculatedCheck = (11 - (sum % 11)) % 11;
|
|
|
|
return (calculatedCheck === 10 && checkDigit === 'X') ||
|
|
(calculatedCheck < 10 && checkDigit === calculatedCheck.toString());
|
|
}
|
|
|
|
/**
|
|
* Prüft eine eISSN (elektronische ISSN) auf Plausibilität
|
|
* @param {string} eissn - Die zu prüfende eISSN
|
|
* @returns {boolean} - true wenn gültig, false wenn ungültig
|
|
*/
|
|
function validateEISSN(eissn) {
|
|
// eISSN hat das gleiche Format wie ISSN
|
|
return validateISSN(eissn);
|
|
}
|
|
|
|
/**
|
|
* Validierung verschiedener Identifier-Typen
|
|
* @param {string} identifier - Der zu prüfende Identifier
|
|
* @param {string} identifierType - Identifier Typ ("ISBN" | "ISSN" | "URN" | "DOI" | "Handle")
|
|
* @returns {object} - Objekt mit Typ und Validierungsergebnis
|
|
*/
|
|
function validateIdentifier(identifier, identifierType) {
|
|
|
|
if (!identifier) {
|
|
return { type: 'unknown', valid: false, error: 'Leerer Identifier' };
|
|
}
|
|
|
|
const clean = identifier.trim();
|
|
|
|
switch (identifierType) {
|
|
case "URN": return {
|
|
type: 'URN',
|
|
valid: validateURN(clean),
|
|
identifier: clean
|
|
};
|
|
case "DOI": if (clean.toLowerCase().startsWith('doi:') || clean.startsWith('10.')) {
|
|
return {
|
|
type: 'DOI',
|
|
valid: validateDOI(clean),
|
|
identifier: clean
|
|
};
|
|
}
|
|
case "ISBN": const isbnClean = clean.replace(/[-\s]/g, '');
|
|
if (/^\d{10}$/.test(isbnClean) || /^\d{9}X$/.test(isbnClean)) {
|
|
return {
|
|
type: 'ISBN-10',
|
|
valid: validateISBN10(clean),
|
|
identifier: clean
|
|
};
|
|
}
|
|
|
|
if (/^\d{13}$/.test(isbnClean)) {
|
|
return {
|
|
type: 'ISBN-13',
|
|
valid: validateISBN13(clean),
|
|
identifier: clean
|
|
};
|
|
}
|
|
case "ISSN": const issnClean = clean.replace(/[-\s]/g, '');
|
|
if (/^\d{8}$/.test(issnClean) || /^\d{7}X$/.test(issnClean)) {
|
|
return {
|
|
type: 'ISSN',
|
|
valid: validateISSN(clean),
|
|
identifier: clean
|
|
};
|
|
}
|
|
case "Handle":if (clean.toLowerCase().startsWith('hdl:') ||
|
|
clean.toLowerCase().includes('hdl.handle.net') ||
|
|
clean.toLowerCase().includes('handle.net') ||
|
|
/^\d+(\.\d+)*\//.test(clean)) {
|
|
return {
|
|
type: 'Handle',
|
|
valid: validateHandle(clean),
|
|
identifier: clean
|
|
};
|
|
}
|
|
|
|
default: return { type: 'Identifikator unbekannt: Struktur ist kein DOI, keine URN, ISBN, ISSN oder Handle', valid: false, error: 'Bitte prüfen Sie den gewählten Identifikator und dessen Inhalt (z.B DOI: 10.1515/zfsoz-2025-2017, URN: urn:nbn:de:0035-vetrepository-783215)' };
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Hilfsfunktion: Normalisiert ISBN-Eingaben
|
|
* @param {string} isbn - ISBN mit oder ohne Trennzeichen
|
|
* @returns {string} - Normalisierte ISBN ohne Trennzeichen
|
|
*/
|
|
function normalizeISBN(isbn) {
|
|
return isbn ? isbn.replace(/[-\s]/g, '') : '';
|
|
}
|
|
|
|
/**
|
|
* Hilfsfunktion: Extrahiert DOI ohne Präfix
|
|
* @param {string} doi - DOI mit oder ohne "doi:" Präfix
|
|
* @returns {string} - DOI ohne Präfix
|
|
*/
|
|
function normalizeDOI(doi) {
|
|
return doi ? doi.replace(/^doi:\s*/i, '') : '';
|
|
}
|
|
|
|
/**
|
|
* Batch-Validierung für mehrere Identifier
|
|
* @param {string[]} identifiers - Array von zu prüfenden Identifiern
|
|
* @returns {object[]} - Array von Validierungsergebnissen
|
|
*/
|
|
function validateMultipleIdentifiers(identifiers) {
|
|
if (!Array.isArray(identifiers)) {
|
|
return [];
|
|
}
|
|
|
|
return identifiers.map(id => validateIdentifier(id));
|
|
}
|
|
|
|
/**
|
|
* Prüft ob ein String eine gültige ISBN (10 oder 13) ist
|
|
* @param {string} isbn - Zu prüfende ISBN
|
|
* @returns {boolean} - true wenn gültige ISBN-10 oder ISBN-13
|
|
*/
|
|
function isValidISBN(isbn) {
|
|
return validateISBN10(isbn) || validateISBN13(isbn);
|
|
}
|
|
|
|
|
|
function validateHandle(handle) {
|
|
if (!handle) return false;
|
|
|
|
// Handle kann mit oder ohne "hdl:" Präfix oder "http://hdl.handle.net/" sein
|
|
let clean = handle.trim();
|
|
|
|
// Verschiedene Handle-Präfixe entfernen
|
|
clean = clean.replace(/^hdl:\s*/i, '');
|
|
clean = clean.replace(/^https?:\/\/hdl\.handle\.net\//, '');
|
|
clean = clean.replace(/^https?:\/\/handle\.net\//, '');
|
|
|
|
// Handle-Format: prefix/suffix (z.B. 10.1000/123 oder 1721.1/12345)
|
|
if (!clean.includes('/')) return false;
|
|
|
|
const parts = clean.split('/');
|
|
if (parts.length < 2) return false;
|
|
|
|
const prefix = parts[0];
|
|
const suffix = parts.slice(1).join('/');
|
|
|
|
// Prefix-Validierung
|
|
if (!prefix || prefix.length === 0) return false;
|
|
|
|
// Prefix darf nur Zahlen, Punkte und Bindestriche enthalten
|
|
if (!/^[0-9.-]+$/.test(prefix)) return false;
|
|
|
|
// Suffix-Validierung
|
|
if (!suffix || suffix.length === 0) return false;
|
|
|
|
// Suffix darf keine Leerzeichen enthalten
|
|
if (/\s/.test(suffix)) return false;
|
|
|
|
// Reservierte Zeichen prüfen
|
|
const reservedChars = /[<>"{}|\\^`\[\]]/;
|
|
if (reservedChars.test(suffix)) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
// ========================================
|
|
// Beispiele für die Verwendung
|
|
// ========================================
|
|
|
|
/* console.log('=== ISBN-10 Tests ===');
|
|
console.log('0-306-40615-2:', validateISBN10('0-306-40615-2')); // true
|
|
console.log('0306406152:', validateISBN10('0306406152')); // true
|
|
console.log('0306406151:', validateISBN10('0306406151')); // false
|
|
console.log('0-19-852663-6:', validateISBN10('0-19-852663-6')); // true
|
|
|
|
console.log('\n=== ISBN-13 Tests ===');
|
|
console.log('978-0-306-40615-7:', validateISBN13('978-0-306-40615-7')); // true
|
|
console.log('9780306406157:', validateISBN13('9780306406157')); // true
|
|
console.log('9780306406156:', validateISBN13('9780306406156')); // false
|
|
console.log('979-0-000-00000-0:', validateISBN13('979-0-000-00000-0')); // true
|
|
|
|
console.log('\n=== DOI Tests ===');
|
|
console.log('10.1000/182:', validateDOI('10.1000/182')); // true
|
|
console.log('doi:10.1000/182:', validateDOI('doi:10.1000/182')); // true
|
|
console.log('10.1038/nature12373:', validateDOI('10.1038/nature12373')); // true
|
|
console.log('10.123/456:', validateDOI('10.123/456')); // false (zu kurz)
|
|
|
|
console.log('\n=== URN Tests ===');
|
|
console.log('urn:isbn:0451450523:', validateURN('urn:isbn:0451450523')); // true
|
|
console.log('urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66:', validateURN('urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66')); // true
|
|
console.log('urn:nbn:de:bvb:19-146642:', validateURN('urn:nbn:de:bvb:19-146642')); // true
|
|
console.log('not-a-urn:', validateURN('not-a-urn')); // false
|
|
|
|
|
|
console.log('\n=== Hilfsfunktionen ===');
|
|
console.log('Normalisierte ISBN:', normalizeISBN('978-0-306-40615-7'));
|
|
console.log('Normalisierter DOI:', normalizeDOI('doi:10.1000/182'));
|
|
console.log('Gültige ISBN?:', isValidISBN('978-0-306-40615-7'));
|
|
|
|
*/
|