From fcba29f774850c109a51f88746e6732b00efa980 Mon Sep 17 00:00:00 2001 From: "Ashley Engelund (weedySeaDragon @ github)" Date: Sun, 16 Oct 2022 11:08:01 -0700 Subject: [PATCH] functions and specs: removeExistingElements --- packages/mermaid/src/mermaidAPI.spec.ts | 72 ++++++++++++++++++++++++- packages/mermaid/src/mermaidAPI.ts | 46 ++++++++++------ 2 files changed, 101 insertions(+), 17 deletions(-) diff --git a/packages/mermaid/src/mermaidAPI.spec.ts b/packages/mermaid/src/mermaidAPI.spec.ts index 8361f368c..08aaef6a7 100644 --- a/packages/mermaid/src/mermaidAPI.spec.ts +++ b/packages/mermaid/src/mermaidAPI.spec.ts @@ -4,7 +4,7 @@ import { vi } from 'vitest'; import mermaid from './mermaid'; import { MermaidConfig } from './config.type'; -import mermaidAPI from './mermaidAPI'; +import mermaidAPI, { removeExistingElements } from './mermaidAPI'; import { encodeEntities, decodeEntities, @@ -454,6 +454,76 @@ describe('when using mermaidAPI and ', function () { }); }); + describe('removeExistingElements', () => { + const svgId = 'svgId'; + const tempDivId = 'tempDivId'; + const tempIframeId = 'tempIFrameId'; + const givenDocument = new Document(); + const rootHtml = givenDocument.createElement('html'); + givenDocument.append(rootHtml); + + const svgElement = givenDocument.createElement('svg'); // doesn't matter what the tag is in the test + svgElement.id = svgId; + const tempDivElement = givenDocument.createElement('div'); // doesn't matter what the tag is in the test + tempDivElement.id = tempDivId; + const tempiFrameElement = givenDocument.createElement('div'); // doesn't matter what the tag is in the test + tempiFrameElement.id = tempIframeId; + + it('removes an existing element with given id', () => { + rootHtml.appendChild(svgElement); + expect(givenDocument.getElementById(svgElement.id)).toEqual(svgElement); + removeExistingElements(givenDocument, false, svgId, tempDivId, tempIframeId); + expect(givenDocument.getElementById(svgElement.id)).toBeNull(); + }); + + describe('is in sandboxed mode', () => { + const inSandboxedMode = true; + + it('removes an existing element with the given iFrame selector', () => { + tempiFrameElement.append(svgElement); + rootHtml.append(tempiFrameElement); + rootHtml.append(tempDivElement); + + expect(givenDocument.getElementById(tempIframeId)).toEqual(tempiFrameElement); + expect(givenDocument.getElementById(tempDivId)).toEqual(tempDivElement); + expect(givenDocument.getElementById(svgId)).toEqual(svgElement); + removeExistingElements( + givenDocument, + inSandboxedMode, + svgId, + '#' + tempDivId, + '#' + tempIframeId + ); + expect(givenDocument.getElementById(tempDivId)).toEqual(tempDivElement); + expect(givenDocument.getElementById(tempIframeId)).toBeNull(); + expect(givenDocument.getElementById(svgId)).toBeNull(); + }); + }); + describe('not in sandboxed mode', () => { + const inSandboxedMode = false; + + it('removes an existing element with the given enclosing div selector', () => { + tempDivElement.append(svgElement); + rootHtml.append(tempDivElement); + rootHtml.append(tempiFrameElement); + + expect(givenDocument.getElementById(tempIframeId)).toEqual(tempiFrameElement); + expect(givenDocument.getElementById(tempDivId)).toEqual(tempDivElement); + expect(givenDocument.getElementById(svgId)).toEqual(svgElement); + removeExistingElements( + givenDocument, + inSandboxedMode, + svgId, + '#' + tempDivId, + '#' + tempIframeId + ); + expect(givenDocument.getElementById(tempIframeId)).toEqual(tempiFrameElement); + expect(givenDocument.getElementById(tempDivId)).toBeNull(); + expect(givenDocument.getElementById(svgId)).toBeNull(); + }); + }); + }); + describe('doing initialize ', function () { beforeEach(function () { document.body.innerHTML = ''; diff --git a/packages/mermaid/src/mermaidAPI.ts b/packages/mermaid/src/mermaidAPI.ts index 73597d1c8..54e1758fb 100644 --- a/packages/mermaid/src/mermaidAPI.ts +++ b/packages/mermaid/src/mermaidAPI.ts @@ -86,10 +86,10 @@ function parse(text: string, parseError?: ParseErrorFunction): boolean { export const encodeEntities = function (text: string): string { let txt = text; - txt = txt.replace(/style.*:\S*#.*;/g, function (s) { + txt = txt.replace(/style.*:\S*#.*;/g, function (s): string { return s.substring(0, s.length - 1); }); - txt = txt.replace(/classDef.*:\S*#.*;/g, function (s) { + txt = txt.replace(/classDef.*:\S*#.*;/g, function (s): string { return s.substring(0, s.length - 1); }); @@ -319,6 +319,31 @@ function sandboxedIframe(parentNode: D3Element, iFrameId: string): D3Element { .attr('sandbox', ''); } +/** + * Remove any existing elements from the given document + * + * @param {Document} doc - the document to removed elements from + * @param {string} isSandboxed - whether or not we are in sandboxed mode + * @param {string} id - id for any existing SVG element + * @param {string} divSelector - selector for any existing enclosing div element + * @param {string} iFrameSelector - selector for any existing iFrame element + */ +export const removeExistingElements = ( + doc: Document, + isSandboxed: boolean, + id: string, + divSelector: string, + iFrameSelector: string +) => { + // Remove existing SVG element if it exists + const existingSvg = doc.getElementById(id); + if (existingSvg) existingSvg.remove(); + + // Remove previous temporary element if it exists + const element = isSandboxed ? doc.querySelector(iFrameSelector) : doc.querySelector(divSelector); + if (element) element.remove(); +}; + /** * Function that renders an svg with a graph from a chart definition. Usage example below. * @@ -384,8 +409,8 @@ const render = async function ( // ------------------------------------------------------------------------------- // Define the root d3 node - // In regular execution the svgContainingElement will be the element with a mermaid class + if (typeof svgContainingElement !== 'undefined') { if (svgContainingElement) svgContainingElement.innerHTML = ''; @@ -400,19 +425,9 @@ const render = async function ( appendDivSvgG(root, id, enclosingDivID, `font-family: ${fontFamily}`, XMLNS_XLINK_STD); } else { // No svgContainingElement was provided - // If there is an existing element with the id, we remove it - // this likely a previously rendered diagram - const existingSvg = document.getElementById(id); - if (existingSvg) existingSvg.remove(); - // Remove previous temporary element if it exists - let element; - if (isSandboxed) { - element = document.querySelector(iFrameID_selector); - } else { - element = document.querySelector(enclosingDivID_selector); - } - if (element) element.remove(); + // If there is an existing element with the id, we remove it. This likely a previously rendered diagram + removeExistingElements(document, isSandboxed, id, iFrameID_selector, enclosingDivID_selector); // Add the temporary div used for rendering with the enclosingDivID. // This temporary div will contain a svg with the id == id @@ -420,7 +435,6 @@ const render = async function ( if (isSandboxed) { // If we are in sandboxed mode, we do everything mermaid related in a (sandboxed) iFrame const iframe = sandboxedIframe(select('body'), iFrameID); - root = select(iframe.nodes()[0]!.contentDocument!.body); root.node().style.margin = 0; } else root = select('body');