UNPKG

@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
/*! * * 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