summaryrefslogtreecommitdiff
path: root/static/js/shadow.js
blob: 8439639e22dc76820845b1ce1aebad1054b8cf46 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/* ShadowRenderer.js - A tiny library to render safely encapsulated HTML */
const ShadowRenderer = {
    render: function (hostElement, htmlContent, options = {}) {
        // 1. Attach Shadow Root (if not exists)
        let shadow = hostElement.shadowRoot;
        if (!shadow) {
            shadow = hostElement.attachShadow({ mode: 'open' });
        }

        // 2. Parse HTML
        const parser = new DOMParser();
        const doc = parser.parseFromString(htmlContent, 'text/html');

        // 3. Rewrite Styles (Body -> Wrapper)
        const styles = doc.querySelectorAll('style');
        styles.forEach(style => {
            // Replace 'body' selector with '.mail-body-content'
            style.textContent = style.textContent.replace(/(^|[\}\s,;])body(?=[\s,\.\{])/gi, '$1.mail-body-content');
        });

        // 4. Create Wrapper
        const wrapper = document.createElement('div');
        wrapper.className = 'mail-body-content';

        // Copy attributes & move children
        if (doc.body) {
            Array.from(doc.body.attributes).forEach(attr => {
                if (attr.name === 'class') {
                    if (attr.value) wrapper.classList.add(...attr.value.split(' '));
                } else {
                    wrapper.setAttribute(attr.name, attr.value);
                }
            });
            // Handle legacy bgcolor if present
            if (doc.body.bgColor) wrapper.style.backgroundColor = doc.body.bgColor;

            while (doc.body.firstChild) wrapper.appendChild(doc.body.firstChild);
        }

        // 5. Build Shadow Content
        shadow.innerHTML = ''; // Clear previous

        // Append Head Content (Styles)
        if (doc.head) {
            while (doc.head.firstChild) shadow.appendChild(doc.head.firstChild);
        }

        // Append Body Wrapper
        shadow.appendChild(wrapper);

        // 6. Apply Default Styles
        const defaultStyle = document.createElement('style');
        defaultStyle.textContent = `
            :host { display: block; overflow: auto; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; }
            .mail-body-content { margin: 0; padding: 16px; min-height: 100%; }
            img { max-width: 100%; height: auto; }
            a { color: #1a73e8; }
        `;
        shadow.prepend(defaultStyle);

        return shadow;
    }
};