diff --git a/frontend/src/pages/ontologies/entities/entityPageSections/addLinksToText.tsx b/frontend/src/pages/ontologies/entities/entityPageSections/addLinksToText.tsx index af88583dc..5349879df 100644 --- a/frontend/src/pages/ontologies/entities/entityPageSections/addLinksToText.tsx +++ b/frontend/src/pages/ontologies/entities/entityPageSections/addLinksToText.tsx @@ -4,7 +4,6 @@ import { randomString } from "../../../../app/util"; import EntityLink from "../../../../components/EntityLink"; import Entity from "../../../../model/Entity"; import LinkedEntities from "../../../../model/LinkedEntities"; -import Image3D from "../../../../components/Image3D"; export default function addLinksToText( text: string, @@ -13,15 +12,53 @@ export default function addLinksToText( currentEntity: Entity | undefined, entityType: "ontologies" | "classes" | "properties" | "individuals" ) { - let linksToSplice: Array<{ start: number; end: number; link: JSX.Element }> = - []; + let linksToSplice: Array<{ start: number; end: number; link: JSX.Element }> = []; + let urlMatches: Array<{ start: number; end: number }> = []; // To store the ranges of URLs + + // First, find all URLs and record their ranges + const urlRe = /[A-z]+:\/\/[^\s]+/g; + for (let match = urlRe.exec(text); match; match = urlRe.exec(text)) { + const url = match[0]; + linksToSplice.push({ + start: match.index, + end: match.index + url.length, + link: ( + + + {url} + + + ), + }); + urlMatches.push({ start: match.index, end: match.index + url.length }); + } + + // Then, process entity IDs for (let entityId of Object.keys(linkedEntities.linkedEntities)) { for ( let n = text.indexOf(entityId, 0); n !== -1; n = text.indexOf(entityId, n) ) { + // We need to handle this case when entity ID is part of a URL and it then gets linked to an entity but + // resulting url is broken. So, we need to keep the URL as is if the entity ID is part of a URL. + // Check if the entity ID is within any URL range + let isWithinURL = urlMatches.some( + (urlRange) => + n >= urlRange.start && n + entityId.length <= urlRange.end + ); + if (isWithinURL) { + // Skip this entity ID because it's part of a URL + n += entityId.length; + continue; + } + linksToSplice.push({ start: n, end: n + entityId.length, @@ -41,65 +78,45 @@ export default function addLinksToText( } } - const urlRe = /[A-z]+:\/\/[^\s]+/g; - for (let match = urlRe.exec(text); match; match = urlRe.exec(text)) { - const url = match[0]; - // console.log("found match " + url); - linksToSplice.push({ - start: match.index, - end: match.index + url.length, - link: ( - - - {url} - - - ), - }); - } - - removeOverlapping: for (let n = 0; n < linksToSplice.length; ) { - for (let n2 = 0; n2 < linksToSplice.length; ++n2) { - let spliceA = linksToSplice[n]; - let spliceB = linksToSplice[n2]; + // Remove overlapping links by sorting and keeping the first one + linksToSplice.sort((a, b) => a.start - b.start); - if (spliceA === spliceB) continue; - - // The splices overlap if neither ends before the other starts - if (spliceA.end >= spliceB.start && spliceB.end >= spliceA.start) { - // console.log("Removing overlapping"); - linksToSplice.splice(n2, 1); - continue removeOverlapping; - } + // Remove overlaps + for (let i = 0; i < linksToSplice.length - 1; i++) { + const current = linksToSplice[i]; + const next = linksToSplice[i + 1]; + if (current.end > next.start) { + // Overlap detected, remove the next link + linksToSplice.splice(i + 1, 1); + i--; // Adjust index after removal } - ++n; } if (linksToSplice.length === 0) return text; - // linksToSplice.sort((a, b) => a.start - b.start); - // console.dir(linksToSplice); + // Build the final result let res: JSX.Element[] = []; - let n = 0; + let lastIndex = 0; for (let link of linksToSplice) { + if (lastIndex < link.start) { + res.push( + + {text.substring(lastIndex, link.start)} + + ); + } + res.push(link.link); + lastIndex = link.end; + } + + if (lastIndex < text.length) { res.push( - - {text.substring(n, link.start)} - + + {text.substring(lastIndex)} + ); - res.push(link.link); - n = link.end; } - res.push( - {text.slice(n)} - ); return res; }