Skip to content

Commit d22e0d6

Browse files
committed
Merge branch 'feat/isbn-formatter' into chore/all-my-stuffs
# Conflicts: # package.json # pnpm-lock.yaml # src/tools/index.ts
2 parents 709fed7 + 84d0f7f commit d22e0d6

File tree

5 files changed

+126
-0
lines changed

5 files changed

+126
-0
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@
217217
"ip-cidr": "^4.0.0",
218218
"is-cidr": "^5.0.3",
219219
"is-ip": "^5.0.1",
220+
"isbn3": "^1.1.44",
220221
"json5": "^2.2.3",
221222
"jsonpath": "^1.1.1",
222223
"jsonar-mod": "^1.9.0",

pnpm-lock.yaml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/tools/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,7 @@ import { tool as powerConverter } from './power-converter';
202202
import { tool as dockerComposeConverter } from './docker-compose-converter';
203203
import { tool as imageExifReader } from './image-exif-reader';
204204
import { tool as ipInRange } from './ip-in-range';
205+
import { tool as isbnValidatorAndParser } from './isbn-validator-and-parser';
205206
import { tool as yamlViewer } from './yaml-viewer';
206207
import { tool as barcodeReader } from './barcode-reader';
207208
import { tool as barcodeGenerator } from './barcode-generator';
@@ -506,6 +507,7 @@ export const toolsByCategory: ToolCategory[] = [
506507
luhnValidator,
507508
vatValidator,
508509
airCodes,
510+
isbnValidatorAndParser,
509511
],
510512
},
511513
];
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Books } from '@vicons/tabler';
2+
import { defineTool } from '../tool';
3+
4+
export const tool = defineTool({
5+
name: 'ISBN Validator and Parser',
6+
path: '/isbn-validator-and-parser',
7+
description: 'Parse, validate, format and get infos for an ISBN',
8+
keywords: ['isbn', 'validator', 'parser', 'formatter'],
9+
component: () => import('./isbn-validator-and-parser.vue'),
10+
icon: Books,
11+
createdAt: new Date('2024-01-10'),
12+
});
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<script setup lang="ts">
2+
import ISBN3 from 'isbn3';
3+
import type { CKeyValueListItems } from '@/ui/c-key-value-list/c-key-value-list.types';
4+
5+
const rawIsbn = ref('9782021304534');
6+
7+
const isbnInfos = computed<CKeyValueListItems>(() => {
8+
const isbn = ISBN3.parse(rawIsbn.value);
9+
10+
if (isbn == null) {
11+
return [];
12+
}
13+
14+
return [
15+
{
16+
label: 'Is ISBN valid ?',
17+
value: isbn.isValid,
18+
},
19+
{
20+
label: 'Country',
21+
value: isbn.groupname,
22+
},
23+
{
24+
label: 'ISBN 13',
25+
value: isbn.isbn13,
26+
},
27+
{
28+
label: 'ISBN 13 Formatted',
29+
value: isbn.isbn13h,
30+
},
31+
{
32+
label: 'ISBN 10',
33+
value: isbn.isbn10,
34+
},
35+
{
36+
label: 'ISBN 10 Formatted',
37+
value: isbn.isbn10h,
38+
},
39+
];
40+
});
41+
42+
function isbnChecksum(isbn: string) {
43+
let check = 0;
44+
45+
if (isbn.length === 9) {
46+
for (let n = 0; n < 9; n += 1) {
47+
check += (10 - n) * Number(isbn.charAt(n));
48+
}
49+
check = (11 - check % 11) % 11;
50+
return check === 10 ? 'X' : String(check);
51+
}
52+
else if (isbn.length === 12) {
53+
for (let n = 0; n < 12; n += 2) {
54+
check += Number(isbn.charAt(n)) + 3 * Number(isbn.charAt(n + 1));
55+
}
56+
return String((10 - check % 10) % 10);
57+
}
58+
59+
return null;
60+
}
61+
62+
const normalizedISBN = computed(() => {
63+
let normalized = rawIsbn.value.replace(/[^\dX]/g, '');
64+
if (normalized.length >= 13) {
65+
normalized = normalized.substring(0, 12);
66+
}
67+
else if (normalized.length >= 10) {
68+
normalized = normalized.substring(0, 9);
69+
}
70+
const checksum = isbnChecksum(normalized);
71+
return ISBN3.parse(`${normalized}${checksum}`)?.isbn13h;
72+
});
73+
74+
const isbnAuditInfos = computed<{ isValid: boolean; clues: CKeyValueListItems }>(() => {
75+
const isbn = ISBN3.audit(rawIsbn.value);
76+
77+
const isValid = (isbn?.validIsbn ?? false);
78+
return {
79+
isValid,
80+
clues: Array.from((isbn?.clues ?? []),
81+
clue => ({ label: `${clue.message} (${clue.groupname})`, value: clue.candidate })),
82+
};
83+
});
84+
</script>
85+
86+
<template>
87+
<div>
88+
<c-input-text v-model:value="rawIsbn" placeholder="Enter an ISBN to check for validity..." test-id="isbn-input" />
89+
<n-alert v-if="!isbnAuditInfos.isValid" type="error">
90+
Invalid ISBN.
91+
<input-copyable v-if="normalizedISBN" label="Probably correct" label-position="left" :value="normalizedISBN" disabled="true" />
92+
</n-alert>
93+
94+
<c-card v-if="isbnInfos.length > 0" mt-5 title="ISBN Infos">
95+
<c-key-value-list :items="isbnInfos" data-test-id="isbn-info" />
96+
</c-card>
97+
<c-card v-if="isbnAuditInfos.clues.length > 0" mt-5 title="ISBN Audit Infos">
98+
<c-key-value-list :items="isbnAuditInfos" data-test-id="isbn-info" />
99+
</c-card>
100+
</div>
101+
</template>

0 commit comments

Comments
 (0)