You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			189 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Django/Jinja
		
	
			
		
		
	
	
			189 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Django/Jinja
		
	
{%- macro mermaid_js(
 | 
						|
url="https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.7.0/mermaid.esm.min.mjs"
 | 
						|
) -%}
 | 
						|
<script type="module">
 | 
						|
  document.addEventListener("DOMContentLoaded", async () => {
 | 
						|
    const diagrams = document.querySelectorAll(".jp-Mermaid > pre.mermaid");
 | 
						|
    // do not load mermaidjs if not needed
 | 
						|
    if (!diagrams.length) {
 | 
						|
      return;
 | 
						|
    }
 | 
						|
    const mermaid = (await import("{{ url }}")).default;
 | 
						|
    const parser = new DOMParser();
 | 
						|
 | 
						|
    mermaid.initialize({
 | 
						|
      maxTextSize: 100000,
 | 
						|
      maxEdges: 100000,
 | 
						|
      startOnLoad: false,
 | 
						|
      fontFamily: window
 | 
						|
        .getComputedStyle(document.body)
 | 
						|
        .getPropertyValue("--jp-ui-font-family"),
 | 
						|
      theme: document.querySelector("body[data-jp-theme-light='true']")
 | 
						|
        ? "default"
 | 
						|
        : "dark",
 | 
						|
    });
 | 
						|
 | 
						|
    let _nextMermaidId = 0;
 | 
						|
 | 
						|
    function makeMermaidImage(svg) {
 | 
						|
      const img = document.createElement("img");
 | 
						|
      const doc = parser.parseFromString(svg, "image/svg+xml");
 | 
						|
      const svgEl = doc.querySelector("svg");
 | 
						|
      const { maxWidth } = svgEl?.style || {};
 | 
						|
      const firstTitle = doc.querySelector("title");
 | 
						|
      const firstDesc = doc.querySelector("desc");
 | 
						|
 | 
						|
      img.setAttribute("src", `data:image/svg+xml,${encodeURIComponent(svg)}`);
 | 
						|
      if (maxWidth) {
 | 
						|
        img.width = parseInt(maxWidth);
 | 
						|
      }
 | 
						|
      if (firstTitle) {
 | 
						|
        img.setAttribute("alt", firstTitle.textContent);
 | 
						|
      }
 | 
						|
      if (firstDesc) {
 | 
						|
        const caption = document.createElement("figcaption");
 | 
						|
        caption.className = "sr-only";
 | 
						|
        caption.textContent = firstDesc.textContent;
 | 
						|
        return [img, caption];
 | 
						|
      }
 | 
						|
      return [img];
 | 
						|
    }
 | 
						|
 | 
						|
    async function makeMermaidError(text) {
 | 
						|
      let errorMessage = "";
 | 
						|
      try {
 | 
						|
        await mermaid.parse(text);
 | 
						|
      } catch (err) {
 | 
						|
        errorMessage = `${err}`;
 | 
						|
      }
 | 
						|
 | 
						|
      const result = document.createElement("details");
 | 
						|
      result.className = 'jp-RenderedMermaid-Details';
 | 
						|
      const summary = document.createElement("summary");
 | 
						|
      summary.className = 'jp-RenderedMermaid-Summary';
 | 
						|
      const pre = document.createElement("pre");
 | 
						|
      const code = document.createElement("code");
 | 
						|
      code.innerText = text;
 | 
						|
      pre.appendChild(code);
 | 
						|
      summary.appendChild(pre);
 | 
						|
      result.appendChild(summary);
 | 
						|
 | 
						|
      const warning = document.createElement("pre");
 | 
						|
      warning.innerText = errorMessage;
 | 
						|
      result.appendChild(warning);
 | 
						|
      return [result];
 | 
						|
    }
 | 
						|
 | 
						|
    async function renderOneMarmaid(src) {
 | 
						|
      const id = `jp-mermaid-${_nextMermaidId++}`;
 | 
						|
      const parent = src.parentNode;
 | 
						|
      let raw = src.textContent.trim();
 | 
						|
      const el = document.createElement("div");
 | 
						|
      el.style.visibility = "hidden";
 | 
						|
      document.body.appendChild(el);
 | 
						|
      let results = null;
 | 
						|
      let output = null;
 | 
						|
      try {
 | 
						|
        let { svg } = await mermaid.render(id, raw, el);
 | 
						|
        svg = cleanMermaidSvg(svg);
 | 
						|
        results = makeMermaidImage(svg);
 | 
						|
        output = document.createElement("figure");
 | 
						|
        results.map(output.appendChild, output);
 | 
						|
      } catch (err) {
 | 
						|
        parent.classList.add("jp-mod-warning");
 | 
						|
        results = await makeMermaidError(raw);
 | 
						|
        output = results[0];
 | 
						|
      } finally {
 | 
						|
        el.remove();
 | 
						|
      }
 | 
						|
      parent.classList.add("jp-RenderedMermaid");
 | 
						|
      parent.appendChild(output);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * Post-process to ensure mermaid diagrams contain only valid SVG and XHTML.
 | 
						|
     */
 | 
						|
    function cleanMermaidSvg(svg) {
 | 
						|
      return svg.replace(RE_VOID_ELEMENT, replaceVoidElement);
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * A regular expression for all void elements, which may include attributes and
 | 
						|
     * a slash.
 | 
						|
     *
 | 
						|
     * @see https://developer.mozilla.org/en-US/docs/Glossary/Void_element
 | 
						|
     *
 | 
						|
     * Of these, only `<br>` is generated by Mermaid in place of `\n`,
 | 
						|
     * but _any_ "malformed" tag will break the SVG rendering entirely.
 | 
						|
     */
 | 
						|
    const RE_VOID_ELEMENT =
 | 
						|
      /<\s*(area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)\s*([^>]*?)\s*>/gi;
 | 
						|
 | 
						|
    /**
 | 
						|
     * Ensure a void element is closed with a slash, preserving any attributes.
 | 
						|
     */
 | 
						|
    function replaceVoidElement(match, tag, rest) {
 | 
						|
      rest = rest.trim();
 | 
						|
      if (!rest.endsWith('/')) {
 | 
						|
        rest = `${rest} /`;
 | 
						|
      }
 | 
						|
      return `<${tag} ${rest}>`;
 | 
						|
    }
 | 
						|
 | 
						|
    void Promise.all([...diagrams].map(renderOneMarmaid));
 | 
						|
  });
 | 
						|
</script>
 | 
						|
<style>
 | 
						|
  .jp-Mermaid:not(.jp-RenderedMermaid) {
 | 
						|
    display: none;
 | 
						|
  }
 | 
						|
 | 
						|
  .jp-RenderedMermaid {
 | 
						|
    overflow: auto;
 | 
						|
    display: flex;
 | 
						|
  }
 | 
						|
 | 
						|
  .jp-RenderedMermaid.jp-mod-warning {
 | 
						|
    width: auto;
 | 
						|
    padding: 0.5em;
 | 
						|
    margin-top: 0.5em;
 | 
						|
    border: var(--jp-border-width) solid var(--jp-warn-color2);
 | 
						|
    border-radius: var(--jp-border-radius);
 | 
						|
    color: var(--jp-ui-font-color1);
 | 
						|
    font-size: var(--jp-ui-font-size1);
 | 
						|
    white-space: pre-wrap;
 | 
						|
    word-wrap: break-word;
 | 
						|
  }
 | 
						|
 | 
						|
  .jp-RenderedMermaid figure {
 | 
						|
    margin: 0;
 | 
						|
    overflow: auto;
 | 
						|
    max-width: 100%;
 | 
						|
  }
 | 
						|
 | 
						|
  .jp-RenderedMermaid img {
 | 
						|
    max-width: 100%;
 | 
						|
  }
 | 
						|
 | 
						|
  .jp-RenderedMermaid-Details > pre {
 | 
						|
    margin-top: 1em;
 | 
						|
  }
 | 
						|
 | 
						|
  .jp-RenderedMermaid-Summary {
 | 
						|
    color: var(--jp-warn-color2);
 | 
						|
  }
 | 
						|
 | 
						|
  .jp-RenderedMermaid:not(.jp-mod-warning) pre {
 | 
						|
    display: none;
 | 
						|
  }
 | 
						|
 | 
						|
  .jp-RenderedMermaid-Summary > pre {
 | 
						|
    display: inline-block;
 | 
						|
    white-space: normal;
 | 
						|
  }
 | 
						|
</style>
 | 
						|
<!-- End of mermaid configuration -->
 | 
						|
{%- endmacro %}
 |