diff --git a/src/component/annotator/AnnotatorContent.tsx b/src/component/annotator/AnnotatorContent.tsx
index 1ec43025..f54a6bbc 100644
--- a/src/component/annotator/AnnotatorContent.tsx
+++ b/src/component/annotator/AnnotatorContent.tsx
@@ -35,8 +35,16 @@ const PREPROCESSING_INSTRUCTIONS = [
shouldPreprocessNode: (node: any): boolean =>
node.name && node.name === "a",
preprocessNode: (node: any) => {
- node.attribs["data-href"] = node.attribs.href;
- delete node.attribs.href;
+ // Remove href from relative links, absolute links will open in blank tab
+ if (!Utils.isLink(node.attribs.href)) {
+ node.attribs["data-href"] = node.attribs.href;
+ delete node.attribs.href;
+ } else {
+ node.attribs["data-target"] = node.attribs.target;
+ node.attribs["target"] = "_blank";
+ node.attribs["data-rel"] = node.attribs.rel;
+ node.attribs["rel"] = "noopener noreferrer";
+ }
},
},
];
diff --git a/src/component/annotator/HtmlParserUtils.ts b/src/component/annotator/HtmlParserUtils.ts
index e0e20842..469d91ef 100644
--- a/src/component/annotator/HtmlParserUtils.ts
+++ b/src/component/annotator/HtmlParserUtils.ts
@@ -4,12 +4,33 @@ import render from "dom-serializer";
const RDF_ATTRIBUTE_NAMES = ["about", "property", "resource", "typeof"];
+function removeAddedAttributes(elem: Element) {
+ // Restore links to their original state - see AnnotatorContent.PREPROCESSING_INSTRUCTIONS
+ if (elem.tagName === "a") {
+ if (elem.attribs["data-href"]) {
+ elem.attribs.href = elem.attribs["data-href"];
+ delete elem.attribs["data-href"];
+ }
+ delete elem.attribs["target"];
+ delete elem.attribs["rel"];
+ if (elem.attribs["data-rel"]) {
+ elem.attribs.rel = elem.attribs["data-rel"];
+ delete elem.attribs["data-rel"];
+ }
+ if (elem.attribs["data-target"]) {
+ elem.attribs.target = elem.attribs["data-target"];
+ delete elem.attribs["data-target"];
+ }
+ }
+}
+
const HtmlParserUtils = {
html2dom(html: string): Node[] {
// Do not decode HTML entities (e.g., <) when parsing content for object representation, it caused issues
// with rendering
const options = { decodeEntities: false };
- const handler = new DomHandler();
+ // @ts-ignore
+ const handler = new DomHandler(null, null, removeAddedAttributes);
const parser = new HtmlParser(handler, options);
parser.parseComplete(html);
return handler.dom as Node[];
diff --git a/src/component/annotator/__tests__/Annotator.test.tsx b/src/component/annotator/__tests__/Annotator.test.tsx
index 31af1e03..99226224 100644
--- a/src/component/annotator/__tests__/Annotator.test.tsx
+++ b/src/component/annotator/__tests__/Annotator.test.tsx
@@ -118,7 +118,7 @@ describe("Annotator", () => {
expect(wrapper.html().includes(sampleContent)).toBe(true);
});
- it("renders body of provided html content with replaced anchor hrefs", () => {
+ it("preserves absolute URL href anchors", () => {
const htmlContent = surroundWithHtml(
'This is a link'
);
@@ -138,7 +138,28 @@ describe("Annotator", () => {
)
);
const sampleOutput =
- 'This is a link';
+ 'This is a link';
+ expect(wrapper.html().includes(sampleOutput)).toBe(true);
+ });
+
+ it("renders body of provided html content with replaced relative anchor hrefs", () => {
+ const htmlContent = surroundWithHtml('This is a link');
+
+ const wrapper = mountWithIntl(
+ withWebSocket(
+
+
+
+ )
+ );
+ const sampleOutput = 'This is a link';
expect(wrapper.html().includes(sampleOutput)).toBe(true);
});
diff --git a/src/component/annotator/__tests__/HtmlParserUtils.test.ts b/src/component/annotator/__tests__/HtmlParserUtils.test.ts
new file mode 100644
index 00000000..620db37f
--- /dev/null
+++ b/src/component/annotator/__tests__/HtmlParserUtils.test.ts
@@ -0,0 +1,22 @@
+import { Element } from "domhandler";
+import HtmlParserUtils from "../HtmlParserUtils";
+
+describe("HtmlParserUtils", () => {
+ describe("html2dom", () => {
+ it("remove target and rel attributes added when rendering HTML", () => {
+ const html =
+ 'Example';
+ const nodes = HtmlParserUtils.html2dom(html);
+ expect((nodes[0] as Element).attribs.href).toEqual("http://example.com");
+ expect((nodes[0] as Element).attribs.target).not.toBeDefined();
+ expect((nodes[0] as Element).attribs.rel).not.toBeDefined();
+ });
+
+ it("set href attribute to data-href attribute created when rendering HTML", () => {
+ const html = 'Example';
+ const nodes = HtmlParserUtils.html2dom(html);
+ expect((nodes[0] as Element).attribs.href).toEqual("./about.html");
+ expect((nodes[0] as Element).attribs["data-href"]).not.toBeDefined();
+ });
+ });
+});
diff --git a/src/util/Utils.ts b/src/util/Utils.ts
index 45b54beb..b4fd2cb9 100644
--- a/src/util/Utils.ts
+++ b/src/util/Utils.ts
@@ -51,10 +51,11 @@ const Utils = {
*/
isLink(str: string): boolean {
return (
- str.startsWith("http://") ||
- str.startsWith("https://") ||
- str.startsWith("ftp://") ||
- str.startsWith("sftp://")
+ str !== undefined &&
+ (str.startsWith("http://") ||
+ str.startsWith("https://") ||
+ str.startsWith("ftp://") ||
+ str.startsWith("sftp://"))
);
},