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.

1,147 lines (1,133 loc) 109 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. * */ 'use strict'; var index = require('./index-SLWnk0w6.js'); require('./json-viewer.entry.cjs.js'); class GenericIdentifierType { constructor(value, settings) { this._isDarkMode = false; this._settings = []; this._items = []; this._actions = []; this._value = value; this._settings = settings ? settings : []; this.updateDarkMode(); } get isDarkMode() { return this._isDarkMode; } get settings() { return this._settings; } set settings(value) { this._settings = value; this.updateDarkMode(); } get items() { return this._items; } get actions() { return this._actions; } get value() { return this._value; } get data() { return undefined; } isResolvable() { return true; } renderBody() { return undefined; } updateDarkMode() { var _a; const darkModeSetting = (_a = this._settings) === null || _a === void 0 ? void 0 : _a.find(setting => setting.name === 'darkMode'); if (darkModeSetting) { const darkMode = darkModeSetting.value; if (darkMode === 'dark') { this._isDarkMode = true; } else if (darkMode === 'light') { this._isDarkMode = false; } else if (darkMode === 'system' && typeof window !== 'undefined' && window.matchMedia) { this._isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches; } } else { this._isDarkMode = false; } } } let cacheInstance; async function open() { if ('caches' in self) { cacheInstance = await caches.open('pid-component'); } } async function cachedFetch(url, init) { await open(); if (cacheInstance) { const response = await cacheInstance.match(url); if (response) { return response.json(); } else { let response; try { const parts = url.split('://'); if (parts.length > 1 && parts[0] !== 'https') { response = await fetch(`https://${parts[1]}`, init); if (!response) { console.log(`404 for https://${parts[1]} - trying http://${parts[1]}`); response = await fetch(`http://${parts[1]}`, init); } } else { response = await fetch(url, init); } } catch (error) { console.error('Fetch failed:', error); throw error; } await cacheInstance.put(url, response.clone()); return response.json(); } } else { const response = await fetch(url, init); return response.json(); } } async function clearCache() { if (cacheInstance) { await cacheInstance.delete('pid-component'); } } class ORCIDInfo { constructor(orcid, ORCiDJSON, familyName, givenNames, employments, preferredLocale, biography, emails, keywords, researcherUrls, country) { this._orcid = orcid; this._familyName = familyName; this._givenNames = givenNames; this._employments = employments; this._preferredLocale = preferredLocale; this._biography = biography; this._emails = emails; this._keywords = keywords; this._researcherUrls = researcherUrls; this._country = country; this._ORCiDJSON = ORCiDJSON; } get orcid() { return this._orcid; } get familyName() { return this._familyName; } get givenNames() { return this._givenNames; } get ORCiDJSON() { return this._ORCiDJSON; } get employments() { return this._employments; } get preferredLocale() { return this._preferredLocale; } get biography() { return this._biography; } get emails() { return this._emails; } get keywords() { return this._keywords; } get researcherUrls() { return this._researcherUrls; } get country() { return this._country; } static isORCiD(text) { const regex = new RegExp('^(https://orcid.org/)?[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]'); return regex.test(text); } static async getORCiDInfo(orcid) { if (!ORCIDInfo.isORCiD(orcid)) throw new Error('Invalid input'); if (orcid.match('^(https://orcid.org/)?[0-9]{4}-[0-9]{4}-[0-9]{4}-[0-9]{3}[0-9X]') !== null) orcid = orcid.replace('https://orcid.org/', ''); orcid = orcid .replace(/^orcid:/i, '') .replace(/^https?:\/\/orcid.org\//i, '') .replace(/\/+$/, '') .replace(/\.+$/, '') .replace(/"+/g, '') .replace(/'+/g, '') .replace(/\s+/g, '') .replace(/,+$/, '') .replace(/;+$/, '') .replace(/:+$/, '') .trim(); const rawOrcidJSON = await cachedFetch(`https://pub.orcid.org/v3.0/${orcid}`, { headers: { Accept: 'application/json', }, }); let familyName = ''; let givenNames = []; try { familyName = rawOrcidJSON['person']['name']['family-name']['value']; } catch (e) { console.debug(e); } try { givenNames = rawOrcidJSON['person']['name']['given-names']['value']; } catch (e) { console.debug(e); } const affiliations = rawOrcidJSON['activities-summary']['employments']['affiliation-group']; const employments = []; try { for (let i = 0; i < affiliations.length; i++) { const employmentSummary = affiliations[i]['summaries'][0]['employment-summary']; const employment = new Employment(new Date(), null, '', ''); if (employmentSummary['start-date'] !== null) employment.startDate = new Date(employmentSummary['start-date']['year']['value'], employmentSummary['start-date']['month']['value'], employmentSummary['start-date']['day']['value']); if (employmentSummary['end-date'] !== null) employment.endDate = new Date(employmentSummary['end-date']['year']['value'], employmentSummary['end-date']['month']['value'], employmentSummary['end-date']['day']['value']); employment.organization = employmentSummary['organization']['name']; employment.department = employmentSummary['department-name']; employments.push(employment); } } catch (e) { console.debug(e); } const preferredLocale = rawOrcidJSON['preferences']['locale'] !== null ? rawOrcidJSON['preferences']['locale'] : undefined; const biography = rawOrcidJSON['person']['biography'] !== null ? rawOrcidJSON['person']['biography']['content'] : undefined; const emails = []; if (rawOrcidJSON['person']['emails']['email'] !== null) { for (const email of rawOrcidJSON['person']['emails']['email']) { emails.push({ email: email['email'], primary: email['primary'], verified: email['verified'], }); } } const keywords = []; if (rawOrcidJSON['person']['keywords']['keyword'] !== null) { for (const keyword of rawOrcidJSON['person']['keywords']['keyword']) { keywords.push({ content: keyword['content'], index: keyword['display-index'], }); } keywords.sort((a, b) => a.index - b.index); } const researcherUrls = []; if (rawOrcidJSON['person']['researcher-urls']['researcher-url'] !== null) { for (const researcherUrl of rawOrcidJSON['person']['researcher-urls']['researcher-url']) { researcherUrls.push({ url: researcherUrl['url']['value'], name: researcherUrl['url-name'], index: researcherUrl['display-index'], }); } researcherUrls.sort((a, b) => a.index - b.index); } const country = rawOrcidJSON['person']['addresses']['address'].length > 0 ? rawOrcidJSON['person']['addresses']['address'][0]['country']['value'] : undefined; return new ORCIDInfo(orcid, rawOrcidJSON, familyName, givenNames, employments, preferredLocale, biography, emails, keywords, researcherUrls, country); } static fromJSON(serialized) { const data = JSON.parse(serialized); const employments = data.employments.map(employment => Employment.fromJSON(employment)); return new ORCIDInfo(data.orcid, data.ORCiDJSON, data.familyName, data.givenNames, employments, data.preferredLocale, data.biography, data.emails, data.keywords, data.researcherUrls, data.country); } getAffiliationsAt(date) { const affiliations = []; for (const employment of this._employments) { if (employment.startDate <= date && employment.endDate === null) affiliations.push(employment); if (employment.startDate <= date && employment.endDate !== null && employment.endDate >= date) affiliations.push(employment); } return affiliations; } getAffiliationAsString(affiliation, showDepartment = true) { if (affiliation === undefined || affiliation.organization === null) return undefined; else { if (showDepartment && affiliation.department !== null) return `${affiliation.organization} [${affiliation.department}]`; else return affiliation.organization; } } toObject() { return { orcid: this._orcid, ORCiDJSON: this._ORCiDJSON, familyName: this._familyName, givenNames: this._givenNames, employments: this._employments.map(employment => JSON.stringify(employment.toObject())), preferredLocale: this._preferredLocale, biography: this._biography, emails: this._emails, keywords: this._keywords, researcherUrls: this._researcherUrls, country: this._country, }; } } class Employment { constructor(startDate, endDate, organization, department) { this.startDate = startDate; this.endDate = endDate; this.organization = organization; this.department = department; } static fromJSON(serialized) { const data = JSON.parse(serialized); const startDate = new Date(data.startDate); const endDate = data.endDate === null ? null : new Date(data.endDate); return new Employment(startDate, endDate, data.organization, data.department); } toObject() { return { startDate: this.startDate, endDate: this.endDate, organization: this.organization, department: this.department, }; } } class FoldableItem { constructor(priority, keyTitle, value, keyTooltip, keyLink, valueRegex, renderDynamically) { this._estimatedTypePriority = 0; this._priority = priority; this._keyTitle = keyTitle; this._value = value; this._keyTooltip = keyTooltip; this._keyLink = keyLink; this._valueRegex = valueRegex; this._renderDynamically = renderDynamically !== undefined ? renderDynamically : true; this._estimatedTypePriority = this._renderDynamically ? 0 : Number.MAX_SAFE_INTEGER; } get priority() { return this._priority; } get keyTitle() { return this._keyTitle; } get value() { return this._value; } get keyTooltip() { return this._keyTooltip; } get keyLink() { return this._keyLink; } get valueRegex() { return this._valueRegex; } get renderDynamically() { return this._renderDynamically; } get estimatedTypePriority() { return this._estimatedTypePriority; } isValidValue() { return this._valueRegex ? this._valueRegex.test(this._value) : true; } equals(other) { return (this._keyTitle === other._keyTitle && this._value === other._value && this._keyTooltip === other._keyTooltip && this._keyLink === other._keyLink && this._renderDynamically === other._renderDynamically); } } class FoldableAction { constructor(priority, title, link, style) { this._priority = priority; this._title = title; this._link = link; this._style = style; } get priority() { return this._priority; } get title() { return this._title; } get link() { return this._link; } get style() { return this._style; } equals(other) { return this._priority === other._priority && this._title === other._title && this._link === other._link && this._style === other._style; } } class ORCIDType extends GenericIdentifierType { constructor() { super(...arguments); this.affiliationAt = new Date(Date.now()); this.showAffiliation = true; } get data() { var _a, _b; return JSON.stringify((_b = (_a = this._orcidInfo) === null || _a === void 0 ? void 0 : _a.toObject()) !== null && _b !== void 0 ? _b : {}); } quickCheck() { return ORCIDInfo.isORCiD(this.value); } async hasMeaningfulInformation() { this._orcidInfo = await ORCIDInfo.getORCiDInfo(this.value); return this._orcidInfo.ORCiDJSON !== undefined; } async init(data) { if (data !== undefined) { this._orcidInfo = ORCIDInfo.fromJSON(data); console.debug('reload ORCIDInfo from data', this._orcidInfo); } else { this._orcidInfo = await ORCIDInfo.getORCiDInfo(this.value); console.debug('load ORCIDInfo from API', this._orcidInfo); } if (this.settings) { for (const i of this.settings) { switch (i['name']) { case 'affiliationAt': this.affiliationAt = new Date(i['value']); break; case 'showAffiliation': this.showAffiliation = i['value'] === true || i['value'] === 'true' || i['value'] === '1'; break; } } } this.items.push(new FoldableItem(0, 'ORCiD', this._orcidInfo.orcid, 'ORCiD is a free service for researchers to distinguish themselves by creating a unique personal identifier.', 'https://orcid.org', undefined, false)); try { const givenNames = this._orcidInfo.givenNames; if (givenNames) { new FoldableItem(2, 'Given Names', this._orcidInfo.givenNames.toString(), 'The given names of the person.'); } } catch (e) { console.log('Failed to obtain given names from ORCiD record.', e); } this.actions.push(new FoldableAction(0, 'Open ORCiD profile', `https://orcid.org/${this._orcidInfo.orcid}`, 'primary')); try { const affiliations = this._orcidInfo.getAffiliationsAt(new Date(Date.now())); for (const data of affiliations) { const affiliation = this._orcidInfo.getAffiliationAsString(data); if (affiliation !== undefined && affiliation.length > 2) this.items.push(new FoldableItem(50, 'Current Affiliation', affiliation, 'The current affiliation of the person.', undefined, undefined, false)); } } catch (e) { console.log('Failed to obtain affiliations from ORCiD record.', e); } if (this._orcidInfo.getAffiliationsAt(this.affiliationAt) !== this._orcidInfo.getAffiliationsAt(new Date()) && this.affiliationAt.toLocaleDateString('en-US') !== new Date().toLocaleDateString('en-US')) { const affiliationsThen = this._orcidInfo.getAffiliationsAt(this.affiliationAt); for (const data of affiliationsThen) { const affiliation = this._orcidInfo.getAffiliationAsString(data); if (affiliation !== undefined && affiliation.length > 2) this.items.push(new FoldableItem(49, 'Affiliation at ' + this.affiliationAt.toLocaleDateString('en-US', { year: 'numeric', month: 'numeric', day: 'numeric', }), affiliation, 'The affiliation of the person at the given date.', undefined, undefined, false)); } } if (this._orcidInfo.emails) { const primary = this._orcidInfo.emails.filter(email => email.primary)[0]; const other = this._orcidInfo.emails.filter(email => !email.primary); if (primary) { this.items.push(new FoldableItem(20, 'Primary E-Mail address', primary.email, 'The primary e-mail address of the person.')); this.actions.push(new FoldableAction(0, 'Send E-Mail', `mailto:${primary.email}`, 'secondary')); } if (other.length > 0) this.items.push(new FoldableItem(70, 'Other E-Mail addresses', other.map(email => email.email).join(', '), 'All other e-mail addresses of the person.')); if (this._orcidInfo.preferredLocale) this.items.push(new FoldableItem(25, 'Preferred Language', this._orcidInfo.preferredLocale, 'The preferred locale/language of the person.')); for (const url of this._orcidInfo.researcherUrls) { this.items.push(new FoldableItem(100, url.name.length > 1 ? url.name : 'User-specified reference', url.url, 'A link to a website specified by the person.')); } if (this._orcidInfo.keywords.length > 50) this.items.push(new FoldableItem(60, 'Keywords', this._orcidInfo.keywords.map(keyword => keyword.content).join(', '), 'Keywords specified by the person.', undefined, undefined, false)); if (this._orcidInfo.biography) this.items.push(new FoldableItem(200, 'Biography', this._orcidInfo.biography, 'The biography of the person.', undefined, undefined, false)); if (this._orcidInfo.country) this.items.push(new FoldableItem(30, 'Country', this._orcidInfo.country, 'The country of the person.')); } } isResolvable() { return this._orcidInfo.ORCiDJSON !== undefined; } renderPreview() { return (index.h("span", { class: `inline-flex flex-nowrap items-baseline font-mono min-w-0 max-w-full ${this.isDarkMode ? 'text-gray-200' : ''}` }, index.h("svg", { version: "1.1", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 256 256", class: 'mr-1 h-4 flex-none px-0.5 self-center' }, index.h("style", { type: "text/css" }, `.st0{fill:#A6CE39;}`, `.st1{fill:#FFFFFF;}`), index.h("path", { class: "st0", d: "M256,128c0,70.7-57.3,128-128,128C57.3,256,0,198.7,0,128C0,57.3,57.3,0,128,0C198.7,0,256,57.3,256,128z" }), index.h("g", null, index.h("path", { class: "st1", d: "M86.3,186.2H70.9V79.1h15.4v48.4V186.2z" }), index.h("path", { class: "st1", d: "M108.9,79.1h41.6c39.6,0,57,28.3,57,53.6c0,27.5-21.5,53.6-56.8,53.6h-41.8V79.1z M124.3,172.4h24.5\n c34.9,0,42.9-26.5,42.9-39.7c0-21.5-13.7-39.7-43.7-39.7h-23.7V172.4z" }), index.h("path", { class: "st1", d: "M88.7,56.8c0,5.5-4.5,10.1-10.1,10.1c-5.6,0-10.1-4.6-10.1-10.1c0-5.6,4.5-10.1,10.1-10.1\n C84.2,46.7,88.7,51.3,88.7,56.8z" }))), index.h("span", { class: `min-w-0 overflow-hidden text-ellipsis whitespace-nowrap ${this.isDarkMode ? 'text-gray-200' : ''}` }, this._orcidInfo.familyName, ", ", this._orcidInfo.givenNames, ' ', this.showAffiliation && this._orcidInfo.getAffiliationsAt(new Date()).length > 0 ? `(${this._orcidInfo.getAffiliationAsString(this._orcidInfo.getAffiliationsAt(new Date())[0], false)}${this._orcidInfo.getAffiliationsAt(this.affiliationAt).length > 0 && this.affiliationAt.toLocaleDateString() !== new Date().toLocaleDateString() && this._orcidInfo.getAffiliationsAt(this.affiliationAt)[0].organization !== this._orcidInfo.getAffiliationsAt(new Date())[0].organization ? `, then: ${this._orcidInfo.getAffiliationsAt(this.affiliationAt)[0].organization}` : ''})` : ''))); } getSettingsKey() { return 'ORCIDType'; } } class HandleType extends GenericIdentifierType { constructor() { super(...arguments); this._parts = []; } get data() { var _a, _b; return JSON.stringify((_b = (_a = this._pidRecord) === null || _a === void 0 ? void 0 : _a.toObject()) !== null && _b !== void 0 ? _b : {}); } quickCheck() { return PID.isPID(this.value); } async hasMeaningfulInformation() { const pid = PID.getPIDFromString(this.value); this._pidRecord = await pid.resolve(); return this._pidRecord.values.length > 0; } async init(data) { if (data !== undefined) { this._pidRecord = PIDRecord.fromJSON(data); this._parts = await Promise.all([ { text: this._pidRecord.pid.prefix, nextExists: true, }, { text: this._pidRecord.pid.suffix, nextExists: false, }, ]); console.debug('reload PIDRecord from data', this._pidRecord); } else { const pid = PID.getPIDFromString(this.value); this._parts = [ { text: pid.prefix, nextExists: true, }, { text: pid.suffix, nextExists: false, }, ]; if (!this._pidRecord) { this._pidRecord = await pid.resolve(); console.debug('load PIDRecord from API', this._pidRecord); } else { console.debug('using cached PIDRecord'); } } for (const value of this._pidRecord.values) { if (value.type instanceof PIDDataType) { this.items.push(new FoldableItem(0, value.type.name, value.data.value, value.type.description, value.type.redirectURL, value.type.regex)); } } this.actions.push(new FoldableAction(0, 'Open in FAIR-DOscope', `https://kit-data-manager.github.io/fairdoscope/?pid=${this._pidRecord.pid.toString()}`, 'primary')); this.actions.push(new FoldableAction(0, 'View in Handle.net registry', `https://hdl.handle.net/${this._pidRecord.pid.toString()}`, 'secondary')); return; } isResolvable() { return this._pidRecord.values.length > 0; } renderPreview() { return (index.h("span", { class: 'font-mono font-bold align-baseline' }, this._parts.map(element => { return (index.h("span", null, index.h("color-highlight", { text: element.text }), element.nextExists ? index.h("span", { class: `mx-0.5` }, "/") : '')); }))); } getSettingsKey() { return 'HandleType'; } } class DOI { constructor(doi) { this._doi = doi .replace(/^https?:\/\/doi\.org\//i, '') .replace(/^https?:\/\/dx\.doi\.org\//i, '') .replace(/^doi:/i, '') .replace(/\/+$/, '') .replace(/\.+$/, '') .trim(); } get doi() { return this._doi; } static isDOI(text) { const cleaned = text .replace(/^https?:\/\/doi\.org\//i, '') .replace(/^https?:\/\/dx\.doi\.org\//i, '') .replace(/^doi:/i, ''); return /^10\.\d{4,9}\/[-._;()/:A-Za-z0-9]+$/.test(cleaned); } static getDOIFromString(doi) { if (!DOI.isDOI(doi)) throw new Error('Invalid DOI format'); return new DOI(doi); } static fromJSON(serialized) { const data = JSON.parse(serialized); return new DOI(data.doi); } toURL() { return `https://doi.org/${this._doi}`; } toString() { return this._doi; } toObject() { return { doi: this._doi, }; } } const userFriendlyResourceType = { 'article': '📰 Article', 'book': '📚 Book', 'chapter': '📖 Chapter', 'software': '💻 Software', 'dataset': '📊 Dataset', 'image': '🖼️ Image', 'video': '🎥 Video', 'audio': '🎵 Audio', 'presentation': '🧑‍🏫 Presentation', 'preprint': '📝 Preprint', 'thesis': '🎓 Thesis', 'report': '📋 Report', 'standard': '📜 Standard', 'workflow': '🔄 Workflow', 'model': '🧮 Model', 'paper': '📄 Paper', 'journal': '📰 Journal', 'code': '💻 Code', 'institution': '🏛️ Institution', 'conferencepaper': '🎤 Conference Paper', 'database': '🗄️ Database', 'other': '❓ Other', 'journalarticle': '📄 Journal Article', 'proceedingsarticle': '🎤 Proceedings Article', 'reportseries': '📋 Report Series', 'bookchapter': '📖 Book Chapter', 'monograph': '📚 Monograph', 'editedbook': '📚 Edited Book', 'reportpaper': '📋 Report Paper', }; function beautifyResourceType(resourceType) { const normalized = resourceType .toLowerCase() .replace('_', '').replace('-', ''); return userFriendlyResourceType[normalized] || resourceType; } const DataCiteLogo = () => (index.h("svg", { version: "1.1", xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 130 103.8", class: 'h-4' }, index.h("defs", null, index.h("style", null, `.cls-1{fill:#00b1e2;}`, `.cls-2{fill:#243b54;}`)), index.h("path", { class: "cls-1", d: "M5,73.38c.86.16,1.63.32,2.65.47,43.5,8.64,108.26-2.8,116.59-23.89,6.54-17.05-30-29.11-72.62-27.63a13.94,13.94,0,0,0-2.49.15c22.73,2.26,43.2,11.29,35,25.38C75.2,63.27,37.3,73.54,5,73.38" }), index.h("path", { class: "cls-2", d: "M100.27,75.53C92.72,121.3,53.18,94,43.84,62.15,30.3,29.07,42.36-13.43,76.22,11.48c-28-12.85-31,24.36-19,50.82,10,22.11,31.59,36.35,42,13.54.47-.15.86-.15,1-.31" }))); const CrossRefLogo = () => (index.h("svg", { version: "1.1", xmlns: "http://www.w3.org/2000/svg", x: "0px", y: "0px", class: 'h-4', viewBox: "0 0 65.400002 95.199999" }, index.h("style", { type: "text/css" }, `.st0{fill:#3EB1C8;}`, `.st1{fill:#D8D2C4;}`, `.st2{fill:#4F5858;}`, `.st3{fill:#FFC72C;}`, `.st4{fill:#EF3340;}`), index.h("g", { id: "g16", transform: "translate(-69.570409,-17.62496)" }, index.h("polygon", { class: "st0", points: "111.8,66.8 135.4,59 177.2,73.3 111.8,95.5 ", id: "polygon1", transform: "translate(-42.229591,17.32496)" }), index.h("polygon", { class: "st1", points: "111.8,51.2 135.4,59 177.2,44.6 153.6,36.8 ", id: "polygon2", transform: "translate(-42.229591,17.32496)" }), index.h("polygon", { class: "st2", points: "135.4,59 177.2,44.6 177.2,73.3 ", id: "polygon3", transform: "translate(-42.229591,17.32496)" }), index.h("polygon", { class: "st3", points: "177.2,29 153.6,36.8 111.8,22.5 177.2,0.3 ", id: "polygon4", transform: "translate(-42.229591,17.32496)" }), index.h("polygon", { class: "st4", points: "153.6,36.8 111.8,51.2 111.8,22.5 ", id: "polygon5", transform: "translate(-42.229591,17.32496)" })))); class DataCiteInfo { constructor(doi, response) { var _a; this._doi = doi; this._rawMetadata = response; this._attributes = ((_a = response.data) === null || _a === void 0 ? void 0 : _a.attributes) || {}; } get title() { const titles = this._attributes.titles || []; const mainTitle = titles.find((t) => { if (typeof t === 'string') return true; return !t.titleType || t.titleType === 'Title'; }); if (typeof mainTitle === 'string') return mainTitle; return (mainTitle === null || mainTitle === void 0 ? void 0 : mainTitle.title) || ''; } get creators() { const creators = this._attributes.creators || []; return creators.map((creator) => { var _a, _b; if (typeof creator === 'string') { return { name: creator }; } const result = { name: creator.name || '', givenName: creator.givenName, familyName: creator.familyName, }; if (!result.name && creator.givenName && creator.familyName) { result.name = `${creator.givenName} ${creator.familyName}`; } else if (!result.name) { result.name = creator.givenName || creator.familyName || ''; } const orcidIdentifier = (_a = creator.nameIdentifiers) === null || _a === void 0 ? void 0 : _a.find((id) => { var _a; return ((_a = id.nameIdentifierScheme) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'orcid'; }); if (orcidIdentifier === null || orcidIdentifier === void 0 ? void 0 : orcidIdentifier.nameIdentifier) { result.orcid = orcidIdentifier.nameIdentifier .replace(/^https?:\/\/orcid\.org\//i, '') .replace(/^orcid:/i, ''); } if (creator.affiliation && creator.affiliation.length > 0) { const primaryAffiliation = creator.affiliation[0]; result.affiliation = primaryAffiliation.name; const rorIdentifier = primaryAffiliation.affiliationIdentifier; if (rorIdentifier && ((_b = primaryAffiliation.affiliationIdentifierScheme) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'ror') { result.ror = rorIdentifier.replace(/^https?:\/\/ror\.org\//i, ''); } } return result; }).filter((c) => c.name); } get correspondingAuthor() { var _a, _b; const contributors = this._attributes.contributors || []; const corresponding = contributors.find((c) => { var _a; return ((_a = c.contributorType) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'contactperson'; }); if (!corresponding) return undefined; const result = { name: corresponding.name || '', givenName: corresponding.givenName, familyName: corresponding.familyName, isCorresponding: true, }; if (!result.name && corresponding.givenName && corresponding.familyName) { result.name = `${corresponding.givenName} ${corresponding.familyName}`; } const orcidIdentifier = (_a = corresponding.nameIdentifiers) === null || _a === void 0 ? void 0 : _a.find((id) => { var _a; return ((_a = id.nameIdentifierScheme) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'orcid'; }); if (orcidIdentifier === null || orcidIdentifier === void 0 ? void 0 : orcidIdentifier.nameIdentifier) { result.orcid = orcidIdentifier.nameIdentifier .replace(/^https?:\/\/orcid\.org\//i, '') .replace(/^orcid:/i, ''); } if (corresponding.affiliation && corresponding.affiliation.length > 0) { const primaryAffiliation = corresponding.affiliation[0]; result.affiliation = primaryAffiliation.name; const rorIdentifier = primaryAffiliation.affiliationIdentifier; if (rorIdentifier && ((_b = primaryAffiliation.affiliationIdentifierScheme) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'ror') { result.ror = rorIdentifier.replace(/^https?:\/\/ror\.org\//i, ''); } } return result; } get publisher() { return this._attributes.publisher; } get publicationDate() { const dates = this._attributes.dates || []; const issued = dates.find((d) => { var _a; return ((_a = d.dateType) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'issued'; }); if (issued === null || issued === void 0 ? void 0 : issued.date) return issued.date; if (this._attributes.publicationYear) { return `${this._attributes.publicationYear}`; } return undefined; } get resourceType() { var _a; return ((_a = this._attributes.types) === null || _a === void 0 ? void 0 : _a.resourceTypeGeneral) || this._attributes.resourceTypeGeneral; } get resourceTypeSpecific() { var _a; return (_a = this._attributes.types) === null || _a === void 0 ? void 0 : _a.resourceType; } get description() { const descriptions = this._attributes.descriptions || []; const abstract = descriptions.find((d) => { var _a; if (typeof d === 'string') return true; return ((_a = d.descriptionType) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === 'abstract' || !d.descriptionType; }); if (typeof abstract === 'string') return abstract; return abstract === null || abstract === void 0 ? void 0 : abstract.description; } get url() { return this._attributes.url || this._doi.toURL(); } get subjects() { const subjects = this._attributes.subjects || []; return subjects.map((s) => { if (typeof s === 'string') return s; return s.subject || ''; }).filter(Boolean); } get rawMetadata() { return this._rawMetadata; } static async fetch(doi) { const apiUrl = `https://api.datacite.org/dois/${encodeURIComponent(doi.toString())}`; try { const response = (await cachedFetch(apiUrl, { headers: { Accept: 'application/vnd.api+json', }, })); if (!response || !response.data) return null; return new DataCiteInfo(doi, response); } catch (error) { console.debug('DataCite API error:', error); return null; } } static fromObject(doiObj, obj) { return new DataCiteInfo(doiObj, obj.rawMetadata); } generateItems() { const items = []; let index = 10; if (this.title) { items.push(new FoldableItem(index++, 'Title', this.title, 'The title of the resource.', undefined, undefined, false)); } const correspondingAuthor = this.correspondingAuthor; if (correspondingAuthor) { items.push(new FoldableItem(index++, 'Corresponding Author', correspondingAuthor.orcid || `${correspondingAuthor.name}${correspondingAuthor.affiliation ? ` (${correspondingAuthor.affiliation})` : ''}`, `This field indicates the first author of the resource, who is often the corresponding author.`)); } const creators = this.creators; creators.forEach((creator, idx) => { if (idx === 0 && correspondingAuthor) return; items.push(new FoldableItem(index++, `Author`, creator.orcid || `${creator.name}${creator.affiliation ? ` (${creator.affiliation})` : ''}`, 'A creator/author of the resource.', creator.orcid ? `https://orcid.org/${creator.orcid}` : undefined)); }); if (this.publisher) { items.push(new FoldableItem(index++, 'Publisher', this.publisher, 'The publisher of the resource.')); } if (this.publicationDate) { items.push(new FoldableItem(index++, 'Publication Date', this.publicationDate, 'The publication date in ISO 8601 format.')); } if (this.resourceType) { const displayType = this.resourceTypeSpecific || this.resourceType; items.push(new FoldableItem(index++, 'Resource Type', beautifyResourceType(displayType), 'The type of the resource.')); } if (this.description) { items.push(new FoldableItem(index++, 'Description', this.description, 'The description or abstract of the resource.', undefined, undefined, false)); } this.subjects.forEach((subject) => { items.push(new FoldableItem(index++, 'Subject', subject, 'A subject area or keyword associated with the resource.')); }); return items; } toObject() { return { doi: JSON.stringify(this._doi.toObject()), rawMetadata: this._rawMetadata, }; } } var CitationStyle; (function (CitationStyle) { CitationStyle["APA"] = "APA"; CitationStyle["CHICAGO"] = "Chicago"; CitationStyle["IEEE"] = "IEEE"; CitationStyle["HARVARD"] = "Harvard"; CitationStyle["ANGLIA_RUSKIN"] = "Anglia Ruskin"; })(CitationStyle || (CitationStyle = {})); function formatCitationPreview(title, creators, year, style = CitationStyle.APA) { if (!title || creators.length === 0) { return { citation: title || 'Untitled', tooltip: title || 'Untitled' }; } const firstAuthor = creators[0]; const authorCount = creators.length; switch (style) { case CitationStyle.APA: return { citation: formatAPA(firstAuthor, authorCount, title, year), tooltip: formatAPA(firstAuthor, authorCount, title, year), }; case CitationStyle.CHICAGO: return { citation: formatChicago(firstAuthor, authorCount, title, year), tooltip: formatChicago(firstAuthor, authorCount, title, year), }; case CitationStyle.IEEE: return { citation: formatIEEE(firstAuthor, authorCount, title, year), tooltip: formatIEEE(firstAuthor, authorCount, title, year), }; case CitationStyle.HARVARD: return { citation: formatHarvard(firstAuthor, authorCount, title, year), tooltip: formatHarvard(firstAuthor, authorCount, title, year), }; case CitationStyle.ANGLIA_RUSKIN: return { citation: formatAngliaRuskin(firstAuthor, authorCount, title, year), tooltip: formatAngliaRuskin(firstAuthor, authorCount, title, year), }; default: return { citation: formatAPA(firstAuthor, authorCount, title, year), tooltip: formatAPA(firstAuthor, authorCount, title, year), }; } } function formatAPA(author, count, title, year, truncate) { const authorName = author.familyName || author.name.split(' ').pop() || author.name; const etAl = count > 1 ? ' et al.' : ''; const yearPart = year ? ` (${year.split('-')[0]})` : ''; truncateTitle(title, 60); return `${authorName}${etAl}${yearPart}. ${title}`; } function formatChicago(author, count, title, year, truncate) { const authorName = author.familyName || author.name.split(' ').pop() || author.name; const firstName = author.givenName || author.name.split(' ')[0] || ''; const etAl = count > 1 ? ' et al.' : ''; const yearPart = year ? ` ${year.split('-')[0]}.` : ''; truncateTitle(title, 60); return `${authorName}, ${firstName}${etAl}.${yearPart} "${title}"`; } function formatIEEE(author, count, title, year, truncate) { const initial = author.givenName ? author.givenName.charAt(0) + '.' : ''; const authorName = author.familyName || author.name.split(' ').pop() || author.name; const etAl = count > 1 ? ' et al.' : ''; truncateTitle(title, 60); const yearPart = year ? `, ${year.split('-')[0]}` : ''; return `${initial} ${authorName}${etAl}, "${title}"${yearPart}`; } function formatHarvard(author, count, title, year, truncate) { const authorName = author.familyName || author.name.split(' ').pop() || author.name; const initials = author.givenName ? author.givenName.split(' ').map(n => n.charAt(0) + '.').join('') : ''; const etAl = count > 1 ? ' et al.' : ''; const yearPart = year ? `, ${year.split('-')[0]}` : ''; truncateTitle(title, 60); return `${authorName}, ${initials}${etAl}${yearPart}. ${title}`; } function formatAngliaRuskin(author, count, title, year, truncate) { const authorName = (author.familyName || author.name.split(' ').pop() || author.name).toUpperCase(); const etAl = count > 1 ? ' ET AL.' : ''; const yearPart = year ? ` ${year.split('-')[0]}.` : ''; truncateTitle(title, 60); return `${authorName}${etAl}${yearPart} ${title}`; } function truncateTitle(title, maxLength) { if (title.length <= maxLength) return title; const truncated = title.substring(0, maxLength); const lastSpace = truncated.lastIndexOf(' '); if (lastSpace > maxLength * 0.7) { return truncated.substring(0, lastSpace) + '...'; } return truncated + '...'; } function getDefaultCitationStyle() { return CitationStyle.APA; } function getCitationStyleFromSettings(settings) { if (!settings) return getDefaultCitationStyle(); const styleSetting = settings.find(s => s.name === 'citationStyle'); if (!styleSetting) return getDefaultCitationStyle(); const styleValue = String(styleSetting.value).toUpperCase(); switch (styleValue) { case 'APA': return CitationStyle.APA; case 'CHICAGO': return CitationStyle.CHICAGO; case 'IEEE': return CitationStyle.IEEE; case 'HARVARD': return CitationStyle.HARVARD; case 'ANGLIA_RUSKIN': return CitationStyle.ANGLIA_RUSKIN; default: return getDefaultCitationStyle(); } } class DataCiteDOIType extends GenericIdentifierType { get data() { var _a, _b; return JSON.stringify((_b = (_a = this._dataCiteInfo) === null || _a === void 0 ? void 0 : _a.toObject()) !== null && _b !== void 0 ? _b : {}); } quickCheck() { return DOI.isDOI(this.value); } async hasMeaningfulInformation() { this._doi = DOI.getDOIFromString(this.value); this._dataCiteInfo = await DataCiteInfo.fetch(this._doi); return this._dataCiteInfo !== null && this._dataCiteInfo.title !== ''; } async init(data) { if (data !== undefined) { this._dataCiteInfo = DataCiteInfo.fromObject(this._doi, JSON.parse(data)); } else { this._doi = DOI.getDOIFromString(this.value); this._dataCiteInfo = await DataCiteInfo.fetch(this._doi); } if (!this._dataCiteInfo) return; this.items.push(new FoldableItem(0, 'DOI', this._doi.toString(), 'The DOI used for this resource. Digital Object Identifier is a persistent identifier for academic and research resources.', 'https://www.doi.org/', undefined, false)); this.items.push(new FoldableItem(1, 'Metadata Source', 'DataCite', 'Metadata provided by DataCite', 'https://datacite.org')); const metadataItems = this._dataCiteInfo.generateItems(); this.items.push(...metadataItems); if (this._dataCiteInfo.url) { this.actions.push(new FoldableAction(0, 'Open Resource', this._dataCiteInfo.url, 'primary')); } this.actions.push(new FoldableAction(1, 'Resolve DOI', this._doi.toURL(), 'secondary')); this.actions.push(new FoldableAction(2, 'View DataCite Metadata', `https://api.datacite.org/dois/${encodeURIComponent(this._doi.toString())}`, 'secondary')); } isResolvable() { return this._dataCiteInfo !== null && this._dataCiteInfo.title !== ''; } renderPreview() { var _a, _b, _c; const citationStyle = getCitationStyleFromSettings(this.settings); const creators = ((_a = this._dataCiteInfo) === null || _a === void 0 ? void 0 : _a.creators) || []; const year = (_b = this._dataCiteInfo) === null || _b === void 0 ? void 0 : _b.publicationDate; const { citation, tooltip } = formatCitationPreview(((_c = this._dataCiteInfo) === null || _c === void 0 ? void 0 : _c.title) || '', creators, year, citationStyle); return (index.h("span", { class: `inline-flex flex-nowrap items-baseline font-mono min-w-0 max-w-full ${this.isDarkMode ? 'text-gray-200' : ''}` }, index.h("span", { class: 'flex-none px-0.5 h-4 self-center' }, DataCiteLogo()), index.h("span", { class: 'min-w-0 pl-2 overflow-hidden text-ellipsis whitespace-nowrap', title: tooltip }, citation))); } getSettingsKey() { return 'DataCiteDOIType'; } } class CrossRefInfo { constructor(doi, response, type = 'work') { this._doi = doi; this._type = type; this._rawMetadata = response; if (type === 'funder') { this._message = {}; } else { const cr = response; this._message = typeof cr.message === 'string' ? {} : cr.message || {}; } } get type() { return this._type; } get title() { if (this._type === 'funder') { const funderResponse = this._rawMetadata; const funderMsg = typeof funderResponse.message === 'string' ? null : funderResponse.message; return (funderMsg === null || funderMsg === void 0 ? void 0 : funderMsg.name) || ''; } const titles = this._message.title || []; return titles[0] || ''; } get creators() { if (this._type === 'funder') return []; const authors = this._message.author || []; return authors.map((author) => { const result = { name: author.name || '', givenName: author.given, familyName: author.family, }; if (!result.name) { if (author.given && author.family) { result.name = `${author.given} ${author.family}`; } else { result.name = author.given || author.family || ''; } } if (author.ORCID) { result.orcid = author.ORCID.replace(/^https?:\/\/orcid\.org\//i, ''); } if (author.affiliation && author.affiliation.length > 0) { result.affiliation = author.affiliation[0].name; } return result; }).filter((c) => c.name); } get correspondingAuthor() { if (this._type === 'funder') return undefined; const authors = this._message.author || []; const firstAuthor = authors.find((a) => a.sequence === 'first') || authors[0]; if (!firstAuthor) return undefined; const result = { name: firstAuthor.name || '', givenName: firstAuthor.given, familyName: firstAuthor.family, isCorresponding: true, }; if (!result.name) { if (firstAuthor.given && firstAuthor.family) { result.name = `${firstAuthor.given} ${firstAuthor.family}`; } else { result.name = firstAuthor.given || firstAuthor.family || ''; } } if (firstAuthor.ORCID) { result.orcid = firstAuthor.ORCID.replace(/^https?:\/\/orcid\.org\//i, ''); } if (firstAuthor.affiliation && firstAuthor.affiliation.length > 0) { result.affiliation = firstAuthor.affiliation[0].name; } return result; } get publisher() { if (this._type === 'funder') return undefined; return this._message.publisher; } get publicationDate() { var _a; if (this._type === 'funder') { const funderResponse = this._rawMetadata; const funderMsg = typeof funderResponse.message === 'string' ? null : funderResponse.message; if (funderMsg === null || funderMsg === void 0 ? void 0 : funderMsg.established) { return `${funderMsg.established}`; } return undefined; } const dateObj = this._message.issued || this._message.published || this._message.created; if (!((_a = dateObj === null || dateObj === void 0 ? void 0 : dateObj['date-parts']) === null || _a === void 0 ? void 0 : _a[0])) return undefined; const parts = dateObj['date-parts'][0]; const year = parts[0]; const month = parts[1]; const day = parts[2]; if (year && month && day) { return `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`; } else if (year && month) { return `${year}-${String(month).p