@shippack/isbn-verify
Version:
Verify ISBN string format and check digit.
174 lines (155 loc) • 4.65 kB
text/typescript
interface isOption {
check_digit?: boolean; // Verify format including check digit
}
/**
* ISBN Verify
*/
export default class {
#isbnNoHyphens: string; // ハイフンなしの ISBN
#isbn13 = false; // 現行規格(13桁)の ISBN か
#isbn10 = false; // 旧規格(10桁)の ISBN か
/**
* @param {string} isbn - ISBN value to check
* @param {boolean} strict - Strict mode. If `true`, syntax without hyphens is an error. If not specified, it defaults to `false`
*/
constructor(isbn: string, strict = false) {
const isbnNoHyphens = isbn.replace(/-/g, '');
this.#isbnNoHyphens = isbnNoHyphens;
if (strict) {
const length = isbn.length;
if (length === 17 && /^(978|979)-\d{1,5}-\d{1,7}-\d{1,7}-\d$/.test(isbn)) {
this.#isbn13 = true;
} else if (length === 13 && /^\d{1,5}-\d{1,7}-\d{1,7}-[\dX]$/.test(isbn)) {
this.#isbn10 = true;
}
} else {
if (!isbn.includes('--')) {
if (/^(978|979)\d{10}$/.test(isbnNoHyphens)) {
if (/^\d[\d-]{11,15}\d$/.test(isbn)) {
this.#isbn13 = true;
}
} else if (/^\d{9}[\dX]$/.test(isbnNoHyphens)) {
if (/^\d[\d-]{8,11}[\dX]$/.test(isbn)) {
this.#isbn10 = true;
}
}
}
}
}
/**
* Alias of `verifyCheckDigit()`
*
* @returns {boolean} `true` if both format and check digit are correct
*/
isValid(): boolean {
return this.verifyCheckDigit();
}
/**
* Whether it is a 13-digit ISBN
*
* @param {isOption} options - Specifies characteristics about the check item.
*
* @returns {boolean} `true` for current standard (13 digit) ISBN
*/
isIsbn13(options?: isOption): boolean {
if (options !== undefined && options.check_digit) {
return this.#isbn13 && this.verifyCheckDigit();
}
return this.#isbn13;
}
/**
* Whether it is a 10-digit ISBN
*
* @param {isOption} options - Specifies characteristics about the check item.
*
* @returns {boolean} `true` for old standard (10 digit) ISBN
*/
isIsbn10(options?: isOption): boolean {
if (options !== undefined && options.check_digit) {
return this.#isbn10 && this.verifyCheckDigit();
}
return this.#isbn10;
}
/**
* Verify format (do not verify check digit)
*
* @returns {boolean} `true` if the format is correct
*/
verifyFormat(): boolean {
return this.#isbn13 || this.#isbn10;
}
/**
* Verify format including check digit (not necessarily applicable publication)
*
* @returns {boolean} `true` if both format and check digit are correct
*/
verifyCheckDigit(): boolean {
if (this.#isbn13) {
const isbnNoHyphens = this.#isbnNoHyphens;
return isbnNoHyphens.substring(12) === this._getCheckDigit13(isbnNoHyphens);
} else if (this.#isbn10) {
const isbnNoHyphens = this.#isbnNoHyphens;
return isbnNoHyphens.substring(9) === this._getCheckDigit10(isbnNoHyphens);
}
return false;
}
/**
* ISBN-13 のチェックデジットを取得する
*
* @param {string} isbnNoHyphens - ハイフンなしの ISBN
*
* @returns {string} チェックデジット
*/
private _getCheckDigit13(isbnNoHyphens: string): string {
const checkDigit = String(
10 -
((Number(isbnNoHyphens.substring(0, 1)) +
Number(isbnNoHyphens.substring(1, 2)) * 3 +
Number(isbnNoHyphens.substring(2, 3)) +
Number(isbnNoHyphens.substring(3, 4)) * 3 +
Number(isbnNoHyphens.substring(4, 5)) +
Number(isbnNoHyphens.substring(5, 6)) * 3 +
Number(isbnNoHyphens.substring(6, 7)) +
Number(isbnNoHyphens.substring(7, 8)) * 3 +
Number(isbnNoHyphens.substring(8, 9)) +
Number(isbnNoHyphens.substring(9, 10)) * 3 +
Number(isbnNoHyphens.substring(10, 11)) +
Number(isbnNoHyphens.substring(11, 12)) * 3) %
10)
);
switch (checkDigit) {
case '10':
return '0';
}
return checkDigit;
}
/**
* ISBN-10 のチェックデジットを取得する
*
* @param {string} isbnNoHyphens - ハイフンなしの ISBN
*
* @returns {string} チェックデジット
*/
private _getCheckDigit10(isbnNoHyphens: string): string {
const checkDigit = String(
11 -
((Number(isbnNoHyphens.substring(0, 1)) * 10 +
Number(isbnNoHyphens.substring(1, 2)) * 9 +
Number(isbnNoHyphens.substring(2, 3)) * 8 +
Number(isbnNoHyphens.substring(3, 4)) * 7 +
Number(isbnNoHyphens.substring(4, 5)) * 6 +
Number(isbnNoHyphens.substring(5, 6)) * 5 +
Number(isbnNoHyphens.substring(6, 7)) * 4 +
Number(isbnNoHyphens.substring(7, 8)) * 3 +
Number(isbnNoHyphens.substring(8, 9)) * 2) %
11)
);
switch (checkDigit) {
case '10':
return 'X';
case '11':
return '0';
}
return checkDigit;
}
}