code.gitea.io/gitea@v1.21.7/web_src/js/markup/mermaid.js (about)

     1  import {isDarkTheme} from '../utils.js';
     2  import {makeCodeCopyButton} from './codecopy.js';
     3  import {displayError} from './common.js';
     4  
     5  const {mermaidMaxSourceCharacters} = window.config;
     6  
     7  // margin removal is for https://github.com/mermaid-js/mermaid/issues/4907
     8  const iframeCss = `:root {color-scheme: normal}
     9  body {margin: 0; padding: 0; overflow: hidden}
    10  #mermaid {display: block; margin: 0 auto}
    11  blockquote, dd, dl, figure, h1, h2, h3, h4, h5, h6, hr, p, pre {margin: 0}`;
    12  
    13  export async function renderMermaid() {
    14    const els = document.querySelectorAll('.markup code.language-mermaid');
    15    if (!els.length) return;
    16  
    17    const {default: mermaid} = await import(/* webpackChunkName: "mermaid" */'mermaid');
    18  
    19    mermaid.initialize({
    20      startOnLoad: false,
    21      theme: isDarkTheme() ? 'dark' : 'neutral',
    22      securityLevel: 'strict',
    23    });
    24  
    25    for (const el of els) {
    26      const pre = el.closest('pre');
    27      if (pre.hasAttribute('data-render-done')) continue;
    28  
    29      const source = el.textContent;
    30      if (mermaidMaxSourceCharacters >= 0 && source.length > mermaidMaxSourceCharacters) {
    31        displayError(pre, new Error(`Mermaid source of ${source.length} characters exceeds the maximum allowed length of ${mermaidMaxSourceCharacters}.`));
    32        continue;
    33      }
    34  
    35      try {
    36        await mermaid.parse(source);
    37      } catch (err) {
    38        displayError(pre, err);
    39        continue;
    40      }
    41  
    42      try {
    43        // can't use bindFunctions here because we can't cross the iframe boundary. This
    44        // means js-based interactions won't work but they aren't intended to work either
    45        const {svg} = await mermaid.render('mermaid', source);
    46  
    47        const iframe = document.createElement('iframe');
    48        iframe.classList.add('markup-render', 'gt-invisible');
    49        iframe.srcdoc = `<html><head><style>${iframeCss}</style></head><body>${svg}</body></html>`;
    50  
    51        const mermaidBlock = document.createElement('div');
    52        mermaidBlock.classList.add('mermaid-block', 'is-loading', 'gt-hidden');
    53        mermaidBlock.append(iframe);
    54  
    55        const btn = makeCodeCopyButton();
    56        btn.setAttribute('data-clipboard-text', source);
    57        mermaidBlock.append(btn);
    58  
    59        iframe.addEventListener('load', () => {
    60          pre.replaceWith(mermaidBlock);
    61          mermaidBlock.classList.remove('gt-hidden');
    62          iframe.style.height = `${iframe.contentWindow.document.body.clientHeight}px`;
    63          setTimeout(() => { // avoid flash of iframe background
    64            mermaidBlock.classList.remove('is-loading');
    65            iframe.classList.remove('gt-invisible');
    66          }, 0);
    67        });
    68  
    69        document.body.append(mermaidBlock);
    70      } catch (err) {
    71        displayError(pre, err);
    72      }
    73    }
    74  }