import React, { useState, useEffect } from "react"; import bibtexParse from 'bibtex-parser-js'; interface BibEntry { ENTRYTYPE: string; TITLE?: string; AUTHOR?: string; journal?: string; volume?: string; pages?: string; year?: string; doi?: string; [key: string]: any; } interface BibtexParserProps { bibtexSources: string[]; // Accept an array of BibTeX strings special?: string, start?: number } function formatPages(pages: string | undefined): JSX.Element | null { // Check if pages is provided and is a non-empty string if (pages && pages.length > 0) { // Check for common page range separators const pageRangeRegex = /--|-|–|–/; // RegEx to match various dash types if (pageRangeRegex.test(pages)) { const pag = pages.split(pageRangeRegex).map(p => p.trim()); const begin = pag[0]; const end = pag[1]; // Return formatted JSX return ( <> , <span property="schema:pageBegin">{begin}</span>-<span property="schema:pageEnd">{end}</span> </> ); } else if (/^\d+(-\d+)?$/.test(pages)) { // If pages is a single numeric range, return it directly return ( <> , <span property="schema:pageBegin">{pages}</span> </> ); } else { // Handle non-numeric page info console.warn(`Non-numeric page information detected ('${pages}'). Treating as missing.`); return null; // Return null if invalid } } else { console.warn("Sorry, no page information."); return null; // Return null if no page info } } export const BibtexParser: React.FC<BibtexParserProps> = ({ bibtexSources , special, start}) => { const [parsedEntries, setParsedEntries] = useState<BibEntry[]>([]); // Parse BibTeX on component mount or when sources change useEffect(() => { //console.log("Parsing BibTeX sources: ", bibtexSources); try { const allEntries: BibEntry[] = []; bibtexSources.forEach((bibtex) => { // console.log(`Parsing BibTeX entry #${index + 1}: `, bibtex); const parsed = bibtexParse.toJSON(bibtex); // console.log(`Parsed entry: `, parsed); allEntries.push(...parsed); }); setParsedEntries(allEntries); //console.log("All parsed entries: ", allEntries); } catch (error) { console.error("Error parsing BibTeX: ", error); alert("An error occurred while parsing the BibTeX entries. Please check the format." + bibtexSources); } }, [bibtexSources]); // Helper function to render AUTHORS const formatAuthors = (authors: string): string => { //console.log("Original input:", authors); // Bereinigen des Eingabestrings und Ersetzen von "and" durch "|" const cleanedAuthors = authors .replace(/\s*and\s*/g, "|") // "and" durch "|" ersetzen .replace(/\{|\}/g, "") // geschweifte Klammern entfernen .trim(); //console.log("Cleaned authors string:", cleanedAuthors); // Autoren in ein Array aufteilen const authorList = cleanedAuthors.split("|").map(author => author.trim()); //console.log("Split author list:", authorList); // Maximale Anzahl der anzuzeigenden Autoren const maxAuthors = 7; // Formatiere jeden Autor const formattedAuthors = authorList.map((author, _index) => { //console.log(`Processing author #${index + 1}:`, author); // Nachname und Vornamen aufteilen const [last, firstNames] = author.includes(",") ? author.split(",").map(part => part.trim()) : ['', author]; // Wenn kein Komma vorhanden ist, wird der gesamte Name als Vorname behandelt // console.log(`Last name: "${last}", First names: "${firstNames}"`); // Initialen für Vornamen erstellen const initials = firstNames.split(' ').map(n => n[0] + '.').join(' '); //console.log(`Initials for "${firstNames}": "${initials}"`); const formattedName = `${last}, ${initials}`.trim(); // Rückgabe des formatierten Namens //console.log(`Formatted name: "${formattedName}"`); return formattedName; }); //console.log("Formatted authors before adding et al.:", formattedAuthors); // Kombiniere die formatierten Autoren mit korrekter Interpunktion const output = formattedAuthors.slice(0, maxAuthors).join('; ') + (formattedAuthors.length > maxAuthors ? ' et al.' : ''); //console.log("Final output:", output); return output; }; let specialthing = ""; if (special) { specialthing = `#${special}`; } // Helper function to render individual citations based on their type const renderCitation = (entry: BibEntry, index: number) => { // console.log(`Rendering citation for entry #${index + 1}: `, entry); // Use the index as citation number let citationNumber = index +1; if(start){ citationNumber += start -1; } const entryType = entry.entryType.toLowerCase(); // Convert to lowercase for consistent comparison const entryTags = entry.entryTags; // Adjust based on your data structure // console.log("Entry Tags: ", entryTags); switch (entryType) { case "article": return ( <li key={index} typeof="schema:ScholarlyArticle" role="doc-biblioentry" property="schema:citation" id={`desc-${citationNumber}${specialthing}`}> {/* Citation number as comment */} {/*<!-- Citation num ${citationNumber} --> */} {formatAuthors(entryTags.AUTHOR || entryTags.EDITOR || "")} <span property="schema:name">{entryTags.TITLE.replace(/[?!.]/g, '').replace(/\n/g, ' ').trim()}.</span> <i property="schema:publisher" typeof="schema:Organization">{entryTags.JOURNAL}</i> <b property="issueNumber" typeof="PublicationIssue">{entryTags.VOLUME}</b> {formatPages(entryTags.PAGES) && <span>{formatPages(entryTags.PAGES)}</span>} {entryTags.YEAR && ( <span> (<time property="schema:datePublished" datatype="xsd:gYear" dateTime={entryTags.YEAR}>{entryTags.YEAR}</time>).</span> )} {entryTags.DOI && ( <span> <a className="doi" href={`https://doi.org/${entryTags.DOI}`}>doi: {entryTags.DOI}</a></span> )} </li> ); case "book": return ( <li key={index} typeof="schema:Book" role="doc-biblioentry" property="schema:citation" id={`desc-${citationNumber}`}> {/* Render authors */} {formatAuthors(entryTags.AUTHOR || entryTags.EDITOR || "")} {/* Render title or booktitle */} {entryTags.TITLE ? ( <span property="schema:name"> {entryTags.TITLE.replace(/[?!.]/g, '').replace(/\n/g, ' ').trim()}.</span> ) : entryTags.BOOKTITLE ? ( <span property="schema:name"> {entryTags.BOOKTITLE.replace(/[?!.]/g, '').replace(/\n/g, ' ').trim()}.</span> ) : ( console.warn(`No title or booktitle found for entry ${citationNumber}`) )} {/* Render publisher */} {entryTags.PUBLISHER && ( <i property="schema:publisher" typeof="schema:Organization"> {entryTags.PUBLISHER} </i> )} {/* Render year */} {entryTags.YEAR && ( <span> (<time property="schema:datePublished" datatype="xsd:gYear" dateTime={entryTags.YEAR}> {entryTags.YEAR} </time>). </span> )} {entryTags.ISBN && ( <span property="isbn"> {entryTags.ISBN}</span> ) } </li> ); case "misc": return ( <li key={index} typeof="schema:WebPage" role="doc-biblioentry" property="schema:citation" id={`desc-${citationNumber}`}> {/* Render authors */} {(entryTags.AUTHOR || entryTags.EDITOR || "")} {/* Render title */} {entryTags.TITLE && ( <span property="schema:name">. {entryTags.TITLE.replace(/[?!.]/g, '').replace(/\n/g, ' ').trim()}.</span> )} {/* Render howpublished */} {entryTags.HOWPUBLISHED && ( <i property="schema:publisher" typeof="schema:Organization"> {entryTags.HOWPUBLISHED}</i> )} {/* Render year */} {entryTags.YEAR && ( <span> (<time property="schema:datePublished" datatype="xsd:gYear" dateTime={entryTags.YEAR}>{entryTags.YEAR}</time>).</span> )} </li> ); // Handle additional entry types here case "inproceedings": return ( <li key={index}> <span>{formatAuthors(entryTags.AUTHOR || "")}</span> <span>{entryTags.TITLE}</span>. In <i>{entryTags.BOOKTITLE}</i>, <b>{entryTags.editor}</b>, {entryTags.YEAR}. </li> ); case "phdthesis": return ( <li key={index}> <span>{formatAuthors(entryTags.AUTHOR || "")}</span> <span>{entryTags.TITLE}</span>, PhD thesis, {entryTags.SCHOOL}, {entryTags.YEAR}. </li> ); default: console.warn(`Unknown entry type: ${entryType}`); return <li key={index}>Unknown entry type: {entryType}</li>; } }; let startnumber = 1; if(start) { startnumber = start; } return ( <div> {parsedEntries.length === 0 ? ( <p>No citations available.</p> ) : ( <ol start={startnumber}> {parsedEntries.map((entry, index) => renderCitation(entry, index))} </ol> )} </div> ); }; export default BibtexParser;