@kit-data-manager/pid-component
Version:
The PID-Component is a web component that can be used to evaluate and display FAIR Digital Objects, PIDs, ORCiDs, and possibly other identifiers in a user-friendly way. It is easily extensible to support other identifier types.
207 lines (206 loc) • 9.15 kB
JavaScript
/*!
*
* Copyright 2024-2026 Karlsruhe Institute of Technology.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
import { h } from "@stencil/core";
import { GenericIdentifierType } from "../../utils/GenericIdentifierType";
import { FoldableItem } from "../../utils/FoldableItem";
import { FoldableAction } from "../../utils/FoldableAction";
export class ISBNType extends GenericIdentifierType {
constructor() {
super(...arguments);
this.normalizedIsbn = '';
this.bookData = null;
}
get data() {
var _a;
return JSON.stringify({
isbn: this.normalizedIsbn,
bookData: (_a = this.bookData) !== null && _a !== void 0 ? _a : {},
});
}
getSettingsKey() {
return 'ISBNType';
}
quickCheck() {
const normalized = this.normalizeInput(this.value);
if (ISBNType.ISBN10_FORMAT.test(normalized))
return this.isValidIsbn10(normalized);
if (ISBNType.ISBN13_FORMAT.test(normalized))
return this.isValidIsbn13(normalized);
return false;
}
async hasMeaningfulInformation() {
const normalized = this.normalizeInput(this.value);
if (!this.isValid(normalized))
return false;
this.normalizedIsbn = normalized;
const apiUrl = `https://openlibrary.org/api/books?bibkeys=ISBN:${encodeURIComponent(normalized)}&format=json&jscmd=data`;
try {
const response = await fetch(apiUrl);
if (!response.ok)
return false;
const payload = (await response.json());
const book = payload[`ISBN:${normalized}`];
if (!book)
return false;
if (!this.hasUsefulBookMetadata(book))
return false;
this.bookData = book;
return true;
}
catch (_a) {
return false;
}
}
async init(data) {
if (data !== undefined) {
this.loadFromCache(data);
}
if (!this.bookData) {
const success = await this.hasMeaningfulInformation();
if (!success) {
console.info(`ISBNType: No meaningful data found for ISBN ${this.normalizedIsbn}.`);
return;
}
}
this.populateItems();
this.populateActions();
}
isResolvable() {
return this.bookData !== null;
}
renderPreview() {
var _a;
return (h("span", { class: `inline-flex flex-nowrap items-baseline font-mono min-w-0 max-w-full ${this.isDarkMode ? 'text-gray-200' : ''}` }, h("span", { class: 'flex-none pr-2' }, "\uD83D\uDCDA"), h("span", { class: 'min-w-0 overflow-hidden text-ellipsis whitespace-nowrap' }, ((_a = this.bookData) === null || _a === void 0 ? void 0 : _a.title) || `ISBN ${this.normalizedIsbn || this.value}`)));
}
renderBody() {
var _a, _b, _c, _d, _e, _f;
const coverUrl = ((_b = (_a = this.bookData) === null || _a === void 0 ? void 0 : _a.cover) === null || _b === void 0 ? void 0 : _b.medium) || ((_d = (_c = this.bookData) === null || _c === void 0 ? void 0 : _c.cover) === null || _d === void 0 ? void 0 : _d.small) || ((_e = this.bookData) === null || _e === void 0 ? void 0 : _e.thumbnail_url);
if (!coverUrl)
return undefined;
return (h("div", { class: "flex w-full justify-center" }, h("img", { src: coverUrl, alt: `Cover preview for ${((_f = this.bookData) === null || _f === void 0 ? void 0 : _f.title) || this.normalizedIsbn}`, class: "max-h-64 rounded border border-gray-200 object-contain", loading: "lazy" })));
}
normalizeInput(value) {
return value.trim().replace(ISBNType.PREFIX_REGEX, '').replace(ISBNType.NOISE_REGEX, '').toUpperCase();
}
isValid(normalized) {
if (ISBNType.ISBN10_FORMAT.test(normalized))
return this.isValidIsbn10(normalized);
if (ISBNType.ISBN13_FORMAT.test(normalized))
return this.isValidIsbn13(normalized);
return false;
}
isValidIsbn10(isbn) {
let sum = 0;
for (let i = 0; i < 10; i++) {
const char = isbn[i];
const digit = i === 9 && char === 'X' ? 10 : Number(char);
if (!Number.isInteger(digit))
return false;
sum += (10 - i) * digit;
}
return sum % 11 === 0;
}
isValidIsbn13(isbn) {
if (!isbn.startsWith('978') && !isbn.startsWith('979'))
return false;
let sum = 0;
for (let i = 0; i < 12; i++) {
const digit = Number(isbn[i]);
if (!Number.isInteger(digit))
return false;
sum += digit * (i % 2 === 0 ? 1 : 3);
}
const checksum = (10 - (sum % 10)) % 10;
return checksum === Number(isbn[12]);
}
hasUsefulBookMetadata(book) {
return Boolean(book.title ||
book.publish_date ||
(book.authors && book.authors.length > 0) ||
(book.publishers && book.publishers.length > 0));
}
loadFromCache(data) {
try {
const parsed = JSON.parse(data);
this.normalizedIsbn = parsed.isbn || this.normalizeInput(this.value);
this.bookData = parsed.bookData || null;
}
catch (_a) {
this.normalizedIsbn = this.normalizeInput(this.value);
}
}
populateItems() {
if (!this.bookData)
return;
this.items.push(new FoldableItem(0, 'ISBN', this.normalizedIsbn, 'International Standard Book Number used to identify this publication', 'https://en.wikipedia.org/wiki/ISBN', undefined, false));
this.items.push(new FoldableItem(1, 'Metadata Source', 'OpenLibrary', 'Metadata provided by OpenLibrary', 'https://openlibrary.org/developers/api'));
if (this.bookData.title)
this.items.push(new FoldableItem(2, 'Title', this.bookData.title, 'Title of the publication'));
if (this.bookData.subtitle)
this.items.push(new FoldableItem(3, 'Subtitle', this.bookData.subtitle, 'Subtitle of the publication'));
if (this.bookData.publish_date)
this.items.push(new FoldableItem(4, 'Date', new Date(this.bookData.publish_date).toDateString(), 'Publication date'));
if (this.bookData.number_of_pages)
this.items.push(new FoldableItem(5, 'Pages', String(this.bookData.number_of_pages), 'Number of pages'));
(this.bookData.authors || [])
.map(author => author.name)
.filter((name) => Boolean(name))
.map(name => new FoldableItem(6, 'Author', name))
.forEach((item) => this.items.push(item));
const publisherNames = (this.bookData.publishers || []).map(publisher => publisher.name).filter((name) => Boolean(name));
if (publisherNames.length > 0)
this.items.push(new FoldableItem(7, 'Publisher', publisherNames.join(', '), 'Publisher(s) of the publication'));
const summary = this.extractSummary();
if (summary)
this.items.push(new FoldableItem(8, 'Abstract', summary, 'Brief description of the publication', undefined, undefined, false));
}
populateActions() {
if (!this.bookData)
return;
const openLibraryUrl = this.bookData.url ? this.bookData.url : `https://openlibrary.org/isbn/${this.normalizedIsbn}`;
this.actions.push(new FoldableAction(0, 'View on OpenLibrary', openLibraryUrl, 'primary'));
if (this.bookData.preview_url) {
this.actions.push(new FoldableAction(2, 'Open Preview', this.bookData.preview_url, 'secondary'));
}
}
extractSummary() {
var _a, _b;
if (!this.bookData)
return null;
const fromDescription = this.extractText(this.bookData.description);
if (fromDescription)
return fromDescription;
const fromNotes = this.extractText(this.bookData.notes);
if (fromNotes)
return fromNotes;
const excerpt = (_b = (_a = this.bookData.excerpts) === null || _a === void 0 ? void 0 : _a.find(item => item.text)) === null || _b === void 0 ? void 0 : _b.text;
return excerpt || null;
}
extractText(value) {
if (!value)
return null;
if (typeof value === 'string')
return value;
return value.value || null;
}
}
ISBNType.PREFIX_REGEX = /^ISBN(?:-1[03])?:?\s*/i;
ISBNType.NOISE_REGEX = /[\s-]+/g;
ISBNType.ISBN10_FORMAT = /^\d{9}[\dX]$/;
ISBNType.ISBN13_FORMAT = /^\d{13}$/;
//# sourceMappingURL=ISBNType.js.map